Files
mc/server/world/chunk/decode.go
T
TarnaWijaya 26ed99fda6
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
up3
2026-07-09 08:33:57 +08:00

188 lines
6.3 KiB
Go

package chunk
import (
"bytes"
"fmt"
"github.com/df-mc/dragonfly/server/block/cube"
)
// NetworkDecode decodes the network serialised data passed into a Chunk if successful. If not, the chunk
// returned is nil and the error non-nil.
// The sub chunk count passed must be that found in the LevelChunk packet.
// NetworkDecode creates a new buffer and calls NetworkDecodeBuffer.
//
// The BlockRegistry passed must be finalized and must correspond to the runtime IDs used in the chunk data.
// noinspection GoUnusedExportedFunction
func NetworkDecode(br BlockRegistry, data []byte, count int, r cube.Range) (*Chunk, error) {
return NetworkDecodeBuffer(br, bytes.NewBuffer(data), count, r)
}
// NetworkDecodeBuffer decodes the network serialised data from buf passed into a Chunk if successful. If not, the chunk
// returned is nil and the error non-nil.
// The sub chunk count passed must be that found in the LevelChunk packet.
// noinspection GoUnusedExportedFunction
func NetworkDecodeBuffer(br BlockRegistry, buf *bytes.Buffer, count int, r cube.Range) (*Chunk, error) {
var (
c = New(br, r)
err error
)
for i := 0; i < count; i++ {
index := uint8(i)
c.sub[index], err = decodeSubChunk(buf, c, &index, NetworkEncoding)
if err != nil {
return nil, err
}
}
var last *PalettedStorage
for i := 0; i < len(c.sub); i++ {
b, err := decodePalettedStorage(buf, NetworkEncoding, BiomePaletteEncoding)
if err != nil {
return nil, err
}
if b == nil {
// b == nil means this paletted storage had the flag pointing to the previous one. It basically means we should
// inherit whatever palette we decoded last.
if i == 0 {
// This should never happen and there is no way to handle this.
return nil, fmt.Errorf("first biome storage pointed to previous one")
}
b = last
} else {
last = b
}
c.biomes[i] = b
}
return c, nil
}
// DiskDecode decodes the data from a SerialisedData object into a chunk and returns it. If the data was invalid,
// an error is returned.
//
// The BlockRegistry passed must be finalized and must correspond to the runtime IDs used in the chunk data.
func DiskDecode(br BlockRegistry, data SerialisedData, r cube.Range) (*Chunk, error) {
c := New(br, r)
err := decodeBiomes(bytes.NewBuffer(data.Biomes), c, DiskEncoding)
if err != nil {
return nil, err
}
for i, sub := range data.SubChunks {
if len(sub) == 0 {
// No data for this sub chunk.
continue
}
index := uint8(i)
if c.sub[index], err = decodeSubChunk(bytes.NewBuffer(sub), c, &index, DiskEncoding); err != nil {
return nil, err
}
}
return c, nil
}
// decodeSubChunk decodes a SubChunk from a bytes.Buffer. The Encoding passed defines how the block storages of the
// SubChunk are decoded.
func decodeSubChunk(buf *bytes.Buffer, c *Chunk, index *byte, e Encoding) (*SubChunk, error) {
ver, err := buf.ReadByte()
if err != nil {
return nil, fmt.Errorf("error reading version: %w", err)
}
sub := NewSubChunk(c.air)
switch ver {
default:
return nil, fmt.Errorf("unknown sub chunk version %v: can't decode", ver)
case 1:
// Version 1 only has one layer for each sub chunk, but uses the format with palettes.
storage, err := decodePalettedStorage(buf, e, BlockPaletteEncoding{Blocks: c.br})
if err != nil {
return nil, err
}
sub.storages = append(sub.storages, storage)
case 8, 9:
// Version 8 allows up to 256 layers for one sub chunk.
storageCount, err := buf.ReadByte()
if err != nil {
return nil, fmt.Errorf("error reading storage count: %w", err)
}
if ver == 9 {
uIndex, err := buf.ReadByte()
if err != nil {
return nil, fmt.Errorf("error reading sub-chunk index: %w", err)
}
// The index as written here isn't the actual index of the sub-chunk within the chunk. Rather, it is the Y
// value of the sub-chunk. This means that we need to translate it to an index.
*index = uint8(int8(uIndex) - int8(c.r[0]>>4))
}
sub.storages = make([]*PalettedStorage, storageCount)
for i := byte(0); i < storageCount; i++ {
sub.storages[i], err = decodePalettedStorage(buf, e, BlockPaletteEncoding{Blocks: c.br})
if err != nil {
return nil, err
}
}
}
return sub, nil
}
// decodeBiomes reads the paletted storages holding biomes from buf and stores it into the Chunk passed.
func decodeBiomes(buf *bytes.Buffer, c *Chunk, e Encoding) error {
var last *PalettedStorage
if buf.Len() != 0 {
for i := 0; i < len(c.sub); i++ {
b, err := decodePalettedStorage(buf, e, BiomePaletteEncoding)
if err != nil {
return err
}
// b == nil means this paletted storage had the flag pointing to the previous one. It basically means we should
// inherit whatever palette we decoded last.
if i == 0 && b == nil {
// This should never happen and there is no way to handle this.
return fmt.Errorf("first biome storage pointed to previous one")
}
if b == nil {
// This means this paletted storage had the flag pointing to the previous one. It basically means we should
// inherit whatever palette we decoded last.
b = last
} else {
last = b
}
c.biomes[i] = b
}
}
return nil
}
// decodePalettedStorage decodes a PalettedStorage from a bytes.Buffer. The Encoding passed is used to read either a
// network or disk block storage.
func decodePalettedStorage(buf *bytes.Buffer, e Encoding, pe paletteEncoding) (*PalettedStorage, error) {
blockSize, err := buf.ReadByte()
if err != nil {
return nil, fmt.Errorf("error reading block size: %w", err)
}
blockSize >>= 1
if blockSize == 0x7f {
return nil, nil
}
size := paletteSize(blockSize)
if size > 32 {
return nil, fmt.Errorf("cannot read paletted storage (size=%v) %T: size too large", blockSize, pe)
}
uint32Count := size.uint32s()
uint32s := make([]uint32, uint32Count)
byteCount := uint32Count * 4
data := buf.Next(byteCount)
if len(data) != byteCount {
return nil, fmt.Errorf("cannot read paletted storage (size=%v) %T: not enough block data present: expected %v bytes, got %v", blockSize, pe, byteCount, len(data))
}
for i := 0; i < uint32Count; i++ {
// Explicitly don't use the binary package to greatly improve performance of reading the uint32s.
uint32s[i] = uint32(data[i*4]) | uint32(data[i*4+1])<<8 | uint32(data[i*4+2])<<16 | uint32(data[i*4+3])<<24
}
p, err := e.decodePalette(buf, paletteSize(blockSize), pe)
return newPalettedStorage(uint32s, p), err
}