fix bugs in PaletteContainer
This commit is contained in:
@ -1,170 +0,0 @@
|
||||
package save
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
const indexOutOfBounds = "index out of bounds"
|
||||
const valueOutOfBounds = "value out of bounds"
|
||||
|
||||
// BitStorage implement the compacted data array used in chunk storage and heightmaps.
|
||||
// You can think of this as a []intN whose N is called "bits" in NewBitStorage.
|
||||
// For more info, see: https://wiki.vg/Chunk_Format
|
||||
// This is implementation of the format since Minecraft 1.16
|
||||
type BitStorage struct {
|
||||
data []uint64
|
||||
mask uint64
|
||||
|
||||
bits, length int
|
||||
valuesPerLong int
|
||||
}
|
||||
|
||||
// NewBitStorage create a new BitStorage. Return nil if bits == 0.
|
||||
//
|
||||
// The "bits" is the number of bits per value, which can be calculated by math/bits.Len()
|
||||
// The "length" is the number of values.
|
||||
// The "data" is optional for initializing. Panic if data != nil && len(data) != calcBitStorageSize(bits, length).
|
||||
func NewBitStorage(bits, length int, data []uint64) (b *BitStorage) {
|
||||
if bits == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
b = &BitStorage{
|
||||
mask: 1<<bits - 1,
|
||||
bits: bits,
|
||||
length: length,
|
||||
valuesPerLong: 64 / bits,
|
||||
}
|
||||
dataLen := calcBitStorageSize(bits, length)
|
||||
if data != nil {
|
||||
if len(data) != dataLen {
|
||||
panic(newBitStorageErr{ArrlLen: len(data), WantLen: dataLen})
|
||||
}
|
||||
b.data = data
|
||||
} else {
|
||||
b.data = make([]uint64, dataLen)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// calcBitStorageSize calculate how many uint64 is needed for given bits and length.
|
||||
func calcBitStorageSize(bits, length int) (size int) {
|
||||
if bits == 0 {
|
||||
return 0
|
||||
}
|
||||
valuesPerLong := 64 / bits
|
||||
return (length + valuesPerLong - 1) / valuesPerLong
|
||||
}
|
||||
|
||||
type newBitStorageErr struct {
|
||||
ArrlLen int
|
||||
WantLen int
|
||||
}
|
||||
|
||||
func (i newBitStorageErr) Error() string {
|
||||
return fmt.Sprintf("invalid length given for storage, got: %d but expected: %d", i.ArrlLen, i.WantLen)
|
||||
}
|
||||
|
||||
func (b *BitStorage) calcIndex(n int) (c, o int) {
|
||||
c = n / b.valuesPerLong
|
||||
o = (n - c*b.valuesPerLong) * b.bits
|
||||
return
|
||||
}
|
||||
|
||||
// Swap sets v into [i], and return the previous [i] value.
|
||||
func (b *BitStorage) Swap(i, v int) (old int) {
|
||||
if b == nil || i < 0 || i > b.length-1 {
|
||||
panic(indexOutOfBounds)
|
||||
}
|
||||
if v < 0 || uint64(v) > b.mask {
|
||||
panic(valueOutOfBounds)
|
||||
}
|
||||
|
||||
c, offset := b.calcIndex(i)
|
||||
l := b.data[c]
|
||||
old = int(l >> offset & b.mask)
|
||||
b.data[c] = l&(b.mask<<offset^math.MaxUint64) | (uint64(v)&b.mask)<<offset
|
||||
return
|
||||
}
|
||||
|
||||
// Set sets v into [i].
|
||||
func (b *BitStorage) Set(i, v int) {
|
||||
if b == nil || i < 0 || i > b.length-1 {
|
||||
panic(indexOutOfBounds)
|
||||
}
|
||||
if v < 0 || uint64(v) > b.mask {
|
||||
panic(valueOutOfBounds)
|
||||
}
|
||||
|
||||
c, offset := b.calcIndex(i)
|
||||
l := b.data[c]
|
||||
b.data[c] = l&(b.mask<<offset^math.MaxUint64) | (uint64(v)&b.mask)<<offset
|
||||
}
|
||||
|
||||
// Get gets [i] value.
|
||||
func (b *BitStorage) Get(i int) int {
|
||||
if b == nil || i < 0 || i > b.length-1 {
|
||||
panic(indexOutOfBounds)
|
||||
}
|
||||
|
||||
c, offset := b.calcIndex(i)
|
||||
l := b.data[c]
|
||||
return int(l >> offset & b.mask)
|
||||
}
|
||||
|
||||
// Len is the number of stored values.
|
||||
func (b *BitStorage) Len() int {
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
return b.length
|
||||
}
|
||||
|
||||
// Raw return the underling array of uint64 for encoding/decoding.
|
||||
func (b *BitStorage) Raw() []uint64 {
|
||||
if b == nil {
|
||||
return []uint64{}
|
||||
}
|
||||
return b.data
|
||||
}
|
||||
|
||||
func (b *BitStorage) ReadFrom(r io.Reader) (int64, error) {
|
||||
var Len pk.VarInt
|
||||
n, err := Len.ReadFrom(r)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if cap(b.data) >= int(Len) {
|
||||
b.data = b.data[:Len]
|
||||
} else {
|
||||
b.data = make([]uint64, Len)
|
||||
}
|
||||
var v pk.Long
|
||||
for i := range b.data {
|
||||
nn, err := v.ReadFrom(r)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
b.data[i] = uint64(v)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (b *BitStorage) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := pk.VarInt(len(b.data)).WriteTo(w)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
for _, v := range b.data {
|
||||
nn, err := pk.Long(v).WriteTo(w)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package save
|
||||
|
||||
import (
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"math/bits"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var data = []uint64{0x0020863148418841, 0x01018A7260F68C87}
|
||||
var want = []int{1, 2, 2, 3, 4, 4, 5, 6, 6, 4, 8, 0, 7, 4, 3, 13, 15, 16, 9, 14, 10, 12, 0, 2}
|
||||
|
||||
func TestBitStorage_Get(t *testing.T) {
|
||||
bs := NewBitStorage(5, 24, data)
|
||||
for i := 0; i < 24; i++ {
|
||||
if got := bs.Get(i); got != want[i] {
|
||||
t.Errorf("Decode error, got: %d but expected: %d", got, want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitStorage_Set(t *testing.T) {
|
||||
bs := NewBitStorage(5, 24, nil)
|
||||
for i := 0; i < 24; i++ {
|
||||
bs.Set(i, want[i])
|
||||
}
|
||||
if !reflect.DeepEqual(bs.data, data) {
|
||||
t.Errorf("Encode error, got %v but expected: %v", bs.data, data)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleNewBitStorage_heightmaps() {
|
||||
// Create a BitStorage
|
||||
bs := NewBitStorage(bits.Len(256), 16*16, nil)
|
||||
// Fill your data
|
||||
for i := 0; i < 16; i++ {
|
||||
for j := 0; j < 16; j++ {
|
||||
bs.Set(i*16+j, 0)
|
||||
}
|
||||
}
|
||||
// Encode as NBT, and this is ready for packet.Marshal
|
||||
type HeightMaps struct {
|
||||
MotionBlocking []uint64 `nbt:"MOTION_BLOCKING"`
|
||||
}
|
||||
_ = pk.NBT(HeightMaps{bs.Raw()})
|
||||
}
|
275
save/palette.go
275
save/palette.go
@ -1,275 +0,0 @@
|
||||
package save
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
)
|
||||
|
||||
type BlockState interface {
|
||||
}
|
||||
|
||||
type PaletteContainer struct {
|
||||
bits int
|
||||
maps IdMaps
|
||||
config func(maps IdMaps, bits int) palette
|
||||
palette palette
|
||||
data *BitStorage
|
||||
}
|
||||
|
||||
func NewStatesPaletteContainer(maps IdMaps, length int) *PaletteContainer {
|
||||
return &PaletteContainer{
|
||||
bits: 0,
|
||||
maps: maps,
|
||||
config: createStatesPalette,
|
||||
palette: createStatesPalette(maps, 0),
|
||||
data: NewBitStorage(0, length, nil),
|
||||
}
|
||||
}
|
||||
|
||||
func NewBiomesPaletteContainer(maps IdMaps, length int) *PaletteContainer {
|
||||
return &PaletteContainer{
|
||||
bits: 0,
|
||||
maps: maps,
|
||||
config: createBiomesPalette,
|
||||
palette: createBiomesPalette(maps, 0),
|
||||
data: NewBitStorage(0, length, nil),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PaletteContainer) Get(i int) BlockState {
|
||||
return p.palette.value(p.data.Get(i))
|
||||
}
|
||||
|
||||
func (p *PaletteContainer) Set(i int, v BlockState) {
|
||||
if vv, ok := p.palette.id(v); ok {
|
||||
p.data.Set(i, vv)
|
||||
} else {
|
||||
// resize
|
||||
oldLen := p.data.Len()
|
||||
newPalette := PaletteContainer{
|
||||
bits: vv,
|
||||
maps: p.maps,
|
||||
config: p.config,
|
||||
palette: p.config(p.maps, vv),
|
||||
data: NewBitStorage(vv, oldLen+1, nil),
|
||||
}
|
||||
// copy
|
||||
for i := 0; i < oldLen; i++ {
|
||||
raw := p.palette.value(i)
|
||||
if vv, ok := newPalette.palette.id(raw); !ok {
|
||||
panic("not reachable")
|
||||
} else {
|
||||
newPalette.data.Set(i, vv)
|
||||
}
|
||||
}
|
||||
|
||||
if vv, ok := newPalette.palette.id(v); !ok {
|
||||
panic("not reachable")
|
||||
} else {
|
||||
newPalette.data.Set(oldLen, vv)
|
||||
}
|
||||
*p = newPalette
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PaletteContainer) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var bits pk.UnsignedByte
|
||||
n, err = bits.ReadFrom(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.palette = p.config(p.maps, int(bits))
|
||||
|
||||
nn, err := p.palette.ReadFrom(r)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
nn, err = p.data.ReadFrom(r)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func createStatesPalette(maps IdMaps, bits int) palette {
|
||||
switch bits {
|
||||
case 0:
|
||||
return &singleValuePalette{
|
||||
maps: maps,
|
||||
v: nil,
|
||||
}
|
||||
case 1, 2, 3, 4:
|
||||
return &linearPalette{
|
||||
maps: maps,
|
||||
bits: 4,
|
||||
}
|
||||
case 5, 6, 7, 8:
|
||||
// TODO: HashMapPalette
|
||||
return &linearPalette{
|
||||
maps: maps,
|
||||
bits: bits,
|
||||
}
|
||||
default:
|
||||
return &globalPalette{
|
||||
maps: maps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createBiomesPalette(maps IdMaps, bits int) palette {
|
||||
switch bits {
|
||||
case 0:
|
||||
return &singleValuePalette{
|
||||
maps: maps,
|
||||
v: nil,
|
||||
}
|
||||
case 1, 2, 3:
|
||||
return &linearPalette{
|
||||
maps: maps,
|
||||
bits: bits,
|
||||
}
|
||||
default:
|
||||
return &globalPalette{
|
||||
maps: maps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PaletteContainer) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return pk.Tuple{
|
||||
pk.UnsignedByte(p.bits),
|
||||
p.palette,
|
||||
p.data,
|
||||
}.WriteTo(w)
|
||||
}
|
||||
|
||||
type palette interface {
|
||||
pk.FieldEncoder
|
||||
pk.FieldDecoder
|
||||
id(v BlockState) (int, bool)
|
||||
value(i int) BlockState
|
||||
}
|
||||
|
||||
type IdMaps interface {
|
||||
getID(state BlockState) (id int)
|
||||
getValue(id int) (state BlockState)
|
||||
}
|
||||
|
||||
type singleValuePalette struct {
|
||||
maps IdMaps
|
||||
v BlockState
|
||||
}
|
||||
|
||||
func (s *singleValuePalette) id(v BlockState) (int, bool) {
|
||||
if s.v == nil {
|
||||
s.v = v
|
||||
return 0, true
|
||||
}
|
||||
if s.v == v {
|
||||
return 0, true
|
||||
}
|
||||
// We have 2 values now. At least 1 bit is required.
|
||||
return 1, false
|
||||
}
|
||||
|
||||
func (s *singleValuePalette) value(i int) BlockState {
|
||||
if s.v != nil && i == 0 {
|
||||
return s.v
|
||||
}
|
||||
panic("singleValuePalette: " + strconv.Itoa(i) + " out of bounds")
|
||||
}
|
||||
|
||||
func (s *singleValuePalette) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var i pk.VarInt
|
||||
n, err = i.ReadFrom(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.v = s.maps.getValue(int(i))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *singleValuePalette) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return pk.VarInt(s.maps.getID(s.v)).WriteTo(w)
|
||||
}
|
||||
|
||||
type linearPalette struct {
|
||||
maps IdMaps
|
||||
values []BlockState
|
||||
bits int
|
||||
}
|
||||
|
||||
func (l *linearPalette) id(v BlockState) (int, bool) {
|
||||
for i, t := range l.values {
|
||||
if t == v {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
if cap(l.values)-len(l.values) > 0 {
|
||||
l.values = append(l.values, v)
|
||||
return len(l.values) - 1, true
|
||||
}
|
||||
return l.bits + 1, false
|
||||
}
|
||||
|
||||
func (l *linearPalette) value(i int) BlockState {
|
||||
if i >= 0 && i < len(l.values) {
|
||||
return l.values[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *linearPalette) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var size, value pk.VarInt
|
||||
if n, err = size.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < int(size); i++ {
|
||||
if nn, err := value.ReadFrom(r); err != nil {
|
||||
return n + nn, err
|
||||
} else {
|
||||
n += nn
|
||||
}
|
||||
l.values[i] = l.maps.getValue(int(value))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *linearPalette) WriteTo(w io.Writer) (n int64, err error) {
|
||||
if n, err = pk.VarInt(len(l.values)).WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range l.values {
|
||||
if nn, err := pk.VarInt(l.maps.getID(v)).WriteTo(w); err != nil {
|
||||
return n + nn, err
|
||||
} else {
|
||||
n += nn
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type globalPalette struct {
|
||||
maps IdMaps
|
||||
}
|
||||
|
||||
func (g *globalPalette) id(v BlockState) (int, bool) {
|
||||
return g.maps.getID(v), true
|
||||
}
|
||||
|
||||
func (g *globalPalette) value(i int) BlockState {
|
||||
return g.value(i)
|
||||
}
|
||||
|
||||
func (g *globalPalette) ReadFrom(_ io.Reader) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (g *globalPalette) WriteTo(_ io.Writer) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
Reference in New Issue
Block a user