From 809039a2108fc40ffdd61e84099a401fb392200c Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:59:41 +0300 Subject: [PATCH 01/25] Implement shulker boxes (no behaviour) --- cmd/blockhash/main.go | 3 +- server/block/hash.go | 5 + server/block/register.go | 5 + server/block/shulker_box.go | 29 ++++++ server/block/shulker_box_types.go | 167 ++++++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 server/block/shulker_box.go create mode 100644 server/block/shulker_box_types.go diff --git a/cmd/blockhash/main.go b/cmd/blockhash/main.go index 8244acfdf..9718ca27a 100644 --- a/cmd/blockhash/main.go +++ b/cmd/blockhash/main.go @@ -249,7 +249,8 @@ func (b *hashBuilder) ftype(structName, s string, expr ast.Expr, directives map[ case "CoralType", "SkullType": return "uint64(" + s + ".Uint8())", 3 case "AnvilType", "SandstoneType", "PrismarineType", "StoneBricksType", "NetherBricksType", "FroglightType", - "WallConnectionType", "BlackstoneType", "DeepslateType", "TallGrassType", "CopperType", "OxidationType": + "WallConnectionType", "BlackstoneType", "DeepslateType", "TallGrassType", "CopperType", "OxidationType", + "ShulkerBoxType": return "uint64(" + s + ".Uint8())", 2 case "OreType", "FireType", "DoubleTallGrassType": return "uint64(" + s + ".Uint8())", 1 diff --git a/server/block/hash.go b/server/block/hash.go index 098786c88..38ec3bd57 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -150,6 +150,7 @@ const ( hashSeaPickle hashShortGrass hashShroomlight + hashShulkerBox hashSign hashSkull hashSlab @@ -774,6 +775,10 @@ func (Shroomlight) Hash() (uint64, uint64) { return hashShroomlight, 0 } +func (s ShulkerBox) Hash() (uint64, uint64) { + return hashShulkerBox, uint64(s.Type.Uint8()) | uint64(s.Axis)<<2 +} + func (s Sign) Hash() (uint64, uint64) { return hashSign, uint64(s.Wood.Uint8()) | uint64(s.Attach.Uint8())<<4 } diff --git a/server/block/register.go b/server/block/register.go index 1ce675a6c..d2cb69713 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -203,6 +203,7 @@ func init() { registerAll(allCopperDoors()) registerAll(allCopperGrates()) registerAll(allCopperTrapdoors()) + registerAll(allShulkerBox()) } func init() { @@ -461,6 +462,10 @@ func init() { world.RegisterItem(Copper{Type: c, Oxidation: o, Waxed: true}) } } + + for _, t := range ShulkerBoxTypes() { + world.RegisterItem(ShulkerBox{Type: t}) + } } func registerAll(blocks []world.Block) { diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go new file mode 100644 index 000000000..d20cb3849 --- /dev/null +++ b/server/block/shulker_box.go @@ -0,0 +1,29 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" +) + +type ShulkerBox struct { + chest + + Type ShulkerBoxType + Axis cube.Direction +} + +func (s ShulkerBox) EncodeBlock() (name string, properties map[string]any) { + return "minecraft:" + s.Type.String(), nil // map[string]any{"facing": s.Axis.String()} +} + +func (s ShulkerBox) EncodeItem() (id string, meta int16) { + return "minecraft:" + s.Type.String(), 0 +} + +func allShulkerBox() (shulkerboxes []world.Block) { + for _, t := range ShulkerBoxTypes() { + shulkerboxes = append(shulkerboxes, ShulkerBox{Type: t}) + } + + return +} diff --git a/server/block/shulker_box_types.go b/server/block/shulker_box_types.go new file mode 100644 index 000000000..a26a2880b --- /dev/null +++ b/server/block/shulker_box_types.go @@ -0,0 +1,167 @@ +package block + +type ShulkerBoxType struct { + shulkerBox +} + +type shulkerBox uint8 + +func NormalShulkerBox() ShulkerBoxType { + return ShulkerBoxType{0} +} +func WhiteShulkerBox() ShulkerBoxType { + return ShulkerBoxType{1} +} +func OrangeShulkerBox() ShulkerBoxType { + return ShulkerBoxType{2} +} +func MagentaShulkerBox() ShulkerBoxType { + return ShulkerBoxType{3} +} +func LightBlueShulkerBox() ShulkerBoxType { + return ShulkerBoxType{4} +} +func YellowShulkerBox() ShulkerBoxType { + return ShulkerBoxType{5} +} +func LimeShulkerBox() ShulkerBoxType { + return ShulkerBoxType{6} +} +func PinkShulkerBox() ShulkerBoxType { + return ShulkerBoxType{7} +} +func GrayShulkerBox() ShulkerBoxType { + return ShulkerBoxType{8} +} +func LightGrayShulkerBox() ShulkerBoxType { + return ShulkerBoxType{9} +} +func CyanShulkerBox() ShulkerBoxType { + return ShulkerBoxType{10} +} +func PurpleShulkerBox() ShulkerBoxType { + return ShulkerBoxType{11} +} +func BlueShulkerBox() ShulkerBoxType { + return ShulkerBoxType{12} +} +func BrownShulkerBox() ShulkerBoxType { + return ShulkerBoxType{13} +} +func GreenShulkerBox() ShulkerBoxType { + return ShulkerBoxType{14} +} +func RedShulkerBox() ShulkerBoxType { + return ShulkerBoxType{15} +} +func BlackShulkerBox() ShulkerBoxType { + return ShulkerBoxType{16} +} + +func (s shulkerBox) Uint8() uint8 { + return uint8(s) +} + +func (s shulkerBox) Name() string { + switch s { + case 0: + return "Shulker Box" + case 1: + return "White Shulker Box" + case 2: + return "Orange Shulker Box" + case 3: + return "Magenta Shulker Box" + case 4: + return "Light Blue Shulker Box" + case 5: + return "Yellow Shulker Box" + case 6: + return "Lime Shulker Box" + case 7: + return "Pink Shulker Box" + case 8: + return "Gray Shulker Box" + case 9: + return "Light Gray Shulker Box" + case 10: + return "Cyan Shulker Box" + case 11: + return "Purple Shulker Box" + case 12: + return "Blue Shulker Box" + case 13: + return "Brown Shulker Box" + case 14: + return "Green Shulker Box" + case 15: + return "Red Shulker Box" + case 16: + return "Black Shulker Box" + } + + panic("unknown shulker box type") +} + +func (s shulkerBox) String() string { + switch s { + case 0: + return "undyed_shulker_box" + case 1: + return "white_shulker_box" + case 2: + return "orange_shulker_box" + case 3: + return "magenta_shulker_box" + case 4: + return "light_blue_shulker_box" + case 5: + return "yellow_shulker_box" + case 6: + return "lime_shulker_box" + case 7: + return "pink_shulker_box" + case 8: + return "gray_shulker_box" + case 9: + return "light_gray_shulker_box" + case 10: + return "cyan_shulker_box" + case 11: + return "purple_shulker_box" + case 12: + return "blue_shulker_box" + case 13: + return "brown_shulker_box" + case 14: + return "green_shulker_box" + case 15: + return "red_shulker_box" + case 16: + return "black_shulker_box" + } + + panic("unkown shulker box type") +} + +func ShulkerBoxTypes() []ShulkerBoxType { + return []ShulkerBoxType{ + NormalShulkerBox(), + WhiteShulkerBox(), + OrangeShulkerBox(), + MagentaShulkerBox(), + LightBlueShulkerBox(), + YellowShulkerBox(), + LimeShulkerBox(), + PinkShulkerBox(), + GrayShulkerBox(), + LightGrayShulkerBox(), + CyanShulkerBox(), + PurpleShulkerBox(), + BlueShulkerBox(), + BrownShulkerBox(), + GreenShulkerBox(), + RedShulkerBox(), + BlackShulkerBox(), + } +} From d93e10ec7929ecb2aa3cea06f7a0a62060894d48 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 4 Dec 2024 17:00:48 +0300 Subject: [PATCH 02/25] Forgot BreakInfo --- server/block/shulker_box.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index d20cb3849..49e3babec 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -12,6 +12,10 @@ type ShulkerBox struct { Axis cube.Direction } +func (s ShulkerBox) BreakInfo() BreakInfo { + return newBreakInfo(2, alwaysHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(10) +} + func (s ShulkerBox) EncodeBlock() (name string, properties map[string]any) { return "minecraft:" + s.Type.String(), nil // map[string]any{"facing": s.Axis.String()} } From 180fd7466ac5c3986df071e45b57a1e171e6b248 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 4 Dec 2024 17:02:52 +0300 Subject: [PATCH 03/25] Oops --- server/block/shulker_box.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 49e3babec..aa11bb496 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -17,7 +17,7 @@ func (s ShulkerBox) BreakInfo() BreakInfo { } func (s ShulkerBox) EncodeBlock() (name string, properties map[string]any) { - return "minecraft:" + s.Type.String(), nil // map[string]any{"facing": s.Axis.String()} + return "minecraft:" + s.Type.String(), nil } func (s ShulkerBox) EncodeItem() (id string, meta int16) { From dc2f99bfe9f02096cc53212c775646bb012a17b8 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:33:08 +0300 Subject: [PATCH 04/25] Implement most of shulker box functionality Implemented inventory Implemented sounds Implemented opening/closing animations --- server/block/hash.go | 2 +- server/block/shulker_box.go | 115 +++++++++++++++++++++++++++++++++++- server/session/player.go | 2 +- server/session/world.go | 4 ++ server/world/sound/block.go | 4 ++ 5 files changed, 122 insertions(+), 5 deletions(-) diff --git a/server/block/hash.go b/server/block/hash.go index 38ec3bd57..26e5b135d 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -776,7 +776,7 @@ func (Shroomlight) Hash() (uint64, uint64) { } func (s ShulkerBox) Hash() (uint64, uint64) { - return hashShulkerBox, uint64(s.Type.Uint8()) | uint64(s.Axis)<<2 + return hashShulkerBox, uint64(s.Type.Uint8()) } func (s Sign) Hash() (uint64, uint64) { diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index aa11bb496..af944cbcd 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -2,14 +2,103 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/internal/nbtconv" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/sound" + "github.com/go-gl/mathgl/mgl64" + "sync" ) type ShulkerBox struct { - chest + solid // TODO: I don't think it should be solid + Type ShulkerBoxType + Facing cube.Face + CustomName string // TODO: Implement custom names - Type ShulkerBoxType - Axis cube.Direction + inventory *inventory.Inventory + viewerMu *sync.RWMutex + viewers map[ContainerViewer]struct{} +} + +func NewShulkerBox() ShulkerBox { + s := ShulkerBox{ + viewerMu: new(sync.RWMutex), + viewers: make(map[ContainerViewer]struct{}, 1), + } + + s.inventory = inventory.New(27, func(slot int, _, after item.Stack) { + s.viewerMu.RLock() + defer s.viewerMu.RUnlock() + for viewer := range s.viewers { + viewer.ViewSlotChange(slot, after) + } + }) + + return s +} + +func (s ShulkerBox) AddViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) { + s.viewerMu.Lock() + defer s.viewerMu.Unlock() + if len(s.viewers) == 0 { + s.open(tx, pos) + } + + s.viewers[v] = struct{}{} +} + +func (s ShulkerBox) RemoveViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) { + s.viewerMu.Lock() + defer s.viewerMu.Unlock() + if len(s.viewers) == 0 { + return + } + delete(s.viewers, v) + if len(s.viewers) == 0 { + s.close(tx, pos) + } +} + +func (s ShulkerBox) Inventory(tx *world.Tx, pos cube.Pos) *inventory.Inventory { + return s.inventory +} + +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 { + opener.OpenBlockContainer(pos, tx) + } + return true + } + + return false +} + +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 { + return + } + s = NewShulkerBox() + s.Facing = face + place(tx, pos, s, user, ctx) + return placed(ctx) +} + +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{}) +} + +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 } func (s ShulkerBox) BreakInfo() BreakInfo { @@ -24,6 +113,26 @@ func (s ShulkerBox) EncodeItem() (id string, meta int16) { return "minecraft:" + s.Type.String(), 0 } +func (s ShulkerBox) DecodeNBT(data map[string]any) any { + s = NewShulkerBox() + nbtconv.InvFromNBT(s.inventory, nbtconv.Slice(data, "Items")) + s.Facing = cube.Face(nbtconv.Uint8(data, "facing")) + return s +} + +func (s ShulkerBox) EncodeNBT() map[string]any { + if s.inventory == nil { + s = NewShulkerBox() + } + + m := map[string]any{ + "Items": nbtconv.InvToNBT(s.inventory), + "id": "ShulkerBox", + "facing": uint8(s.Facing), + } + return m +} + func allShulkerBox() (shulkerboxes []world.Block) { for _, t := range ShulkerBoxTypes() { shulkerboxes = append(shulkerboxes, ShulkerBox{Type: t}) diff --git a/server/session/player.go b/server/session/player.go index 9bd4cf2ca..63ce7f325 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -254,7 +254,7 @@ func (s *Session) invByID(id int32, tx *world.Tx) (*inventory.Inventory, bool) { return nil, false } switch id { - case protocol.ContainerLevelEntity: + case protocol.ContainerLevelEntity, protocol.ContainerShulkerBox: return s.openedWindow.Load(), true case protocol.ContainerBarrel: if _, barrel := tx.Block(*s.openedPos.Load()).(block.Barrel); barrel { diff --git a/server/session/world.go b/server/session/world.go index e4628bb13..0d7e50e61 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -788,6 +788,10 @@ func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool) EventType: packet.LevelEventSoundTotemUsed, Position: vec64To32(pos), }) + case sound.ShulkerBoxClose: + pk.SoundType = packet.SoundEventShulkerBoxClosed + case sound.ShulkerBoxOpen: + pk.SoundType = packet.SoundEventShulkerBoxOpen } s.writePacket(pk) } diff --git a/server/world/sound/block.go b/server/world/sound/block.go index d9a35c73d..d13d990ae 100644 --- a/server/world/sound/block.go +++ b/server/world/sound/block.go @@ -60,6 +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 } +type ShulkerBoxOpen struct{ sound } + +type ShulkerBoxClose struct{ sound } + // DoorOpen is a sound played when a door is opened. type DoorOpen struct { // Block is the block which is being opened, for which a sound should be played. The sound played depends on the From 58f7dbb3e10784a8585a908cd1a5fc7db9c7eedf Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:40:20 +0300 Subject: [PATCH 05/25] Oops --- server/block/shulker_box.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index af944cbcd..57db0cfc3 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -122,7 +122,9 @@ func (s ShulkerBox) DecodeNBT(data map[string]any) any { func (s ShulkerBox) EncodeNBT() map[string]any { if s.inventory == nil { + facing := s.Facing s = NewShulkerBox() + s.Facing = facing } m := map[string]any{ From 5bba5e780358147fc10ec2142b3e9fc06fe81283 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:42:52 +0300 Subject: [PATCH 06/25] Ignore inspection --- server/block/shulker_box.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 57db0cfc3..4b05db968 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -81,6 +81,7 @@ func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3 if !used { return } + //noinspection GoAssignmentToReceiver s = NewShulkerBox() s.Facing = face place(tx, pos, s, user, ctx) @@ -114,6 +115,7 @@ func (s ShulkerBox) EncodeItem() (id string, meta int16) { } func (s ShulkerBox) DecodeNBT(data map[string]any) any { + //noinspection GoAssignmentToReceiver s = NewShulkerBox() nbtconv.InvFromNBT(s.inventory, nbtconv.Slice(data, "Items")) s.Facing = cube.Face(nbtconv.Uint8(data, "facing")) @@ -123,6 +125,7 @@ func (s ShulkerBox) DecodeNBT(data map[string]any) any { func (s ShulkerBox) EncodeNBT() map[string]any { if s.inventory == nil { facing := s.Facing + //noinspection GoAssignmentToReceiver s = NewShulkerBox() s.Facing = facing } From d955b44bb320c2950ca944a59d9a455061f16fdc Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:58:13 +0300 Subject: [PATCH 07/25] Implement custom names --- server/block/shulker_box.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 4b05db968..f97eaaa53 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -1,6 +1,7 @@ package block import ( + "fmt" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item" @@ -8,6 +9,7 @@ import ( "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" + "strings" "sync" ) @@ -15,7 +17,7 @@ type ShulkerBox struct { solid // TODO: I don't think it should be solid Type ShulkerBoxType Facing cube.Face - CustomName string // TODO: Implement custom names + CustomName string inventory *inventory.Inventory viewerMu *sync.RWMutex @@ -39,6 +41,11 @@ func NewShulkerBox() ShulkerBox { return s } +func (s ShulkerBox) WithName(a ...any) world.Item { + s.CustomName = strings.TrimSuffix(fmt.Sprintln(a...), "\n") + return s +} + func (s ShulkerBox) AddViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) { s.viewerMu.Lock() defer s.viewerMu.Unlock() @@ -119,15 +126,16 @@ func (s ShulkerBox) DecodeNBT(data map[string]any) any { s = NewShulkerBox() nbtconv.InvFromNBT(s.inventory, nbtconv.Slice(data, "Items")) s.Facing = cube.Face(nbtconv.Uint8(data, "facing")) + s.CustomName = nbtconv.String(data, "CustomName") return s } func (s ShulkerBox) EncodeNBT() map[string]any { if s.inventory == nil { - facing := s.Facing + facing, customName := s.Facing, s.CustomName //noinspection GoAssignmentToReceiver s = NewShulkerBox() - s.Facing = facing + s.Facing, s.CustomName = facing, customName } m := map[string]any{ @@ -135,6 +143,10 @@ func (s ShulkerBox) EncodeNBT() map[string]any { "id": "ShulkerBox", "facing": uint8(s.Facing), } + + if s.CustomName != "" { + m["CustomName"] = s.CustomName + } return m } From c42eb4d409920fcd112eda9a5d41bce5aa9637e7 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:15:19 +0300 Subject: [PATCH 08/25] Fixed items disappearing after breaking shulker box Hope it doesn't break anything else --- server/block/shulker_box.go | 8 ++++++++ server/internal/nbtconv/item.go | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index f97eaaa53..0f4f5df1a 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -88,8 +88,16 @@ func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3 if !used { return } + + inv := s.inventory + customName := s.CustomName //noinspection GoAssignmentToReceiver s = NewShulkerBox() + if inv != nil { + s.inventory = inv + } + s.CustomName = customName + s.Facing = face place(tx, pos, s, user, ctx) return placed(ctx) diff --git a/server/internal/nbtconv/item.go b/server/internal/nbtconv/item.go index 1443ddc16..2a3886187 100644 --- a/server/internal/nbtconv/item.go +++ b/server/internal/nbtconv/item.go @@ -17,8 +17,8 @@ func InvFromNBT(inv *inventory.Inventory, items []any) { } // InvToNBT encodes an inventory to a data slice which may be encoded as NBT. -func InvToNBT(inv *inventory.Inventory) []map[string]any { - var items []map[string]any +func InvToNBT(inv *inventory.Inventory) []any { + var items []any for index, i := range inv.Slots() { if i.Empty() { continue From 60313b900c45a5b023787d49c2d49e9b368955b7 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:34:45 +0300 Subject: [PATCH 09/25] Make shulker box unstackable --- server/block/shulker_box.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 0f4f5df1a..aef3a7303 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -121,6 +121,10 @@ func (s ShulkerBox) BreakInfo() BreakInfo { return newBreakInfo(2, alwaysHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(10) } +func (s ShulkerBox) MaxCount() int { + return 1 +} + func (s ShulkerBox) EncodeBlock() (name string, properties map[string]any) { return "minecraft:" + s.Type.String(), nil } From bd0063d137854e0c14eba44b0f823dd8de7d4d5d Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:42:02 +0300 Subject: [PATCH 10/25] Gotta learn how to type good code --- server/block/shulker_box.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index aef3a7303..33c8ec01c 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -89,14 +89,12 @@ func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3 return } - inv := s.inventory - customName := s.CustomName - //noinspection GoAssignmentToReceiver - s = NewShulkerBox() - if inv != nil { - s.inventory = inv + if s.inventory == nil { + customName := s.CustomName + //noinspection GoAssignmentToReceiver + s = NewShulkerBox() + s.CustomName = customName } - s.CustomName = customName s.Facing = face place(tx, pos, s, user, ctx) From 8e3c84e44dc08595cbc4601d6cd558bdf8fef568 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:47:50 +0300 Subject: [PATCH 11/25] Fixed inability to place colored shulker boxes --- server/block/shulker_box.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 33c8ec01c..ff0586151 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -90,10 +90,10 @@ func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3 } if s.inventory == nil { - customName := s.CustomName + typ, customName := s.Type, s.CustomName //noinspection GoAssignmentToReceiver s = NewShulkerBox() - s.CustomName = customName + s.Type, s.CustomName = typ, customName } s.Facing = face @@ -132,8 +132,10 @@ func (s ShulkerBox) EncodeItem() (id string, meta int16) { } func (s ShulkerBox) DecodeNBT(data map[string]any) any { + typ := s.Type //noinspection GoAssignmentToReceiver s = NewShulkerBox() + s.Type = typ nbtconv.InvFromNBT(s.inventory, nbtconv.Slice(data, "Items")) s.Facing = cube.Face(nbtconv.Uint8(data, "facing")) s.CustomName = nbtconv.String(data, "CustomName") @@ -142,10 +144,10 @@ func (s ShulkerBox) DecodeNBT(data map[string]any) any { func (s ShulkerBox) EncodeNBT() map[string]any { if s.inventory == nil { - facing, customName := s.Facing, s.CustomName + typ, facing, customName := s.Type, s.Facing, s.CustomName //noinspection GoAssignmentToReceiver s = NewShulkerBox() - s.Facing, s.CustomName = facing, customName + s.Type, s.Facing, s.CustomName = typ, facing, customName } m := map[string]any{ From 6e2f1731d9526a5169f805ab9fa87d1a5489f896 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:24:46 +0300 Subject: [PATCH 12/25] Add docs --- server/block/shulker_box.go | 27 +++++++++++++++++++--- server/block/shulker_box_types.go | 38 +++++++++++++++++++++++++++++++ server/world/sound/block.go | 2 ++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index ff0586151..55829ba68 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -13,10 +13,15 @@ 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 @@ -24,6 +29,7 @@ type ShulkerBox struct { 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,6 +113,7 @@ 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{}) @@ -108,6 +121,7 @@ func (s ShulkerBox) open(tx *world.Tx, pos cube.Pos) { 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{}) @@ -115,22 +129,27 @@ func (s ShulkerBox) close(tx *world.Tx, pos cube.Pos) { 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}) diff --git a/server/block/shulker_box_types.go b/server/block/shulker_box_types.go index a26a2880b..4a5c90639 100644 --- a/server/block/shulker_box_types.go +++ b/server/block/shulker_box_types.go @@ -1,67 +1,103 @@ package block +// ShulkerBoxType represents a type of shulker box. 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(), diff --git a/server/world/sound/block.go b/server/world/sound/block.go index d13d990ae..38d4f9f6a 100644 --- a/server/world/sound/block.go +++ b/server/world/sound/block.go @@ -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. From fd50d9922e9ac9729540007c56c6cc5f7e77c28c Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:27:08 +0300 Subject: [PATCH 13/25] Refactore allShulkerBox to allShulkerBoxes --- server/block/register.go | 2 +- server/block/shulker_box.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/block/register.go b/server/block/register.go index d2cb69713..dc6ed8d5e 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -203,7 +203,7 @@ func init() { registerAll(allCopperDoors()) registerAll(allCopperGrates()) registerAll(allCopperTrapdoors()) - registerAll(allShulkerBox()) + registerAll(allShulkerBoxes()) } func init() { diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 55829ba68..5fc296ea9 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -182,8 +182,8 @@ func (s ShulkerBox) EncodeNBT() map[string]any { return m } -// allShulkerBox ... -func allShulkerBox() (shulkerboxes []world.Block) { +// allShulkerBoxes ... +func allShulkerBoxes() (shulkerboxes []world.Block) { for _, t := range ShulkerBoxTypes() { shulkerboxes = append(shulkerboxes, ShulkerBox{Type: t}) } From 765500881cf8c5ae9f53edcab047d07996c5ada9 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:57:59 +0300 Subject: [PATCH 14/25] Fixed doc typo --- server/block/shulker_box.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 5fc296ea9..734f636e1 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -113,7 +113,7 @@ 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. +// open opens the shulker box, displaying the 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{}) @@ -121,7 +121,7 @@ func (s ShulkerBox) open(tx *world.Tx, pos cube.Pos) { tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxOpen{}) } -// close closes the shulker box, displaying animation and playing a sound. +// close closes the shulker box, displaying the 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{}) From 12091d706424a2a182bf11e0b33666906e8ab2ac Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:36:12 +0300 Subject: [PATCH 15/25] Fixed the sound and closing action being out of sync --- server/block/shulker_box.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 734f636e1..f0340d1b2 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -9,8 +9,10 @@ import ( "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" + "math/rand" "strings" "sync" + "time" ) // ShulkerBox is a dye-able block that stores items. Unlike other blocks, it keeps its contents when broken. @@ -126,7 +128,13 @@ 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 + tx.ScheduleBlockUpdate(pos, time.Millisecond*50*9) +} + +func (s ShulkerBox) ScheduledTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) { + if len(s.viewers) == 0 { + tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxClose{}) + } } // BreakInfo ... From 1f7c0bad4a7c633b7f0704918ee9cfd336258251 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:44:54 +0300 Subject: [PATCH 16/25] Add missing doc for ScheduledTick --- server/block/shulker_box.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index f0340d1b2..aa17fb2f8 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -131,6 +131,7 @@ func (s ShulkerBox) close(tx *world.Tx, pos cube.Pos) { tx.ScheduleBlockUpdate(pos, time.Millisecond*50*9) } +// ScheduledTick ... func (s ShulkerBox) ScheduledTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) { if len(s.viewers) == 0 { tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxClose{}) From 3018fb6fa689d7597a5c63f3888ba9ffb2225e87 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:00:36 +0300 Subject: [PATCH 17/25] Fixed doc typo for CustomName field --- server/block/shulker_box.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index aa17fb2f8..7f34d4da8 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -22,7 +22,7 @@ type ShulkerBox struct { 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 + // CustomName is the custom name of the shulker box. This name is displayed when the shulker box is opened, and may // include colour codes. CustomName string From 6c6dd6b2779805aa944510933ba3a743cef2c982 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:05:38 +0300 Subject: [PATCH 18/25] Validate that the shulker box actaully exists --- server/session/player.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/session/player.go b/server/session/player.go index 63ce7f325..e4ff17963 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -254,8 +254,12 @@ func (s *Session) invByID(id int32, tx *world.Tx) (*inventory.Inventory, bool) { return nil, false } switch id { - case protocol.ContainerLevelEntity, protocol.ContainerShulkerBox: + case protocol.ContainerLevelEntity: return s.openedWindow.Load(), true + case protocol.ContainerShulkerBox: + if _, shulkerbox := tx.Block(*s.openedPos.Load()).(block.ShulkerBox); shulkerbox { + return s.openedWindow.Load(), true + } case protocol.ContainerBarrel: if _, barrel := tx.Block(*s.openedPos.Load()).(block.Barrel); barrel { return s.openedWindow.Load(), true From 828690d1267a053262c90c3f7859b6d1881aab68 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:13:26 +0300 Subject: [PATCH 19/25] Make shulker boxes transparent and water loggable --- server/block/shulker_box.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index aa17fb2f8..7c8bf3383 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -18,6 +18,8 @@ import ( // 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 + transparent + sourceWaterDisplacer // Type is the type of shulker box of the block. Type ShulkerBoxType // Facing is the direction that the shulker box is facing. From cb436a9bc452da839be8dbb7ca103f98b6a6f417 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:25:29 +0300 Subject: [PATCH 20/25] Remove unnecessary withBlastResistance() Co-authored-by: DaPigGuy --- server/block/shulker_box.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 7588f3ef3..5bca8884c 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -142,7 +142,7 @@ func (s ShulkerBox) ScheduledTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) { // BreakInfo ... func (s ShulkerBox) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(10) + return newBreakInfo(2, alwaysHarvestable, pickaxeEffective, oneOf(s)) } // MaxCount always returns 1. From 87b507aa0add40cdeb1589191e9c37b6892932ea Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:55:06 +0300 Subject: [PATCH 21/25] Update tx.ScheduleBlockUpdate() Reference: 9cb575ed1955f4093762edd05463a97b2f0e6549 --- server/block/shulker_box.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index 5bca8884c..a44d98194 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -130,7 +130,7 @@ func (s ShulkerBox) close(tx *world.Tx, pos cube.Pos) { for _, v := range tx.Viewers(pos.Vec3()) { v.ViewBlockAction(pos, CloseAction{}) } - tx.ScheduleBlockUpdate(pos, time.Millisecond*50*9) + tx.ScheduleBlockUpdate(pos, s, time.Millisecond*50*9) } // ScheduledTick ... From e2c84188c2ae64977d0336b4bd7de4c7f9078308 Mon Sep 17 00:00:00 2001 From: Superomarking Date: Thu, 27 Feb 2025 20:05:58 +0300 Subject: [PATCH 22/25] Add ShulkerBox model, fix conflict --- server/block/model/shulker.go | 36 ++++++++++++++++++ server/block/shulker_box.go | 70 ++++++++++++++++++++++++++++------- server/session/world.go | 4 -- 3 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 server/block/model/shulker.go diff --git a/server/block/model/shulker.go b/server/block/model/shulker.go new file mode 100644 index 000000000..1a1fe2c4a --- /dev/null +++ b/server/block/model/shulker.go @@ -0,0 +1,36 @@ +package model + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" +) + +// Shulker is the model of a shulker. It depends on the opening/closing progress of the shulker. +type Shulker struct { + // Facing is the face that the shulker faces. + Facing cube.Face + // Progress is the opening/closing progress of the shulker. It is a float between 0 and 1. + Progress int32 +} + +// BBox returns a BBox that depends on the opening/closing progress of the shulker. +func (s Shulker) BBox(cube.Pos, world.BlockSource) []cube.BBox { + peak := physicalPeak(s.Progress) + + bbox := full + bbox.ExtendTowards(s.Facing, peak) + + return []cube.BBox{bbox} +} + +// physicalPeak returns the peak of which the shulker reaches in its current progress +func physicalPeak(progress int32) float64 { + fp := float64(progress) / 10.0 + openness := 1.0 - fp + return (1.0 - openness*openness*openness) * 0.5 +} + +// FaceSolid always returns false. +func (Shulker) FaceSolid(cube.Pos, cube.Face, world.BlockSource) bool { + return false +} diff --git a/server/block/shulker_box.go b/server/block/shulker_box.go index a44d98194..2df272de9 100644 --- a/server/block/shulker_box.go +++ b/server/block/shulker_box.go @@ -3,21 +3,28 @@ package block import ( "fmt" "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/model" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" - "math/rand" + "math/rand/v2" "strings" "sync" - "time" + "sync/atomic" +) + +const ( + StateClosed = iota + StateOpening + StateOpened + StateClosing ) // 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 transparent sourceWaterDisplacer // Type is the type of shulker box of the block. @@ -31,26 +38,40 @@ type ShulkerBox struct { inventory *inventory.Inventory viewerMu *sync.RWMutex viewers map[ContainerViewer]struct{} + // progress is the openness of the shulker box opening or closing. It is a float between 0 and 1. + progress *atomic.Int32 + // animationStatus is the current openness state of the shulker box (whether its opened, closing, etc.). + animationStatus *atomic.Int32 } // NewShulkerBox creates a new initialised shulker box. The inventory is properly initialised. func NewShulkerBox() ShulkerBox { s := ShulkerBox{ - viewerMu: new(sync.RWMutex), - viewers: make(map[ContainerViewer]struct{}, 1), + viewerMu: new(sync.RWMutex), + viewers: make(map[ContainerViewer]struct{}, 1), + progress: new(atomic.Int32), + animationStatus: new(atomic.Int32), } s.inventory = inventory.New(27, func(slot int, _, after item.Stack) { s.viewerMu.RLock() defer s.viewerMu.RUnlock() for viewer := range s.viewers { - viewer.ViewSlotChange(slot, after) + // A shulker box inventory can't store shulker boxes, this is mostly handled by the client. + if _, ok := after.Item().(ShulkerBox); !ok { + viewer.ViewSlotChange(slot, after) + } } }) return s } +// Model ... +func (s ShulkerBox) Model() world.BlockModel { + return model.Shulker{Facing: s.Facing, Progress: s.progress.Load()} +} + // 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") @@ -82,12 +103,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 { +func (s ShulkerBox) Inventory(*world.Tx, 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 { +func (s ShulkerBox) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, _ *item.UseContext) bool { if opener, ok := u.(ContainerOpener); ok { if d, ok := tx.Block(pos.Side(s.Facing)).(LightDiffuser); ok && d.LightDiffusionLevel() <= 2 { opener.OpenBlockContainer(pos, tx) @@ -99,7 +120,7 @@ func (s ShulkerBox) Activate(pos cube.Pos, clickedFace cube.Face, tx *world.Tx, } // 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) { +func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) (used bool) { pos, _, used = firstReplaceable(tx, pos, face, s) if !used { return @@ -122,7 +143,9 @@ func (s ShulkerBox) open(tx *world.Tx, pos cube.Pos) { for _, v := range tx.Viewers(pos.Vec3()) { v.ViewBlockAction(pos, OpenAction{}) } + s.animationStatus.Store(StateOpening) tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxOpen{}) + tx.ScheduleBlockUpdate(pos, s, 0) } // close closes the shulker box, displaying the animation and playing a sound. @@ -130,13 +153,32 @@ func (s ShulkerBox) close(tx *world.Tx, pos cube.Pos) { for _, v := range tx.Viewers(pos.Vec3()) { v.ViewBlockAction(pos, CloseAction{}) } - tx.ScheduleBlockUpdate(pos, s, time.Millisecond*50*9) + s.animationStatus.Store(StateClosing) + tx.ScheduleBlockUpdate(pos, s, 0) } // ScheduledTick ... -func (s ShulkerBox) ScheduledTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) { - if len(s.viewers) == 0 { - tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxClose{}) +func (s ShulkerBox) ScheduledTick(pos cube.Pos, tx *world.Tx, _ *rand.Rand) { + switch s.animationStatus.Load() { + case StateClosed: + s.progress.Store(0) + case StateOpening: + s.progress.Add(1) + if s.progress.Load() >= 10 { + s.progress.Store(10) + s.animationStatus.Store(StateOpened) + } + tx.ScheduleBlockUpdate(pos, s, 0) + case StateOpened: + s.progress.Store(10) + case StateClosing: + s.progress.Add(-1) + if s.progress.Load() <= 0 { + tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxClose{}) + s.progress.Store(0) + s.animationStatus.Store(StateClosed) + } + tx.ScheduleBlockUpdate(pos, s, 0) } } @@ -193,7 +235,7 @@ func (s ShulkerBox) EncodeNBT() map[string]any { return m } -// allShulkerBoxes ... +// allShulkerBoxes ...e func allShulkerBoxes() (shulkerboxes []world.Block) { for _, t := range ShulkerBoxTypes() { shulkerboxes = append(shulkerboxes, ShulkerBox{Type: t}) diff --git a/server/session/world.go b/server/session/world.go index e5bc00c89..c56502561 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -788,10 +788,6 @@ func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool) EventType: packet.LevelEventSoundTotemUsed, Position: vec64To32(pos), }) - case sound.ShulkerBoxClose: - pk.SoundType = packet.SoundEventShulkerBoxClosed - case sound.ShulkerBoxOpen: - pk.SoundType = packet.SoundEventShulkerBoxOpen } s.writePacket(pk) } From 4b2f6e7d5643ca2c647768433b8b797931f7382d Mon Sep 17 00:00:00 2001 From: Superomarking Date: Thu, 27 Feb 2025 20:07:57 +0300 Subject: [PATCH 23/25] Re-added sounds --- server/session/world.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/session/world.go b/server/session/world.go index 5183ce3dc..eb4919c21 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -815,6 +815,11 @@ func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool) EventType: packet.LevelEventSoundTotemUsed, Position: vec64To32(pos), }) + return + case sound.ShulkerBoxClose: + pk.SoundType = packet.SoundEventShulkerBoxClosed + case sound.ShulkerBoxOpen: + pk.SoundType = packet.SoundEventShulkerBoxOpen case sound.DecoratedPotInserted: s.writePacket(&packet.PlaySound{ SoundName: "block.decorated_pot.insert", From e4791420cab58283474f4167083d631f7a769a63 Mon Sep 17 00:00:00 2001 From: Superomarking Date: Thu, 27 Feb 2025 20:46:45 +0300 Subject: [PATCH 24/25] Fix bounding box --- server/block/model/shulker.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/block/model/shulker.go b/server/block/model/shulker.go index 1a1fe2c4a..f682a6f97 100644 --- a/server/block/model/shulker.go +++ b/server/block/model/shulker.go @@ -16,9 +16,9 @@ type Shulker struct { // BBox returns a BBox that depends on the opening/closing progress of the shulker. func (s Shulker) BBox(cube.Pos, world.BlockSource) []cube.BBox { peak := physicalPeak(s.Progress) - + // Adds peak to the top and subtracts peak from the bottom. (according to BDS) bbox := full - bbox.ExtendTowards(s.Facing, peak) + bbox.ExtendTowards(s.Facing, peak).ExtendTowards(s.Facing.Opposite(), -peak) return []cube.BBox{bbox} } From 27c31c7cd744c0ba4956b4a3b88cce2d86bf2739 Mon Sep 17 00:00:00 2001 From: Superomarking Date: Mon, 17 Mar 2025 21:52:01 +0300 Subject: [PATCH 25/25] fix doc --- server/block/model/shulker.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/block/model/shulker.go b/server/block/model/shulker.go index f682a6f97..2cedcfda1 100644 --- a/server/block/model/shulker.go +++ b/server/block/model/shulker.go @@ -5,11 +5,11 @@ import ( "github.com/df-mc/dragonfly/server/world" ) -// Shulker is the model of a shulker. It depends on the opening/closing progress of the shulker. +// Shulker is the model of a shulker. It depends on the opening/closing progress of the shulker block. type Shulker struct { // Facing is the face that the shulker faces. Facing cube.Face - // Progress is the opening/closing progress of the shulker. It is a float between 0 and 1. + // Progress is the opening/closing progress of the shulker. Progress int32 }