github.com/df-mc/dragonfly@v0.9.13/server/world/chunk/encoding.go (about) 1 package chunk 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "github.com/df-mc/worldupgrader/blockupgrader" 8 "github.com/sandertv/gophertunnel/minecraft/nbt" 9 "github.com/sandertv/gophertunnel/minecraft/protocol" 10 ) 11 12 type ( 13 // Encoding is an encoding type used for Chunk encoding. Implementations of this interface are DiskEncoding and 14 // NetworkEncoding, which can be used to encode a Chunk to an intermediate disk or network representation respectively. 15 Encoding interface { 16 encodePalette(buf *bytes.Buffer, p *Palette, e paletteEncoding) 17 decodePalette(buf *bytes.Buffer, blockSize paletteSize, e paletteEncoding) (*Palette, error) 18 network() byte 19 } 20 // paletteEncoding is an encoding type used for Chunk encoding. It is used to encode different types of palettes 21 // (for example, blocks or biomes) differently. 22 paletteEncoding interface { 23 encode(buf *bytes.Buffer, v uint32) 24 decode(buf *bytes.Buffer) (uint32, error) 25 } 26 ) 27 28 var ( 29 // DiskEncoding is the Encoding for writing a Chunk to disk. It writes block palettes using NBT and does not use 30 // varints. 31 DiskEncoding diskEncoding 32 // NetworkEncoding is the Encoding used for sending a Chunk over network. It does not use NBT and writes varints. 33 NetworkEncoding networkEncoding 34 // BiomePaletteEncoding is the paletteEncoding used for encoding a palette of biomes. 35 BiomePaletteEncoding biomePaletteEncoding 36 // BlockPaletteEncoding is the paletteEncoding used for encoding a palette of block states encoded as NBT. 37 BlockPaletteEncoding blockPaletteEncoding 38 ) 39 40 // biomePaletteEncoding implements the encoding of biome palettes to disk. 41 type biomePaletteEncoding struct{} 42 43 func (biomePaletteEncoding) encode(buf *bytes.Buffer, v uint32) { 44 _ = binary.Write(buf, binary.LittleEndian, v) 45 } 46 func (biomePaletteEncoding) decode(buf *bytes.Buffer) (uint32, error) { 47 var v uint32 48 return v, binary.Read(buf, binary.LittleEndian, &v) 49 } 50 51 // blockPaletteEncoding implements the encoding of block palettes to disk. 52 type blockPaletteEncoding struct{} 53 54 func (blockPaletteEncoding) encode(buf *bytes.Buffer, v uint32) { 55 // Get the block state registered with the runtime IDs we have in the palette of the block storage 56 // as we need the name and data value to store. 57 name, props, _ := RuntimeIDToState(v) 58 _ = nbt.NewEncoderWithEncoding(buf, nbt.LittleEndian).Encode(blockEntry{Name: name, State: props, Version: CurrentBlockVersion}) 59 } 60 func (blockPaletteEncoding) decode(buf *bytes.Buffer) (uint32, error) { 61 var m map[string]any 62 if err := nbt.NewDecoderWithEncoding(buf, nbt.LittleEndian).Decode(&m); err != nil { 63 return 0, fmt.Errorf("error decoding block palette entry: %w", err) 64 } 65 66 // Decode the name and version of the block entry. 67 name, _ := m["name"].(string) 68 version, _ := m["version"].(int32) 69 70 // Now check for a state field. 71 stateI, ok := m["states"] 72 if !ok { 73 // If it doesn't exist, this is likely a pre-1.13 block state, so decode the meta value instead. 74 meta, _ := m["val"].(int16) 75 76 // Upgrade the pre-1.13 state into a post-1.13 state. 77 state, ok := upgradeLegacyEntry(name, meta) 78 if !ok { 79 return 0, fmt.Errorf("cannot find mapping for legacy block entry: %v, %v", name, meta) 80 } 81 82 // Update the name, state, and version. 83 name = state.Name 84 stateI = state.State 85 version = state.Version 86 } 87 state, ok := stateI.(map[string]any) 88 if !ok { 89 return 0, fmt.Errorf("invalid state in block entry") 90 } 91 92 // Upgrade the block state if necessary. 93 upgraded := blockupgrader.Upgrade(blockupgrader.BlockState{ 94 Name: name, 95 Properties: state, 96 Version: version, 97 }) 98 99 v, ok := StateToRuntimeID(upgraded.Name, upgraded.Properties) 100 if !ok { 101 return 0, fmt.Errorf("cannot get runtime ID of block state %v{%+v} %v", upgraded.Name, upgraded.Properties, upgraded.Version) 102 } 103 return v, nil 104 } 105 106 // diskEncoding implements the Chunk encoding for writing to disk. 107 type diskEncoding struct{} 108 109 func (diskEncoding) network() byte { return 0 } 110 func (diskEncoding) encodePalette(buf *bytes.Buffer, p *Palette, e paletteEncoding) { 111 if p.size != 0 { 112 _ = binary.Write(buf, binary.LittleEndian, uint32(p.Len())) 113 } 114 for _, v := range p.values { 115 e.encode(buf, v) 116 } 117 } 118 func (diskEncoding) decodePalette(buf *bytes.Buffer, blockSize paletteSize, e paletteEncoding) (*Palette, error) { 119 paletteCount := uint32(1) 120 if blockSize != 0 { 121 if err := binary.Read(buf, binary.LittleEndian, &paletteCount); err != nil { 122 return nil, fmt.Errorf("error reading palette entry count: %w", err) 123 } 124 } 125 126 var err error 127 palette := newPalette(blockSize, make([]uint32, paletteCount)) 128 for i := uint32(0); i < paletteCount; i++ { 129 palette.values[i], err = e.decode(buf) 130 if err != nil { 131 return nil, err 132 } 133 } 134 if paletteCount == 0 { 135 return palette, fmt.Errorf("invalid palette entry count: found 0, but palette with %v bits per block must have at least 1 value", blockSize) 136 } 137 return palette, nil 138 } 139 140 // networkEncoding implements the Chunk encoding for sending over network. 141 type networkEncoding struct{} 142 143 func (networkEncoding) network() byte { return 1 } 144 func (networkEncoding) encodePalette(buf *bytes.Buffer, p *Palette, _ paletteEncoding) { 145 if p.size != 0 { 146 _ = protocol.WriteVarint32(buf, int32(p.Len())) 147 } 148 for _, val := range p.values { 149 _ = protocol.WriteVarint32(buf, int32(val)) 150 } 151 } 152 func (networkEncoding) decodePalette(buf *bytes.Buffer, blockSize paletteSize, _ paletteEncoding) (*Palette, error) { 153 var paletteCount int32 = 1 154 if blockSize != 0 { 155 if err := protocol.Varint32(buf, &paletteCount); err != nil { 156 return nil, fmt.Errorf("error reading palette entry count: %w", err) 157 } 158 if paletteCount <= 0 { 159 return nil, fmt.Errorf("invalid palette entry count %v", paletteCount) 160 } 161 } 162 163 blocks, temp := make([]uint32, paletteCount), int32(0) 164 for i := int32(0); i < paletteCount; i++ { 165 if err := protocol.Varint32(buf, &temp); err != nil { 166 return nil, fmt.Errorf("error decoding palette entry: %w", err) 167 } 168 blocks[i] = uint32(temp) 169 } 170 return &Palette{values: blocks, size: blockSize}, nil 171 }