diff --git a/cmd/blockhash/main.go b/cmd/blockhash/main.go index 5fdbe8d24a..7fcf4147ea 100644 --- a/cmd/blockhash/main.go +++ b/cmd/blockhash/main.go @@ -248,10 +248,12 @@ func (b *hashBuilder) ftype(structName, s string, expr ast.Expr, directives map[ return "uint64(" + s + ".Uint8())", 5 case "GrindstoneAttachment": return "uint64(" + s + ".Uint8())", 2 + case "Connections": + return "uint64(" + s + ".Uint8())", 4 case "WoodType", "LeavesType", "FlowerType", "DoubleFlowerType", "Colour": // Assuming these were all based on metadata, it should be safe to assume a bit size of 4 for this. return "uint64(" + s + ".Uint8())", 4 - case "CoralType", "SkullType": + case "CoralType", "SkullType", "StairsCorner": return "uint64(" + s + ".Uint8())", 3 case "AnvilType", "SandstoneType", "PrismarineType", "StoneBricksType", "NetherBricksType", "FroglightType", "WallConnectionType", "BlackstoneType", "DeepslateType", "TallGrassType", "CopperType", "OxidationType": diff --git a/go.mod b/go.mod index b6bb230112..66c0f98253 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.26.0 require ( github.com/brentp/intintmap v0.0.0-20251106190759-56907b1f8479 github.com/cespare/xxhash/v2 v2.3.0 - github.com/df-mc/go-nethernet v1.0.20 + github.com/df-mc/go-nethernet v1.0.20-0.20260819165039-03e28450b31f github.com/df-mc/goleveldb v1.1.9 github.com/df-mc/worldupgrader v1.0.21 github.com/go-gl/mathgl v1.2.0 github.com/google/uuid v1.6.0 github.com/pelletier/go-toml v1.9.5 - github.com/sandertv/gophertunnel v1.59.0 + github.com/sandertv/gophertunnel v1.59.1-0.20260819162346-27901ba2e835 github.com/segmentio/fasthash v1.0.3 golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 golang.org/x/mod v0.32.0 diff --git a/go.sum b/go.sum index b86e309292..48fa22a45b 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNv github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/df-mc/go-nethernet v1.0.20-0.20260819165039-03e28450b31f h1:gA9heSifQCNz7aSfodEX6CJEsCIVDlqZqgqz5FT0QyY= +github.com/df-mc/go-nethernet v1.0.20-0.20260819165039-03e28450b31f/go.mod h1:11MfQo5F7PDKfHg1H0zM5mMkrCv29jRE0lK5B4egW9w= github.com/df-mc/go-nethernet v1.0.20 h1:H0MO0GkVlAypu9IDQhzg5Pph3mCD7r40+ExiImsXT2M= github.com/df-mc/go-nethernet v1.0.20/go.mod h1:11MfQo5F7PDKfHg1H0zM5mMkrCv29jRE0lK5B4egW9w= github.com/df-mc/go-playfab/v2 v2.0.2 h1:JqOsrpe0nt6Ni4V2JrdmOAhbBgTm7LBw1ppGXlmknhs= @@ -88,8 +90,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sandertv/go-raknet v1.15.2-0.20260705184311-0d1fd09e2cf6 h1:Oj7QsiwWTOLmCpOjjmW0Qwde4BE8F5+Kjz817qzICyk= github.com/sandertv/go-raknet v1.15.2-0.20260705184311-0d1fd09e2cf6/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU= -github.com/sandertv/gophertunnel v1.59.0 h1:hIjmLycavwSjrqRlKFDMiV8LrP64tgzjgEUqWOOxvuQ= -github.com/sandertv/gophertunnel v1.59.0/go.mod h1:JMKsf8DmVmn+qEVJ1IA9m7zEo9qHJ3ZN6IEvySEk9bs= +github.com/sandertv/gophertunnel v1.59.1-0.20260819162346-27901ba2e835 h1:lF1kkxzIGnMAn/vNN8NXNlBsk7mgyc+RQIxLRgp9lUY= +github.com/sandertv/gophertunnel v1.59.1-0.20260819162346-27901ba2e835/go.mod h1:LZsch9elPoPzsJGDbx7ClWEHFjgHZ/lONvXF7m16Qn8= github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= diff --git a/server/block/connections.go b/server/block/connections.go new file mode 100644 index 0000000000..c0ff1c12e3 --- /dev/null +++ b/server/block/connections.go @@ -0,0 +1,50 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" +) + +// Connections holds the sides that a block such as a fence connects to. +type Connections struct { + north, east, south, west bool +} + +// Uint8 returns the Connections as a uint8. +func (c Connections) Uint8() uint8 { + return boolByte(c.north) | boolByte(c.east)<<1 | boolByte(c.south)<<2 | boolByte(c.west)<<3 +} + +// properties returns the block state properties of the Connections. +func (c Connections) properties() map[string]any { + return map[string]any{ + "minecraft:connection_north": c.north, + "minecraft:connection_east": c.east, + "minecraft:connection_south": c.south, + "minecraft:connection_west": c.west, + } +} + +// connector is a block or block model that reports the sides it connects to. +type connector interface { + // Connects returns true if the block connects to the block at the face passed. + Connects(pos cube.Pos, face cube.Face, src world.BlockSource) bool +} + +// calculateConnections returns the Connections of the connector at a position. +func calculateConnections(c connector, tx *world.Tx, pos cube.Pos) Connections { + return Connections{ + north: c.Connects(pos, cube.FaceNorth, tx), + east: c.Connects(pos, cube.FaceEast, tx), + south: c.Connects(pos, cube.FaceSouth, tx), + west: c.Connects(pos, cube.FaceWest, tx), + } +} + +// allConnections returns a list of all combinations of Connections. +func allConnections() (all []Connections) { + for i := range 16 { + all = append(all, Connections{north: i&0x1 != 0, east: i&0x2 != 0, south: i&0x4 != 0, west: i&0x8 != 0}) + } + return +} diff --git a/server/block/copper_bars.go b/server/block/copper_bars.go index c66fea39e3..d3a55cd388 100644 --- a/server/block/copper_bars.go +++ b/server/block/copper_bars.go @@ -19,6 +19,16 @@ type CopperBars struct { Oxidation OxidationType // Waxed bool is whether the copper bars has been waxed with honeycomb. Waxed bool + // Connections holds the sides that the copper bars connects to. + Connections Connections +} + +// NeighbourUpdateTick ... +func (c CopperBars) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { + if connections := calculateConnections(c.Model().(connector), tx, pos); connections != c.Connections { + c.Connections = connections + tx.SetBlock(pos, c, nil) + } } // BreakInfo ... @@ -80,14 +90,16 @@ func (c CopperBars) EncodeItem() (name string, meta int16) { // EncodeBlock ... func (c CopperBars) EncodeBlock() (name string, properties map[string]any) { - return copperBlockName("copper_bars", c.Oxidation, c.Waxed), nil + return copperBlockName("copper_bars", c.Oxidation, c.Waxed), c.Connections.properties() } // allCopperBars ... func allCopperBars() (bars []world.Block) { f := func(waxed bool) { for _, o := range OxidationTypes() { - bars = append(bars, CopperBars{Oxidation: o, Waxed: waxed}) + for _, conn := range allConnections() { + bars = append(bars, CopperBars{Oxidation: o, Waxed: waxed, Connections: conn}) + } } } f(true) diff --git a/server/block/glass_pane.go b/server/block/glass_pane.go index fe546407d9..2734390d69 100644 --- a/server/block/glass_pane.go +++ b/server/block/glass_pane.go @@ -11,6 +11,17 @@ type GlassPane struct { thin clicksAndSticks sourceWaterDisplacer + + // Connections holds the sides that the glass pane connects to. + Connections Connections +} + +// NeighbourUpdateTick ... +func (p GlassPane) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { + if connections := calculateConnections(p.Model().(connector), tx, pos); connections != p.Connections { + p.Connections = connections + tx.SetBlock(pos, p, nil) + } } // SideClosed ... @@ -29,6 +40,14 @@ func (GlassPane) EncodeItem() (name string, meta int16) { } // EncodeBlock ... -func (GlassPane) EncodeBlock() (string, map[string]any) { - return "minecraft:glass_pane", nil +func (p GlassPane) EncodeBlock() (string, map[string]any) { + return "minecraft:glass_pane", p.Connections.properties() +} + +// allGlassPane ... +func allGlassPane() (panes []world.Block) { + for _, c := range allConnections() { + panes = append(panes, GlassPane{Connections: c}) + } + return } diff --git a/server/block/hash.go b/server/block/hash.go index a46a00eb5e..8db5025020 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -417,7 +417,7 @@ func (c Copper) Hash() (uint64, uint64) { } func (c CopperBars) Hash() (uint64, uint64) { - return hashCopperBars, uint64(c.Oxidation.Uint8()) | uint64(boolByte(c.Waxed))<<2 + return hashCopperBars, uint64(c.Oxidation.Uint8()) | uint64(boolByte(c.Waxed))<<2 | uint64(c.Connections.Uint8())<<3 } func (c CopperChain) Hash() (uint64, uint64) { @@ -592,8 +592,8 @@ func (Glass) Hash() (uint64, uint64) { return hashGlass, 0 } -func (GlassPane) Hash() (uint64, uint64) { - return hashGlassPane, 0 +func (p GlassPane) Hash() (uint64, uint64) { + return hashGlassPane, uint64(p.Connections.Uint8()) } func (t GlazedTerracotta) Hash() (uint64, uint64) { @@ -664,8 +664,8 @@ func (Iron) Hash() (uint64, uint64) { return hashIron, 0 } -func (IronBars) Hash() (uint64, uint64) { - return hashIronBars, 0 +func (i IronBars) Hash() (uint64, uint64) { + return hashIronBars, uint64(i.Connections.Uint8()) } func (c IronChain) Hash() (uint64, uint64) { @@ -768,8 +768,8 @@ func (m MuddyMangroveRoots) Hash() (uint64, uint64) { return hashMuddyMangroveRoots, uint64(m.Axis) } -func (NetherBrickFence) Hash() (uint64, uint64) { - return hashNetherBrickFence, 0 +func (n NetherBrickFence) Hash() (uint64, uint64) { + return hashNetherBrickFence, uint64(n.Connections.Uint8()) } func (n NetherBricks) Hash() (uint64, uint64) { @@ -1009,7 +1009,7 @@ func (g StainedGlass) Hash() (uint64, uint64) { } func (p StainedGlassPane) Hash() (uint64, uint64) { - return hashStainedGlassPane, uint64(p.Colour.Uint8()) + return hashStainedGlassPane, uint64(p.Colour.Uint8()) | uint64(p.Connections.Uint8())<<4 } func (t StainedTerracotta) Hash() (uint64, uint64) { @@ -1017,7 +1017,7 @@ func (t StainedTerracotta) Hash() (uint64, uint64) { } func (s Stairs) Hash() (uint64, uint64) { - return hashStairs, world.BlockHash(s.Block) | uint64(boolByte(s.UpsideDown))<<32 | uint64(s.Facing)<<33 + return hashStairs, world.BlockHash(s.Block) | uint64(boolByte(s.UpsideDown))<<32 | uint64(s.Facing)<<33 | uint64(s.Corner.Uint8())<<35 } func (s Stone) Hash() (uint64, uint64) { @@ -1033,7 +1033,7 @@ func (s Stonecutter) Hash() (uint64, uint64) { } func (s String) Hash() (uint64, uint64) { - return hashString, uint64(boolByte(s.Attached)) | uint64(boolByte(s.Disarmed))<<1 | uint64(boolByte(s.Powered))<<2 | uint64(boolByte(s.Suspended))<<3 + return hashString, uint64(boolByte(s.Attached)) | uint64(boolByte(s.Disarmed))<<1 | uint64(boolByte(s.Powered))<<2 | uint64(boolByte(s.Suspended))<<3 | uint64(s.Connections.Uint8())<<4 } func (c SugarCane) Hash() (uint64, uint64) { @@ -1097,7 +1097,7 @@ func (d WoodDoor) Hash() (uint64, uint64) { } func (w WoodFence) Hash() (uint64, uint64) { - return hashWoodFence, uint64(w.Wood.Uint8()) + return hashWoodFence, uint64(w.Wood.Uint8()) | uint64(w.Connections.Uint8())<<4 } func (f WoodFenceGate) Hash() (uint64, uint64) { diff --git a/server/block/iron_bars.go b/server/block/iron_bars.go index 45e11de9a4..53333b7670 100644 --- a/server/block/iron_bars.go +++ b/server/block/iron_bars.go @@ -10,6 +10,17 @@ type IronBars struct { transparent thin sourceWaterDisplacer + + // Connections holds the sides that the iron bars connects to. + Connections Connections +} + +// NeighbourUpdateTick ... +func (i IronBars) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { + if connections := calculateConnections(i.Model().(connector), tx, pos); connections != i.Connections { + i.Connections = connections + tx.SetBlock(pos, i, nil) + } } // BreakInfo ... @@ -28,6 +39,14 @@ func (IronBars) EncodeItem() (name string, meta int16) { } // EncodeBlock ... -func (IronBars) EncodeBlock() (string, map[string]any) { - return "minecraft:iron_bars", nil +func (i IronBars) EncodeBlock() (string, map[string]any) { + return "minecraft:iron_bars", i.Connections.properties() +} + +// allIronBars ... +func allIronBars() (bars []world.Block) { + for _, c := range allConnections() { + bars = append(bars, IronBars{Connections: c}) + } + return } diff --git a/server/block/model/fence.go b/server/block/model/fence.go index b21d170b79..e1a226d8bf 100644 --- a/server/block/model/fence.go +++ b/server/block/model/fence.go @@ -22,8 +22,8 @@ const ( func (f Fence) BBox(pos cube.Pos, s world.BlockSource) []cube.BBox { boxes := make([]cube.BBox, 0, 2) - connectWest, connectEast := f.checkConnection(pos, cube.FaceWest, s), f.checkConnection(pos, cube.FaceEast, s) - connectNorth, connectSouth := f.checkConnection(pos, cube.FaceNorth, s), f.checkConnection(pos, cube.FaceSouth, s) + connectWest, connectEast := f.Connects(pos, cube.FaceWest, s), f.Connects(pos, cube.FaceEast, s) + connectNorth, connectSouth := f.Connects(pos, cube.FaceNorth, s), f.Connects(pos, cube.FaceSouth, s) // Check if we have any connections on the Z axis if connectWest || connectEast { @@ -60,8 +60,10 @@ func (f Fence) FaceSolid(_ cube.Pos, face cube.Face, _ world.BlockSource) bool { return face == cube.FaceDown || face == cube.FaceUp } -// checkConnection checks if the block at the given position and face has a connection to the current fence block. -func (f Fence) checkConnection(pos cube.Pos, face cube.Face, src world.BlockSource) bool { +// Connects returns true if the Fence connects to the block at the face passed. +// A Fence connects to fences of the same kind, to fence gates and to blocks +// with a solid face towards it. +func (f Fence) Connects(pos cube.Pos, face cube.Face, src world.BlockSource) bool { sidePos := pos.Side(face) sideBlock := src.Block(sidePos) if fence, ok := sideBlock.Model().(Fence); ok && fence.Wood == f.Wood { diff --git a/server/block/model/stair.go b/server/block/model/stair.go index 8f9816076a..82aa6c1862 100644 --- a/server/block/model/stair.go +++ b/server/block/model/stair.go @@ -7,48 +7,41 @@ import ( ) // Stair is a model for stair-like blocks. These have different solid sides depending on the direction the -// stairs are facing, the surrounding blocks and whether it is upside down or not. +// stairs are facing, the corner they form with the stairs around them and whether it is upside down or not. type Stair struct { // Facing specifies the direction that the full side of the Stair faces. Facing cube.Direction // UpsideDown turns the Stair upside-down, meaning the full side of the Stair is turned to the top side of the // block. UpsideDown bool + // Corner specifies if the Stair forms a corner with the stairs around it. + Corner bool + // Inner specifies if the corner formed turns inwards, filling up the block rather than a quarter of it. Inner is + // only used if Corner is true. + Inner bool + // Left specifies if the corner formed is on the left side of the Stair. Left is only used if Corner is true. + Left bool } -// BBox returns a slice of physics.BBox depending on if the Stair is upside down and which direction it is facing. -// Additionally, these BBoxs depend on the Stair blocks surrounding this one, which can influence the model. -func (s Stair) BBox(pos cube.Pos, bs world.BlockSource) []cube.BBox { +// BBox returns a slice of physics.BBox depending on if the Stair is upside down, which direction it is facing and the +// corner it forms with the stairs around it. +func (s Stair) BBox(cube.Pos, world.BlockSource) []cube.BBox { b := []cube.BBox{cube.Box(0, 0, 0, 1, 0.5, 1)} if s.UpsideDown { b[0] = cube.Box(0, 0.5, 0, 1, 1, 1) } - t := s.cornerType(pos, bs) - - face, oppositeFace := s.Facing.Face(), s.Facing.Opposite().Face() - switch t { - case noCorner, cornerRightInner, cornerLeftInner: - b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5). - ExtendTowards(face, 0.5). - Stretch(s.Facing.RotateRight().Face().Axis(), 0.5)) + step := cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5) + if !s.Corner || s.Inner { + b = append(b, step.ExtendTowards(s.Facing.Face(), 0.5).Stretch(s.Facing.RotateRight().Face().Axis(), 0.5)) } - switch t { - case cornerRightOuter: - b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5). - ExtendTowards(face, 0.5). - ExtendTowards(s.Facing.RotateLeft().Face(), 0.5)) - case cornerLeftOuter: - b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5). - ExtendTowards(face, 0.5). - ExtendTowards(s.Facing.RotateRight().Face(), 0.5)) - case cornerRightInner: - b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5). - ExtendTowards(oppositeFace, 0.5). - ExtendTowards(s.Facing.RotateRight().Face(), 0.5)) - case cornerLeftInner: - b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5). - ExtendTowards(oppositeFace, 0.5). - ExtendTowards(s.Facing.RotateLeft().Face(), 0.5)) + if s.Corner { + // An outer corner only fills up the quarter of the block in front of the stairs, while an inner corner fills up + // the quarter behind them on top of the step above. + face := s.Facing.Face() + if s.Inner { + face = s.Facing.Opposite().Face() + } + b = append(b, step.ExtendTowards(face, 0.5).ExtendTowards(s.cornerFace(), 0.5)) } if s.UpsideDown { for i := range b[1:] { @@ -59,60 +52,26 @@ func (s Stair) BBox(pos cube.Pos, bs world.BlockSource) []cube.BBox { } // FaceSolid returns true for all faces of the Stair that are completely filled. -func (s Stair) FaceSolid(pos cube.Pos, face cube.Face, bs world.BlockSource) bool { +func (s Stair) FaceSolid(_ cube.Pos, face cube.Face, _ world.BlockSource) bool { // Stairs always have a closed side at the top or bottom based on their orientation. if (face == cube.FaceUp && s.UpsideDown) || (face == cube.FaceDown && !s.UpsideDown) { return true } - - switch t := s.cornerType(pos, bs); t { - case cornerRightOuter, cornerLeftOuter: + if !s.Corner { + // Not a corner, so only the side behind the stairs is closed. + return face == s.Facing.Face() + } + if !s.Inner { // Small corner blocks, they do not block water flowing out horizontally. return false - case noCorner: - // Not a corner, so only block directly behind the stairs. - return s.Facing.Face() == face - case cornerRightInner: - return face == s.Facing.RotateRight().Face() || face == s.Facing.Face() - default: - return face == s.Facing.RotateLeft().Face() || face == s.Facing.Face() } + return face == s.cornerFace() || face == s.Facing.Face() } -const ( - noCorner = iota - cornerRightInner - cornerLeftInner - cornerRightOuter - cornerLeftOuter -) - -// cornerType returns the type of the corner that the stairs form, or 0 if it does not form a corner with any -// other stairs. -func (s Stair) cornerType(pos cube.Pos, bs world.BlockSource) uint8 { - rotatedFacing := s.Facing.RotateRight() - if closedSide, ok := bs.Block(pos.Side(s.Facing.Face())).Model().(Stair); ok && closedSide.UpsideDown == s.UpsideDown { - if closedSide.Facing == rotatedFacing { - return cornerLeftOuter - } else if closedSide.Facing == rotatedFacing.Opposite() { - // This will only form a corner if there is not a stair on the right of this one with the same - // direction. - if side, ok := bs.Block(pos.Side(s.Facing.RotateRight().Face())).Model().(Stair); !ok || side.Facing != s.Facing || side.UpsideDown != s.UpsideDown { - return cornerRightOuter - } - return noCorner - } - } - if openSide, ok := bs.Block(pos.Side(s.Facing.Opposite().Face())).Model().(Stair); ok && openSide.UpsideDown == s.UpsideDown { - if openSide.Facing == rotatedFacing { - // This will only form a corner if there is not a stair on the right of this one with the same - // direction. - if side, ok := bs.Block(pos.Side(s.Facing.RotateRight().Face())).Model().(Stair); !ok || side.Facing != s.Facing || side.UpsideDown != s.UpsideDown { - return cornerRightInner - } - } else if openSide.Facing == rotatedFacing.Opposite() { - return cornerLeftInner - } +// cornerFace returns the face of the side that the corner of the Stair is on. +func (s Stair) cornerFace() cube.Face { + if s.Left { + return s.Facing.RotateLeft().Face() } - return noCorner + return s.Facing.RotateRight().Face() } diff --git a/server/block/model/thin.go b/server/block/model/thin.go index 62c0d2ae33..a05d0e3f50 100644 --- a/server/block/model/thin.go +++ b/server/block/model/thin.go @@ -20,7 +20,7 @@ func (t Thin) BBox(pos cube.Pos, s world.BlockSource) []cube.BBox { boxes := make([]cube.BBox, 0, 2) // Check if we have any connections on the Z axis - connectWest, connectEast := t.checkConnection(pos, cube.FaceWest, s), t.checkConnection(pos, cube.FaceEast, s) + connectWest, connectEast := t.Connects(pos, cube.FaceWest, s), t.Connects(pos, cube.FaceEast, s) if connectWest || connectEast { box := cube.Box(0, 0, 0, 1, thinHeight, 1).Stretch(cube.Z, -thinInset) if !connectWest { @@ -32,7 +32,7 @@ func (t Thin) BBox(pos cube.Pos, s world.BlockSource) []cube.BBox { } // Check if we have any connections on the X axis - connectNorth, connectSouth := t.checkConnection(pos, cube.FaceNorth, s), t.checkConnection(pos, cube.FaceSouth, s) + connectNorth, connectSouth := t.Connects(pos, cube.FaceNorth, s), t.Connects(pos, cube.FaceSouth, s) if connectNorth || connectSouth { box := cube.Box(0, 0, 0, 1, thinHeight, 1).Stretch(cube.X, -thinInset) if !connectNorth { @@ -56,8 +56,10 @@ func (t Thin) FaceSolid(_ cube.Pos, face cube.Face, _ world.BlockSource) bool { return face == cube.FaceDown } -// checkConnection checks if the block at the given position and face has a connection to the current thin block. -func (t Thin) checkConnection(pos cube.Pos, face cube.Face, s world.BlockSource) bool { +// Connects returns true if the Thin block connects to the block at the face +// passed. A Thin block connects to other thin blocks, to walls and to blocks +// with a solid face towards it. +func (t Thin) Connects(pos cube.Pos, face cube.Face, s world.BlockSource) bool { sidePos := pos.Side(face) sideBlock := s.Block(sidePos) _, isThin := sideBlock.Model().(Thin) diff --git a/server/block/nether_brick_fence.go b/server/block/nether_brick_fence.go index 8761a66535..06cdf98c25 100644 --- a/server/block/nether_brick_fence.go +++ b/server/block/nether_brick_fence.go @@ -10,6 +10,17 @@ import ( type NetherBrickFence struct { transparent sourceWaterDisplacer + + // Connections holds the sides that the fence connects to. + Connections Connections +} + +// NeighbourUpdateTick ... +func (n NetherBrickFence) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { + if connections := calculateConnections(n.Model().(connector), tx, pos); connections != n.Connections { + n.Connections = connections + tx.SetBlock(pos, n, nil) + } } // BreakInfo ... @@ -33,6 +44,14 @@ func (NetherBrickFence) EncodeItem() (name string, meta int16) { } // EncodeBlock ... -func (NetherBrickFence) EncodeBlock() (string, map[string]any) { - return "minecraft:nether_brick_fence", nil +func (n NetherBrickFence) EncodeBlock() (string, map[string]any) { + return "minecraft:nether_brick_fence", n.Connections.properties() +} + +// allNetherBrickFence ... +func allNetherBrickFence() (fence []world.Block) { + for _, c := range allConnections() { + fence = append(fence, NetherBrickFence{Connections: c}) + } + return } diff --git a/server/block/register.go b/server/block/register.go index 084c586a1a..afa30adce6 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -58,7 +58,7 @@ func init() { } world.RegisterBlock(EndStone{}) world.RegisterBlock(FletchingTable{}) - world.RegisterBlock(GlassPane{}) + registerAll(allGlassPane()) world.RegisterBlock(Glass{}) world.RegisterBlock(Glowstone{}) world.RegisterBlock(Gold{}) @@ -76,7 +76,7 @@ func init() { world.RegisterBlock(b) } world.RegisterBlock(InvisibleBedrock{}) - world.RegisterBlock(IronBars{}) + registerAll(allIronBars()) world.RegisterBlock(Iron{}) world.RegisterBlock(Jukebox{}) world.RegisterBlock(Lapis{}) @@ -86,7 +86,7 @@ func init() { world.RegisterBlock(MossCarpet{}) world.RegisterBlock(MudBricks{}) world.RegisterBlock(Mud{}) - world.RegisterBlock(NetherBrickFence{}) + registerAll(allNetherBrickFence()) world.RegisterBlock(NetherGoldOre{}) world.RegisterBlock(NetherQuartzOre{}) world.RegisterBlock(NetherSprouts{}) diff --git a/server/block/stained_glass_pane.go b/server/block/stained_glass_pane.go index 919d2520fb..971bd09019 100644 --- a/server/block/stained_glass_pane.go +++ b/server/block/stained_glass_pane.go @@ -15,6 +15,16 @@ type StainedGlassPane struct { // Colour specifies the colour of the block. Colour item.Colour + // Connections holds the sides that the glass pane connects to. + Connections Connections +} + +// NeighbourUpdateTick ... +func (p StainedGlassPane) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { + if connections := calculateConnections(p.Model().(connector), tx, pos); connections != p.Connections { + p.Connections = connections + tx.SetBlock(pos, p, nil) + } } // SideClosed ... @@ -34,14 +44,16 @@ func (p StainedGlassPane) EncodeItem() (name string, meta int16) { // EncodeBlock ... func (p StainedGlassPane) EncodeBlock() (name string, properties map[string]any) { - return "minecraft:" + p.Colour.String() + "_stained_glass_pane", nil + return "minecraft:" + p.Colour.String() + "_stained_glass_pane", p.Connections.properties() } // allStainedGlassPane returns stained-glass panes with all possible colours. func allStainedGlassPane() []world.Block { b := make([]world.Block, 0, 16) - for _, c := range item.Colours() { - b = append(b, StainedGlassPane{Colour: c}) + for _, colour := range item.Colours() { + for _, c := range allConnections() { + b = append(b, StainedGlassPane{Colour: colour, Connections: c}) + } } return b } diff --git a/server/block/stairs.go b/server/block/stairs.go index ff0e4827d9..0bc176da2a 100644 --- a/server/block/stairs.go +++ b/server/block/stairs.go @@ -21,6 +21,8 @@ type Stairs struct { UpsideDown bool // Facing is the direction that the full side of the stairs is facing. Facing cube.Direction + // Corner is the corner that the stairs form with the stairs around them. + Corner StairsCorner } // UseOnBlock handles the directional placing of stairs and makes sure they are properly placed upside down @@ -35,13 +37,58 @@ func (s Stairs) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3, tx s.UpsideDown = true } + s.Corner = s.calculateCorner(tx, pos) place(tx, pos, s, user, ctx) return placed(ctx) } +// NeighbourUpdateTick ... +func (s Stairs) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { + if corner := s.calculateCorner(tx, pos); corner != s.Corner { + s.Corner = corner + tx.SetBlock(pos, s, nil) + } +} + +// calculateCorner calculates the corner that the stairs form with the stairs around them at a position in a world. +func (s Stairs) calculateCorner(tx *world.Tx, pos cube.Pos) StairsCorner { + rotated := s.Facing.RotateRight() + if closed, ok := tx.Block(pos.Side(s.Facing.Face())).(Stairs); ok && closed.UpsideDown == s.UpsideDown { + if closed.Facing == rotated { + return OuterRightStairsCorner() + } else if closed.Facing == rotated.Opposite() { + if s.continued(tx, pos) { + return NoStairsCorner() + } + return OuterLeftStairsCorner() + } + } + if open, ok := tx.Block(pos.Side(s.Facing.Opposite().Face())).(Stairs); ok && open.UpsideDown == s.UpsideDown { + if open.Facing == rotated && !s.continued(tx, pos) { + return InnerRightStairsCorner() + } else if open.Facing == rotated.Opposite() { + return InnerLeftStairsCorner() + } + } + return NoStairsCorner() +} + +// continued returns true if the stairs are continued on their right side by stairs facing the same way. Stairs like +// these do not form a corner. +func (s Stairs) continued(tx *world.Tx, pos cube.Pos) bool { + side, ok := tx.Block(pos.Side(s.Facing.RotateRight().Face())).(Stairs) + return ok && side.Facing == s.Facing && side.UpsideDown == s.UpsideDown +} + // Model ... func (s Stairs) Model() world.BlockModel { - return model.Stair{Facing: s.Facing, UpsideDown: s.UpsideDown} + return model.Stair{ + Facing: s.Facing, + UpsideDown: s.UpsideDown, + Corner: s.Corner != NoStairsCorner(), + Inner: s.Corner.Inner(), + Left: s.Corner.Left(), + } } // BreakInfo ... @@ -84,7 +131,7 @@ func (s Stairs) EncodeItem() (name string, meta int16) { // EncodeBlock ... func (s Stairs) EncodeBlock() (name string, properties map[string]any) { - return "minecraft:" + encodeStairsBlock(s.Block) + "_stairs", map[string]any{"upside_down_bit": s.UpsideDown, "weirdo_direction": toStairsDirection(s.Facing)} + return "minecraft:" + encodeStairsBlock(s.Block) + "_stairs", map[string]any{"minecraft:corner": s.Corner.String(), "upside_down_bit": s.UpsideDown, "weirdo_direction": toStairsDirection(s.Facing)} } // toStairDirection converts a facing to a stair's direction for Minecraft. @@ -106,7 +153,9 @@ func (Stairs) CanRedstoneWireStepDown(cube.Pos, cube.Pos, *world.Tx) bool { func allStairs() (stairs []world.Block) { f := func(facing cube.Direction, upsideDown bool) { for _, s := range StairsBlocks() { - stairs = append(stairs, Stairs{Facing: facing, UpsideDown: upsideDown, Block: s}) + for _, corner := range StairsCorners() { + stairs = append(stairs, Stairs{Facing: facing, UpsideDown: upsideDown, Block: s, Corner: corner}) + } } } for i := cube.Direction(0); i <= 3; i++ { diff --git a/server/block/stairs_corner.go b/server/block/stairs_corner.go new file mode 100644 index 0000000000..0a220e2644 --- /dev/null +++ b/server/block/stairs_corner.go @@ -0,0 +1,72 @@ +package block + +// StairsCorner represents the corner that stairs form with the stairs around +// them. It is calculated by the server whenever the stairs or the stairs around +// them change. +type StairsCorner struct { + stairsCorner +} + +// NoStairsCorner returns the corner of stairs that do not form a corner with any of the stairs around them. +func NoStairsCorner() StairsCorner { + return StairsCorner{0} +} + +// InnerLeftStairsCorner returns the corner of stairs that turn inwards on their left side. +func InnerLeftStairsCorner() StairsCorner { + return StairsCorner{1} +} + +// InnerRightStairsCorner returns the corner of stairs that turn inwards on their right side. +func InnerRightStairsCorner() StairsCorner { + return StairsCorner{2} +} + +// OuterLeftStairsCorner returns the corner of stairs that turn outwards on their left side. +func OuterLeftStairsCorner() StairsCorner { + return StairsCorner{3} +} + +// OuterRightStairsCorner returns the corner of stairs that turn outwards on their right side. +func OuterRightStairsCorner() StairsCorner { + return StairsCorner{4} +} + +// StairsCorners returns a list of all stairs corners. +func StairsCorners() []StairsCorner { + return []StairsCorner{NoStairsCorner(), InnerLeftStairsCorner(), InnerRightStairsCorner(), OuterLeftStairsCorner(), OuterRightStairsCorner()} +} + +type stairsCorner uint8 + +// Uint8 returns the stairs corner as a uint8. +func (s stairsCorner) Uint8() uint8 { + return uint8(s) +} + +// Inner returns true if the corner turns inwards, filling up the block rather than a quarter of it. +func (s stairsCorner) Inner() bool { + return s == 1 || s == 2 +} + +// Left returns true if the corner is on the left side of the stairs. +func (s stairsCorner) Left() bool { + return s == 1 || s == 3 +} + +// String ... +func (s stairsCorner) String() string { + switch s { + case 0: + return "none" + case 1: + return "inner_left" + case 2: + return "inner_right" + case 3: + return "outer_left" + case 4: + return "outer_right" + } + panic("should never happen") +} diff --git a/server/block/string.go b/server/block/string.go index 97769d0a98..1572814cad 100644 --- a/server/block/string.go +++ b/server/block/string.go @@ -22,6 +22,8 @@ type String struct { Powered bool // Suspended is true if the tripwire is not resting on a solid surface. Suspended bool + // Connections holds the sides that the tripwire connects to. + Connections Connections } // UseOnBlock places the string as a tripwire on the target surface. @@ -32,6 +34,7 @@ func (s String) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world } below := pos.Side(cube.FaceDown) s.Suspended = !tx.Block(below).Model().FaceSolid(below, cube.FaceUp, tx) + s.Connections = calculateConnections(s, tx, pos) place(tx, pos, s, user, ctx) return placed(ctx) } @@ -39,11 +42,19 @@ func (s String) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world // NeighbourUpdateTick ... func (s String) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { below := pos.Side(cube.FaceDown) - suspended := !tx.Block(below).Model().FaceSolid(below, cube.FaceUp, tx) - if suspended != s.Suspended { - s.Suspended = suspended - tx.SetBlock(pos, s, nil) + suspended, connections := !tx.Block(below).Model().FaceSolid(below, cube.FaceUp, tx), calculateConnections(s, tx, pos) + if suspended == s.Suspended && connections == s.Connections { + return } + s.Suspended, s.Connections = suspended, connections + tx.SetBlock(pos, s, nil) +} + +// Connects returns true if the tripwire connects to the block at the face +// passed. Tripwire connects to other tripwire. +func (String) Connects(pos cube.Pos, face cube.Face, src world.BlockSource) bool { + _, ok := src.Block(pos.Side(face)).(String) + return ok } // HasLiquidDrops ... @@ -63,23 +74,26 @@ func (s String) EncodeItem() (name string, meta int16) { // EncodeBlock ... func (s String) EncodeBlock() (name string, properties map[string]any) { - return "minecraft:trip_wire", map[string]any{ - "attached_bit": boolByte(s.Attached), - "disarmed_bit": boolByte(s.Disarmed), - "powered_bit": boolByte(s.Powered), - "suspended_bit": boolByte(s.Suspended), - } + properties = s.Connections.properties() + properties["attached_bit"] = boolByte(s.Attached) + properties["disarmed_bit"] = boolByte(s.Disarmed) + properties["powered_bit"] = boolByte(s.Powered) + properties["suspended_bit"] = boolByte(s.Suspended) + return "minecraft:trip_wire", properties } // allString ... func allString() (blocks []world.Block) { for meta := 0; meta < 16; meta++ { - blocks = append(blocks, String{ - Powered: meta&0x1 != 0, - Suspended: meta&0x2 != 0, - Attached: meta&0x4 != 0, - Disarmed: meta&0x8 != 0, - }) + for _, c := range allConnections() { + blocks = append(blocks, String{ + Powered: meta&0x1 != 0, + Suspended: meta&0x2 != 0, + Attached: meta&0x4 != 0, + Disarmed: meta&0x8 != 0, + Connections: c, + }) + } } return blocks } diff --git a/server/block/wood_fence.go b/server/block/wood_fence.go index 3c9c3b45a8..15520317cb 100644 --- a/server/block/wood_fence.go +++ b/server/block/wood_fence.go @@ -18,6 +18,16 @@ type WoodFence struct { // Wood is the type of wood of the fence. This field must have one of the values found in the wood // package. Wood WoodType + // Connections holds the sides that the fence connects to. + Connections Connections +} + +// NeighbourUpdateTick ... +func (w WoodFence) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { + if connections := calculateConnections(w.Model().(connector), tx, pos); connections != w.Connections { + w.Connections = connections + tx.SetBlock(pos, w, nil) + } } // BreakInfo ... @@ -48,7 +58,7 @@ func (w WoodFence) FuelInfo() item.FuelInfo { // EncodeBlock ... func (w WoodFence) EncodeBlock() (name string, properties map[string]any) { - return "minecraft:" + w.Wood.String() + "_fence", nil + return "minecraft:" + w.Wood.String() + "_fence", w.Connections.properties() } // Model ... @@ -64,7 +74,9 @@ func (w WoodFence) EncodeItem() (name string, meta int16) { // allFence ... func allFence() (fence []world.Block) { for _, w := range WoodTypes() { - fence = append(fence, WoodFence{Wood: w}) + for _, c := range allConnections() { + fence = append(fence, WoodFence{Wood: w, Connections: c}) + } } return } diff --git a/server/item/creative/creative.go b/server/item/creative/creative.go index c652f9da6b..ce9054ab40 100644 --- a/server/item/creative/creative.go +++ b/server/item/creative/creative.go @@ -131,10 +131,13 @@ func itemStackFromEntry(data creativeItemEntry) (item.Stack, bool) { // Item with a block, try parsing the block, then try asserting that to an item. Blocks no longer // have their metadata sent, but we still need to get that metadata in order to be able to register // different block states as different items. - if b, ok := world.BlockByName(data.Name, data.BlockProperties); ok { - if it, ok = b.(world.Item); !ok { - return item.Stack{}, false - } + b, ok := world.BlockByName(data.Name, data.BlockProperties) + if !ok { + // The block state wasn't registered, so don't register it as a creative item. + return item.Stack{}, false + } + if it, ok = b.(world.Item); !ok { + return item.Stack{}, false } } else { if it, ok = world.ItemByName(data.Name, data.Meta); !ok { diff --git a/server/item/creative/creative_items.nbt b/server/item/creative/creative_items.nbt index d8319469be..171fd47c7e 100644 Binary files a/server/item/creative/creative_items.nbt and b/server/item/creative/creative_items.nbt differ diff --git a/server/item/recipe/crafting_data.nbt b/server/item/recipe/crafting_data.nbt index 5ee024e956..6f01c9b8ae 100644 Binary files a/server/item/recipe/crafting_data.nbt and b/server/item/recipe/crafting_data.nbt differ diff --git a/server/item/recipe/item_tag.go b/server/item/recipe/item_tag.go index cdce36bfac..85d7102510 100644 --- a/server/item/recipe/item_tag.go +++ b/server/item/recipe/item_tag.go @@ -3,6 +3,7 @@ package recipe import ( _ "embed" "encoding/json" + "slices" ) var ( @@ -51,10 +52,5 @@ func (i ItemTag) Tag() string { // Contains returns true if the item tag contains the item with the name passed. func (i ItemTag) Contains(name string) bool { - for _, item := range i.items { - if item == name { - return true - } - } - return false + return slices.Contains(i.items, name) } diff --git a/server/item/recipe/item_tags.json b/server/item/recipe/item_tags.json index 59031ed59f..e999d50e6a 100644 --- a/server/item/recipe/item_tags.json +++ b/server/item/recipe/item_tags.json @@ -24,6 +24,8 @@ "minecraft:oak_chest_boat", "minecraft:pale_oak_boat", "minecraft:pale_oak_chest_boat", + "minecraft:poplar_boat", + "minecraft:poplar_chest_boat", "minecraft:spruce_boat", "minecraft:spruce_chest_boat" ], @@ -46,6 +48,8 @@ "minecraft:oak_chest_boat", "minecraft:pale_oak_boat", "minecraft:pale_oak_chest_boat", + "minecraft:poplar_boat", + "minecraft:poplar_chest_boat", "minecraft:spruce_boat", "minecraft:spruce_chest_boat" ], @@ -65,6 +69,18 @@ "minecraft:charcoal", "minecraft:coal" ], + "minecraft:copper_tier": [ + "minecraft:copper_axe", + "minecraft:copper_boots", + "minecraft:copper_chestplate", + "minecraft:copper_helmet", + "minecraft:copper_hoe", + "minecraft:copper_leggings", + "minecraft:copper_pickaxe", + "minecraft:copper_shovel", + "minecraft:copper_spear", + "minecraft:copper_sword" + ], "minecraft:crimson_stems": [ "minecraft:crimson_hyphae", "minecraft:crimson_stem", @@ -106,10 +122,15 @@ "minecraft:diamond_leggings", "minecraft:diamond_pickaxe", "minecraft:diamond_shovel", + "minecraft:diamond_spear", "minecraft:diamond_sword", "minecraft:mace" ], "minecraft:digger": [ + "minecraft:copper_axe", + "minecraft:copper_hoe", + "minecraft:copper_pickaxe", + "minecraft:copper_shovel", "minecraft:diamond_axe", "minecraft:diamond_hoe", "minecraft:diamond_pickaxe", @@ -149,6 +170,7 @@ "minecraft:mangrove_door", "minecraft:oxidized_copper_door", "minecraft:pale_oak_door", + "minecraft:poplar_door", "minecraft:spruce_door", "minecraft:warped_door", "minecraft:waxed_copper_door", @@ -158,6 +180,11 @@ "minecraft:weathered_copper_door", "minecraft:wooden_door" ], + "minecraft:egg": [ + "minecraft:blue_egg", + "minecraft:brown_egg", + "minecraft:egg" + ], "minecraft:golden_tier": [ "minecraft:golden_axe", "minecraft:golden_boots", @@ -167,6 +194,7 @@ "minecraft:golden_leggings", "minecraft:golden_pickaxe", "minecraft:golden_shovel", + "minecraft:golden_spear", "minecraft:golden_sword" ], "minecraft:hanging_actor": [ @@ -183,14 +211,35 @@ "minecraft:mangrove_hanging_sign", "minecraft:oak_hanging_sign", "minecraft:pale_oak_hanging_sign", + "minecraft:poplar_hanging_sign", "minecraft:spruce_hanging_sign", "minecraft:warped_hanging_sign" ], + "minecraft:harness": [ + "minecraft:black_harness", + "minecraft:blue_harness", + "minecraft:brown_harness", + "minecraft:cyan_harness", + "minecraft:gray_harness", + "minecraft:green_harness", + "minecraft:light_blue_harness", + "minecraft:light_gray_harness", + "minecraft:lime_harness", + "minecraft:magenta_harness", + "minecraft:orange_harness", + "minecraft:pink_harness", + "minecraft:purple_harness", + "minecraft:red_harness", + "minecraft:white_harness", + "minecraft:yellow_harness" + ], "minecraft:horse_armor": [ + "minecraft:copper_horse_armor", "minecraft:diamond_horse_armor", "minecraft:golden_horse_armor", "minecraft:iron_horse_armor", - "minecraft:leather_horse_armor" + "minecraft:leather_horse_armor", + "minecraft:netherite_horse_armor" ], "minecraft:iron_tier": [ "minecraft:iron_axe", @@ -201,6 +250,7 @@ "minecraft:iron_leggings", "minecraft:iron_pickaxe", "minecraft:iron_shovel", + "minecraft:iron_spear", "minecraft:iron_sword" ], "minecraft:is_armor": [ @@ -208,6 +258,10 @@ "minecraft:chainmail_chestplate", "minecraft:chainmail_helmet", "minecraft:chainmail_leggings", + "minecraft:copper_boots", + "minecraft:copper_chestplate", + "minecraft:copper_helmet", + "minecraft:copper_leggings", "minecraft:diamond_boots", "minecraft:diamond_chestplate", "minecraft:diamond_helmet", @@ -232,6 +286,7 @@ "minecraft:turtle_helmet" ], "minecraft:is_axe": [ + "minecraft:copper_axe", "minecraft:diamond_axe", "minecraft:golden_axe", "minecraft:iron_axe", @@ -266,28 +321,40 @@ "minecraft:bread", "minecraft:carrot", "minecraft:chicken", + "minecraft:chorus_fruit", + "minecraft:cod", "minecraft:cooked_beef", "minecraft:cooked_chicken", + "minecraft:cooked_cod", "minecraft:cooked_mutton", "minecraft:cooked_porkchop", "minecraft:cooked_rabbit", + "minecraft:cooked_salmon", "minecraft:cookie", "minecraft:dried_kelp", "minecraft:enchanted_golden_apple", "minecraft:golden_apple", "minecraft:golden_carrot", + "minecraft:honey_bottle", "minecraft:melon_slice", "minecraft:mushroom_stew", "minecraft:mutton", + "minecraft:poisonous_potato", "minecraft:porkchop", "minecraft:potato", + "minecraft:pufferfish", "minecraft:pumpkin_pie", "minecraft:rabbit", "minecraft:rabbit_stew", "minecraft:rotten_flesh", - "minecraft:sweet_berries" + "minecraft:salmon", + "minecraft:spider_eye", + "minecraft:suspicious_stew", + "minecraft:sweet_berries", + "minecraft:tropical_fish" ], "minecraft:is_hoe": [ + "minecraft:copper_hoe", "minecraft:diamond_hoe", "minecraft:golden_hoe", "minecraft:iron_hoe", @@ -317,6 +384,7 @@ "minecraft:tnt_minecart" ], "minecraft:is_pickaxe": [ + "minecraft:copper_pickaxe", "minecraft:diamond_pickaxe", "minecraft:golden_pickaxe", "minecraft:iron_pickaxe", @@ -328,6 +396,7 @@ "minecraft:shears" ], "minecraft:is_shovel": [ + "minecraft:copper_shovel", "minecraft:diamond_shovel", "minecraft:golden_shovel", "minecraft:iron_shovel", @@ -335,7 +404,17 @@ "minecraft:stone_shovel", "minecraft:wooden_shovel" ], + "minecraft:is_spear": [ + "minecraft:copper_spear", + "minecraft:diamond_spear", + "minecraft:golden_spear", + "minecraft:iron_spear", + "minecraft:netherite_spear", + "minecraft:stone_spear", + "minecraft:wooden_spear" + ], "minecraft:is_sword": [ + "minecraft:copper_sword", "minecraft:diamond_sword", "minecraft:golden_sword", "minecraft:iron_sword", @@ -345,6 +424,11 @@ "minecraft:wooden_sword" ], "minecraft:is_tool": [ + "minecraft:copper_axe", + "minecraft:copper_hoe", + "minecraft:copper_pickaxe", + "minecraft:copper_shovel", + "minecraft:copper_sword", "minecraft:diamond_axe", "minecraft:diamond_hoe", "minecraft:diamond_pickaxe", @@ -409,6 +493,8 @@ "minecraft:oak_wood", "minecraft:pale_oak_log", "minecraft:pale_oak_wood", + "minecraft:poplar_log", + "minecraft:poplar_wood", "minecraft:spruce_log", "minecraft:spruce_wood", "minecraft:stripped_acacia_log", @@ -429,6 +515,8 @@ "minecraft:stripped_oak_wood", "minecraft:stripped_pale_oak_log", "minecraft:stripped_pale_oak_wood", + "minecraft:stripped_poplar_log", + "minecraft:stripped_poplar_wood", "minecraft:stripped_spruce_log", "minecraft:stripped_spruce_wood", "minecraft:stripped_warped_hyphae", @@ -453,6 +541,8 @@ "minecraft:oak_wood", "minecraft:pale_oak_log", "minecraft:pale_oak_wood", + "minecraft:poplar_log", + "minecraft:poplar_wood", "minecraft:spruce_log", "minecraft:spruce_wood", "minecraft:stripped_acacia_log", @@ -471,6 +561,8 @@ "minecraft:stripped_oak_wood", "minecraft:stripped_pale_oak_log", "minecraft:stripped_pale_oak_wood", + "minecraft:stripped_poplar_log", + "minecraft:stripped_poplar_wood", "minecraft:stripped_spruce_log", "minecraft:stripped_spruce_wood" ], @@ -480,16 +572,28 @@ "minecraft:stripped_mangrove_log", "minecraft:stripped_mangrove_wood" ], + "minecraft:metal_nuggets": [ + "minecraft:copper_nugget", + "minecraft:gold_nugget", + "minecraft:iron_nugget" + ], + "minecraft:mushrooms_for_stew": [ + "minecraft:brown_mushroom", + "minecraft:red_mushroom", + "minecraft:shelf_mushroom" + ], "minecraft:music_disc": [ "minecraft:music_disc_11", "minecraft:music_disc_13", "minecraft:music_disc_5", "minecraft:music_disc_blocks", + "minecraft:music_disc_bounce", "minecraft:music_disc_cat", "minecraft:music_disc_chirp", "minecraft:music_disc_creator", "minecraft:music_disc_creator_music_box", "minecraft:music_disc_far", + "minecraft:music_disc_lava_chicken", "minecraft:music_disc_mall", "minecraft:music_disc_mellohi", "minecraft:music_disc_otherside", @@ -498,18 +602,29 @@ "minecraft:music_disc_relic", "minecraft:music_disc_stal", "minecraft:music_disc_strad", + "minecraft:music_disc_tears", "minecraft:music_disc_wait", "minecraft:music_disc_ward" ], + "minecraft:nautilus_armor": [ + "minecraft:copper_nautilus_armor", + "minecraft:diamond_nautilus_armor", + "minecraft:golden_nautilus_armor", + "minecraft:iron_nautilus_armor", + "minecraft:netherite_nautilus_armor" + ], "minecraft:netherite_tier": [ "minecraft:netherite_axe", "minecraft:netherite_boots", "minecraft:netherite_chestplate", "minecraft:netherite_helmet", "minecraft:netherite_hoe", + "minecraft:netherite_horse_armor", "minecraft:netherite_leggings", + "minecraft:netherite_nautilus_armor", "minecraft:netherite_pickaxe", "minecraft:netherite_shovel", + "minecraft:netherite_spear", "minecraft:netherite_sword" ], "minecraft:planks": [ @@ -523,6 +638,7 @@ "minecraft:mangrove_planks", "minecraft:oak_planks", "minecraft:pale_oak_planks", + "minecraft:poplar_planks", "minecraft:spruce_planks", "minecraft:warped_planks" ], @@ -551,6 +667,8 @@ "minecraft:oak_sign", "minecraft:pale_oak_hanging_sign", "minecraft:pale_oak_sign", + "minecraft:poplar_hanging_sign", + "minecraft:poplar_sign", "minecraft:spruce_hanging_sign", "minecraft:spruce_sign", "minecraft:warped_hanging_sign", @@ -570,11 +688,13 @@ "minecraft:blaze_spawn_egg", "minecraft:bogged_spawn_egg", "minecraft:breeze_spawn_egg", + "minecraft:camel_husk_spawn_egg", "minecraft:camel_spawn_egg", "minecraft:cat_spawn_egg", "minecraft:cave_spider_spawn_egg", "minecraft:chicken_spawn_egg", "minecraft:cod_spawn_egg", + "minecraft:copper_golem_spawn_egg", "minecraft:cow_spawn_egg", "minecraft:creaking_spawn_egg", "minecraft:creeper_spawn_egg", @@ -592,6 +712,7 @@ "minecraft:glow_squid_spawn_egg", "minecraft:goat_spawn_egg", "minecraft:guardian_spawn_egg", + "minecraft:happy_ghast_spawn_egg", "minecraft:hoglin_spawn_egg", "minecraft:horse_spawn_egg", "minecraft:husk_spawn_egg", @@ -600,9 +721,11 @@ "minecraft:magma_cube_spawn_egg", "minecraft:mooshroom_spawn_egg", "minecraft:mule_spawn_egg", + "minecraft:nautilus_spawn_egg", "minecraft:npc_spawn_egg", "minecraft:ocelot_spawn_egg", "minecraft:panda_spawn_egg", + "minecraft:parched_spawn_egg", "minecraft:parrot_spawn_egg", "minecraft:phantom_spawn_egg", "minecraft:pig_spawn_egg", @@ -627,6 +750,7 @@ "minecraft:squid_spawn_egg", "minecraft:stray_spawn_egg", "minecraft:strider_spawn_egg", + "minecraft:sulfur_cube_spawn_egg", "minecraft:tadpole_spawn_egg", "minecraft:trader_llama_spawn_egg", "minecraft:tropical_fish_spawn_egg", @@ -642,6 +766,7 @@ "minecraft:wolf_spawn_egg", "minecraft:zoglin_spawn_egg", "minecraft:zombie_horse_spawn_egg", + "minecraft:zombie_nautilus_spawn_egg", "minecraft:zombie_pigman_spawn_egg", "minecraft:zombie_spawn_egg", "minecraft:zombie_villager_spawn_egg" @@ -662,6 +787,7 @@ "minecraft:stone_hoe", "minecraft:stone_pickaxe", "minecraft:stone_shovel", + "minecraft:stone_spear", "minecraft:stone_sword" ], "minecraft:stone_tool_materials": [ @@ -669,6 +795,367 @@ "minecraft:cobbled_deepslate", "minecraft:cobblestone" ], + "minecraft:sulfur_cube_archetype_bouncy": [ + "minecraft:acacia_log", + "minecraft:acacia_planks", + "minecraft:acacia_wood", + "minecraft:bamboo_block", + "minecraft:bamboo_mosaic", + "minecraft:bamboo_planks", + "minecraft:birch_log", + "minecraft:birch_planks", + "minecraft:birch_wood", + "minecraft:cherry_log", + "minecraft:cherry_planks", + "minecraft:cherry_wood", + "minecraft:crimson_hyphae", + "minecraft:crimson_planks", + "minecraft:crimson_stem", + "minecraft:dark_oak_log", + "minecraft:dark_oak_planks", + "minecraft:dark_oak_wood", + "minecraft:jungle_log", + "minecraft:jungle_planks", + "minecraft:jungle_wood", + "minecraft:mangrove_log", + "minecraft:mangrove_planks", + "minecraft:mangrove_wood", + "minecraft:oak_log", + "minecraft:oak_planks", + "minecraft:oak_wood", + "minecraft:pale_oak_log", + "minecraft:pale_oak_planks", + "minecraft:pale_oak_wood", + "minecraft:poplar_log", + "minecraft:poplar_planks", + "minecraft:poplar_wood", + "minecraft:spruce_log", + "minecraft:spruce_planks", + "minecraft:spruce_wood", + "minecraft:stripped_acacia_log", + "minecraft:stripped_acacia_wood", + "minecraft:stripped_bamboo_block", + "minecraft:stripped_birch_log", + "minecraft:stripped_birch_wood", + "minecraft:stripped_cherry_log", + "minecraft:stripped_cherry_wood", + "minecraft:stripped_crimson_hyphae", + "minecraft:stripped_crimson_stem", + "minecraft:stripped_dark_oak_log", + "minecraft:stripped_dark_oak_wood", + "minecraft:stripped_jungle_log", + "minecraft:stripped_jungle_wood", + "minecraft:stripped_mangrove_log", + "minecraft:stripped_mangrove_wood", + "minecraft:stripped_oak_log", + "minecraft:stripped_oak_wood", + "minecraft:stripped_pale_oak_log", + "minecraft:stripped_pale_oak_wood", + "minecraft:stripped_poplar_log", + "minecraft:stripped_poplar_wood", + "minecraft:stripped_spruce_log", + "minecraft:stripped_spruce_wood", + "minecraft:stripped_warped_hyphae", + "minecraft:stripped_warped_stem", + "minecraft:warped_hyphae", + "minecraft:warped_planks", + "minecraft:warped_stem" + ], + "minecraft:sulfur_cube_archetype_explosive": [ + "minecraft:tnt" + ], + "minecraft:sulfur_cube_archetype_fast_flat": [ + "minecraft:brain_coral_block", + "minecraft:bubble_coral_block", + "minecraft:carved_pumpkin", + "minecraft:chiseled_resin_bricks", + "minecraft:dead_brain_coral_block", + "minecraft:dead_bubble_coral_block", + "minecraft:dead_fire_coral_block", + "minecraft:dead_horn_coral_block", + "minecraft:dead_tube_coral_block", + "minecraft:dried_kelp_block", + "minecraft:fire_coral_block", + "minecraft:hay_block", + "minecraft:horn_coral_block", + "minecraft:lit_pumpkin", + "minecraft:melon_block", + "minecraft:moss_block", + "minecraft:ochre_froglight", + "minecraft:pale_moss_block", + "minecraft:pearlescent_froglight", + "minecraft:pumpkin", + "minecraft:resin_block", + "minecraft:resin_bricks", + "minecraft:sponge", + "minecraft:tube_coral_block", + "minecraft:verdant_froglight", + "minecraft:wet_sponge" + ], + "minecraft:sulfur_cube_archetype_fast_sliding": [ + "minecraft:blue_ice", + "minecraft:packed_ice", + "minecraft:snow" + ], + "minecraft:sulfur_cube_archetype_high_resistance": [ + "minecraft:soul_sand", + "minecraft:soul_soil" + ], + "minecraft:sulfur_cube_archetype_hot": [ + "minecraft:magma" + ], + "minecraft:sulfur_cube_archetype_light": [ + "minecraft:black_wool", + "minecraft:blue_wool", + "minecraft:brown_wool", + "minecraft:cyan_wool", + "minecraft:gray_wool", + "minecraft:green_wool", + "minecraft:light_blue_wool", + "minecraft:light_gray_wool", + "minecraft:lime_wool", + "minecraft:magenta_wool", + "minecraft:orange_wool", + "minecraft:pink_wool", + "minecraft:purple_wool", + "minecraft:red_wool", + "minecraft:white_wool", + "minecraft:yellow_wool" + ], + "minecraft:sulfur_cube_archetype_regular": [ + "minecraft:black_concrete_powder", + "minecraft:blue_concrete_powder", + "minecraft:bone_block", + "minecraft:brown_concrete_powder", + "minecraft:clay", + "minecraft:coal_block", + "minecraft:coarse_dirt", + "minecraft:cyan_concrete_powder", + "minecraft:dirt", + "minecraft:dirt_with_roots", + "minecraft:grass_block", + "minecraft:gray_concrete_powder", + "minecraft:green_concrete_powder", + "minecraft:light_blue_concrete_powder", + "minecraft:light_gray_concrete_powder", + "minecraft:lime_concrete_powder", + "minecraft:magenta_concrete_powder", + "minecraft:mud", + "minecraft:muddy_mangrove_roots", + "minecraft:orange_concrete_powder", + "minecraft:packed_mud", + "minecraft:pink_concrete_powder", + "minecraft:podzol", + "minecraft:purple_concrete_powder", + "minecraft:red_concrete_powder", + "minecraft:white_concrete_powder", + "minecraft:yellow_concrete_powder" + ], + "minecraft:sulfur_cube_archetype_slow_bouncy": [ + "minecraft:amethyst_block", + "minecraft:andesite", + "minecraft:basalt", + "minecraft:black_concrete", + "minecraft:black_glazed_terracotta", + "minecraft:black_terracotta", + "minecraft:blackstone", + "minecraft:blue_concrete", + "minecraft:blue_glazed_terracotta", + "minecraft:blue_terracotta", + "minecraft:brick_block", + "minecraft:brown_concrete", + "minecraft:brown_glazed_terracotta", + "minecraft:brown_terracotta", + "minecraft:calcite", + "minecraft:chiseled_cinnabar", + "minecraft:chiseled_deepslate", + "minecraft:chiseled_nether_bricks", + "minecraft:chiseled_polished_blackstone", + "minecraft:chiseled_quartz_block", + "minecraft:chiseled_red_sandstone", + "minecraft:chiseled_sandstone", + "minecraft:chiseled_stone_bricks", + "minecraft:chiseled_sulfur", + "minecraft:chiseled_tuff", + "minecraft:chiseled_tuff_bricks", + "minecraft:cinnabar", + "minecraft:cinnabar_bricks", + "minecraft:coal_ore", + "minecraft:cobbled_deepslate", + "minecraft:cobblestone", + "minecraft:cracked_deepslate_bricks", + "minecraft:cracked_deepslate_tiles", + "minecraft:cracked_nether_bricks", + "minecraft:cracked_polished_blackstone_bricks", + "minecraft:cracked_stone_bricks", + "minecraft:crimson_nylium", + "minecraft:crying_obsidian", + "minecraft:cut_red_sandstone", + "minecraft:cut_sandstone", + "minecraft:cyan_concrete", + "minecraft:cyan_glazed_terracotta", + "minecraft:cyan_terracotta", + "minecraft:dark_prismarine", + "minecraft:deepslate", + "minecraft:deepslate_bricks", + "minecraft:deepslate_coal_ore", + "minecraft:deepslate_diamond_ore", + "minecraft:deepslate_emerald_ore", + "minecraft:deepslate_lapis_ore", + "minecraft:deepslate_redstone_ore", + "minecraft:deepslate_tiles", + "minecraft:diamond_block", + "minecraft:diamond_ore", + "minecraft:diorite", + "minecraft:dripstone_block", + "minecraft:emerald_block", + "minecraft:emerald_ore", + "minecraft:end_bricks", + "minecraft:end_stone", + "minecraft:gilded_blackstone", + "minecraft:glowstone", + "minecraft:granite", + "minecraft:gray_concrete", + "minecraft:gray_glazed_terracotta", + "minecraft:gray_terracotta", + "minecraft:green_concrete", + "minecraft:green_glazed_terracotta", + "minecraft:green_terracotta", + "minecraft:hardened_clay", + "minecraft:lapis_block", + "minecraft:lapis_ore", + "minecraft:light_blue_concrete", + "minecraft:light_blue_glazed_terracotta", + "minecraft:light_blue_terracotta", + "minecraft:light_gray_concrete", + "minecraft:light_gray_terracotta", + "minecraft:lime_concrete", + "minecraft:lime_glazed_terracotta", + "minecraft:lime_terracotta", + "minecraft:magenta_concrete", + "minecraft:magenta_glazed_terracotta", + "minecraft:magenta_terracotta", + "minecraft:mossy_cobblestone", + "minecraft:mossy_stone_bricks", + "minecraft:mud_bricks", + "minecraft:nether_brick", + "minecraft:netherrack", + "minecraft:obsidian", + "minecraft:orange_concrete", + "minecraft:orange_glazed_terracotta", + "minecraft:orange_terracotta", + "minecraft:pink_concrete", + "minecraft:pink_glazed_terracotta", + "minecraft:pink_terracotta", + "minecraft:polished_andesite", + "minecraft:polished_basalt", + "minecraft:polished_blackstone", + "minecraft:polished_blackstone_bricks", + "minecraft:polished_cinnabar", + "minecraft:polished_deepslate", + "minecraft:polished_diorite", + "minecraft:polished_granite", + "minecraft:polished_sulfur", + "minecraft:polished_tuff", + "minecraft:prismarine", + "minecraft:prismarine_bricks", + "minecraft:purple_concrete", + "minecraft:purple_glazed_terracotta", + "minecraft:purple_terracotta", + "minecraft:purpur_block", + "minecraft:purpur_pillar", + "minecraft:quartz_block", + "minecraft:quartz_bricks", + "minecraft:quartz_ore", + "minecraft:quartz_pillar", + "minecraft:red_concrete", + "minecraft:red_glazed_terracotta", + "minecraft:red_nether_brick", + "minecraft:red_sandstone", + "minecraft:red_terracotta", + "minecraft:redstone_ore", + "minecraft:sandstone", + "minecraft:sea_lantern", + "minecraft:silver_glazed_terracotta", + "minecraft:smooth_basalt", + "minecraft:smooth_quartz", + "minecraft:smooth_red_sandstone", + "minecraft:smooth_sandstone", + "minecraft:smooth_stone", + "minecraft:stone", + "minecraft:stone_bricks", + "minecraft:sulfur", + "minecraft:sulfur_bricks", + "minecraft:tuff", + "minecraft:tuff_bricks", + "minecraft:warped_nylium", + "minecraft:white_concrete", + "minecraft:white_glazed_terracotta", + "minecraft:white_terracotta", + "minecraft:yellow_concrete", + "minecraft:yellow_glazed_terracotta", + "minecraft:yellow_terracotta" + ], + "minecraft:sulfur_cube_archetype_slow_flat": [ + "minecraft:ancient_debris", + "minecraft:chiseled_copper", + "minecraft:copper_block", + "minecraft:copper_bulb", + "minecraft:copper_ore", + "minecraft:cut_copper", + "minecraft:deepslate_copper_ore", + "minecraft:deepslate_gold_ore", + "minecraft:deepslate_iron_ore", + "minecraft:exposed_chiseled_copper", + "minecraft:exposed_copper", + "minecraft:exposed_copper_bulb", + "minecraft:exposed_cut_copper", + "minecraft:gold_block", + "minecraft:gold_ore", + "minecraft:iron_block", + "minecraft:iron_ore", + "minecraft:nether_gold_ore", + "minecraft:netherite_block", + "minecraft:oxidized_chiseled_copper", + "minecraft:oxidized_copper", + "minecraft:oxidized_copper_bulb", + "minecraft:oxidized_cut_copper", + "minecraft:raw_copper_block", + "minecraft:raw_gold_block", + "minecraft:raw_iron_block", + "minecraft:waxed_chiseled_copper", + "minecraft:waxed_copper", + "minecraft:waxed_copper_bulb", + "minecraft:waxed_cut_copper", + "minecraft:waxed_exposed_chiseled_copper", + "minecraft:waxed_exposed_copper", + "minecraft:waxed_exposed_copper_bulb", + "minecraft:waxed_exposed_cut_copper", + "minecraft:waxed_oxidized_chiseled_copper", + "minecraft:waxed_oxidized_copper", + "minecraft:waxed_oxidized_copper_bulb", + "minecraft:waxed_oxidized_cut_copper", + "minecraft:waxed_weathered_chiseled_copper", + "minecraft:waxed_weathered_copper", + "minecraft:waxed_weathered_copper_bulb", + "minecraft:waxed_weathered_cut_copper", + "minecraft:weathered_chiseled_copper", + "minecraft:weathered_copper", + "minecraft:weathered_copper_bulb", + "minecraft:weathered_cut_copper" + ], + "minecraft:sulfur_cube_archetype_slow_sliding": [ + "minecraft:brown_mushroom_block", + "minecraft:mushroom_stem", + "minecraft:mycelium", + "minecraft:nether_wart_block", + "minecraft:red_mushroom_block", + "minecraft:shroomlight", + "minecraft:warped_wart_block" + ], + "minecraft:sulfur_cube_archetype_sticky": [ + "minecraft:honeycomb_block" + ], "minecraft:transform_materials": [ "minecraft:netherite_ingot" ], @@ -681,9 +1168,12 @@ "minecraft:diamond_chestplate", "minecraft:diamond_helmet", "minecraft:diamond_hoe", + "minecraft:diamond_horse_armor", "minecraft:diamond_leggings", + "minecraft:diamond_nautilus_armor", "minecraft:diamond_pickaxe", "minecraft:diamond_shovel", + "minecraft:diamond_spear", "minecraft:diamond_sword", "minecraft:golden_boots" ], @@ -725,6 +1215,10 @@ "minecraft:chainmail_chestplate", "minecraft:chainmail_helmet", "minecraft:chainmail_leggings", + "minecraft:copper_boots", + "minecraft:copper_chestplate", + "minecraft:copper_helmet", + "minecraft:copper_leggings", "minecraft:diamond_boots", "minecraft:diamond_chestplate", "minecraft:diamond_helmet", @@ -750,36 +1244,68 @@ "minecraft:vibration_damper": [ "minecraft:black_carpet", "minecraft:black_wool", + "minecraft:black_wool_slab", + "minecraft:black_wool_stairs", "minecraft:blue_carpet", "minecraft:blue_wool", + "minecraft:blue_wool_slab", + "minecraft:blue_wool_stairs", "minecraft:brown_carpet", "minecraft:brown_wool", + "minecraft:brown_wool_slab", + "minecraft:brown_wool_stairs", "minecraft:cyan_carpet", "minecraft:cyan_wool", + "minecraft:cyan_wool_slab", + "minecraft:cyan_wool_stairs", "minecraft:gray_carpet", "minecraft:gray_wool", + "minecraft:gray_wool_slab", + "minecraft:gray_wool_stairs", "minecraft:green_carpet", "minecraft:green_wool", + "minecraft:green_wool_slab", + "minecraft:green_wool_stairs", "minecraft:light_blue_carpet", "minecraft:light_blue_wool", + "minecraft:light_blue_wool_slab", + "minecraft:light_blue_wool_stairs", "minecraft:light_gray_carpet", "minecraft:light_gray_wool", + "minecraft:light_gray_wool_slab", + "minecraft:light_gray_wool_stairs", "minecraft:lime_carpet", "minecraft:lime_wool", + "minecraft:lime_wool_slab", + "minecraft:lime_wool_stairs", "minecraft:magenta_carpet", "minecraft:magenta_wool", + "minecraft:magenta_wool_slab", + "minecraft:magenta_wool_stairs", "minecraft:orange_carpet", "minecraft:orange_wool", + "minecraft:orange_wool_slab", + "minecraft:orange_wool_stairs", "minecraft:pink_carpet", "minecraft:pink_wool", + "minecraft:pink_wool_slab", + "minecraft:pink_wool_stairs", "minecraft:purple_carpet", "minecraft:purple_wool", + "minecraft:purple_wool_slab", + "minecraft:purple_wool_stairs", "minecraft:red_carpet", "minecraft:red_wool", + "minecraft:red_wool_slab", + "minecraft:red_wool_stairs", "minecraft:white_carpet", "minecraft:white_wool", + "minecraft:white_wool_slab", + "minecraft:white_wool_stairs", "minecraft:yellow_carpet", - "minecraft:yellow_wool" + "minecraft:yellow_wool", + "minecraft:yellow_wool_slab", + "minecraft:yellow_wool_stairs" ], "minecraft:warped_stems": [ "minecraft:stripped_warped_hyphae", @@ -798,6 +1324,7 @@ "minecraft:mangrove_slab", "minecraft:oak_slab", "minecraft:pale_oak_slab", + "minecraft:poplar_slab", "minecraft:spruce_slab", "minecraft:warped_slab" ], @@ -806,6 +1333,7 @@ "minecraft:wooden_hoe", "minecraft:wooden_pickaxe", "minecraft:wooden_shovel", + "minecraft:wooden_spear", "minecraft:wooden_sword" ], "minecraft:wool": [ diff --git a/server/item/recipe/potion_data.nbt b/server/item/recipe/potion_data.nbt index 465959b198..731d8d043e 100644 Binary files a/server/item/recipe/potion_data.nbt and b/server/item/recipe/potion_data.nbt differ diff --git a/server/server.go b/server/server.go index 574515bc58..42775b896d 100644 --- a/server/server.go +++ b/server/server.go @@ -396,15 +396,18 @@ func (srv *Server) startListening() { // registered custom blocks. It allows block components to be created only once // at startup. func (srv *Server) makeBlockEntries() { - custom := slices.Collect(maps.Values(srv.conf.Blocks.CustomBlocks())) - srv.customBlocks = make([]protocol.BlockEntry, len(custom)) + custom, dataDriven := slices.Collect(maps.Values(srv.conf.Blocks.CustomBlocks())), world.DataDrivenBlocks() + srv.customBlocks = make([]protocol.BlockEntry, 0, len(dataDriven)+len(custom)) + for _, b := range dataDriven { + srv.customBlocks = append(srv.customBlocks, protocol.BlockEntry{Name: b.Name, Properties: b.Components}) + } for i, b := range custom { name, _ := b.EncodeBlock() - srv.customBlocks[i] = protocol.BlockEntry{ + srv.customBlocks = append(srv.customBlocks, protocol.BlockEntry{ Name: name, Properties: blockinternal.Components(name, b, 10000+int32(i)), - } + }) } } @@ -441,7 +444,8 @@ func (srv *Server) makeDimensionData() { r := registration.Dimension.Range() srv.customDimensions = append(srv.customDimensions, protocol.DimensionDefinition{ Name: registration.Name, - Range: [2]int32{int32(r.Max() + 1), int32(r.Min())}, + MinimumY: int32(r.Min()), + HeightRange: int32(r.Max() - r.Min()), Generator: protocol.GeneratorVoid, DimensionType: int32(registration.ID), }) diff --git a/server/session/chunk.go b/server/session/chunk.go index ef765bd2dd..dac46a613b 100644 --- a/server/session/chunk.go +++ b/server/session/chunk.go @@ -63,11 +63,12 @@ func (s *Session) ViewSubChunks(centre world.SubChunkPos, offsets []protocol.Sub func (s *Session) subChunkEntry(offset protocol.SubChunkOffset, ind int16, col *world.Column, transaction map[uint64]struct{}) protocol.SubChunkEntry { chunkMap := col.HeightMap() - subMapType, subMap := byte(protocol.HeightMapDataHasData), make([]int8, 256) + subMapType, subMap := byte(protocol.HeightMapDataHasData), make([]int8, 272) higher, lower := true, true - for x := uint8(0); x < 16; x++ { - for z := uint8(0); z < 16; z++ { - y, i := chunkMap.At(x, z), (uint16(z)<<4)|uint16(x) + for z := uint8(0); z < 16; z++ { + subMap[uint16(z)*17] = 16 + for x := uint8(0); x < 16; x++ { + y, i := chunkMap.At(x, z), (uint16(z)*17)+uint16(x)+1 otherInd := col.SubIndex(y) switch { case otherInd > ind: diff --git a/server/session/handler_player_action.go b/server/session/handler_player_action.go index 4d38d9906f..0c70cb8918 100644 --- a/server/session/handler_player_action.go +++ b/server/session/handler_player_action.go @@ -48,7 +48,7 @@ func handlePlayerAction(action int32, face int32, pos protocol.BlockPos, entityR // TODO: Properly utilize these actions. case protocol.PlayerActionStopItemUseOn: c.ReleaseItem() - case protocol.PlayerActionStartBuildingBlock: + case protocol.PlayerActionInteractWithBlock: // Don't do anything for this action. case protocol.PlayerActionCreativePlayerDestroyBlock: // Don't do anything for this action. diff --git a/server/session/session.go b/server/session/session.go index 3154c241e2..94c53c9b39 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -608,6 +608,7 @@ func (s *Session) registerHandlers() { packet.IDRequestAbility: &RequestAbilityHandler{}, packet.IDRequestChunkRadius: &RequestChunkRadiusHandler{}, packet.IDRespawn: &RespawnHandler{}, + packet.IDSetPlayerFurnaceOptions: nil, packet.IDSetPlayerInventoryOptions: nil, packet.IDSubChunkRequest: &SubChunkRequestHandler{}, packet.IDText: &TextHandler{}, diff --git a/server/world/biome/bamboo_jungle.go b/server/world/biome/bamboo_jungle.go index 6f9944d61a..3a31cb713b 100644 --- a/server/world/biome/bamboo_jungle.go +++ b/server/world/biome/bamboo_jungle.go @@ -32,7 +32,7 @@ func (BambooJungle) WaterColour() color.RGBA { // Tags ... func (BambooJungle) Tags() []string { - return []string{"animal", "bamboo", "jungle", "monster", "overworld", "spawns_jungle_mobs", "spawns_warm_variant_farm_animals"} + return []string{"animal", "bamboo", "jungle", "monster", "overworld", "spawns_jungle_mobs", "spawns_warm_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/birch_forest.go b/server/world/biome/birch_forest.go index 16a792ef66..766631f695 100644 --- a/server/world/biome/birch_forest.go +++ b/server/world/biome/birch_forest.go @@ -32,7 +32,7 @@ func (BirchForest) WaterColour() color.RGBA { // Tags ... func (BirchForest) Tags() []string { - return []string{"animal", "birch", "forest", "monster", "overworld", "bee_habitat"} + return []string{"animal", "birch", "forest", "monster", "overworld", "bee_habitat", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/cherry_grove.go b/server/world/biome/cherry_grove.go index d9a1a205d9..c9ca6c85ad 100644 --- a/server/world/biome/cherry_grove.go +++ b/server/world/biome/cherry_grove.go @@ -32,7 +32,7 @@ func (CherryGrove) WaterColour() color.RGBA { // Tags ... func (CherryGrove) Tags() []string { - return []string{"mountains", "monster", "overworld", "cherry_grove", "bee_habitat"} + return []string{"mountains", "monster", "overworld", "cherry_grove", "bee_habitat", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/dappled_forest.go b/server/world/biome/dappled_forest.go new file mode 100644 index 0000000000..42846b0686 --- /dev/null +++ b/server/world/biome/dappled_forest.go @@ -0,0 +1,46 @@ +package biome + +import "image/color" + +// DappledForest ... +type DappledForest struct{} + +// Temperature ... +func (DappledForest) Temperature() float64 { + return 0.6 +} + +// Rainfall ... +func (DappledForest) Rainfall() float64 { + return 0.6 +} + +// Depth ... +func (DappledForest) Depth() float64 { + return 0.1 +} + +// Scale ... +func (DappledForest) Scale() float64 { + return 0.2 +} + +// WaterColour ... +func (DappledForest) WaterColour() color.RGBA { + return color.RGBA{R: 0x60, G: 0xb7, B: 0xff, A: 0xa6} +} + +// Tags ... +func (DappledForest) Tags() []string { + return []string{"animal", "dappled_forest", "monster", "forest", "overworld", "spawns_cold_variant_farm_animals", "has_structure_abandoned_camp"} +} + +// String ... +func (DappledForest) String() string { + return "dappled_forest" +} + +// EncodeBiome ... +func (DappledForest) EncodeBiome() int { + return 195 +} diff --git a/server/world/biome/deep_lukewarm_ocean.go b/server/world/biome/deep_lukewarm_ocean.go index 3f18e8da81..c9319ca35b 100644 --- a/server/world/biome/deep_lukewarm_ocean.go +++ b/server/world/biome/deep_lukewarm_ocean.go @@ -32,7 +32,7 @@ func (DeepLukewarmOcean) WaterColour() color.RGBA { // Tags ... func (DeepLukewarmOcean) Tags() []string { - return []string{"deep", "lukewarm", "monster", "ocean", "overworld", "spawns_warm_variant_farm_animals", "fast_fishing", "high_seas"} + return []string{"deep", "lukewarm", "monster", "ocean", "overworld", "spawns_warm_variant_farm_animals", "fast_fishing", "high_seas", "temperate_ocean"} } // String ... diff --git a/server/world/biome/deep_warm_ocean.go b/server/world/biome/deep_warm_ocean.go index e0efa55e7b..9c799b401a 100644 --- a/server/world/biome/deep_warm_ocean.go +++ b/server/world/biome/deep_warm_ocean.go @@ -32,7 +32,7 @@ func (DeepWarmOcean) WaterColour() color.RGBA { // Tags ... func (DeepWarmOcean) Tags() []string { - return []string{"deep", "monster", "ocean", "overworld", "warm", "spawns_warm_variant_farm_animals", "fast_fishing", "high_seas"} + return []string{"deep", "monster", "ocean", "overworld", "warm", "spawns_warm_variant_farm_animals", "fast_fishing", "high_seas", "temperate_ocean"} } // String ... diff --git a/server/world/biome/flower_forest.go b/server/world/biome/flower_forest.go index fd109d9033..bd797ff60c 100644 --- a/server/world/biome/flower_forest.go +++ b/server/world/biome/flower_forest.go @@ -32,7 +32,7 @@ func (FlowerForest) WaterColour() color.RGBA { // Tags ... func (FlowerForest) Tags() []string { - return []string{"animal", "flower_forest", "monster", "mutated", "overworld", "bee_habitat"} + return []string{"animal", "flower_forest", "monster", "mutated", "overworld", "bee_habitat", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/forest.go b/server/world/biome/forest.go index 14ac8696ae..5ddbb266eb 100644 --- a/server/world/biome/forest.go +++ b/server/world/biome/forest.go @@ -32,7 +32,7 @@ func (Forest) WaterColour() color.RGBA { // Tags ... func (Forest) Tags() []string { - return []string{"animal", "forest", "monster", "overworld", "bee_habitat"} + return []string{"animal", "forest", "monster", "overworld", "bee_habitat", "has_structure_abandoned_camp", "oak_forest"} } // String ... diff --git a/server/world/biome/jungle_edge.go b/server/world/biome/jungle_edge.go index 7aa4f0184d..aa00ecd631 100644 --- a/server/world/biome/jungle_edge.go +++ b/server/world/biome/jungle_edge.go @@ -32,7 +32,7 @@ func (JungleEdge) WaterColour() color.RGBA { // Tags ... func (JungleEdge) Tags() []string { - return []string{"animal", "edge", "jungle", "monster", "overworld", "spawns_jungle_mobs", "spawns_warm_variant_farm_animals"} + return []string{"animal", "edge", "jungle", "monster", "overworld", "spawns_jungle_mobs", "spawns_warm_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/lukewarm_ocean.go b/server/world/biome/lukewarm_ocean.go index 382a0c1401..04ada3b5dd 100644 --- a/server/world/biome/lukewarm_ocean.go +++ b/server/world/biome/lukewarm_ocean.go @@ -32,7 +32,7 @@ func (LukewarmOcean) WaterColour() color.RGBA { // Tags ... func (LukewarmOcean) Tags() []string { - return []string{"lukewarm", "monster", "ocean", "overworld", "spawns_warm_variant_farm_animals", "fast_fishing", "high_seas"} + return []string{"lukewarm", "monster", "ocean", "overworld", "spawns_warm_variant_farm_animals", "fast_fishing", "high_seas", "temperate_ocean"} } // String ... diff --git a/server/world/biome/meadow.go b/server/world/biome/meadow.go index f17c6a48f2..a72aee0096 100644 --- a/server/world/biome/meadow.go +++ b/server/world/biome/meadow.go @@ -32,7 +32,7 @@ func (Meadow) WaterColour() color.RGBA { // Tags ... func (Meadow) Tags() []string { - return []string{"mountains", "monster", "overworld", "meadow", "bee_habitat"} + return []string{"mountains", "monster", "overworld", "meadow", "bee_habitat", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/old_growth_birch_forest.go b/server/world/biome/old_growth_birch_forest.go index 40314e8883..f423952f98 100644 --- a/server/world/biome/old_growth_birch_forest.go +++ b/server/world/biome/old_growth_birch_forest.go @@ -32,7 +32,7 @@ func (OldGrowthBirchForest) WaterColour() color.RGBA { // Tags ... func (OldGrowthBirchForest) Tags() []string { - return []string{"animal", "birch", "forest", "monster", "mutated", "bee_habitat", "overworld_generation", "has_structure_trail_ruins"} + return []string{"animal", "birch", "forest", "monster", "mutated", "bee_habitat", "overworld_generation", "has_structure_trail_ruins", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/old_growth_pine_taiga.go b/server/world/biome/old_growth_pine_taiga.go index c967280efc..123539d9e6 100644 --- a/server/world/biome/old_growth_pine_taiga.go +++ b/server/world/biome/old_growth_pine_taiga.go @@ -32,7 +32,7 @@ func (OldGrowthPineTaiga) WaterColour() color.RGBA { // Tags ... func (OldGrowthPineTaiga) Tags() []string { - return []string{"animal", "forest", "mega", "monster", "overworld", "rare", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals"} + return []string{"animal", "forest", "mega", "monster", "overworld", "rare", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/old_growth_spruce_taiga.go b/server/world/biome/old_growth_spruce_taiga.go index e3f452c3be..c6296cb0f5 100644 --- a/server/world/biome/old_growth_spruce_taiga.go +++ b/server/world/biome/old_growth_spruce_taiga.go @@ -32,7 +32,7 @@ func (OldGrowthSpruceTaiga) WaterColour() color.RGBA { // Tags ... func (OldGrowthSpruceTaiga) Tags() []string { - return []string{"animal", "forest", "mega", "monster", "mutated", "overworld", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals"} + return []string{"animal", "forest", "mega", "monster", "mutated", "overworld", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/pale_garden.go b/server/world/biome/pale_garden.go index 200ff50041..08b58aabd0 100644 --- a/server/world/biome/pale_garden.go +++ b/server/world/biome/pale_garden.go @@ -32,7 +32,7 @@ func (PaleGarden) WaterColour() color.RGBA { // Tags ... func (PaleGarden) Tags() []string { - return []string{"monster", "overworld", "pale_garden"} + return []string{"monster", "overworld", "pale_garden", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/register.go b/server/world/biome/register.go index d5b5aec45b..afec76b8cd 100644 --- a/server/world/biome/register.go +++ b/server/world/biome/register.go @@ -19,6 +19,7 @@ func init() { world.RegisterBiome(CherryGrove{}) world.RegisterBiome(ColdOcean{}) world.RegisterBiome(CrimsonForest{}) + world.RegisterBiome(DappledForest{}) world.RegisterBiome(DarkForestHills{}) world.RegisterBiome(DarkForest{}) world.RegisterBiome(DeepColdOcean{}) diff --git a/server/world/biome/savanna.go b/server/world/biome/savanna.go index beca598f2b..4b3fb9f3ed 100644 --- a/server/world/biome/savanna.go +++ b/server/world/biome/savanna.go @@ -32,7 +32,7 @@ func (Savanna) WaterColour() color.RGBA { // Tags ... func (Savanna) Tags() []string { - return []string{"animal", "monster", "overworld", "savanna", "spawns_savanna_mobs", "spawns_warm_variant_farm_animals"} + return []string{"animal", "monster", "overworld", "savanna", "spawns_savanna_mobs", "spawns_warm_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/shattered_savanna_plateau.go b/server/world/biome/shattered_savanna_plateau.go index c152aa5a46..4ef883ad14 100644 --- a/server/world/biome/shattered_savanna_plateau.go +++ b/server/world/biome/shattered_savanna_plateau.go @@ -22,7 +22,7 @@ func (ShatteredSavannaPlateau) Depth() float64 { // Scale ... func (ShatteredSavannaPlateau) Scale() float64 { - return 1.212 + return 1.2125 } // WaterColour ... diff --git a/server/world/biome/snowy_taiga.go b/server/world/biome/snowy_taiga.go index d539d7c08c..40134acf00 100644 --- a/server/world/biome/snowy_taiga.go +++ b/server/world/biome/snowy_taiga.go @@ -32,7 +32,7 @@ func (SnowyTaiga) WaterColour() color.RGBA { // Tags ... func (SnowyTaiga) Tags() []string { - return []string{"animal", "cold", "forest", "monster", "overworld", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals"} + return []string{"animal", "cold", "forest", "monster", "overworld", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/swamp.go b/server/world/biome/swamp.go index 1e2f793aac..0a11c9aa7c 100644 --- a/server/world/biome/swamp.go +++ b/server/world/biome/swamp.go @@ -32,7 +32,7 @@ func (Swamp) WaterColour() color.RGBA { // Tags ... func (Swamp) Tags() []string { - return []string{"animal", "monster", "overworld", "swamp", "spawns_slimes_on_surface", "slime", "swamp_water_huge_mushroom"} + return []string{"animal", "monster", "overworld", "swamp", "spawns_slimes_on_surface", "slime", "swamp_water_huge_mushroom", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/taiga.go b/server/world/biome/taiga.go index 06f41e006c..3599660a90 100644 --- a/server/world/biome/taiga.go +++ b/server/world/biome/taiga.go @@ -32,7 +32,7 @@ func (Taiga) WaterColour() color.RGBA { // Tags ... func (Taiga) Tags() []string { - return []string{"animal", "forest", "monster", "overworld", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals"} + return []string{"animal", "forest", "monster", "overworld", "taiga", "has_structure_trail_ruins", "spawns_cold_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/warm_ocean.go b/server/world/biome/warm_ocean.go index 17256962ae..7609c9632e 100644 --- a/server/world/biome/warm_ocean.go +++ b/server/world/biome/warm_ocean.go @@ -32,7 +32,7 @@ func (WarmOcean) WaterColour() color.RGBA { // Tags ... func (WarmOcean) Tags() []string { - return []string{"monster", "ocean", "overworld", "warm", "spawns_warm_variant_farm_animals", "spawns_warm_variant_frogs", "fast_fishing", "high_seas"} + return []string{"monster", "ocean", "overworld", "warm", "spawns_warm_variant_farm_animals", "spawns_warm_variant_frogs", "fast_fishing", "high_seas", "temperate_ocean"} } // String ... diff --git a/server/world/biome/windswept_forest.go b/server/world/biome/windswept_forest.go index 974f5ffef9..f911abf6d9 100644 --- a/server/world/biome/windswept_forest.go +++ b/server/world/biome/windswept_forest.go @@ -32,7 +32,7 @@ func (WindsweptForest) WaterColour() color.RGBA { // Tags ... func (WindsweptForest) Tags() []string { - return []string{"animal", "extreme_hills", "forest", "monster", "mountain", "overworld", "spawns_cold_variant_farm_animals"} + return []string{"animal", "extreme_hills", "forest", "monster", "mountain", "overworld", "spawns_cold_variant_farm_animals", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome/windswept_savanna.go b/server/world/biome/windswept_savanna.go index 9252d82214..c37a71986c 100644 --- a/server/world/biome/windswept_savanna.go +++ b/server/world/biome/windswept_savanna.go @@ -17,7 +17,7 @@ func (WindsweptSavanna) Rainfall() float64 { // Depth ... func (WindsweptSavanna) Depth() float64 { - return 0.363 + return 0.3625 } // Scale ... diff --git a/server/world/biome/wooded_badlands_plateau.go b/server/world/biome/wooded_badlands_plateau.go index ad758c5093..7856ea23ed 100644 --- a/server/world/biome/wooded_badlands_plateau.go +++ b/server/world/biome/wooded_badlands_plateau.go @@ -32,7 +32,7 @@ func (WoodedBadlandsPlateau) WaterColour() color.RGBA { // Tags ... func (WoodedBadlandsPlateau) Tags() []string { - return []string{"animal", "mesa", "monster", "overworld", "plateau", "rare", "stone", "spawns_mesa_mobs", "spawns_warm_variant_farm_animals", "surface_mineshaft"} + return []string{"animal", "mesa", "monster", "overworld", "plateau", "rare", "stone", "spawns_mesa_mobs", "spawns_warm_variant_farm_animals", "surface_mineshaft", "has_structure_abandoned_camp"} } // String ... diff --git a/server/world/biome_definition.go b/server/world/biome_definition.go index 59525210b8..ee01062111 100644 --- a/server/world/biome_definition.go +++ b/server/world/biome_definition.go @@ -2,6 +2,7 @@ package world import ( "encoding/binary" + "slices" "github.com/sandertv/gophertunnel/minecraft/protocol" ) @@ -42,10 +43,20 @@ func BiomeDefinitions() ([]protocol.BiomeDefinition, []string) { return index } + ids := make([]int, 0, len(biomes)) + for id := range biomes { + ids = append(ids, id) + } + slices.Sort(ids) + + for _, id := range ids { + intern(biomes[id].String()) + } + encodedBiomes := make([]protocol.BiomeDefinition, 0, len(biomes)) - for _, b := range biomes { + for _, id := range ids { + b := biomes[id] nameIndex := intern(b.String()) - tags := b.Tags() tagIndices := make([]uint16, len(tags)) for i, tag := range tags { @@ -53,7 +64,6 @@ func BiomeDefinitions() ([]protocol.BiomeDefinition, []string) { } var biomeID int16 = -1 - id := b.EncodeBiome() if id > maxVanillaBiomeID { biomeID = int16(id) } diff --git a/server/world/block_state.go b/server/world/block_state.go index a58c56ab6c..01c123bdd0 100644 --- a/server/world/block_state.go +++ b/server/world/block_state.go @@ -16,6 +16,10 @@ import ( var ( //go:embed block_states.nbt blockStateData []byte + + //go:embed data_driven_blocks.nbt + dataDrivenBlockData []byte + dataDrivenBlocks []DataDrivenBlock ) func init() { @@ -30,6 +34,26 @@ func init() { } DefaultBlockRegistry.RegisterBlockState(s) } + + var m struct { + Blocks []DataDrivenBlock `nbt:"blocks"` + } + if err := nbt.NewDecoderWithEncoding(bytes.NewBuffer(dataDrivenBlockData), nbt.LittleEndian).Decode(&m); err != nil { + panic(err) + } + dataDrivenBlocks = m.Blocks +} + +// DataDrivenBlock holds the name of a block and the components that define it. +type DataDrivenBlock struct { + Name string `nbt:"name"` + Components map[string]any `nbt:"components"` +} + +// DataDrivenBlocks returns the vanilla data driven blocks. A client needs +// these to complete its block palette. +func DataDrivenBlocks() []DataDrivenBlock { + return dataDrivenBlocks } // BlockState holds a combination of a name and properties, together with a version. diff --git a/server/world/block_states.nbt b/server/world/block_states.nbt index 7006d9d462..ee29e5e039 100644 Binary files a/server/world/block_states.nbt and b/server/world/block_states.nbt differ diff --git a/server/world/data_driven_blocks.nbt b/server/world/data_driven_blocks.nbt new file mode 100644 index 0000000000..4271381a58 Binary files /dev/null and b/server/world/data_driven_blocks.nbt differ diff --git a/server/world/vanilla_items.nbt b/server/world/vanilla_items.nbt index ec858bab04..f5a06ce962 100644 Binary files a/server/world/vanilla_items.nbt and b/server/world/vanilla_items.nbt differ