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  }