From b9bbb29e67f9a0659d80e4c073e351cbf428ff01 Mon Sep 17 00:00:00 2001 From: olebeck <31539311+olebeck@users.noreply.github.com> Date: Thu, 20 Jun 2024 22:34:19 +0200 Subject: [PATCH] 1.21 --- .gitignore | 3 +- cmd/generate-color-lookup/main.go | 2 +- dragonfly | 2 +- go.work | 2 +- go.work.sum | 30 ++- gophertunnel | 2 +- handlers/worlds/chunk.go | 4 +- handlers/worlds/packets.go | 71 ++++--- handlers/worlds/player.go | 4 +- handlers/worlds/world.go | 24 +-- handlers/worlds/worldstate/entity.go | 34 ++-- handlers/worlds/worldstate/world.go | 23 ++- subcommands/blind-proxy.go | 10 +- subcommands/debug.go | 7 + subcommands/world/world.go | 7 + utils/chunk_render.go | 6 +- utils/colors.go | 7 +- utils/nbtconv/colour.go | 24 --- utils/nbtconv/item.go | 31 --- utils/nbtconv/read.go | 278 --------------------------- utils/nbtconv/write.go | 148 -------------- utils/proxy/conn.go | 10 +- utils/proxy/context.go | 18 +- utils/proxy/proxy.go | 4 +- utils/proxy/replay.go | 8 +- utils/utils.go | 6 +- 26 files changed, 186 insertions(+), 579 deletions(-) delete mode 100644 utils/nbtconv/colour.go delete mode 100644 utils/nbtconv/item.go delete mode 100644 utils/nbtconv/read.go delete mode 100644 utils/nbtconv/write.go diff --git a/.gitignore b/.gitignore index 099e78c..7d655a7 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ entityflags.py *.ignore /libbedrocktool.h -/libbedrocktool.so \ No newline at end of file +/libbedrocktool.so +/tex.png \ No newline at end of file diff --git a/cmd/generate-color-lookup/main.go b/cmd/generate-color-lookup/main.go index c5e928d..d70e819 100644 --- a/cmd/generate-color-lookup/main.go +++ b/cmd/generate-color-lookup/main.go @@ -31,7 +31,7 @@ func main() { } var entries []protocol.BlockEntry - for _, block := range world.Blocks() { + for _, block := range world.DefaultBlockRegistry.Blocks() { if block, ok := block.(world.UnknownBlock); ok { entries = append(entries, protocol.BlockEntry{ Name: block.Name, diff --git a/dragonfly b/dragonfly index a8edabe..16fc96e 160000 --- a/dragonfly +++ b/dragonfly @@ -1 +1 @@ -Subproject commit a8edabe6964d95117b43dff18df82e89cdc756e9 +Subproject commit 16fc96e4e4229d0d0887cc1291904281b626a1c7 diff --git a/go.work b/go.work index 963dba6..e95fcdb 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.21.0 +go 1.22.0 toolchain go1.21.4 diff --git a/go.work.sum b/go.work.sum index b47139f..437f26b 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,10 +1,12 @@ cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= dmitri.shuralyov.com/gpu/mtl v0.0.0-20221208032759-85de2813cf6b/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= git.sr.ht/~jackmordaunt/go-toast v1.0.0/go.mod h1:aIuRX/HdBOz7yRS8rOVYQCwJQlFS7DbYBTpUV0SHeeg= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/andybalholm/stroke v0.0.0-20221221101821-bd29b49d73f0/go.mod h1:ccdDYaY5+gO+cbnQdFxEXqfy0RkoV25H3jLXUDNM3wg= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= @@ -20,33 +22,51 @@ github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u1 github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kamstrup/intmap v0.2.0/go.mod h1:z3uar6/7HP2QxJJoFTWAKsA5k7Uy1UJjAZoT3f62KEE= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/sandertv/gophertunnel v1.37.0/go.mod h1:4El8ZfEpUmOMIJhPt5SCc1PyLNiuQ2+grWczrBvSGVs= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sandertv/go-raknet v1.14.0/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU= +github.com/sandertv/gophertunnel v1.38.0/go.mod h1:nqbZPCBZmKot/DHiY4efq8QQj6gvLvIk8LWPxXF8+6g= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/exp/shiny v0.0.0-20231127185646-65229373498e/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A= golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco= +golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= diff --git a/gophertunnel b/gophertunnel index 2bfb3a7..5e7d804 160000 --- a/gophertunnel +++ b/gophertunnel @@ -1 +1 @@ -Subproject commit 2bfb3a72640687b672c59a2fdc2657367ebb6554 +Subproject commit 5e7d804c9c5a2d5081f831b5ca780e8b5cfca579 diff --git a/handlers/worlds/chunk.go b/handlers/worlds/chunk.go index 6c35d7c..a09d0a4 100644 --- a/handlers/worlds/chunk.go +++ b/handlers/worlds/chunk.go @@ -40,7 +40,7 @@ func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) { //os.WriteFile("chunk.bin", pk.RawPayload, 0777) - ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, w.serverState.useOldBiomes, w.serverState.useHashedRids, w.currentWorld.Range()) + ch, blockNBTs, err := chunk.NetworkDecode(w.serverState.blocks, pk.RawPayload, subChunkCount, w.serverState.useOldBiomes, w.serverState.useHashedRids, w.currentWorld.Range()) if err != nil { logrus.Error(err) return @@ -162,7 +162,7 @@ func (w *worldsHandler) processSubChunk(pk *packet.SubChunk) error { index := uint8(absY) sub, err := chunk.DecodeSubChunk( buf, - world.AirRID(), + w.serverState.blocks, w.currentWorld.Dimension().Range(), &index, chunk.NetworkEncoding, diff --git a/handlers/worlds/packets.go b/handlers/worlds/packets.go index 99b75b7..4c59f4f 100644 --- a/handlers/worlds/packets.go +++ b/handlers/worlds/packets.go @@ -11,10 +11,10 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils/behaviourpack" - "github.com/bedrock-tool/bedrocktool/utils/nbtconv" "github.com/bedrock-tool/bedrocktool/utils/resourcepack" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/item/inventory" + "github.com/df-mc/dragonfly/server/nbtconv" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl32" "github.com/gregwebs/go-recovery" @@ -31,13 +31,20 @@ func (w *worldsHandler) getEntity(id worldstate.EntityRuntimeID) *worldstate.Ent func (w *worldsHandler) packetCB(_pk packet.Packet, toServer bool, timeReceived time.Time, preLogin bool) (packet.Packet, error) { // general / startup switch pk := _pk.(type) { + case *packet.CompressedBiomeDefinitionList: // for client side generation, disabled by proxy + return nil, nil + case *packet.RequestChunkRadius: pk.ChunkRadius = w.settings.ChunkRadius + pk.MaxChunkRadius = w.settings.ChunkRadius case *packet.ChunkRadiusUpdated: w.serverState.radius = pk.ChunkRadius pk.ChunkRadius = w.settings.ChunkRadius + case *packet.SetCommandsEnabled: + pk.Enabled = true + case *packet.SetTime: w.currentWorld.SetTime(timeReceived, int(pk.Time)) @@ -47,6 +54,8 @@ func (w *worldsHandler) packetCB(_pk packet.Packet, toServer bool, timeReceived w.currentWorld.SetTime(timeReceived, int(pk.Time)) w.serverState.useHashedRids = pk.UseBlockNetworkIDHashes + w.serverState.blocks = world.DefaultBlockRegistry.Clone().(*world.BlockRegistryImpl) + world.InsertCustomItems(pk.Items) for _, ie := range pk.Items { w.bp.AddItem(ie) @@ -56,10 +65,10 @@ func (w *worldsHandler) packetCB(_pk packet.Packet, toServer bool, timeReceived for _, be := range pk.Blocks { w.bp.AddBlock(be) } - // telling the chunk code what custom blocks there are so it can generate offsets - world.InsertCustomBlocks(pk.Blocks) + world.AddCustomBlocks(w.serverState.blocks, pk.Blocks) w.customBlocks = pk.Blocks } + w.serverState.blocks.Finalize() w.serverState.WorldName = pk.WorldName if pk.WorldName != "" { @@ -90,6 +99,8 @@ func (w *worldsHandler) packetCB(_pk packet.Packet, toServer bool, timeReceived dim, _ := world.DimensionByID(int(pk.Dimension)) w.currentWorld.SetDimension(dim) + w.currentWorld.BiomeRegistry = w.serverState.biomes + w.currentWorld.BlockRegistry = w.serverState.blocks w.openWorldState(w.settings.StartPaused) } @@ -104,20 +115,22 @@ func (w *worldsHandler) packetCB(_pk packet.Packet, toServer bool, timeReceived w.bp.ApplyComponentEntries(pk.Items) case *packet.BiomeDefinitionList: - err := nbt.UnmarshalEncoding(pk.SerialisedBiomeDefinitions, &w.serverState.biomes, nbt.NetworkLittleEndian) + var biomes map[string]any + err := nbt.UnmarshalEncoding(pk.SerialisedBiomeDefinitions, &biomes, nbt.NetworkLittleEndian) if err != nil { logrus.Error(err) } - for k, v := range w.serverState.biomes { - _, ok := world.BiomeByName(k) + + for k, v := range biomes { + _, ok := w.serverState.biomes.BiomeByName(k) if !ok { - world.RegisterBiome(&customBiome{ + w.serverState.biomes.Register(&customBiome{ name: k, data: v.(map[string]any), }) } } - w.bp.AddBiomes(w.serverState.biomes) + w.bp.AddBiomes(biomes) } _pk = w.itemPackets(_pk) @@ -157,7 +170,7 @@ func (w *worldsHandler) playersPackets(_pk packet.Packet) { geometryName := resourcePatch["geometry"]["default"] - var geom *resourcepack.Geometry + var geometry *resourcepack.GeometryFile var isDefault bool if len(skin.SkinGeometry) > 0 { skinGeometry, _, err := utils.ParseSkinGeometry(skin.SkinGeometry) @@ -165,27 +178,35 @@ func (w *worldsHandler) playersPackets(_pk packet.Packet) { logrus.Error(err) return } - geom = &resourcepack.Geometry{ - Description: skinGeometry.Description, - Bones: skinGeometry.Bones, + if skinGeometry != nil { + geometry = &resourcepack.GeometryFile{ + FormatVersion: string(skin.GeometryDataEngineVersion), + Geometry: []*resourcepack.Geometry{ + &resourcepack.Geometry{ + Description: skinGeometry.Description, + Bones: skinGeometry.Bones, + }, + }, + } } - } else { - geom = &resourcepack.Geometry{ - Description: utils.SkinGeometryDescription{ - Identifier: geometryName, - TextureWidth: int(skin.SkinImageWidth), - TextureHeight: int(skin.SkinImageHeight), + } + if geometry == nil { + geometry = &resourcepack.GeometryFile{ + FormatVersion: string(skin.GeometryDataEngineVersion), + Geometry: []*resourcepack.Geometry{ + &resourcepack.Geometry{ + Description: utils.SkinGeometryDescription{ + Identifier: geometryName, + TextureWidth: int(skin.SkinImageWidth), + TextureHeight: int(skin.SkinImageHeight), + }, + }, }, } isDefault = true } - var geometry = resourcepack.GeometryFile{ - FormatVersion: string(skin.GeometryDataEngineVersion), - Geometry: []*resourcepack.Geometry{geom}, - } - - w.rp.AddPlayer(pk.UUID.String(), skinTexture, capeTexture, skin.CapeID, &geometry, isDefault) + w.rp.AddPlayer(pk.UUID.String(), skinTexture, capeTexture, skin.CapeID, geometry, isDefault) } case *packet.PlayerList: if pk.ActionType == packet.PlayerListActionRemove { // remove @@ -475,7 +496,7 @@ func (w *worldsHandler) itemPackets(_pk packet.Packet) packet.Packet { // create inventory inv := inventory.New(len(existing.Content.Content), nil) for i, c := range existing.Content.Content { - item := utils.StackToItem(c.Stack) + item := utils.StackToItem(w.serverState.blocks, c.Stack) inv.SetItem(i, item) } diff --git a/handlers/worlds/player.go b/handlers/worlds/player.go index 75543f5..19f3a94 100644 --- a/handlers/worlds/player.go +++ b/handlers/worlds/player.go @@ -2,8 +2,8 @@ package worlds import ( "github.com/bedrock-tool/bedrocktool/utils" - "github.com/bedrock-tool/bedrocktool/utils/nbtconv" "github.com/df-mc/dragonfly/server/item/inventory" + "github.com/df-mc/dragonfly/server/nbtconv" ) func (w *worldsHandler) playerData() (ret map[string]any) { @@ -15,7 +15,7 @@ func (w *worldsHandler) playerData() (ret map[string]any) { if len(w.serverState.playerInventory) > 0 && w.settings.SaveInventories { inv := inventory.New(len(w.serverState.playerInventory), nil) for i, ii := range w.serverState.playerInventory { - inv.SetItem(i, utils.StackToItem(ii.Stack)) + inv.SetItem(i, utils.StackToItem(w.serverState.blocks, ii.Stack)) } ret["Inventory"] = nbtconv.InvToNBT(inv) } diff --git a/handlers/worlds/world.go b/handlers/worlds/world.go index b758275..3ffe1de 100644 --- a/handlers/worlds/world.go +++ b/handlers/worlds/world.go @@ -23,7 +23,6 @@ import ( "github.com/bedrock-tool/bedrocktool/utils/resourcepack" "github.com/google/uuid" - "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world" _ "github.com/df-mc/dragonfly/server/world/biome" @@ -55,9 +54,11 @@ type serverState struct { haveStartGame bool worldCounter int WorldName string - biomes map[string]any radius int32 + biomes *world.BiomeRegistry + blocks *world.BlockRegistryImpl + openItemContainers map[byte]*itemContainer playerInventory []protocol.ItemInstance packs []utils.Pack @@ -92,15 +93,6 @@ type itemContainer struct { Content *packet.InventoryContent } -// resets dragonfly globals -func resetGlobals() { - world.ClearStates() - world.LoadBlockStates() - block.InitBlocks() - world.FinaliseBlockRegistry() - world.ResetBiomes() -} - func NewWorldsHandler(settings WorldSettings) *proxy.Handler { settings.ExcludedMobs = slices.DeleteFunc(settings.ExcludedMobs, func(mob string) bool { return mob == "" @@ -120,6 +112,7 @@ func NewWorldsHandler(settings WorldSettings) *proxy.Handler { openItemContainers: make(map[byte]*itemContainer), dimensions: make(map[int]protocol.DimensionDefinition), playerSkins: make(map[uuid.UUID]*protocol.Skin), + biomes: world.DefaultBiomes.Clone(), }, settings: settings, } @@ -201,7 +194,7 @@ func NewWorldsHandler(settings WorldSettings) *proxy.Handler { w.serverState.Name = hostname // initialize a worldstate - w.currentWorld, err = worldstate.New(w.chunkCB, w.serverState.dimensions) + w.currentWorld, err = worldstate.New(w.chunkCB, w.serverState.dimensions, nil, nil) if err != nil { return err } @@ -271,7 +264,6 @@ func NewWorldsHandler(settings WorldSettings) *proxy.Handler { OnEnd: func() { w.SaveAndReset(true, nil) w.wg.Wait() - resetGlobals() }, Deferred: cancel, } @@ -286,7 +278,7 @@ func (w *worldsHandler) preloadReplay() error { var conn minecraft.IConn var err error conn, err = proxy.CreateReplayConnector(context.Background(), w.settings.PreloadReplay, func(header packet.Header, payload []byte, src, dst net.Addr) { - pk, ok := proxy.DecodePacket(header, payload) + pk, ok := proxy.DecodePacket(header, payload, conn.ShieldID()) if !ok { logrus.Error("unknown packet", header) return @@ -315,7 +307,7 @@ func (w *worldsHandler) preloadReplay() error { w.proxy.Server = nil logrus.Info("finished preload") - resetGlobals() + w.serverState.blocks = nil return nil } @@ -457,7 +449,7 @@ func (w *worldsHandler) chunkCB(cp world.ChunkPos, c *chunk.Chunk) { func (w *worldsHandler) reset(dim world.Dimension) (err error) { // create new world state - w.currentWorld, err = worldstate.New(w.chunkCB, w.serverState.dimensions) + w.currentWorld, err = worldstate.New(w.chunkCB, w.serverState.dimensions, w.serverState.blocks, w.serverState.biomes) if err != nil { return err } diff --git a/handlers/worlds/worldstate/entity.go b/handlers/worlds/worldstate/entity.go index cbaad09..4bdcc6e 100644 --- a/handlers/worlds/worldstate/entity.go +++ b/handlers/worlds/worldstate/entity.go @@ -3,9 +3,7 @@ package worldstate import ( "math" - "github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils/behaviourpack" - "github.com/bedrock-tool/bedrocktool/utils/nbtconv" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl32" @@ -318,22 +316,24 @@ func (s *EntityState) ToServerEntity(links []int64) serverEntity { e.EntityType.NBT["LinksTag"] = linksTag } - if false { - armor := make([]map[string]any, 4) - if s.Helmet != nil { - armor[0] = nbtconv.WriteItem(utils.StackToItem(s.Helmet.Stack), true) - } - if s.Chestplate != nil { - armor[1] = nbtconv.WriteItem(utils.StackToItem(s.Chestplate.Stack), true) - } - if s.Leggings != nil { - armor[2] = nbtconv.WriteItem(utils.StackToItem(s.Leggings.Stack), true) - } - if s.Boots != nil { - armor[3] = nbtconv.WriteItem(utils.StackToItem(s.Boots.Stack), true) + /* + if false { + armor := make([]map[string]any, 4) + if s.Helmet != nil { + armor[0] = nbtconv.WriteItem(utils.StackToItem(w.serverState.blocks, s.Helmet.Stack), true) + } + if s.Chestplate != nil { + armor[1] = nbtconv.WriteItem(utils.StackToItem(w.serverState.blocks, s.Chestplate.Stack), true) + } + if s.Leggings != nil { + armor[2] = nbtconv.WriteItem(utils.StackToItem(w.serverState.blocks, s.Leggings.Stack), true) + } + if s.Boots != nil { + armor[3] = nbtconv.WriteItem(utils.StackToItem(w.serverState.blocks, s.Boots.Stack), true) + } + e.EntityType.NBT["Armor"] = armor } - e.EntityType.NBT["Armor"] = armor - } + */ return e } diff --git a/handlers/worlds/worldstate/world.go b/handlers/worlds/worldstate/world.go index daa1f6d..2c658e2 100644 --- a/handlers/worlds/worldstate/world.go +++ b/handlers/worlds/worldstate/world.go @@ -36,6 +36,9 @@ type World struct { // called when a chunk is added ChunkFunc func(world.ChunkPos, *chunk.Chunk) + BlockRegistry world.BlockRegistry + BiomeRegistry *world.BiomeRegistry + dimension world.Dimension dimRange cube.Range dimensionDefinitions map[int]protocol.DimensionDefinition @@ -79,7 +82,7 @@ type Map struct { MapLocked bool `nbt:"mapLocked"` } -func New(cf func(world.ChunkPos, *chunk.Chunk), dimensionDefinitions map[int]protocol.DimensionDefinition) (*World, error) { +func New(cf func(world.ChunkPos, *chunk.Chunk), dimensionDefinitions map[int]protocol.DimensionDefinition, br world.BlockRegistry, br2 *world.BiomeRegistry) (*World, error) { w := &World{ StoredChunks: make(map[world.ChunkPos]bool), dimensionDefinitions: dimensionDefinitions, @@ -95,6 +98,8 @@ func New(cf func(world.ChunkPos, *chunk.Chunk), dimensionDefinitions map[int]pro players: worldPlayers{ players: make(map[uuid.UUID]*player), }, + BlockRegistry: br, + BiomeRegistry: br2, } return w, nil @@ -118,6 +123,8 @@ func (w *World) storeMemToProvider() error { w.provider, w.err = mcdb.Config{ Log: logrus.StandardLogger(), Compression: opt.DefaultCompression, + Blocks: w.BlockRegistry, + Biomes: w.BiomeRegistry, }.Open(w.Folder) if w.err != nil { return w.err @@ -176,7 +183,17 @@ func (w *World) StoreChunk(pos world.ChunkPos, ch *chunk.Chunk, blockNBT map[cub w.l.Lock() defer w.l.Unlock() - w.StoredChunks[pos] = true + var empty = true + for _, sub := range ch.Sub() { + if !sub.Empty() { + empty = false + break + } + } + if !empty { + w.StoredChunks[pos] = true + } + w.currState().StoreChunk(pos, ch, blockNBT) return nil @@ -330,6 +347,8 @@ func (w *World) Rename(name, folder string) error { w.provider, w.err = mcdb.Config{ Log: logrus.StandardLogger(), Compression: opt.DefaultCompression, + Blocks: w.BlockRegistry, + Biomes: w.BiomeRegistry, }.Open(w.Folder) if w.err != nil { return w.err diff --git a/subcommands/blind-proxy.go b/subcommands/blind-proxy.go index cd9afb4..0c3a23e 100644 --- a/subcommands/blind-proxy.go +++ b/subcommands/blind-proxy.go @@ -16,12 +16,14 @@ import ( type BlindProxyCMD struct { ServerAddress string + ListenAddress string } func (*BlindProxyCMD) Name() string { return "blind-proxy" } func (*BlindProxyCMD) Synopsis() string { return "raknet proxy" } func (c *BlindProxyCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.ServerAddress, "address", "", "server address") + f.StringVar(&c.ListenAddress, "listen", "", "example :19132 or 127.0.0.1:19132") } func packet_forward(src, dst *raknet.Conn) error { @@ -43,12 +45,16 @@ func (c *BlindProxyCMD) Execute(ctx context.Context) error { return err } - listener, err := raknet.Listen("0.0.0.0:19132") + if c.ListenAddress == "" { + c.ListenAddress = "127.0.0.1:19132" + } + + listener, err := raknet.Listen(c.ListenAddress) if err != nil { return err } defer listener.Close() - logrus.Info("Listening on 0.0.0.0:19132") + logrus.Infof("Listening on %s", c.ListenAddress) listener.PongData([]byte(fmt.Sprintf("MCPE;%v;%v;%v;%v;%v;%v;Gophertunnel;%v;%v;%v;%v;", "Proxy For "+server.Name, protocol.CurrentProtocol, protocol.CurrentVersion, 0, 1, diff --git a/subcommands/debug.go b/subcommands/debug.go index 192f2b0..b4550c4 100644 --- a/subcommands/debug.go +++ b/subcommands/debug.go @@ -12,19 +12,26 @@ import ( type DebugProxyCMD struct { ServerAddress string + ListenAddress string } func (*DebugProxyCMD) Name() string { return "debug-proxy" } func (*DebugProxyCMD) Synopsis() string { return locale.Loc("debug_proxy_synopsis", nil) } func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.ServerAddress, "address", "", locale.Loc("remote_address", nil)) + f.StringVar(&c.ListenAddress, "listen", "", "example :19132 or 127.0.0.1:19132") } func (c *DebugProxyCMD) Execute(ctx context.Context) error { + if c.ListenAddress == "" { + c.ListenAddress = "127.0.0.1:19132" + } + proxy, err := proxy.New(true) if err != nil { return err } + proxy.ListenAddress = c.ListenAddress utils.Options.Debug = true return proxy.Run(ctx, c.ServerAddress) } diff --git a/subcommands/world/world.go b/subcommands/world/world.go index e85eaa8..f90054a 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -14,6 +14,7 @@ import ( type WorldCMD struct { ServerAddress string + ListenAddress string Packs bool EnableVoid bool SaveEntities bool @@ -31,6 +32,7 @@ func (*WorldCMD) Synopsis() string { return locale.Loc("world_synopsis", nil) } func (c *WorldCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.ServerAddress, "address", "", locale.Loc("remote_address", nil)) + f.StringVar(&c.ListenAddress, "listen", "", "example :19132 or 127.0.0.1:19132") f.BoolVar(&c.Packs, "packs", false, locale.Loc("save_packs_with_world", nil)) f.BoolVar(&c.EnableVoid, "void", true, locale.Loc("enable_void", nil)) f.BoolVar(&c.SaveImage, "image", false, locale.Loc("save_image", nil)) @@ -58,6 +60,11 @@ func (c *WorldCMD) Execute(ctx context.Context) error { return err } + if c.ListenAddress == "" { + c.ListenAddress = "127.0.0.1:19132" + } + proxy.ListenAddress = c.ListenAddress + proxy.AddHandler(worlds.NewWorldsHandler(worlds.WorldSettings{ VoidGen: c.EnableVoid, WithPacks: c.Packs, diff --git a/utils/chunk_render.go b/utils/chunk_render.go index 2b95124..a769bea 100644 --- a/utils/chunk_render.go +++ b/utils/chunk_render.go @@ -41,7 +41,8 @@ func blockColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) (blockColor color.R */ blockColor = color.RGBA{0xff, 0, 0xff, 0xff} - b, found := world.BlockByRuntimeID(rid) + br := c.BlockRegistry.(world.BlockRegistry) + b, found := br.BlockByRuntimeID(rid) if !found { return blockColor } @@ -125,6 +126,7 @@ func blockColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) (blockColor color.R } func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA { + br := c.BlockRegistry.(world.BlockRegistry) haveUp := false cube.Pos{int(x), int(y), int(z)}. Side(cube.FaceUp). @@ -134,7 +136,7 @@ func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA { } blockRid := c.Block(uint8(neighbour[0]), int16(neighbour[1]), uint8(neighbour[2]), 0) if blockRid > 0 { - b, found := world.BlockByRuntimeID(blockRid) + b, found := br.BlockByRuntimeID(blockRid) if found { if isBlockLightblocking(b) { haveUp = true diff --git a/utils/colors.go b/utils/colors.go index 497ec1d..4750142 100644 --- a/utils/colors.go +++ b/utils/colors.go @@ -13,7 +13,6 @@ import ( "strings" "github.com/dblezek/tga" - "github.com/df-mc/dragonfly/server/world" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sirupsen/logrus" ) @@ -278,8 +277,8 @@ func ResolveColors(entries []protocol.BlockEntry, packs []Pack, addToBlocks bool customBlockColors = colors } - m := NewTextureMap() - ridToIdx = m.SetTextures(world.Blocks(), images) + //m := NewTextureMap() + //ridToIdx = m.SetTextures(world.Blocks(), images) - return m.Lookup, colors + return nil, colors } diff --git a/utils/nbtconv/colour.go b/utils/nbtconv/colour.go deleted file mode 100644 index 6a0cb1b..0000000 --- a/utils/nbtconv/colour.go +++ /dev/null @@ -1,24 +0,0 @@ -package nbtconv - -import ( - "encoding/binary" - "image/color" -) - -// Int32FromRGBA converts a color.RGBA into an int32. These int32s are present in, for example, signs. -func Int32FromRGBA(x color.RGBA) int32 { - if x.R == 0 && x.G == 0 && x.B == 0 { - // Default to black colour. The default (0x000000) is a transparent colour. Text with this colour will not show - // up on the sign. - return int32(-0x1000000) - } - return int32(binary.BigEndian.Uint32([]byte{x.A, x.R, x.G, x.B})) -} - -// RGBAFromInt32 converts an int32 into a color.RGBA. These int32s are present in, for example, signs. -func RGBAFromInt32(x int32) color.RGBA { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, uint32(x)) - - return color.RGBA{A: b[0], R: b[1], G: b[2], B: b[3]} -} diff --git a/utils/nbtconv/item.go b/utils/nbtconv/item.go deleted file mode 100644 index 1443ddc..0000000 --- a/utils/nbtconv/item.go +++ /dev/null @@ -1,31 +0,0 @@ -package nbtconv - -import ( - "github.com/df-mc/dragonfly/server/item/inventory" -) - -// InvFromNBT decodes the data of an NBT slice into the inventory passed. -func InvFromNBT(inv *inventory.Inventory, items []any) { - for _, itemData := range items { - data, _ := itemData.(map[string]any) - it := Item(data, nil) - if it.Empty() { - continue - } - _ = inv.SetItem(int(Uint8(data, "Slot")), it) - } -} - -// InvToNBT encodes an inventory to a data slice which may be encoded as NBT. -func InvToNBT(inv *inventory.Inventory) []map[string]any { - var items []map[string]any - for index, i := range inv.Slots() { - if i.Empty() { - continue - } - data := WriteItem(i, true) - data["Slot"] = byte(index) - items = append(items, data) - } - return items -} diff --git a/utils/nbtconv/read.go b/utils/nbtconv/read.go deleted file mode 100644 index 81d6e9d..0000000 --- a/utils/nbtconv/read.go +++ /dev/null @@ -1,278 +0,0 @@ -package nbtconv - -import ( - "bytes" - "encoding/gob" - "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/item" - "github.com/df-mc/dragonfly/server/world" - "github.com/go-gl/mathgl/mgl64" - "golang.org/x/exp/constraints" - "time" -) - -// Bool reads a uint8 value from a map at key k and returns true if it equals 1. -func Bool(m map[string]any, k string) bool { - return Uint8(m, k) == 1 -} - -// Uint8 reads a uint8 value from a map at key k. -func Uint8(m map[string]any, k string) uint8 { - v, _ := m[k].(uint8) - return v -} - -// String reads a string value from a map at key k. -func String(m map[string]any, k string) string { - v, _ := m[k].(string) - return v -} - -// Int16 reads an int16 value from a map at key k. -func Int16(m map[string]any, k string) int16 { - v, _ := m[k].(int16) - return v -} - -// Int32 reads an int32 value from a map at key k. -func Int32(m map[string]any, k string) int32 { - v, _ := m[k].(int32) - return v -} - -// Int64 reads an int16 value from a map at key k. -func Int64(m map[string]any, k string) int64 { - v, _ := m[k].(int64) - return v -} - -// TickDuration reads a uint8/int16/in32 value from a map at key k and converts -// it from ticks to a time.Duration. -func TickDuration[T constraints.Integer](m map[string]any, k string) time.Duration { - var v time.Duration - switch any(*new(T)).(type) { - case uint8: - v = time.Duration(Uint8(m, k)) - case int16: - v = time.Duration(Int16(m, k)) - case int32: - v = time.Duration(Int32(m, k)) - default: - panic("invalid tick duration value type") - } - return v * time.Millisecond * 50 -} - -// Float32 reads a float32 value from a map at key k. -func Float32(m map[string]any, k string) float32 { - v, _ := m[k].(float32) - return v -} - -// Float64 reads a float64 value from a map at key k. -func Float64(m map[string]any, k string) float64 { - v, _ := m[k].(float64) - return v -} - -// Slice reads a []any value from a map at key k. -func Slice(m map[string]any, k string) []any { - v, _ := m[k].([]any) - return v -} - -// Vec3 converts x, y and z values in an NBT map to an mgl64.Vec3. -func Vec3(x map[string]any, k string) mgl64.Vec3 { - if i, ok := x[k].([]any); ok { - if len(i) != 3 { - return mgl64.Vec3{} - } - var v mgl64.Vec3 - for index, f := range i { - f32, _ := f.(float32) - v[index] = float64(f32) - } - return v - } else if i, ok := x[k].([]float32); ok { - if len(i) != 3 { - return mgl64.Vec3{} - } - return mgl64.Vec3{float64(i[0]), float64(i[1]), float64(i[2])} - } - return mgl64.Vec3{} -} - -// Vec3ToFloat32Slice converts an mgl64.Vec3 to a []float32 with 3 elements. -func Vec3ToFloat32Slice(x mgl64.Vec3) []float32 { - return []float32{float32(x[0]), float32(x[1]), float32(x[2])} -} - -// Pos converts x, y and z values in an NBT map to a cube.Pos. -func Pos(x map[string]any, k string) cube.Pos { - if i, ok := x[k].([]any); ok { - if len(i) != 3 { - return cube.Pos{} - } - var v cube.Pos - for index, f := range i { - f32, _ := f.(int32) - v[index] = int(f32) - } - return v - } else if i, ok := x[k].([]int32); ok { - if len(i) != 3 { - return cube.Pos{} - } - return cube.Pos{int(i[0]), int(i[1]), int(i[2])} - } - return cube.Pos{} -} - -// PosToInt32Slice converts a cube.Pos to a []int32 with 3 elements. -func PosToInt32Slice(x cube.Pos) []int32 { - return []int32{int32(x[0]), int32(x[1]), int32(x[2])} -} - -// MapItem converts an item's name, count, damage (and properties when it is a block) in a map obtained by decoding NBT -// to a world.Item. -func MapItem(x map[string]any, k string) item.Stack { - if m, ok := x[k].(map[string]any); ok { - tag, ok := m["tag"].(map[string]any) - if !ok { - tag = map[string]any{} - } - - s := readItemStack(m, tag) - readDamage(tag, &s, true) - readEnchantments(tag, &s) - readDisplay(tag, &s) - readDragonflyData(tag, &s) - return s - } - return item.Stack{} -} - -// Item decodes the data of an item into an item stack. -func Item(data map[string]any, s *item.Stack) item.Stack { - disk, tag := s == nil, data - if disk { - t, ok := data["tag"].(map[string]any) - if !ok { - t = map[string]any{} - } - tag = t - - a := readItemStack(data, tag) - s = &a - } - - readAnvilCost(tag, s) - readDamage(tag, s, disk) - readDisplay(tag, s) - readDragonflyData(tag, s) - readEnchantments(tag, s) - return *s -} - -// Block decodes the data of a block into a world.Block. -func Block(m map[string]any, k string) world.Block { - if mk, ok := m[k].(map[string]any); ok { - name, _ := mk["name"].(string) - properties, _ := mk["states"].(map[string]any) - b, _ := world.BlockByName(name, properties) - return b - } - return nil -} - -// readItemStack reads an item.Stack from the NBT in the map passed. -func readItemStack(m, t map[string]any) item.Stack { - var it world.Item - if blockItem, ok := Block(m, "Block").(world.Item); ok { - it = blockItem - } - if v, ok := world.ItemByName(String(m, "Name"), Int16(m, "Damage")); ok { - it = v - } - if it == nil { - return item.Stack{} - } - if n, ok := it.(world.NBTer); ok { - it = n.DecodeNBT(t).(world.Item) - } - return item.NewStack(it, int(Uint8(m, "Count"))) -} - -// readDamage reads the damage value stored in the NBT with the Damage tag and saves it to the item.Stack passed. -func readDamage(m map[string]any, s *item.Stack, disk bool) { - if disk { - *s = s.Damage(int(Int16(m, "Damage"))) - return - } - *s = s.Damage(int(Int32(m, "Damage"))) -} - -// readAnvilCost ... -func readAnvilCost(m map[string]any, s *item.Stack) { - *s = s.WithAnvilCost(int(Int32(m, "RepairCost"))) -} - -// readEnchantments reads the enchantments stored in the ench tag of the NBT passed and stores it into an item.Stack. -func readEnchantments(m map[string]any, s *item.Stack) { - enchantments, ok := m["ench"].([]map[string]any) - if !ok { - for _, e := range Slice(m, "ench") { - if v, ok := e.(map[string]any); ok { - enchantments = append(enchantments, v) - } - } - } - for _, ench := range enchantments { - if t, ok := item.EnchantmentByID(int(Int16(ench, "id"))); ok { - *s = s.WithEnchantments(item.NewEnchantment(t, int(Int16(ench, "lvl")))) - } - } -} - -// readDisplay reads the display data present in the display field in the NBT. It includes a custom name of the item -// and the lore. -func readDisplay(m map[string]any, s *item.Stack) { - if display, ok := m["display"].(map[string]any); ok { - if name, ok := display["Name"].(string); ok { - // Only add the custom name if actually set. - *s = s.WithCustomName(name) - } - if lore, ok := display["Lore"].([]string); ok { - *s = s.WithLore(lore...) - } else if lore, ok := display["Lore"].([]any); ok { - loreLines := make([]string, 0, len(lore)) - for _, l := range lore { - loreLines = append(loreLines, l.(string)) - } - *s = s.WithLore(loreLines...) - } - } -} - -// readDragonflyData reads data written to the dragonflyData field in the NBT of an item and adds it to the item.Stack -// passed. -func readDragonflyData(m map[string]any, s *item.Stack) { - if customData, ok := m["dragonflyData"]; ok { - d, ok := customData.([]byte) - if !ok { - if itf, ok := customData.([]any); ok { - for _, v := range itf { - b, _ := v.(byte) - d = append(d, b) - } - } - } - var values []mapValue - if err := gob.NewDecoder(bytes.NewBuffer(d)).Decode(&values); err != nil { - panic("error decoding item user data: " + err.Error()) - } - for _, val := range values { - *s = s.WithValue(val.K, val.V) - } - } -} diff --git a/utils/nbtconv/write.go b/utils/nbtconv/write.go deleted file mode 100644 index 4102251..0000000 --- a/utils/nbtconv/write.go +++ /dev/null @@ -1,148 +0,0 @@ -package nbtconv - -import ( - "bytes" - "encoding/gob" - "github.com/df-mc/dragonfly/server/item" - "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/chunk" - "sort" -) - -// WriteItem encodes an item stack into a map that can be encoded using NBT. -func WriteItem(s item.Stack, disk bool) map[string]any { - tag := make(map[string]any) - if nbt, ok := s.Item().(world.NBTer); ok { - for k, v := range nbt.EncodeNBT() { - tag[k] = v - } - } - writeAnvilCost(tag, s) - writeDamage(tag, s, disk) - writeDisplay(tag, s) - writeDragonflyData(tag, s) - writeEnchantments(tag, s) - - data := make(map[string]any) - if disk { - writeItemStack(data, tag, s) - } else { - for k, v := range tag { - data[k] = v - } - } - return data -} - -// WriteBlock encodes a world.Block into a map that can be encoded using NBT. -func WriteBlock(b world.Block) map[string]any { - name, properties := b.EncodeBlock() - return map[string]any{ - "name": name, - "states": properties, - "version": chunk.CurrentBlockVersion, - } -} - -// writeItemStack writes the name, metadata value, count and NBT of an item to a map ready for NBT encoding. -func writeItemStack(m, t map[string]any, s item.Stack) { - m["Name"], m["Damage"] = s.Item().EncodeItem() - if b, ok := s.Item().(world.Block); ok { - v := map[string]any{} - writeBlock(v, b) - m["Block"] = v - } - m["Count"] = byte(s.Count()) - if len(t) > 0 { - m["tag"] = t - } -} - -// writeBlock writes the name, properties and version of a block to a map ready for NBT encoding. -func writeBlock(m map[string]any, b world.Block) { - m["name"], m["states"] = b.EncodeBlock() - m["version"] = chunk.CurrentBlockVersion -} - -// writeDragonflyData writes additional data associated with an item.Stack to a map for NBT encoding. -func writeDragonflyData(m map[string]any, s item.Stack) { - if v := s.Values(); len(v) != 0 { - buf := new(bytes.Buffer) - if err := gob.NewEncoder(buf).Encode(mapToSlice(v)); err != nil { - panic("error encoding item user data: " + err.Error()) - } - m["dragonflyData"] = buf.Bytes() - } -} - -// mapToSlice converts a map to a slice of the type mapValue and orders the slice by the keys in the map to ensure a -// deterministic order. -func mapToSlice(m map[string]any) []mapValue { - values := make([]mapValue, 0, len(m)) - for k, v := range m { - values = append(values, mapValue{K: k, V: v}) - } - sort.Slice(values, func(i, j int) bool { - return values[i].K < values[j].K - }) - return values -} - -// mapValue represents a value in a map. It is used to convert maps to a slice and order the slice before encoding to -// NBT to ensure a deterministic output. -type mapValue struct { - K string - V any -} - -// writeEnchantments writes the enchantments of an item to a map for NBT encoding. -func writeEnchantments(m map[string]any, s item.Stack) { - if len(s.Enchantments()) != 0 { - var enchantments []map[string]any - for _, e := range s.Enchantments() { - if eType, ok := item.EnchantmentID(e.Type()); ok { - enchantments = append(enchantments, map[string]any{ - "id": int16(eType), - "lvl": int16(e.Level()), - }) - } - } - m["ench"] = enchantments - } -} - -// writeDisplay writes the display name and lore of an item to a map for NBT encoding. -func writeDisplay(m map[string]any, s item.Stack) { - name, lore := s.CustomName(), s.Lore() - v := map[string]any{} - if name != "" { - v["Name"] = name - } - if len(lore) != 0 { - v["Lore"] = lore - } - if len(v) != 0 { - m["display"] = v - } -} - -// writeDamage writes the damage to an item.Stack (either an int16 for disk or int32 for network) to a map for NBT -// encoding. -func writeDamage(m map[string]any, s item.Stack, disk bool) { - if v, ok := m["Damage"]; !ok || v.(int16) == 0 { - if _, ok := s.Item().(item.Durable); ok { - if disk { - m["Damage"] = int16(s.MaxDurability() - s.Durability()) - } else { - m["Damage"] = int32(s.MaxDurability() - s.Durability()) - } - } - } -} - -// writeAnvilCost ... -func writeAnvilCost(m map[string]any, s item.Stack) { - if cost := s.AnvilCost(); cost > 0 { - m["RepairCost"] = int32(cost) - } -} diff --git a/utils/proxy/conn.go b/utils/proxy/conn.go index 94fb67a..c061966 100644 --- a/utils/proxy/conn.go +++ b/utils/proxy/conn.go @@ -96,11 +96,15 @@ func (p *Context) connectClient(ctx context.Context, serverAddress string) (err extraClientDebug, extraClientDebugEnd = newExtraDebug("packets-client.log") } + if p.ListenAddress == "" { + p.ListenAddress = "127.0.0.1:19132" + } + p.listener, err = minecraft.ListenConfig{ - StatusProvider: minecraft.NewStatusProvider(fmt.Sprintf("%s Proxy", serverAddress)), + StatusProvider: minecraft.NewStatusProvider(fmt.Sprintf("%s Proxy", serverAddress), "Bedrocktool"), PacketFunc: func(header packet.Header, payload []byte, src, dst net.Addr) { if extraClientDebug != nil { - pk, ok := DecodePacket(header, payload) + pk, ok := DecodePacket(header, payload, p.Client.ShieldID()) if !ok { return } @@ -117,7 +121,7 @@ func (p *Context) connectClient(ctx context.Context, serverAddress string) (err c.ResourcePackHandler = p.rpHandler close(p.clientConnecting) }, - }.Listen("raknet", ":19132") + }.Listen("raknet", p.ListenAddress) if err != nil { return err } diff --git a/utils/proxy/context.go b/utils/proxy/context.go index 4bc34b0..4ffa5ef 100644 --- a/utils/proxy/context.go +++ b/utils/proxy/context.go @@ -28,12 +28,13 @@ import ( ) type Context struct { - Server minecraft.IConn - Client minecraft.IConn - listener *minecraft.Listener - Player Player - ExtraDebug bool - PlayerMoveCB []func() + Server minecraft.IConn + Client minecraft.IConn + listener *minecraft.Listener + Player Player + ExtraDebug bool + PlayerMoveCB []func() + ListenAddress string withClient bool addedPacks []*resource.Pack @@ -247,7 +248,7 @@ func (p *Context) packetFunc(header packet.Header, payload []byte, src, dst net. } if !p.spawned { - pk, ok := DecodePacket(header, payload) + pk, ok := DecodePacket(header, payload, p.Server.ShieldID()) if !ok { return } @@ -265,6 +266,9 @@ func (p *Context) packetFunc(header packet.Header, payload []byte, src, dst net. if err != nil { logrus.Error(err) } + if pk == nil { + break + } } } } diff --git a/utils/proxy/proxy.go b/utils/proxy/proxy.go index a5218e7..cbfe620 100644 --- a/utils/proxy/proxy.go +++ b/utils/proxy/proxy.go @@ -56,7 +56,7 @@ var errCancelConnect = fmt.Errorf("cancelled connecting") var serverPool = packet.NewServerPool() var clientPool = packet.NewClientPool() -func DecodePacket(header packet.Header, payload []byte) (pk packet.Packet, ok bool) { +func DecodePacket(header packet.Header, payload []byte, shieldID int32) (pk packet.Packet, ok bool) { pkFunc, ok := serverPool[header.PacketID] if !ok { pkFunc, ok = clientPool[header.PacketID] @@ -75,6 +75,6 @@ func DecodePacket(header packet.Header, payload []byte) (pk packet.Packet, ok bo ok = false } }() - pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0, false)) + pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), shieldID, false)) return pk, ok } diff --git a/utils/proxy/replay.go b/utils/proxy/replay.go index e53c007..77366ff 100644 --- a/utils/proxy/replay.go +++ b/utils/proxy/replay.go @@ -40,6 +40,7 @@ type replayConnector struct { clientData login.ClientData gameData minecraft.GameData + shieldID atomic.Int32 packetFunc PacketFunc @@ -125,6 +126,11 @@ func (r *replayConnector) handleLoginSequence(pk packet.Packet) (bool, error) { DisablePlayerInteractions: pk.DisablePlayerInteractions, UseBlockNetworkIDHashes: pk.UseBlockNetworkIDHashes, }) + for _, item := range pk.Items { + if item.Name == "minecraft:shield" { + r.shieldID.Store(int32(item.RuntimeID)) + } + } case *packet.ResourcePacksInfo: return false, r.resourcePackHandler.OnResourcePacksInfo(pk) @@ -439,7 +445,7 @@ func (r *replayConnector) Pool() packet.Pool { } func (r *replayConnector) ShieldID() int32 { - return 0 + return r.shieldID.Load() } func (r *replayConnector) Proto() minecraft.Protocol { diff --git a/utils/utils.go b/utils/utils.go index 9fd1fd0..dfdcea1 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -17,9 +17,9 @@ import ( "strings" "unsafe" - "github.com/bedrock-tool/bedrocktool/utils/nbtconv" "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/nbtconv" "github.com/df-mc/dragonfly/server/world" "github.com/google/uuid" "github.com/sirupsen/logrus" @@ -183,7 +183,7 @@ func ParseJson(s []byte, out any) error { } // stackToItem converts a network ItemStack representation back to an item.Stack. -func StackToItem(it protocol.ItemStack) item.Stack { +func StackToItem(reg world.BlockRegistry, it protocol.ItemStack) item.Stack { t, ok := world.ItemByRuntimeID(it.NetworkID, int16(it.MetadataValue)) if !ok { t = block.Air{} @@ -192,7 +192,7 @@ func StackToItem(it protocol.ItemStack) item.Stack { // It shouldn't matter if it (for whatever reason) wasn't able to get the block runtime ID, // since on the next line, we assert that the block is an item. If it didn't succeed, it'll // return air anyway. - b, _ := world.BlockByRuntimeID(uint32(it.BlockRuntimeID)) + b, _ := reg.BlockByRuntimeID(uint32(it.BlockRuntimeID)) if t, ok = b.(world.Item); !ok { t = block.Air{} }