add SetBlock function

This commit is contained in:
Tnze
2020-05-19 14:15:44 +08:00
parent fa75535f37
commit 32527442ed
2 changed files with 41 additions and 17 deletions

View File

@ -27,14 +27,14 @@ func DecodeChunkColumn(mask int32, data []byte) (*Chunk, error) {
return &c, nil return &c, nil
} }
func perBits(BitsPerBlock byte) uint32 { func perBits(BitsPerBlock byte) int {
switch { switch {
case BitsPerBlock <= 4: case BitsPerBlock <= 4:
return 4 return 4
case BitsPerBlock < 9: case BitsPerBlock < 9:
return uint32(BitsPerBlock) return int(BitsPerBlock)
default: default:
return uint32(data.BitsPerBlock) // DefaultBitsPerBlock return data.BitsPerBlock // DefaultBitsPerBlock
} }
} }
@ -49,20 +49,20 @@ func readSection(data pk.DecodeReader) (s Section, err error) {
} }
// If bpb values greater than or equal to 9, use directSection. // If bpb values greater than or equal to 9, use directSection.
// Otherwise use paletteSection. // Otherwise use paletteSection.
var palettes []uint32 var palettes []BlockStatus
if bpb < 9 { if bpb < 9 {
// read palettes // read palettes
var length pk.VarInt var length pk.VarInt
if err := length.Decode(data); err != nil { if err := length.Decode(data); err != nil {
return nil, fmt.Errorf("read palettes length error: %w", err) return nil, fmt.Errorf("read palettes length error: %w", err)
} }
palettes = make([]uint32, length) palettes = make([]BlockStatus, length)
for i := 0; i < int(length); i++ { for i := 0; i < int(length); i++ {
var v pk.VarInt var v pk.VarInt
if err := v.Decode(data); err != nil { if err := v.Decode(data); err != nil {
return nil, fmt.Errorf("read palettes[%d] error: %w", i, err) return nil, fmt.Errorf("read palettes[%d] error: %w", i, err)
} }
palettes[i] = uint32(v) palettes[i] = BlockStatus(v)
} }
} }
@ -71,13 +71,13 @@ func readSection(data pk.DecodeReader) (s Section, err error) {
if err := dataLen.Decode(data); err != nil { if err := dataLen.Decode(data); err != nil {
return nil, fmt.Errorf("read data array length error: %w", err) return nil, fmt.Errorf("read data array length error: %w", err)
} }
dataArray := make([]int64, dataLen) dataArray := make([]uint64, dataLen)
for i := 0; i < int(dataLen); i++ { for i := 0; i < int(dataLen); i++ {
var v pk.Long var v pk.Long
if err := v.Decode(data); err != nil { if err := v.Decode(data); err != nil {
return nil, fmt.Errorf("read dataArray[%d] error: %w", i, err) return nil, fmt.Errorf("read dataArray[%d] error: %w", i, err)
} }
dataArray[i] = int64(v) dataArray[i] = uint64(v)
} }
sec := directSection{bpb: perBits(byte(bpb)), data: dataArray} sec := directSection{bpb: perBits(byte(bpb)), data: dataArray}
@ -89,30 +89,53 @@ func readSection(data pk.DecodeReader) (s Section, err error) {
} }
type directSection struct { type directSection struct {
bpb uint32 bpb int
data []int64 data []uint64
} }
func (d *directSection) GetBlock(x, y, z int) BlockStatus { func (d *directSection) GetBlock(x, y, z int) BlockStatus {
// According to wiki.vg: Data Array is given for each block with increasing x coordinates, // According to wiki.vg: Data Array is given for each block with increasing x coordinates,
// within rows of increasing z coordinates, within layers of increasing y coordinates. // within rows of increasing z coordinates, within layers of increasing y coordinates.
// So offset equals to ( x*16^0 + z*16^1 + y*16^2 )*(bits per block). // So offset equals to ( x*16^0 + z*16^1 + y*16^2 )*(bits per block).
offset := uint32(x+z*16+y*16*16) * d.bpb offset := (x + z*16 + y*16*16) * d.bpb
block := uint32(d.data[offset/64]) padding := offset % 64
block >>= offset % 64 block := uint32(d.data[offset/64] >> padding)
if offset%64 > 64-d.bpb { if padding > 64-d.bpb {
l := 64 - offset%64 l := 64 - padding
block |= uint32(d.data[offset/64+1] << l) block |= uint32(d.data[offset/64+1] << l)
} }
return BlockStatus(block & (1<<d.bpb - 1)) // mask return BlockStatus(block & (1<<d.bpb - 1)) // mask
} }
func (d *directSection) SetBlock(x, y, z int, s BlockStatus) {
offset := (x + z*16 + y*16*16) * d.bpb
padding := offset % 64
const maxUint64 = 1<<64 - 1
mask := uint64(maxUint64<<(padding+d.bpb) | (1<<padding - 1))
d.data[offset/64] = uint64(d.data[offset/64])&mask | uint64(s)<<padding
if padding > 64-d.bpb {
l := padding - (64 - d.bpb)
d.data[offset/64+1] = d.data[offset/64+1]&(maxUint64<<l) | uint64(s)>>(64-padding)
}
}
type paletteSection struct { type paletteSection struct {
palette []uint32 palette []BlockStatus
directSection directSection
} }
func (p *paletteSection) GetBlock(x, y, z int) BlockStatus { func (p *paletteSection) GetBlock(x, y, z int) BlockStatus {
v := p.directSection.GetBlock(x, y, z) v := p.directSection.GetBlock(x, y, z)
return BlockStatus(p.palette[v]) return p.palette[v]
}
func (p *paletteSection) SetBlock(x, y, z int, s BlockStatus) {
for i := 0; i < len(p.palette); i++ {
if p.palette[i] == s {
p.directSection.SetBlock(x, y, z, BlockStatus(i))
return
}
}
p.palette = append(p.palette, s) // TODO: Handle bpb overflow
p.directSection.SetBlock(x, y, z, BlockStatus(len(p.palette)+1))
} }

View File

@ -18,6 +18,7 @@ type Chunk struct {
// Section store a 16*16*16 cube blocks // Section store a 16*16*16 cube blocks
type Section interface { type Section interface {
GetBlock(x, y, z int) BlockStatus GetBlock(x, y, z int) BlockStatus
SetBlock(x, y, z int, s BlockStatus)
} }
type BlockStatus uint32 type BlockStatus uint32