github.com/df-mc/dragonfly@v0.9.13/server/world/chunk/encode.go (about) 1 package chunk 2 3 import ( 4 "bytes" 5 "sync" 6 ) 7 8 const ( 9 // SubChunkVersion is the current version of the written sub chunks, specifying the format they are 10 // written on disk and over network. 11 SubChunkVersion = 9 12 // CurrentBlockVersion is the current version of blocks (states) of the game. This version is composed 13 // of 4 bytes indicating a version, interpreted as a big endian int. The current version represents 14 // 1.16.0.14 {1, 16, 0, 14}. 15 CurrentBlockVersion int32 = 18040335 16 ) 17 18 var ( 19 // RuntimeIDToState must hold a function to convert a runtime ID to a name and its state properties. 20 RuntimeIDToState func(runtimeID uint32) (name string, properties map[string]any, found bool) 21 // pool is used to pool byte buffers used for encoding chunks. 22 pool = sync.Pool{ 23 New: func() any { 24 return bytes.NewBuffer(make([]byte, 0, 1024)) 25 }, 26 } 27 ) 28 29 type ( 30 // SerialisedData holds the serialised data of a chunk. It consists of the chunk's block data itself, a height 31 // map, the biomes and entities and block entities. 32 SerialisedData struct { 33 // sub holds the data of the serialised sub chunks in a chunk. Sub chunks that are empty or that otherwise 34 // don't exist are represented as an empty slice (or technically, nil). 35 SubChunks [][]byte 36 // Biomes is the biome data of the chunk, which is composed of a biome storage for each sub-chunk. 37 Biomes []byte 38 } 39 // blockEntry represents a block as found in a disk save of a world. 40 blockEntry struct { 41 Name string `nbt:"name"` 42 State map[string]any `nbt:"states"` 43 Version int32 `nbt:"version"` 44 } 45 ) 46 47 // Encode encodes Chunk to an intermediate representation SerialisedData. An Encoding may be passed to encode either for 48 // network or disk purposed, the most notable difference being that the network encoding generally uses varints and no 49 // NBT. 50 func Encode(c *Chunk, e Encoding) SerialisedData { 51 d := SerialisedData{SubChunks: make([][]byte, len(c.sub))} 52 for i := range c.sub { 53 d.SubChunks[i] = EncodeSubChunk(c, e, i) 54 } 55 d.Biomes = EncodeBiomes(c, e) 56 return d 57 } 58 59 // EncodeSubChunk encodes a sub-chunk from a chunk into bytes. An Encoding may be passed to encode either for network or 60 // disk purposed, the most notable difference being that the network encoding generally uses varints and no NBT. 61 func EncodeSubChunk(c *Chunk, e Encoding, ind int) []byte { 62 buf := pool.Get().(*bytes.Buffer) 63 defer func() { 64 buf.Reset() 65 pool.Put(buf) 66 }() 67 68 s := c.sub[ind] 69 _, _ = buf.Write([]byte{SubChunkVersion, byte(len(s.storages)), uint8(ind + (c.r[0] >> 4))}) 70 for _, storage := range s.storages { 71 encodePalettedStorage(buf, storage, nil, e, BlockPaletteEncoding) 72 } 73 sub := make([]byte, buf.Len()) 74 _, _ = buf.Read(sub) 75 return sub 76 } 77 78 // EncodeBiomes encodes the biomes of a chunk into bytes. An Encoding may be passed to encode either for network or 79 // disk purposed, the most notable difference being that the network encoding generally uses varints and no NBT. 80 func EncodeBiomes(c *Chunk, e Encoding) []byte { 81 buf := pool.Get().(*bytes.Buffer) 82 defer func() { 83 buf.Reset() 84 pool.Put(buf) 85 }() 86 87 var previous *PalettedStorage 88 for _, b := range c.biomes { 89 encodePalettedStorage(buf, b, previous, e, BiomePaletteEncoding) 90 previous = b 91 } 92 biomes := make([]byte, buf.Len()) 93 _, _ = buf.Read(biomes) 94 return biomes 95 } 96 97 // encodePalettedStorage encodes a PalettedStorage into a bytes.Buffer. The Encoding passed is used to write the Palette 98 // of the PalettedStorage. 99 func encodePalettedStorage(buf *bytes.Buffer, storage, previous *PalettedStorage, e Encoding, pe paletteEncoding) { 100 if storage.Equal(previous) { 101 _, _ = buf.Write([]byte{0x7f<<1 | e.network()}) 102 return 103 } 104 b := make([]byte, len(storage.indices)*4+1) 105 b[0] = byte(storage.bitsPerIndex<<1) | e.network() 106 107 for i, v := range storage.indices { 108 // Explicitly don't use the binary package to greatly improve performance of writing the uint32s. 109 b[i*4+1], b[i*4+2], b[i*4+3], b[i*4+4] = byte(v), byte(v>>8), byte(v>>16), byte(v>>24) 110 } 111 _, _ = buf.Write(b) 112 113 e.encodePalette(buf, storage.palette, pe) 114 }