Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement shulker boxes #956

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
809039a
Implement shulker boxes (no behaviour)
mmm545 Dec 4, 2024
d93e10e
Forgot BreakInfo
mmm545 Dec 4, 2024
180fd74
Oops
mmm545 Dec 4, 2024
dc2f99b
Implement most of shulker box functionality
mmm545 Dec 5, 2024
58f7dbb
Oops
mmm545 Dec 5, 2024
5bba5e7
Ignore inspection
mmm545 Dec 5, 2024
d955b44
Implement custom names
mmm545 Dec 5, 2024
c42eb4d
Fixed items disappearing after breaking shulker box
mmm545 Dec 10, 2024
60313b9
Make shulker box unstackable
mmm545 Dec 11, 2024
bd0063d
Gotta learn how to type good code
mmm545 Dec 11, 2024
8e3c84e
Fixed inability to place colored shulker boxes
mmm545 Dec 11, 2024
6e2f173
Add docs
mmm545 Dec 11, 2024
fd50d99
Refactore allShulkerBox to allShulkerBoxes
mmm545 Dec 11, 2024
7655008
Fixed doc typo
mmm545 Dec 11, 2024
12091d7
Fixed the sound and closing action being out of sync
mmm545 Dec 11, 2024
1f7c0ba
Add missing doc for ScheduledTick
mmm545 Dec 11, 2024
3018fb6
Fixed doc typo for CustomName field
mmm545 Dec 13, 2024
6c6dd6b
Validate that the shulker box actaully exists
mmm545 Dec 22, 2024
828690d
Make shulker boxes transparent and water loggable
mmm545 Dec 22, 2024
da2d235
Merge branch 'feature/shulker-box' of https://github.com/mmm545/drago…
mmm545 Dec 22, 2024
cb436a9
Remove unnecessary withBlastResistance()
mmm545 Dec 22, 2024
5209327
Merge branch 'master' into feature/shulker-box
mmm545 Dec 22, 2024
87b507a
Update tx.ScheduleBlockUpdate()
mmm545 Dec 22, 2024
e2c8418
Add ShulkerBox model, fix conflict
Feb 27, 2025
152a97f
Merge branch 'df-mc:master' into feature/shulker-box
Superomarking Feb 27, 2025
4b2f6e7
Re-added sounds
Feb 27, 2025
e479142
Fix bounding box
Feb 27, 2025
689fa9b
Merge branch 'master' into feature/shulker-box
Superomarking Mar 4, 2025
09d3f5c
Merge branch 'df-mc:master' into feature/shulker-box
Superomarking Mar 16, 2025
27c31c7
fix doc
Mar 17, 2025
06f2d86
Merge remote-tracking branch 'origin/feature/shulker-box' into featur…
Mar 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add docs
mmm545 committed Dec 11, 2024

Verified

This commit was signed with the committer’s verified signature.
commit 6e2f1731d9526a5169f805ab9fa87d1a5489f896
27 changes: 24 additions & 3 deletions server/block/shulker_box.go
Original file line number Diff line number Diff line change
@@ -13,17 +13,23 @@ import (
"sync"
)

// ShulkerBox is a dye-able block that stores items. Unlike other blocks, it keeps its contents when broken.
type ShulkerBox struct {
solid // TODO: I don't think it should be solid
Type ShulkerBoxType
Facing cube.Face
solid // TODO: I don't think it should be solid
// Type is the type of shulker box of the block.
Type ShulkerBoxType
// Facing is the direction that the shulker box is facing.
Facing cube.Face
// CustomName is the custom name of the shulker box. This name is displayed when the chest is opened, and may
// include colour codes.
CustomName string

inventory *inventory.Inventory
viewerMu *sync.RWMutex
viewers map[ContainerViewer]struct{}
}

