185 lines
6.2 KiB
Go
185 lines
6.2 KiB
Go
|
|
package chunk
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"encoding/binary"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
"github.com/df-mc/worldupgrader/blockupgrader"
|
||
|
|
"github.com/sandertv/gophertunnel/minecraft/nbt"
|
||
|
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||
|
|
)
|
||
|
|
|
||
|
|
type (
|
||
|
|
// Encoding is an encoding type used for Chunk encoding. Implementations of this interface are DiskEncoding and
|
||
|
|
// NetworkEncoding, which can be used to encode a Chunk to an intermediate disk or network representation respectively.
|
||
|
|
Encoding interface {
|
||
|
|
encodePalette(buf *bytes.Buffer, p *Palette, e paletteEncoding)
|
||
|
|
decodePalette(buf *bytes.Buffer, blockSize paletteSize, e paletteEncoding) (*Palette, error)
|
||
|
|
network() byte
|
||
|
|
}
|
||
|
|
// paletteEncoding is an encoding type used for Chunk encoding. It is used to encode different types of palettes
|
||
|
|
// (for example, blocks or biomes) differently.
|
||
|
|
paletteEncoding interface {
|
||
|
|
encode(buf *bytes.Buffer, v uint32)
|
||
|
|
decode(buf *bytes.Buffer) (uint32, error)
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
var (
|
||
|
|
// DiskEncoding is the Encoding for writing a Chunk to disk. It writes block palettes using NBT and does not use
|
||
|
|
// varints.
|
||
|
|
DiskEncoding diskEncoding
|
||
|
|
// NetworkEncoding is the Encoding used for sending a Chunk over network. It does not use NBT and writes varints.
|
||
|
|
NetworkEncoding networkEncoding
|
||
|
|
// BiomePaletteEncoding is the paletteEncoding used for encoding a palette of biomes.
|
||
|
|
BiomePaletteEncoding biomePaletteEncoding
|
||
|
|
)
|
||
|
|
|
||
|
|
// biomePaletteEncoding implements the encoding of biome palettes to disk.
|
||
|
|
type biomePaletteEncoding struct{}
|
||
|
|
|
||
|
|
func (biomePaletteEncoding) encode(buf *bytes.Buffer, v uint32) {
|
||
|
|
_ = binary.Write(buf, binary.LittleEndian, v)
|
||
|
|
}
|
||
|
|
func (biomePaletteEncoding) decode(buf *bytes.Buffer) (uint32, error) {
|
||
|
|
var v uint32
|
||
|
|
return v, binary.Read(buf, binary.LittleEndian, &v)
|
||
|
|
}
|
||
|
|
|
||
|
|
// BlockPaletteEncoding implements the encoding of block palettes to disk. It requires a BlockRegistry for converting
|
||
|
|
// between runtime IDs and block states.
|
||
|
|
type BlockPaletteEncoding struct {
|
||
|
|
Blocks BlockRegistry
|
||
|
|
}
|
||
|
|
|
||
|
|
func (bpe BlockPaletteEncoding) encode(buf *bytes.Buffer, v uint32) {
|
||
|
|
_ = nbt.NewEncoderWithEncoding(buf, nbt.LittleEndian).Encode(bpe.EncodeBlockState(v))
|
||
|
|
}
|
||
|
|
func (bpe BlockPaletteEncoding) decode(buf *bytes.Buffer) (uint32, error) {
|
||
|
|
var m map[string]any
|
||
|
|
if err := nbt.NewDecoderWithEncoding(buf, nbt.LittleEndian).Decode(&m); err != nil {
|
||
|
|
return 0, fmt.Errorf("error decoding block palette entry: %w", err)
|
||
|
|
}
|
||
|
|
return bpe.DecodeBlockState(m)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (bpe BlockPaletteEncoding) EncodeBlockState(v uint32) blockEntry {
|
||
|
|
// Get the block state registered with the runtime IDs we have in the palette of the block storage
|
||
|
|
// as we need the name and data value to store.
|
||
|
|
name, props, _ := bpe.Blocks.RuntimeIDToState(v)
|
||
|
|
return blockEntry{Name: name, State: props, Version: CurrentBlockVersion}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (bpe BlockPaletteEncoding) DecodeBlockState(m map[string]any) (uint32, error) {
|
||
|
|
// Decode the name and version of the block entry.
|
||
|
|
name, _ := m["name"].(string)
|
||
|
|
version, _ := m["version"].(int32)
|
||
|
|
|
||
|
|
// Now check for a state field.
|
||
|
|
stateI, ok := m["states"]
|
||
|
|
if version < 17694723 {
|
||
|
|
// This entry is a pre-1.13 block state, so decode the meta value instead.
|
||
|
|
meta, _ := m["val"].(int16)
|
||
|
|
|
||
|
|
// Upgrade the pre-1.13 state into a post-1.13 state.
|
||
|
|
state, ok := upgradeLegacyEntry(name, meta)
|
||
|
|
if !ok {
|
||
|
|
return 0, fmt.Errorf("cannot find mapping for legacy block entry: %v, %v", name, meta)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update the name, state, and version.
|
||
|
|
name = state.Name
|
||
|
|
stateI = state.State
|
||
|
|
version = state.Version
|
||
|
|
} else if !ok {
|
||
|
|
// The state is a post-1.13 block state, but the states field is missing, likely due to a broken world
|
||
|
|
// conversion.
|
||
|
|
stateI = make(map[string]any)
|
||
|
|
}
|
||
|
|
state, ok := stateI.(map[string]any)
|
||
|
|
if !ok {
|
||
|
|
return 0, fmt.Errorf("invalid state in block entry")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Upgrade the block state if necessary.
|
||
|
|
upgraded := blockupgrader.Upgrade(blockupgrader.BlockState{
|
||
|
|
Name: name,
|
||
|
|
Properties: state,
|
||
|
|
Version: version,
|
||
|
|
})
|
||
|
|
|
||
|
|
v, ok := bpe.Blocks.StateToRuntimeID(upgraded.Name, upgraded.Properties)
|
||
|
|
if !ok {
|
||
|
|
return 0, fmt.Errorf("cannot get runtime ID of block state %v{%+v} %v", upgraded.Name, upgraded.Properties, upgraded.Version)
|
||
|
|
}
|
||
|
|
return v, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// diskEncoding implements the Chunk encoding for writing to disk.
|
||
|
|
type diskEncoding struct{}
|
||
|
|
|
||
|
|
func (diskEncoding) network() byte { return 0 }
|
||
|
|
func (diskEncoding) encodePalette(buf *bytes.Buffer, p *Palette, e paletteEncoding) {
|
||
|
|
if p.size != 0 {
|
||
|
|
_ = binary.Write(buf, binary.LittleEndian, uint32(p.Len()))
|
||
|
|
}
|
||
|
|
for _, v := range p.values {
|
||
|
|
e.encode(buf, v)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
func (diskEncoding) decodePalette(buf *bytes.Buffer, blockSize paletteSize, e paletteEncoding) (*Palette, error) {
|
||
|
|
paletteCount := uint32(1)
|
||
|
|
if blockSize != 0 {
|
||
|
|
if err := binary.Read(buf, binary.LittleEndian, &paletteCount); err != nil {
|
||
|
|
return nil, fmt.Errorf("error reading palette entry count: %w", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var err error
|
||
|
|
palette := newPalette(blockSize, make([]uint32, paletteCount))
|
||
|
|
for i := uint32(0); i < paletteCount; i++ {
|
||
|
|
palette.values[i], err = e.decode(buf)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if paletteCount == 0 {
|
||
|
|
return palette, fmt.Errorf("invalid palette entry count: found 0, but palette with %v bits per block must have at least 1 value", blockSize)
|
||
|
|
}
|
||
|
|
return palette, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// networkEncoding implements the Chunk encoding for sending over network.
|
||
|
|
type networkEncoding struct{}
|
||
|
|
|
||
|
|
func (networkEncoding) network() byte { return 1 }
|
||
|
|
func (networkEncoding) encodePalette(buf *bytes.Buffer, p *Palette, _ paletteEncoding) {
|
||
|
|
if p.size != 0 {
|
||
|
|
_ = protocol.WriteVarint32(buf, int32(p.Len()))
|
||
|
|
}
|
||
|
|
for _, val := range p.values {
|
||
|
|
_ = protocol.WriteVarint32(buf, int32(val))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
func (networkEncoding) decodePalette(buf *bytes.Buffer, blockSize paletteSize, _ paletteEncoding) (*Palette, error) {
|
||
|
|
var paletteCount int32 = 1
|
||
|
|
if blockSize != 0 {
|
||
|
|
if err := protocol.Varint32(buf, &paletteCount); err != nil {
|
||
|
|
return nil, fmt.Errorf("error reading palette entry count: %w", err)
|
||
|
|
}
|
||
|
|
if paletteCount <= 0 {
|
||
|
|
return nil, fmt.Errorf("invalid palette entry count %v", paletteCount)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
blocks, temp := make([]uint32, paletteCount), int32(0)
|
||
|
|
for i := int32(0); i < paletteCount; i++ {
|
||
|
|
if err := protocol.Varint32(buf, &temp); err != nil {
|
||
|
|
return nil, fmt.Errorf("error decoding palette entry: %w", err)
|
||
|
|
}
|
||
|
|
blocks[i] = uint32(temp)
|
||
|
|
}
|
||
|
|
return &Palette{values: blocks, size: blockSize}, nil
|
||
|
|
}
|