diff --git a/go.mod b/go.mod index 9afcbf709..381b9dfcd 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,11 @@ require ( github.com/brentp/intintmap v0.0.0-20251106190759-56907b1f8479 github.com/cespare/xxhash/v2 v2.3.0 github.com/df-mc/goleveldb v1.1.9 - github.com/df-mc/worldupgrader v1.0.20 + 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.56.2 + github.com/sandertv/gophertunnel v1.57.0 github.com/segmentio/fasthash v1.0.3 golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 golang.org/x/mod v0.22.0 diff --git a/go.sum b/go.sum index 17f22d869..4bc90dae7 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/df-mc/goleveldb v1.1.9 h1:ihdosZyy5jkQKrxucTQmN90jq/2lUwQnJZjIYIC/9YU github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk= github.com/df-mc/jsonc v1.0.5 h1:O7oh07kbS5AYY+l2Fji6l4h0iHcdjKbxCtK5VlZlLMU= github.com/df-mc/jsonc v1.0.5/go.mod h1:+Q++JuCE9IKiP8v7sWImdf/RjQX0nfXyfX6PdfTTmc4= -github.com/df-mc/worldupgrader v1.0.20 h1:wfJyG3bFeaM/HXy7TCiO4HKVw3Mf3N4gPFmgxMHsKnc= -github.com/df-mc/worldupgrader v1.0.20/go.mod h1:tsSOLTRm9mpG7VHvYpAjjZrkRHWmSbKZAm9bOLNnlDk= +github.com/df-mc/worldupgrader v1.0.21 h1:Qr4/QB8ek7En0vkTuRXYq4FrZM0HHSOXsJOL7Ko4Cjg= +github.com/df-mc/worldupgrader v1.0.21/go.mod h1:tsSOLTRm9mpG7VHvYpAjjZrkRHWmSbKZAm9bOLNnlDk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-gl/mathgl v1.2.0 h1:v2eOj/y1B2afDxF6URV1qCYmo1KW08lAMtTbOn3KXCY= github.com/go-gl/mathgl v1.2.0/go.mod h1:pf9+b5J3LFP7iZ4XXaVzZrCle0Q/vNpB/vDe5+3ulRE= @@ -41,8 +41,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/sandertv/go-raknet v1.15.1-0.20260112202637-beca0b10c217 h1:UZQq2253Q+7co/C9Et62RYPBggzz+L+2yqGlvQhSNM8= github.com/sandertv/go-raknet v1.15.1-0.20260112202637-beca0b10c217/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU= -github.com/sandertv/gophertunnel v1.56.2 h1:eFc58AkMQo43ntR0Wmvz8GRFSdOgABKVDn52GMbIYag= -github.com/sandertv/gophertunnel v1.56.2/go.mod h1:F8+ZPbzxJ0LqunXEaDjqeyUgHVB0rI5ZU+PHnptXGfI= +github.com/sandertv/gophertunnel v1.57.0 h1:UkgVg1xLCsOSm79rP09WmodGSHgA8M7+l4quL01cIL8= +github.com/sandertv/gophertunnel v1.57.0/go.mod h1:W4VnrX9AIPIVXNDMEIKMIRj1T80EdOgdqXpGbQpyAbE= github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588= diff --git a/server/item/creative/creative_items.nbt b/server/item/creative/creative_items.nbt index 8ebb5d953..ebb532f65 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 800aab6e3..350fc0c12 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/potion_data.nbt b/server/item/recipe/potion_data.nbt index 980737cf3..3b9f707eb 100644 Binary files a/server/item/recipe/potion_data.nbt and b/server/item/recipe/potion_data.nbt differ diff --git a/server/player/bossbar/colour.go b/server/player/bossbar/colour.go index 62af54060..56ef6746e 100644 --- a/server/player/bossbar/colour.go +++ b/server/player/bossbar/colour.go @@ -33,8 +33,8 @@ func Purple() Colour { return Colour{colour(5)} } -// DarkPurple is the colour for a dark purple boss bar. -func DarkPurple() Colour { +// RebeccaPurple is the colour for a rebecca purple boss bar. +func RebeccaPurple() Colour { return Colour{colour(6)} } diff --git a/server/player/debug/shape.go b/server/player/debug/shape.go index 75438c2e9..66bcc9b1e 100644 --- a/server/player/debug/shape.go +++ b/server/player/debug/shape.go @@ -161,3 +161,95 @@ type Text struct { // Entity is an optional entity handle to attach the shape to. Entity *world.EntityHandle } + +// Cylinder represents a hollow cylinder, or frustum, that can be drawn at any point in the world, with a +// height running up the Y axis. The base and top each have their own radius on the X and Z axes, allowing +// for tapered and elliptical cylinders. A Cone is the special case of a Cylinder with a zero top radius. +type Cylinder struct { + shape + + // Colour is the colour that will be used for the outline. If empty, it will default to white. + Colour color.RGBA + // Position is the origin position of the shape in the world. + Position mgl64.Vec3 + // Scale is the rate to scale the shape from its origin point. If zero, it will default to 1.0. + Scale float64 + // BaseRadius is the radius of the cylinder's base along the X and Z axes. If empty, it will default to a + // radius of 1.0 on each axis. Differing X and Z radii produce an elliptical cylinder. + BaseRadius mgl64.Vec2 + // TopRadius is the radius of the cylinder's top along the X and Z axes. If empty, it will default to + // BaseRadius, producing a straight cylinder. A smaller TopRadius tapers the cylinder into a frustum. + TopRadius mgl64.Vec2 + // Height is the height of the cylinder. If zero, it will default to 1.0. + Height float64 + // Segments is the number of segments that the cylinder will be drawn with. The more segments, the + // smoother the cylinder will look. If zero, it will default to 20. + Segments int + // Entity is an optional entity handle to attach the shape to. + Entity *world.EntityHandle +} + +// Pyramid represents a pyramid that can be drawn at any point in the world, with a base on the X and Z axes +// and a height running up the Y axis to a single apex. +type Pyramid struct { + shape + + // Colour is the colour that will be used for the outline. If empty, it will default to white. + Colour color.RGBA + // Position is the origin position of the shape in the world. + Position mgl64.Vec3 + // Scale is the rate to scale the shape from its origin point. If zero, it will default to 1.0. + Scale float64 + // Width is the width along the X axis of the pyramid base. If zero, it will default to 1.0. + Width float64 + // Depth is the depth along the Z axis of the pyramid base. If zero, it will default to Width. + Depth float64 + // Height is the height of the pyramid. If zero, it will default to 1.0. + Height float64 + // Entity is an optional entity handle to attach the shape to. + Entity *world.EntityHandle +} + +// Ellipsoid represents a hollow ellipsoid that can be drawn at any point in the world, with a radius along +// each of the X, Y and Z axes. +type Ellipsoid struct { + shape + + // Colour is the colour that will be used for the outline. If empty, it will default to white. + Colour color.RGBA + // Position is the origin position of the shape in the world. + Position mgl64.Vec3 + // Scale is the rate to scale the shape from its origin point. If zero, it will default to 1.0. + Scale float64 + // Radii are the radii of the ellipsoid along the X, Y and Z axes. If empty, it will default to a radius + // of 1.0 on each axis. + Radii mgl64.Vec3 + // SegmentsPerAxis is the number of segments that the ellipsoid will be drawn with per axis. The more + // segments, the smoother the ellipsoid will look. If zero, it will default to 20. + SegmentsPerAxis int + // Entity is an optional entity handle to attach the shape to. + Entity *world.EntityHandle +} + +// Cone represents a cone that can be drawn at any point in the world, with a base on the X and Z axes and a +// height running up the Y axis to a single apex. +type Cone struct { + shape + + // Colour is the colour that will be used for the outline. If empty, it will default to white. + Colour color.RGBA + // Position is the origin position of the shape in the world. + Position mgl64.Vec3 + // Scale is the rate to scale the shape from its origin point. If zero, it will default to 1.0. + Scale float64 + // Radii are the radii along the X and Z axes of the cone base. If empty, it will default to a radius of + // 1.0 on each axis. + Radii mgl64.Vec2 + // Height is the height of the cone. If zero, it will default to 1.0. + Height float64 + // Segments is the number of segments that the cone will be drawn with. The more segments, the smoother + // the cone will look. If zero, it will default to 20. + Segments int + // Entity is an optional entity handle to attach the shape to. + Entity *world.EntityHandle +} diff --git a/server/session/player.go b/server/session/player.go index a8642a701..e039bb440 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -1117,6 +1117,14 @@ func (s *Session) shapeAttachedEntityRuntimeID(shape debug.Shape) int64 { handle = shape.Entity case *debug.Text: handle = shape.Entity + case *debug.Cylinder: + handle = shape.Entity + case *debug.Pyramid: + handle = shape.Entity + case *debug.Ellipsoid: + handle = shape.Entity + case *debug.Cone: + handle = shape.Entity } if handle == nil { return 0 @@ -1138,7 +1146,7 @@ func debugShapeToProtocol(shape debug.Shape, dim world.Dimension, attachedEntity white := color.RGBA{R: 255, G: 255, B: 255, A: 255} switch shape := shape.(type) { case *debug.Arrow: - ps.Type = protocol.Option(uint8(protocol.PrimitiveShapeArrow)) + ps.Type = protocol.Option(protocol.PrimitiveShapeArrow) ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) ps.Location = protocol.Option(vec64To32(shape.Position)) ps.ExtraShapeData = &protocol.ArrowShape{ @@ -1148,30 +1156,30 @@ func debugShapeToProtocol(shape debug.Shape, dim world.Dimension, attachedEntity Segments: protocol.Option(valueOrDefault(uint8(shape.HeadSegments), 4)), } case *debug.Box: - ps.Type = protocol.Option(uint8(protocol.PrimitiveShapeBox)) + ps.Type = protocol.Option(protocol.PrimitiveShapeBox) ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) ps.Location = protocol.Option(vec64To32(shape.Position)) ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) ps.ExtraShapeData = &protocol.BoxShape{BoxBound: valueOrDefault(vec64To32(shape.Bounds), mgl32.Vec3{1, 1, 1})} case *debug.Circle: - ps.Type = protocol.Option(uint8(protocol.PrimitiveShapeCircle)) + ps.Type = protocol.Option(protocol.PrimitiveShapeCircle) ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) ps.Location = protocol.Option(vec64To32(shape.Position)) ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) ps.ExtraShapeData = &protocol.SphereShape{Segments: valueOrDefault(uint8(shape.Segments), 20)} case *debug.Line: - ps.Type = protocol.Option(uint8(protocol.PrimitiveShapeLine)) + ps.Type = protocol.Option(protocol.PrimitiveShapeLine) ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) ps.Location = protocol.Option(vec64To32(shape.Position)) ps.ExtraShapeData = &protocol.LineShape{LineEndLocation: vec64To32(shape.EndPosition)} case *debug.Sphere: - ps.Type = protocol.Option(uint8(protocol.PrimitiveShapeSphere)) + ps.Type = protocol.Option(protocol.PrimitiveShapeSphere) ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) ps.Location = protocol.Option(vec64To32(shape.Position)) ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) ps.ExtraShapeData = &protocol.SphereShape{Segments: valueOrDefault(uint8(shape.Segments), 20)} case *debug.Text: - ps.Type = protocol.Option(uint8(protocol.PrimitiveShapeText)) + ps.Type = protocol.Option(protocol.PrimitiveShapeText) ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) ps.Location = protocol.Option(vec64To32(shape.Position)) ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) @@ -1192,6 +1200,51 @@ func debugShapeToProtocol(shape debug.Shape, dim world.Dimension, attachedEntity textData.BackgroundColour = protocol.Option(shape.BackgroundColour) } ps.ExtraShapeData = textData + case *debug.Cylinder: + ps.Type = protocol.Option(protocol.PrimitiveShapeCylinder) + ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) + ps.Location = protocol.Option(vec64To32(shape.Position)) + ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) + base := valueOrDefault(shape.BaseRadius, mgl64.Vec2{1, 1}) + top := valueOrDefault(shape.TopRadius, base) + ps.ExtraShapeData = &protocol.CylinderShape{ + RadiusX: mgl32.Vec2{float32(base[0]), float32(top[0])}, + RadiusZ: mgl32.Vec2{float32(base[1]), float32(top[1])}, + Height: valueOrDefault(float32(shape.Height), 1), + NumSegments: valueOrDefault(uint8(shape.Segments), 20), + } + case *debug.Pyramid: + ps.Type = protocol.Option(protocol.PrimitiveShapePyramid) + ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) + ps.Location = protocol.Option(vec64To32(shape.Position)) + ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) + pyramid := &protocol.PyramidShape{ + Width: valueOrDefault(float32(shape.Width), 1), + Height: valueOrDefault(float32(shape.Height), 1), + } + if shape.Depth != 0 { + pyramid.Depth = protocol.Option(float32(shape.Depth)) + } + ps.ExtraShapeData = pyramid + case *debug.Ellipsoid: + ps.Type = protocol.Option(protocol.PrimitiveShapeEllipsoid) + ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) + ps.Location = protocol.Option(vec64To32(shape.Position)) + ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) + ps.ExtraShapeData = &protocol.EllipsoidShape{ + Radii: valueOrDefault(vec64To32(shape.Radii), mgl32.Vec3{1, 1, 1}), + SegmentsPerAxis: valueOrDefault(uint8(shape.SegmentsPerAxis), 20), + } + case *debug.Cone: + ps.Type = protocol.Option(protocol.PrimitiveShapeCone) + ps.Colour = protocol.Option(valueOrDefault(shape.Colour, white)) + ps.Location = protocol.Option(vec64To32(shape.Position)) + ps.Scale = protocol.Option(valueOrDefault(float32(shape.Scale), 1)) + ps.ExtraShapeData = &protocol.ConeShape{ + Radii: valueOrDefault(vec2To32(shape.Radii), mgl32.Vec2{1, 1}), + Height: valueOrDefault(float32(shape.Height), 1), + NumSegments: valueOrDefault(uint8(shape.Segments), 20), + } default: panic(fmt.Sprintf("unknown debug shape type %T", shape)) } diff --git a/server/session/text.go b/server/session/text.go index 4c447b7f5..d8418df4a 100644 --- a/server/session/text.go +++ b/server/session/text.go @@ -1,12 +1,13 @@ package session import ( + "time" + "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/player/scoreboard" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "golang.org/x/text/language" - "time" ) // SendMessage ... @@ -145,7 +146,7 @@ func (s *Session) SendBossBar(text string, colour uint8, healthPercentage float6 EventType: packet.BossEventShow, BossBarTitle: text, HealthPercentage: float32(healthPercentage), - Colour: uint32(colour), + Colour: colour, }) } diff --git a/server/session/world.go b/server/session/world.go index 48f853ee2..d624576de 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -488,7 +488,7 @@ func (s *Session) ViewParticle(pos mgl64.Vec3, p world.Particle) { } // tierToSoundEvent converts an item.ArmourTier to a sound event associated with equipping it. -func tierToSoundEvent(tier item.ArmourTier) uint32 { +func tierToSoundEvent(tier item.ArmourTier) string { switch tier.(type) { case item.ArmourTierLeather: return packet.SoundEventEquipLeather @@ -1413,6 +1413,11 @@ func vec64To32(vec3 mgl64.Vec3) mgl32.Vec3 { return mgl32.Vec3{float32(vec3[0]), float32(vec3[1]), float32(vec3[2])} } +// vec2To32 converts a mgl64.Vec2 to a mgl32.Vec2. +func vec2To32(vec2 mgl64.Vec2) mgl32.Vec2 { + return mgl32.Vec2{float32(vec2[0]), float32(vec2[1])} +} + // blockPosFromProtocol ... func blockPosFromProtocol(pos protocol.BlockPos) cube.Pos { return cube.Pos{int(pos.X()), int(pos.Y()), int(pos.Z())} diff --git a/server/world/biome/register.go b/server/world/biome/register.go index 4db6de511..d5b5aec45 100644 --- a/server/world/biome/register.go +++ b/server/world/biome/register.go @@ -80,6 +80,7 @@ func init() { world.RegisterBiome(SoulSandValley{}) world.RegisterBiome(StonyPeaks{}) world.RegisterBiome(StonyShore{}) + world.RegisterBiome(SulfurCaves{}) world.RegisterBiome(SunflowerPlains{}) world.RegisterBiome(SwampHills{}) world.RegisterBiome(Swamp{}) diff --git a/server/world/biome/sulfur_caves.go b/server/world/biome/sulfur_caves.go new file mode 100644 index 000000000..7c32f06aa --- /dev/null +++ b/server/world/biome/sulfur_caves.go @@ -0,0 +1,46 @@ +package biome + +import "image/color" + +// SulfurCaves ... +type SulfurCaves struct{} + +// Temperature ... +func (SulfurCaves) Temperature() float64 { + return 0.8 +} + +// Rainfall ... +func (SulfurCaves) Rainfall() float64 { + return 0.4 +} + +// Depth ... +func (SulfurCaves) Depth() float64 { + return 0.1 +} + +// Scale ... +func (SulfurCaves) Scale() float64 { + return 0.2 +} + +// WaterColour ... +func (SulfurCaves) WaterColour() color.RGBA { + return color.RGBA{R: 0x60, G: 0xb7, B: 0xff, A: 0xa6} +} + +// Tags ... +func (SulfurCaves) Tags() []string { + return []string{"caves", "sulfur_caves", "overworld", "monster"} +} + +// String ... +func (SulfurCaves) String() string { + return "sulfur_caves" +} + +// EncodeBiome ... +func (SulfurCaves) EncodeBiome() int { + return 194 +} diff --git a/server/world/block_states.nbt b/server/world/block_states.nbt index 73ccf6fe6..1e4d94632 100644 Binary files a/server/world/block_states.nbt and b/server/world/block_states.nbt differ diff --git a/server/world/chunk/chunk.go b/server/world/chunk/chunk.go index 2d09ccd51..ddee338b5 100644 --- a/server/world/chunk/chunk.go +++ b/server/world/chunk/chunk.go @@ -232,14 +232,14 @@ func (chunk *Chunk) SubY(index int16) int16 { return (index << 4) + int16(chunk.r[0]) } -// HighestFilledSubChunk returns the index of the highest sub chunk in the chunk -// that has any blocks in it. 0 is returned if no subchunks have any blocks. +// HighestFilledSubChunk returns the number of sub chunks up to and including the +// highest sub chunk in the chunk that has any blocks in it. 0 is returned if no +// subchunks have any blocks. func (chunk *Chunk) HighestFilledSubChunk() uint16 { - highest := uint16(0) - for highest = uint16(len(chunk.sub) - 1); highest > 0; highest-- { - if !chunk.sub[highest].Empty() { - break + for i, sub := range slices.Backward(chunk.sub) { + if !sub.Empty() { + return uint16(i + 1) } } - return highest + return 0 } diff --git a/server/world/vanilla_items.nbt b/server/world/vanilla_items.nbt index c5d6a1b2b..52af4d9e1 100644 Binary files a/server/world/vanilla_items.nbt and b/server/world/vanilla_items.nbt differ