// NewShulkerBox creates a new initialised shulker box. The inventory is properly initialised.
func NewShulkerBox() ShulkerBox {
s := ShulkerBox{
viewerMu: new(sync.RWMutex),
@@ -41,11 +47,13 @@ func NewShulkerBox() ShulkerBox {
return s
}

// WithName returns the shulker box after applying a specific name to the block.
func (s ShulkerBox) WithName(a ...any) world.Item {
s.CustomName = strings.TrimSuffix(fmt.Sprintln(a...), "\n")
return s
}

// AddViewer adds a viewer to the shulker box, so that it is updated whenever the inventory of the shulker box is changed.
func (s ShulkerBox) AddViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) {
s.viewerMu.Lock()
defer s.viewerMu.Unlock()
@@ -56,6 +64,7 @@ func (s ShulkerBox) AddViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) {
s.viewers[v] = struct{}{}
}

// RemoveViewer removes a viewer from the shulker box, so that slot updates in the inventory are no longer sent to it.
func (s ShulkerBox) RemoveViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) {
s.viewerMu.Lock()
defer s.viewerMu.Unlock()
@@ -68,10 +77,12 @@ func (s ShulkerBox) RemoveViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos)
}
}

// Inventory returns the inventory of the shulker box.
func (s ShulkerBox) Inventory(tx *world.Tx, pos cube.Pos) *inventory.Inventory {
return s.inventory
}

