rewrite chunk data decoder
This commit is contained in:
@ -8,89 +8,111 @@ import (
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
)
|
||||
|
||||
//DecodeChunkColumn decode the chunk data structure
|
||||
// DecodeChunkColumn decode the chunk data structure.
|
||||
// If decoding went error, successful decoded data will be returned.
|
||||
func DecodeChunkColumn(mask int32, data []byte) (*Chunk, error) {
|
||||
var c Chunk
|
||||
r := bytes.NewReader(data)
|
||||
for sectionY := 0; sectionY < 16; sectionY++ {
|
||||
if (mask & (1 << uint(sectionY))) == 0 { // Is the given bit set in the mask?
|
||||
continue
|
||||
// If the section's bit set in the mask
|
||||
if (mask & (1 << uint(sectionY))) != 0 {
|
||||
// read section
|
||||
sec, err := readSection(r)
|
||||
if err != nil {
|
||||
return &c, fmt.Errorf("read section[%d] error: %w", sectionY, err)
|
||||
}
|
||||
var (
|
||||
BlockCount pk.Short
|
||||
BitsPerBlock pk.Byte
|
||||
)
|
||||
if err := BlockCount.Decode(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := BitsPerBlock.Decode(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//读调色板
|
||||
var palette []uint
|
||||
if BitsPerBlock < 9 {
|
||||
var length pk.VarInt
|
||||
if err := length.Decode(r); err != nil {
|
||||
return nil, fmt.Errorf("read palette (id len) fail: %v", err)
|
||||
}
|
||||
palette = make([]uint, length)
|
||||
|
||||
for id := uint(0); id < uint(length); id++ {
|
||||
var stateID pk.VarInt
|
||||
if err := stateID.Decode(r); err != nil {
|
||||
return nil, fmt.Errorf("read palette (id) fail: %v", err)
|
||||
}
|
||||
|
||||
palette[id] = uint(stateID)
|
||||
c.sections[sectionY] = sec
|
||||
}
|
||||
}
|
||||
|
||||
//Section数据
|
||||
var DataArrayLength pk.VarInt
|
||||
if err := DataArrayLength.Decode(r); err != nil {
|
||||
return nil, fmt.Errorf("read DataArrayLength fail: %v", err)
|
||||
}
|
||||
|
||||
DataArray := make([]int64, DataArrayLength)
|
||||
for i := 0; i < int(DataArrayLength); i++ {
|
||||
if err := (*pk.Long)(&DataArray[i]).Decode(r); err != nil {
|
||||
return nil, fmt.Errorf("read DataArray fail: %v", err)
|
||||
}
|
||||
}
|
||||
//用数据填充区块
|
||||
fillSection(&c.sections[sectionY], perBits(byte(BitsPerBlock)), DataArray, palette)
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func perBits(BitsPerBlock byte) uint {
|
||||
func perBits(BitsPerBlock byte) uint32 {
|
||||
switch {
|
||||
case BitsPerBlock <= 4:
|
||||
return 4
|
||||
case BitsPerBlock < 9:
|
||||
return uint(BitsPerBlock)
|
||||
return uint32(BitsPerBlock)
|
||||
default:
|
||||
return uint(data.BitsPerBlock) // DefaultBitsPerBlock
|
||||
return uint32(data.BitsPerBlock) // DefaultBitsPerBlock
|
||||
}
|
||||
}
|
||||
|
||||
func fillSection(s *Section, bpb uint, DataArray []int64, palette []uint) {
|
||||
mask := uint(1<<bpb - 1)
|
||||
for n := 0; n < 16*16*16; n++ {
|
||||
offset := uint(n * int(bpb))
|
||||
data := uint(DataArray[offset/64])
|
||||
data >>= offset % 64
|
||||
if offset%64 > 64-bpb {
|
||||
l := 64 - offset % 64
|
||||
data |= uint(DataArray[offset/64+1] << l)
|
||||
func readSection(data pk.DecodeReader) (s Section, err error) {
|
||||
var BlockCount pk.Short
|
||||
if err := BlockCount.Decode(data); err != nil {
|
||||
return nil, fmt.Errorf("read block count error: %w", err)
|
||||
}
|
||||
data &= mask
|
||||
|
||||
var bpb pk.UnsignedByte
|
||||
if err := bpb.Decode(data); err != nil {
|
||||
return nil, fmt.Errorf("read bits per block error: %w", err)
|
||||
}
|
||||
// If bpb values greater than or equal to 9, use directSection.
|
||||
// Otherwise use paletteSection.
|
||||
var palettes []uint32
|
||||
if bpb < 9 {
|
||||
s.blocks[n%16][n/(16*16)][n%(16*16)/16].id = palette[data]
|
||||
// read palettes
|
||||
var length pk.VarInt
|
||||
if err := length.Decode(data); err != nil {
|
||||
return nil, fmt.Errorf("read palettes length error: %w", err)
|
||||
}
|
||||
palettes = make([]uint32, length)
|
||||
for i := 0; i < int(length); i++ {
|
||||
var v pk.VarInt
|
||||
if err := v.Decode(data); err != nil {
|
||||
return nil, fmt.Errorf("read palettes[%d] error: %w", i, err)
|
||||
}
|
||||
palettes[i] = uint32(v)
|
||||
}
|
||||
}
|
||||
|
||||
// read data array
|
||||
var dataLen pk.VarInt
|
||||
if err := dataLen.Decode(data); err != nil {
|
||||
return nil, fmt.Errorf("read data array length error: %w", err)
|
||||
}
|
||||
dataArray := make([]int64, dataLen)
|
||||
for i := 0; i < int(dataLen); i++ {
|
||||
var v pk.Long
|
||||
if err := v.Decode(data); err != nil {
|
||||
return nil, fmt.Errorf("read dataArray[%d] error: %w", i, err)
|
||||
}
|
||||
dataArray[i] = int64(v)
|
||||
}
|
||||
|
||||
sec := directSection{bpb: perBits(byte(bpb)), data: dataArray}
|
||||
if bpb < 9 {
|
||||
return &paletteSection{palette: palettes, directSection: sec}, nil
|
||||
} else {
|
||||
s.blocks[n%16][n/(16*16)][n%(16*16)/16].id = data
|
||||
return &sec, nil
|
||||
}
|
||||
}
|
||||
|
||||
type directSection struct {
|
||||
bpb uint32
|
||||
data []int64
|
||||
}
|
||||
|
||||
func (d *directSection) GetBlock(x, y, z int) (blockID uint32) {
|
||||
// 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.
|
||||
// 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
|
||||
block := uint32(d.data[offset/64])
|
||||
block >>= offset % 64
|
||||
if offset%64 > 64-d.bpb {
|
||||
l := 64 - offset%64
|
||||
block |= uint32(d.data[offset/64+1] << l)
|
||||
}
|
||||
return block & (1<<d.bpb - 1) // mask
|
||||
}
|
||||
|
||||
type paletteSection struct {
|
||||
palette []uint32
|
||||
directSection
|
||||
}
|
||||
|
||||
func (p *paletteSection) GetBlock(x, y, z int) (blockID uint32) {
|
||||
v := p.directSection.GetBlock(x, y, z)
|
||||
return p.palette[v]
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
package world
|
||||
|
||||
// import (
|
||||
// "math"
|
||||
// "time"
|
||||
// )
|
||||
|
||||
// // SetPosition method move your character around.
|
||||
// // Server will ignore this if changes too much.
|
||||
// func (g *Client) SetPosition(x, y, z float64, onGround bool) {
|
||||
// g.motion <- func() {
|
||||
// g.player.X, g.player.Y, g.player.Z = x, y, z
|
||||
// g.player.OnGround = onGround
|
||||
// sendPlayerPositionPacket(g) //向服务器更新位置
|
||||
// }
|
||||
// }
|
||||
|
||||
// // LookAt method turn player's hand and make it look at a point.
|
||||
// func (g *Client) LookAt(x, y, z float64) {
|
||||
// x0, y0, z0 := g.player.X, g.player.Y, g.player.Z
|
||||
// x, y, z = x-x0, y-y0, z-z0
|
||||
|
||||
// r := math.Sqrt(x*x + y*y + z*z)
|
||||
// yaw := -math.Atan2(x, z) / math.Pi * 180
|
||||
// for yaw < 0 {
|
||||
// yaw = 360 + yaw
|
||||
// }
|
||||
// pitch := -math.Asin(y/r) / math.Pi * 180
|
||||
|
||||
// g.LookYawPitch(float32(yaw), float32(pitch))
|
||||
// }
|
||||
|
||||
// // LookYawPitch set player's hand to the direct by yaw and pitch.
|
||||
// // yaw can be [0, 360) and pitch can be (-180, 180).
|
||||
// // if |pitch|>90 the player's hand will be very strange.
|
||||
// func (g *Client) LookYawPitch(yaw, pitch float32) {
|
||||
// g.motion <- func() {
|
||||
// g.player.Yaw, g.player.Pitch = yaw, pitch
|
||||
// sendPlayerLookPacket(g) //向服务器更新朝向
|
||||
// }
|
||||
// }
|
||||
|
||||
// // SwingHand sent when the player's arm swings.
|
||||
// // if hand is true, swing the main hand
|
||||
// func (g *Client) SwingHand(hand bool) {
|
||||
// if hand {
|
||||
// sendAnimationPacket(g, 0)
|
||||
// } else {
|
||||
// sendAnimationPacket(g, 1)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Dig a block in the position and wait for it's breaked
|
||||
// func (g *Client) Dig(x, y, z int) error {
|
||||
// b := g.GetBlock(x, y, z).id
|
||||
// sendPlayerDiggingPacket(g, 0, x, y, z, Top) //start
|
||||
// sendPlayerDiggingPacket(g, 2, x, y, z, Top) //end
|
||||
|
||||
// for {
|
||||
// time.Sleep(time.Millisecond * 50)
|
||||
// if g.GetBlock(x, y, z).id != b {
|
||||
// break
|
||||
// }
|
||||
// g.SwingHand(true)
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // UseItem use the item in hand.
|
||||
// // if hand is true, swing the main hand
|
||||
// func (g *Client) UseItem(hand bool) {
|
||||
// if hand {
|
||||
// sendUseItemPacket(g, 0)
|
||||
// } else {
|
||||
// sendUseItemPacket(g, 1)
|
||||
// }
|
||||
// }
|
@ -10,19 +10,14 @@ type World struct {
|
||||
Chunks map[ChunkLoc]*Chunk
|
||||
}
|
||||
|
||||
//Chunk store a 256*16*16 clolumn blocks
|
||||
//Chunk store a 256*16*16 column blocks
|
||||
type Chunk struct {
|
||||
sections [16]Section
|
||||
}
|
||||
|
||||
//Section store a 16*16*16 cube blocks
|
||||
type Section struct {
|
||||
blocks [16][16][16]Block
|
||||
}
|
||||
|
||||
//Block is the base of world
|
||||
type Block struct {
|
||||
id uint
|
||||
type Section interface {
|
||||
GetBlock(x, y, z int) (blockID uint32)
|
||||
}
|
||||
|
||||
type ChunkLoc struct {
|
||||
|
@ -1,9 +0,0 @@
|
||||
package world
|
||||
|
||||
// import "testing"
|
||||
|
||||
// func TestBlockString(t *testing.T) {
|
||||
// for i := uint(0); i < 8598+1; i++ {
|
||||
// t.Log(Block{id: i})
|
||||
// }
|
||||
// }
|
Reference in New Issue
Block a user