Add support for dimension ranges and network hashes

This commit is contained in:
Javier León
2026-01-03 05:33:35 -03:00
parent 2ad86d772f
commit 7a4d57fa6a
10 changed files with 372 additions and 82 deletions

View File

@@ -64,7 +64,7 @@ func lookupOrCreateBlockTranslator(protocolVersion, blockVersion int32, blockSta
}
// DowngradeLevelChunkPacket downgrades the given level chunk packet to the specified protocol version.
func DowngradeLevelChunkPacket(protocolVersion int32, pk *packet.LevelChunk) error {
func DowngradeLevelChunkPacket(protocolVersion int32, pk *packet.LevelChunk, conn *minecraft.Conn) error {
blockTranslatorsMu.RLock()
translator, ok := blockTranslators[protocolVersion]
blockTranslatorsMu.RUnlock()
@@ -72,5 +72,5 @@ func DowngradeLevelChunkPacket(protocolVersion int32, pk *packet.LevelChunk) err
return fmt.Errorf("no block translator found for protocol version %d", protocolVersion)
}
return translator.DowngradeLevelChunk(pk)
return translator.DowngradeLevelChunk(pk, conn)
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/cespare/xxhash/v2"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/hashicorp/go-version"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/nbt"
"github.com/sandertv/gophertunnel/minecraft/protocol"
@@ -24,6 +25,7 @@ var (
LatestNetworkPersistentEncoding = chunk.NewNetworkPersistentEncoding(blockMappingLatest, BlockVersionLatest)
// LatestBlockPaletteEncoding is the paletteEncoding used for encoding a palette of block states encoded as NBT.
LatestBlockPaletteEncoding = chunk.NewBlockPaletteEncoding(blockMappingLatest, BlockVersionLatest)
cavesAndCliffsVersion, _ = version.NewVersion("1.18.0")
)
type BlockTranslator interface {
@@ -32,17 +34,18 @@ type BlockTranslator interface {
// UpgradeBlockPackets upgrades the input block packets to the latest block packets.
UpgradeBlockPackets([]packet.Packet, *minecraft.Conn) (result []packet.Packet)
// DowngradeLevelChunk downgrades the given LevelChunk packet to a legacy format.
DowngradeLevelChunk(*packet.LevelChunk) error
DowngradeLevelChunk(*packet.LevelChunk, *minecraft.Conn) error
// BlockMapping returns the block mapping used by this translator.
BlockMapping() mapping.Block
}
type DefaultBlockTranslator struct {
mapping mapping.Block
latest mapping.Block
pse chunk.Encoding
pe chunk.PaletteEncoding
oldFormat bool
mapping mapping.Block
latest mapping.Block
pse chunk.Encoding
pe chunk.PaletteEncoding
oldFormat bool
dimensionDefinitions []protocol.DimensionDefinition
currentDimension int32
}
func NewBlockTranslator(mapping mapping.Block, latestMapping mapping.Block, pse chunk.Encoding, pe chunk.PaletteEncoding, oldFormat bool) *DefaultBlockTranslator {
@@ -53,7 +56,7 @@ func (t *DefaultBlockTranslator) BlockMapping() mapping.Block {
return t.mapping
}
func (t *DefaultBlockTranslator) DowngradeLevelChunk(pk *packet.LevelChunk) error {
func (t *DefaultBlockTranslator) DowngradeLevelChunk(pk *packet.LevelChunk, conn *minecraft.Conn) error {
count := int(pk.SubChunkCount)
if count == protocol.SubChunkRequestModeLimitless || count == protocol.SubChunkRequestModeLimited {
return nil
@@ -62,13 +65,13 @@ func (t *DefaultBlockTranslator) DowngradeLevelChunk(pk *packet.LevelChunk) erro
buf := bytes.NewBuffer(pk.RawPayload)
writeBuf := bytes.NewBuffer(nil)
if !pk.CacheEnabled {
c, err := chunk.NetworkDecode(t.latest.Air(), buf, count, false, world.Overworld.Range(), LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
c, err := chunk.NetworkDecode(t.latest.Air(), buf, count, false, t.getRange(conn), LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding, t.latest, conn.GameData().UseBlockNetworkIDHashes)
if err != nil {
return err
}
c = t.DowngradeChunk(c)
c = t.DowngradeChunk(c, conn)
payload, err := chunk.NetworkEncode(t.mapping.Air(), c, t.oldFormat, t.pe)
payload, err := chunk.NetworkEncode(t.mapping.Air(), c, t.oldFormat, t.pe, t.mapping, false)
if err != nil {
return err
}
@@ -114,7 +117,7 @@ func (t *DefaultBlockTranslator) DowngradeBlockPackets(pks []packet.Packet, conn
if !EnableChunkTranslation {
break
}
if err := t.DowngradeLevelChunk(pk); err != nil {
if err := t.DowngradeLevelChunk(pk, conn); err != nil {
//fmt.Println(err)
break
}
@@ -122,7 +125,7 @@ func (t *DefaultBlockTranslator) DowngradeBlockPackets(pks []packet.Packet, conn
if !EnableChunkTranslation {
break
}
r := world.Overworld.Range()
r := t.getRange(conn)
if t.oldFormat {
r = cube.Range{0, 255}
}
@@ -133,13 +136,13 @@ func (t *DefaultBlockTranslator) DowngradeBlockPackets(pks []packet.Packet, conn
writeBuf := bytes.NewBuffer(nil)
if !pk.CacheEnabled && !conn.ClientCacheEnabled() {
ind := byte(i)
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding, t.latest, conn.GameData().UseBlockNetworkIDHashes)
if err != nil {
//fmt.Println(err)
continue
}
t.DowngradeSubChunk(subChunk)
writeBuf.Write(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind)))
writeBuf.Write(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind), t.mapping, false))
}
enc := nbt.NewEncoderWithEncoding(writeBuf, nbt.NetworkLittleEndian)
@@ -162,7 +165,7 @@ func (t *DefaultBlockTranslator) DowngradeBlockPackets(pks []packet.Packet, conn
}
}
case *packet.ClientCacheMissResponse:
r := world.Overworld.Range()
r := t.getRange(conn)
if t.oldFormat {
r = cube.Range{0, 255}
}
@@ -170,14 +173,13 @@ func (t *DefaultBlockTranslator) DowngradeBlockPackets(pks []packet.Packet, conn
for i, blob := range pk.Blobs {
buf := bytes.NewBuffer(blob.Payload)
ind := byte(0)
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding, t.latest, conn.GameData().UseBlockNetworkIDHashes)
if err != nil {
// Has a possibility to be a biome, ignore then
continue
}
t.DowngradeSubChunk(subChunk)
blob.Payload = append(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind)), buf.Bytes()...)
blob.Payload = append(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind), t.mapping, false), buf.Bytes()...)
blob.Hash = xxhash.Sum64(blob.Payload)
pk.Blobs[i] = blob
}
@@ -280,13 +282,13 @@ func (t *DefaultBlockTranslator) DowngradeBlockRuntimeID(input uint32) uint32 {
return runtimeID
}
func (t *DefaultBlockTranslator) DowngradeChunk(input *chunk.Chunk) *chunk.Chunk {
func (t *DefaultBlockTranslator) DowngradeChunk(input *chunk.Chunk, conn *minecraft.Conn) *chunk.Chunk {
if t.latest == t.mapping {
return input
}
start := 0
r := world.Overworld.Range()
r := t.getRange(conn)
if t.oldFormat {
start = 4
r = cube.Range{0, 255}
@@ -357,3 +359,29 @@ func (t *DefaultBlockTranslator) upgradeEntityMetadata(metadata map[uint32]any)
}
return metadata
}
func (t *DefaultBlockTranslator) getRange(conn *minecraft.Conn) (r cube.Range) {
var dimName string
switch t.currentDimension {
case 0:
dimName = "minecraft:overworld"
r = world.Overworld.Range()
v, err := version.NewVersion(conn.GameData().BaseGameVersion)
if err == nil && v.LessThan(cavesAndCliffsVersion) {
r = cube.Range{0, 255}
}
case 1:
dimName = "minecraft:nether"
r = world.Nether.Range()
case 2:
dimName = "minecraft:the_end"
r = world.End.Range()
}
for _, def := range t.dimensionDefinitions {
if def.Name == dimName {
r = cube.Range{int(def.Range[0]), int(def.Range[1])}
break
}
}
return
}

View File

@@ -0,0 +1,18 @@
package legacypacket
import (
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
type DimensionData struct {
Definitions []protocol.DimensionDefinition
}
func (*DimensionData) ID() uint32 {
return packet.IDDimensionData
}
func (pk *DimensionData) Marshal(io protocol.IO) {
protocol.Slice(io, &pk.Definitions)
}

View File

@@ -31,6 +31,8 @@ func init() {
func convertPacketFunc(pid uint32, cur func() packet.Packet) func() packet.Packet {
switch pid {
case packet.IDDimensionData:
return func() packet.Packet { return &legacypacket.DimensionData{} }
case packet.IDCameraAimAssist:
return func() packet.Packet { return &legacypacket.CameraAimAssist{} }
case packet.IDCameraPresets:
@@ -190,8 +192,14 @@ func (p *Protocol) ConvertFromLatest(pk packet.Packet, conn *minecraft.Conn) []p
}
func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet {
translator, ok := p.blockTranslator.(*DefaultBlockTranslator)
if !ok {
return pks
}
for pkIndex, pk := range pks {
switch pk := pk.(type) {
case *packet.DimensionData:
translator.dimensionDefinitions = pk.Definitions
case *packet.ClientCacheStatus:
// pk.Enabled = false // TODO: enable when chunk translation is not broken
case *packet.SetActorMotion:
@@ -414,6 +422,7 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
DetachFromEntity: pk.DetachFromEntity,
}
case *packet.ChangeDimension:
translator.currentDimension = pk.Dimension
pks[pkIndex] = &legacypacket.ChangeDimension{
Dimension: pk.Dimension,
Position: pk.Position,
@@ -519,6 +528,7 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
ClearRecipes: pk.ClearRecipes,
}
case *packet.StartGame:
translator.currentDimension = pk.Dimension
// Adjust game version
pk.GameVersion = p.ver
pk.BaseGameVersion = p.ver