-
Notifications
You must be signed in to change notification settings - Fork 141
/
decode.go
179 lines (166 loc) · 5.72 KB
/
decode.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
172
173
174
175
176
177
178
179
package chunk
import (
"bytes"
"fmt"
"github.com/df-mc/dragonfly/server/block/cube"
)
// StateToRuntimeID must hold a function to convert a name and its state properties to a runtime ID.
var StateToRuntimeID func(name string, properties map[string]any) (runtimeID uint32, found bool)
// 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.
// noinspection GoUnusedExportedFunction
func NetworkDecode(air uint32, data []byte, count int, r cube.Range) (*Chunk, error) {
var (
c = New(air, r)
buf = bytes.NewBuffer(data)
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.
func DiskDecode(data SerialisedData, r cube.Range) (*Chunk, error) {
air, ok := StateToRuntimeID("minecraft:air", nil)
if !ok {
panic("cannot find air runtime ID")
}
c := New(air, 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)
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)
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)
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
}