// Activate ...
func (s ShulkerBox) Activate(pos cube.Pos, clickedFace cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool {
if opener, ok := u.(ContainerOpener); ok {
if d, ok := tx.Block(pos.Side(s.Facing)).(LightDiffuser); ok && d.LightDiffusionLevel() <= 2 {
@@ -83,6 +94,7 @@ func (s ShulkerBox) Activate(pos cube.Pos, clickedFace cube.Face, tx *world.Tx,
return false
}

// UseOnBlock ...
func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) (used bool) {
pos, _, used = firstReplaceable(tx, pos, face, s)
if !used {
@@ -101,36 +113,43 @@ func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3
return placed(ctx)
}

// open opens the shulker box, displaying animation and playing a sound.
func (s ShulkerBox) open(tx *world.Tx, pos cube.Pos) {
for _, v := range tx.Viewers(pos.Vec3()) {
v.ViewBlockAction(pos, OpenAction{})
}
tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxOpen{})
}

// close closes the shulker box, displaying animation and playing a sound.
func (s ShulkerBox) close(tx *world.Tx, pos cube.Pos) {
for _, v := range tx.Viewers(pos.Vec3()) {
v.ViewBlockAction(pos, CloseAction{})
}
tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxClose{}) //TODO: Make the sound delayed to sync with the closing action
}

// BreakInfo ...
func (s ShulkerBox) BreakInfo() BreakInfo {
return newBreakInfo(2, alwaysHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(10)
}

// MaxCount always returns 1.
func (s ShulkerBox) MaxCount() int {
return 1
}

// EncodeBlock ...
func (s ShulkerBox) EncodeBlock() (name string, properties map[string]any) {
return "minecraft:" + s.Type.String(), nil
}

// EncodeItem ...
func (s ShulkerBox) EncodeItem() (id string, meta int16) {
return "minecraft:" + s.Type.String(), 0
}

// DecodeNBT ...
func (s ShulkerBox) DecodeNBT(data map[string]any) any {
typ := s.Type
//noinspection GoAssignmentToReceiver
@@ -142,6 +161,7 @@ func (s ShulkerBox) DecodeNBT(data map[string]any) any {
return s
}

// EncodeNBT ..
func (s ShulkerBox) EncodeNBT() map[string]any {
if s.inventory == nil {
typ, facing, customName := s.Type, s.Facing, s.CustomName
@@ -162,6 +182,7 @@ func (s ShulkerBox) EncodeNBT() map[string]any {
return m
}

// allShulkerBox ...
func allShulkerBox() (shulkerboxes []world.Block) {
for _, t := range ShulkerBoxTypes() {
shulkerboxes = append(shulkerboxes, ShulkerBox{Type: t})
38 changes: 38 additions & 0 deletions server/block/shulker_box_types.go
Original file line number Diff line number Diff line change
@@ -1,67 +1,103 @@
package block

// ShulkerBoxType represents a type of shulker box.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we could avoid this. Candles & bundles will also have a no color option.

type ShulkerBoxType struct {
shulkerBox
}

type shulkerBox uint8

// NormalShulkerBox is the normal variant of the shulker box.
func NormalShulkerBox() ShulkerBoxType {
return ShulkerBoxType{0}
}

// WhiteShulkerBox is the white variant of the shulker box.
func WhiteShulkerBox() ShulkerBoxType {
return ShulkerBoxType{1}
}

// OrangeShulkerBox is the orange variant of the shulker box.
func OrangeShulkerBox() ShulkerBoxType {
return ShulkerBoxType{2}
}

// MagentaShulkerBox is the magenta variant of the shulker box.
func MagentaShulkerBox() ShulkerBoxType {
return ShulkerBoxType{3}
}

// LightBlueShulkerBox is the light blue variant of the shulker box.
func LightBlueShulkerBox() ShulkerBoxType {
return ShulkerBoxType{4}
}

// YellowShulkerBox is the yellow variant of the shulker box.
func YellowShulkerBox() ShulkerBoxType {
return ShulkerBoxType{5}
}

// LimeShulkerBox is the lime variant of the shulker box.
func LimeShulkerBox() ShulkerBoxType {
return ShulkerBoxType{6}
}

// PinkShulkerBox is the pink variant of the shulker box.
func PinkShulkerBox() ShulkerBoxType {
return ShulkerBoxType{7}
}

// GrayShulkerBox is the gray variant of the shulker box.
func GrayShulkerBox() ShulkerBoxType {
return ShulkerBoxType{8}
}

// LightGrayShulkerBox is the light gray variant of the shulker box.
func LightGrayShulkerBox() ShulkerBoxType {
return ShulkerBoxType{9}
}

// CyanShulkerBox is the cyan variant of the shulker box.
func CyanShulkerBox() ShulkerBoxType {
return ShulkerBoxType{10}
}

// PurpleShulkerBox is the purple variant of the shulker box.
func PurpleShulkerBox() ShulkerBoxType {
return ShulkerBoxType{11}
}

// BlueShulkerBox is the blue variant of the shulker box.
func BlueShulkerBox() ShulkerBoxType {
return ShulkerBoxType{12}
}

// BrownShulkerBox is the brown variant of the shulker box.
func BrownShulkerBox() ShulkerBoxType {
return ShulkerBoxType{13}
}

// GreenShulkerBox is the green variant of the shulker box.
func GreenShulkerBox() ShulkerBoxType {
return ShulkerBoxType{14}
}

// RedShulkerBox is the red variant of the shulker box.
func RedShulkerBox() ShulkerBoxType {
return ShulkerBoxType{15}
}

// BlackShulkerBox is the black variant of the shulker box.
func BlackShulkerBox() ShulkerBoxType {
return ShulkerBoxType{16}
}

// Uint8 returns the shulker box type as a uint8.
func (s shulkerBox) Uint8() uint8 {
return uint8(s)
}

// Name ...
func (s shulkerBox) Name() string {
switch s {
case 0:
@@ -103,6 +139,7 @@ func (s shulkerBox) Name() string {
panic("unknown shulker box type")
}

// String ...
func (s shulkerBox) String() string {
switch s {
case 0:
@@ -144,6 +181,7 @@ func (s shulkerBox) String() string {
panic("unkown shulker box type")
}

// ShulkerBoxTypes returns all shulker box types.
func ShulkerBoxTypes() []ShulkerBoxType {
return []ShulkerBoxType{
NormalShulkerBox(),
2 changes: 2 additions & 0 deletions server/world/sound/block.go
Original file line number Diff line number Diff line change
@@ -60,8 +60,10 @@ type BarrelClose struct{ sound }
// Deny is a sound played when a block is placed or broken above a 'Deny' block from Education edition.
type Deny struct{ sound }

// ShulkerBoxOpen is a sound played when a shulker box is opened.
type ShulkerBoxOpen struct{ sound }

// ShulkerBoxClose is a sound played when a shulker box is closed.
type ShulkerBoxClose struct{ sound }

// DoorOpen is a sound played when a door is opened.