github.com/df-mc/dragonfly@v0.9.13/server/block/barrel.go (about) 1 package block 2 3 import ( 4 "fmt" 5 "github.com/df-mc/dragonfly/server/block/cube" 6 "github.com/df-mc/dragonfly/server/internal/nbtconv" 7 "github.com/df-mc/dragonfly/server/item" 8 "github.com/df-mc/dragonfly/server/item/inventory" 9 "github.com/df-mc/dragonfly/server/world" 10 "github.com/df-mc/dragonfly/server/world/sound" 11 "github.com/go-gl/mathgl/mgl64" 12 "strings" 13 "sync" 14 "time" 15 ) 16 17 // Barrel is a fisherman's job site block, used to store items. It functions like a single chest, although 18 // it requires no airspace above it to be opened. 19 type Barrel struct { 20 solid 21 bass 22 23 // Facing is the direction that the barrel is facing. 24 Facing cube.Face 25 // Open is whether the barrel is open or not. 26 Open bool 27 // CustomName is the custom name of the barrel. This name is displayed when the barrel is opened, and may 28 // include colour codes. 29 CustomName string 30 31 inventory *inventory.Inventory 32 viewerMu *sync.RWMutex 33 viewers map[ContainerViewer]struct{} 34 } 35 36 // NewBarrel creates a new initialised barrel. The inventory is properly initialised. 37 func NewBarrel() Barrel { 38 m := new(sync.RWMutex) 39 v := make(map[ContainerViewer]struct{}, 1) 40 return Barrel{ 41 inventory: inventory.New(27, func(slot int, _, item item.Stack) { 42 m.RLock() 43 defer m.RUnlock() 44 for viewer := range v { 45 viewer.ViewSlotChange(slot, item) 46 } 47 }), 48 viewerMu: m, 49 viewers: v, 50 } 51 } 52 53 // Inventory returns the inventory of the barrel. The size of the inventory will be 27. 54 func (b Barrel) Inventory() *inventory.Inventory { 55 return b.inventory 56 } 57 58 // WithName returns the barrel after applying a specific name to the block. 59 func (b Barrel) WithName(a ...any) world.Item { 60 b.CustomName = strings.TrimSuffix(fmt.Sprintln(a...), "\n") 61 return b 62 } 63 64 // open opens the barrel, displaying the animation and playing a sound. 65 func (b Barrel) open(w *world.World, pos cube.Pos) { 66 b.Open = true 67 w.PlaySound(pos.Vec3Centre(), sound.BarrelOpen{}) 68 w.SetBlock(pos, b, nil) 69 } 70 71 // close closes the barrel, displaying the animation and playing a sound. 72 func (b Barrel) close(w *world.World, pos cube.Pos) { 73 b.Open = false 74 w.PlaySound(pos.Vec3Centre(), sound.BarrelClose{}) 75 w.SetBlock(pos, b, nil) 76 } 77 78 // AddViewer adds a viewer to the barrel, so that it is updated whenever the inventory of the barrel is changed. 79 func (b Barrel) AddViewer(v ContainerViewer, w *world.World, pos cube.Pos) { 80 b.viewerMu.Lock() 81 defer b.viewerMu.Unlock() 82 if len(b.viewers) == 0 { 83 b.open(w, pos) 84 } 85 b.viewers[v] = struct{}{} 86 } 87 88 // RemoveViewer removes a viewer from the barrel, so that slot updates in the inventory are no longer sent to 89 // it. 90 func (b Barrel) RemoveViewer(v ContainerViewer, w *world.World, pos cube.Pos) { 91 b.viewerMu.Lock() 92 defer b.viewerMu.Unlock() 93 if len(b.viewers) == 0 { 94 return 95 } 96 delete(b.viewers, v) 97 if len(b.viewers) == 0 { 98 b.close(w, pos) 99 } 100 } 101 102 // Activate ... 103 func (b Barrel) Activate(pos cube.Pos, _ cube.Face, _ *world.World, u item.User, _ *item.UseContext) bool { 104 if opener, ok := u.(ContainerOpener); ok { 105 opener.OpenBlockContainer(pos) 106 return true 107 } 108 return false 109 } 110 111 // UseOnBlock ... 112 func (b Barrel) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) { 113 pos, _, used = firstReplaceable(w, pos, face, b) 114 if !used { 115 return 116 } 117 //noinspection GoAssignmentToReceiver 118 b = NewBarrel() 119 b.Facing = calculateFace(user, pos) 120 121 place(w, pos, b, user, ctx) 122 return placed(ctx) 123 } 124 125 // BreakInfo ... 126 func (b Barrel) BreakInfo() BreakInfo { 127 return newBreakInfo(2.5, alwaysHarvestable, axeEffective, oneOf(b)) 128 } 129 130 // FlammabilityInfo ... 131 func (b Barrel) FlammabilityInfo() FlammabilityInfo { 132 return newFlammabilityInfo(0, 0, true) 133 } 134 135 // FuelInfo ... 136 func (Barrel) FuelInfo() item.FuelInfo { 137 return newFuelInfo(time.Second * 15) 138 } 139 140 // DecodeNBT ... 141 func (b Barrel) DecodeNBT(data map[string]any) any { 142 facing := b.Facing 143 //noinspection GoAssignmentToReceiver 144 b = NewBarrel() 145 b.Facing = facing 146 b.CustomName = nbtconv.String(data, "CustomName") 147 nbtconv.InvFromNBT(b.inventory, nbtconv.Slice(data, "Items")) 148 return b 149 } 150 151 // EncodeNBT ... 152 func (b Barrel) EncodeNBT() map[string]any { 153 if b.inventory == nil { 154 facing, customName := b.Facing, b.CustomName 155 //noinspection GoAssignmentToReceiver 156 b = NewBarrel() 157 b.Facing, b.CustomName = facing, customName 158 } 159 m := map[string]any{ 160 "Items": nbtconv.InvToNBT(b.inventory), 161 "id": "Barrel", 162 } 163 if b.CustomName != "" { 164 m["CustomName"] = b.CustomName 165 } 166 return m 167 } 168 169 // EncodeBlock ... 170 func (b Barrel) EncodeBlock() (string, map[string]any) { 171 return "minecraft:barrel", map[string]any{"open_bit": boolByte(b.Open), "facing_direction": int32(b.Facing)} 172 } 173 174 // EncodeItem ... 175 func (b Barrel) EncodeItem() (name string, meta int16) { 176 return "minecraft:barrel", 0 177 } 178 179 // allBarrels ... 180 func allBarrels() (b []world.Block) { 181 for i := cube.Face(0); i < 6; i++ { 182 b = append(b, Barrel{Facing: i}) 183 b = append(b, Barrel{Facing: i, Open: true}) 184 } 185 return 186 }