-
Notifications
You must be signed in to change notification settings - Fork 145
/
encoding.go
171 lines (152 loc) · 5.87 KB
/
encoding.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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
// BlockPaletteEncoding is the paletteEncoding used for encoding a palette of block states encoded as NBT.
BlockPaletteEncoding blockPaletteEncoding
)
// 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.
type blockPaletteEncoding struct{}
func (blockPaletteEncoding) encode(buf *bytes.Buffer, v uint32) {
// 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, _ := RuntimeIDToState(v)
_ = nbt.NewEncoderWithEncoding(buf, nbt.LittleEndian).Encode(blockEntry{Name: name, State: props, Version: CurrentBlockVersion})
}
func (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)
}
// 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 !ok {
// If it doesn't exist, this is likely 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
}
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 := 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
}