diff --git a/bot/world/chunk.go b/bot/world/chunk.go index 2d2a97a..cf25df6 100644 --- a/bot/world/chunk.go +++ b/bot/world/chunk.go @@ -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 - } - 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) + // 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) } + 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<>= 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) + } + 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 { + // read palettes + var length pk.VarInt + if err := length.Decode(data); err != nil { + return nil, fmt.Errorf("read palettes length error: %w", err) } - data &= mask - - if bpb < 9 { - s.blocks[n%16][n/(16*16)][n%(16*16)/16].id = palette[data] - } else { - s.blocks[n%16][n/(16*16)][n%(16*16)/16].id = data + 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 { + 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<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) -// } -// } diff --git a/bot/world/world.go b/bot/world/world.go index 975bec6..9445105 100644 --- a/bot/world/world.go +++ b/bot/world/world.go @@ -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 { diff --git a/bot/world/world_test.go b/bot/world/world_test.go deleted file mode 100644 index 641e28b..0000000 --- a/bot/world/world_test.go +++ /dev/null @@ -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}) -// } -// }