Refactoring package go-mc/bot
This commit is contained in:
@ -1,38 +0,0 @@
|
||||
package world
|
||||
|
||||
// bitArray implements a bitfield array where values are packed into uint64
|
||||
// values. If the next value does not fit into remaining space, the remaining
|
||||
// space of a uint64 is unused.
|
||||
type bitArray struct {
|
||||
width uint // bit width of each value
|
||||
valuesPerElement uint // number of values which fit into a single uint64.
|
||||
|
||||
data []uint64
|
||||
}
|
||||
|
||||
// Size returns the number of elements that can fit into the bit array.
|
||||
func (b *bitArray) Size() int {
|
||||
return int(b.valuesPerElement) * len(b.data)
|
||||
}
|
||||
|
||||
func (b *bitArray) Set(idx, val uint) {
|
||||
var (
|
||||
arrayIdx = idx / b.valuesPerElement
|
||||
startBit = (idx % b.valuesPerElement) * b.width
|
||||
mask = ^uint64((1<<b.width - 1) << startBit) // set for all bits except target
|
||||
)
|
||||
b.data[arrayIdx] = (b.data[arrayIdx] & mask) | uint64(val<<startBit)
|
||||
}
|
||||
|
||||
func (b *bitArray) Get(idx uint) uint {
|
||||
var (
|
||||
arrayIdx = idx / b.valuesPerElement
|
||||
offset = (idx % b.valuesPerElement) * b.width
|
||||
mask = uint64((1<<b.width - 1) << offset) // set for just the target
|
||||
)
|
||||
return uint(b.data[arrayIdx]&mask) >> offset
|
||||
}
|
||||
|
||||
func valuesPerBitArrayElement(bitsPerValue uint) uint {
|
||||
return uint(64 / bitsPerValue)
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBitArrayBasic(t *testing.T) {
|
||||
a := bitArray{
|
||||
width: 5,
|
||||
valuesPerElement: valuesPerBitArrayElement(5),
|
||||
data: make([]uint64, 5),
|
||||
}
|
||||
|
||||
if got, want := a.Size(), 12*5; got != want {
|
||||
t.Errorf("size = %d, want %d", got, want)
|
||||
}
|
||||
|
||||
a.Set(0, 4)
|
||||
if v := a.Get(0); v != 4 {
|
||||
t.Errorf("v[0] = %d, want 4", v)
|
||||
}
|
||||
a.Set(12, 8)
|
||||
if v := a.Get(12); v != 8 {
|
||||
t.Errorf("v[12] = %d, want 8", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitArrayHardcoded(t *testing.T) {
|
||||
d1, _ := hex.DecodeString("0020863148418841")
|
||||
d2, _ := hex.DecodeString("01018A7260F68C87")
|
||||
|
||||
a := bitArray{
|
||||
width: 5,
|
||||
valuesPerElement: valuesPerBitArrayElement(5),
|
||||
data: []uint64{binary.BigEndian.Uint64(d1), binary.BigEndian.Uint64(d2)},
|
||||
}
|
||||
|
||||
if got, want := a.Size(), 12*2; got != want {
|
||||
t.Errorf("size = %d, want %d", got, want)
|
||||
}
|
||||
|
||||
want := []uint{1, 2, 2, 3, 4, 4, 5, 6, 6, 4, 8, 0, 7, 4, 3, 13, 15, 16, 9, 14, 10, 12, 0, 2}
|
||||
for idx, want := range want {
|
||||
if got := a.Get(uint(idx)); got != want {
|
||||
t.Errorf("v[%d] = %d, want %d", idx, got, want)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/Tnze/go-mc/data/block"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
)
|
||||
|
||||
const maxPaletteBits = 8
|
||||
|
||||
// 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 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
|
||||
}
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func perBits(bpb byte) uint {
|
||||
switch {
|
||||
case bpb <= 4:
|
||||
return 4
|
||||
case bpb <= maxPaletteBits:
|
||||
return uint(bpb)
|
||||
default:
|
||||
return uint(block.BitsPerBlock)
|
||||
}
|
||||
}
|
||||
|
||||
func readSection(data io.Reader) (s Section, err error) {
|
||||
var nonAirBlockCount pk.Short
|
||||
if _, err := nonAirBlockCount.ReadFrom(data); err != nil {
|
||||
return nil, fmt.Errorf("block count: %w", err)
|
||||
}
|
||||
var bpb pk.UnsignedByte
|
||||
if _, err := bpb.ReadFrom(data); err != nil {
|
||||
return nil, fmt.Errorf("bits per block: %w", err)
|
||||
}
|
||||
// If bpb values greater than or equal to 9, use directSection.
|
||||
// Otherwise use paletteSection.
|
||||
var palettes []BlockStatus
|
||||
var palettesIndex map[BlockStatus]int
|
||||
if bpb <= maxPaletteBits {
|
||||
// read palettes
|
||||
var length pk.VarInt
|
||||
if _, err := length.ReadFrom(data); err != nil {
|
||||
return nil, fmt.Errorf("palette length: %w", err)
|
||||
}
|
||||
palettes = make([]BlockStatus, length)
|
||||
palettesIndex = make(map[BlockStatus]int, length)
|
||||
for i := 0; i < int(length); i++ {
|
||||
var v pk.VarInt
|
||||
if _, err := v.ReadFrom(data); err != nil {
|
||||
return nil, fmt.Errorf("read palettes[%d] error: %w", i, err)
|
||||
}
|
||||
palettes[i] = BlockStatus(v)
|
||||
palettesIndex[BlockStatus(v)] = i
|
||||
}
|
||||
}
|
||||
|
||||
// read data array
|
||||
var dataLen pk.VarInt
|
||||
if _, err := dataLen.ReadFrom(data); err != nil {
|
||||
return nil, fmt.Errorf("read data array length error: %w", err)
|
||||
}
|
||||
if int(dataLen) < 16*16*16*int(bpb)/64 {
|
||||
return nil, fmt.Errorf("data length (%d) is not enough of given bpb (%d)", dataLen, bpb)
|
||||
}
|
||||
dataArray := make([]uint64, dataLen)
|
||||
for i := 0; i < int(dataLen); i++ {
|
||||
var v pk.Long
|
||||
if _, err := v.ReadFrom(data); err != nil {
|
||||
return nil, fmt.Errorf("read dataArray[%d] error: %w", i, err)
|
||||
}
|
||||
dataArray[i] = uint64(v)
|
||||
}
|
||||
|
||||
width := perBits(byte(bpb))
|
||||
sec := directSection{
|
||||
bitArray{
|
||||
width: width,
|
||||
valuesPerElement: valuesPerBitArrayElement(width),
|
||||
data: dataArray,
|
||||
},
|
||||
}
|
||||
if bpb <= maxPaletteBits {
|
||||
return &paletteSection{
|
||||
palette: palettes,
|
||||
palettesIndex: palettesIndex,
|
||||
directSection: sec,
|
||||
}, nil
|
||||
} else {
|
||||
return &sec, nil
|
||||
}
|
||||
}
|
||||
|
||||
type directSection struct {
|
||||
bitArray
|
||||
}
|
||||
|
||||
func (d *directSection) GetBlock(offset uint) BlockStatus {
|
||||
return BlockStatus(d.Get(offset))
|
||||
}
|
||||
|
||||
func (d *directSection) SetBlock(offset uint, s BlockStatus) {
|
||||
d.Set(offset, uint(s))
|
||||
}
|
||||
|
||||
func (d *directSection) CanContain(s BlockStatus) bool {
|
||||
return s <= (1<<d.width - 1)
|
||||
}
|
||||
|
||||
func (d *directSection) clone(bpb uint) *directSection {
|
||||
out := newSectionWithSize(bpb)
|
||||
for offset := uint(0); offset < 16*16*16; offset++ {
|
||||
out.SetBlock(offset, d.GetBlock(offset))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func newSectionWithSize(bpb uint) *directSection {
|
||||
valuesPerElement := valuesPerBitArrayElement(bpb)
|
||||
return &directSection{
|
||||
bitArray{
|
||||
width: bpb,
|
||||
valuesPerElement: valuesPerElement,
|
||||
data: make([]uint64, int(math.Ceil(16*16*16/float64(valuesPerElement)))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type paletteSection struct {
|
||||
palette []BlockStatus
|
||||
palettesIndex map[BlockStatus]int
|
||||
directSection
|
||||
}
|
||||
|
||||
func (p *paletteSection) GetBlock(offset uint) BlockStatus {
|
||||
v := p.directSection.GetBlock(offset)
|
||||
return p.palette[v]
|
||||
}
|
||||
|
||||
func (p *paletteSection) SetBlock(offset uint, s BlockStatus) {
|
||||
if i, ok := p.palettesIndex[s]; ok {
|
||||
p.directSection.SetBlock(offset, BlockStatus(i))
|
||||
return
|
||||
}
|
||||
i := len(p.palette)
|
||||
p.palette = append(p.palette, s)
|
||||
p.palettesIndex[s] = i
|
||||
if !p.directSection.CanContain(BlockStatus(i)) {
|
||||
// Increase the underlying directSection
|
||||
// Suppose that old bpb fit len(p.palette) before it appended.
|
||||
// So bpb+1 must enough for new len(p.palette).
|
||||
p.directSection = *p.directSection.clone(p.width + 1)
|
||||
}
|
||||
p.directSection.SetBlock(offset, BlockStatus(i))
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/Tnze/go-mc/data/entity"
|
||||
item "github.com/Tnze/go-mc/data/item"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// BlockEntity describes the representation of a tile entity at a position.
|
||||
type BlockEntity struct {
|
||||
ID string `nbt:"id"`
|
||||
|
||||
// global co-ordinates
|
||||
X int `nbt:"x"`
|
||||
Y int `nbt:"y"`
|
||||
Z int `nbt:"z"`
|
||||
|
||||
// sign-specific.
|
||||
Color string `nbt:"color"`
|
||||
Text1 string `nbt:"Text1"`
|
||||
Text2 string `nbt:"Text2"`
|
||||
Text3 string `nbt:"Text3"`
|
||||
Text4 string `nbt:"Text4"`
|
||||
}
|
||||
|
||||
//Entity represents an instance of an entity.
|
||||
type Entity struct {
|
||||
ID int32
|
||||
Data int32
|
||||
Base *entity.Entity
|
||||
|
||||
UUID uuid.UUID
|
||||
|
||||
X, Y, Z float64
|
||||
Pitch, Yaw int8
|
||||
VelX, VelY, VelZ int16
|
||||
OnGround bool
|
||||
|
||||
IsLiving bool
|
||||
HeadPitch int8
|
||||
}
|
||||
|
||||
// The Slot data structure is how Minecraft represents an item and its associated data in the Minecraft Protocol
|
||||
type Slot struct {
|
||||
Present bool
|
||||
ItemID item.ID
|
||||
Count int8
|
||||
NBT pk.NBT
|
||||
}
|
||||
|
||||
type SlotNBT struct {
|
||||
data interface{}
|
||||
}
|
||||
|
||||
//Decode implement packet.FieldDecoder interface
|
||||
func (s *Slot) ReadFrom(r io.Reader) (int64, error) {
|
||||
var itemID pk.VarInt
|
||||
n, err := pk.Tuple{
|
||||
(*pk.Boolean)(&s.Present),
|
||||
pk.Opt{
|
||||
Has: (*pk.Boolean)(&s.Present),
|
||||
Field: pk.Tuple{
|
||||
&itemID,
|
||||
(*pk.Byte)(&s.Count),
|
||||
&s.NBT,
|
||||
},
|
||||
},
|
||||
}.ReadFrom(r)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
s.ItemID = item.ID(itemID)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (s Slot) WriteTo(w io.Writer) (int64, error) {
|
||||
return pk.Tuple{
|
||||
pk.Boolean(s.Present),
|
||||
pk.Opt{
|
||||
Has: (*pk.Boolean)(&s.Present),
|
||||
Field: pk.Tuple{
|
||||
pk.VarInt(s.ItemID),
|
||||
pk.Byte(s.Count),
|
||||
s.NBT,
|
||||
},
|
||||
},
|
||||
}.WriteTo(w)
|
||||
}
|
||||
|
||||
func (s Slot) String() string {
|
||||
return item.ByID[s.ItemID].DisplayName
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package player
|
||||
|
||||
import "github.com/Tnze/go-mc/bot/world/entity"
|
||||
|
||||
type Pos struct {
|
||||
X, Y, Z float64
|
||||
Yaw, Pitch float32
|
||||
OnGround bool
|
||||
}
|
||||
|
||||
func (p Pos) PosEqual(other Pos) bool {
|
||||
return p.X == other.X && p.Y == other.Y && p.Z == other.Z
|
||||
}
|
||||
func (p Pos) LookEqual(other Pos) bool {
|
||||
return p.Yaw == other.Yaw && p.Pitch == other.Pitch
|
||||
}
|
||||
func (p Pos) Equal(other Pos) bool {
|
||||
return p.PosEqual(other) && p.LookEqual(other) && p.OnGround == other.OnGround
|
||||
}
|
||||
|
||||
// Player includes the player's status.
|
||||
type Player struct {
|
||||
entity.Entity
|
||||
UUID [2]int64 //128bit UUID
|
||||
|
||||
Pos Pos
|
||||
|
||||
HeldItem int //拿着的物品栏位
|
||||
|
||||
Health float32 //血量
|
||||
Food int32 //饱食度
|
||||
FoodSaturation float32 //食物饱和度
|
||||
|
||||
Level int32
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TilePosition describes the location of a tile/block entity within a chunk.
|
||||
type TilePosition uint32
|
||||
|
||||
func (p TilePosition) Pos() (x, y, z int) {
|
||||
return int((p >> 8) & 0xff), int((p >> 16) & 0xff), int(p & 0xff)
|
||||
}
|
||||
|
||||
func (p TilePosition) String() string {
|
||||
x, y, z := p.Pos()
|
||||
return fmt.Sprintf("(%d, %d, %d)", x, y, z)
|
||||
}
|
||||
|
||||
func ToTilePos(x, y, z int) TilePosition {
|
||||
return TilePosition((y&0xff)<<16 | (x&15)<<8 | (z & 15))
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Tnze/go-mc/bot/world/entity"
|
||||
)
|
||||
|
||||
// World record all of the things in the world where player at
|
||||
type World struct {
|
||||
entityLock sync.RWMutex
|
||||
Entities map[int32]*entity.Entity
|
||||
chunkLock sync.RWMutex
|
||||
Chunks map[ChunkLoc]*Chunk
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"github.com/Tnze/go-mc/bot/world/entity"
|
||||
"github.com/Tnze/go-mc/data/block"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"github.com/Tnze/go-mc/net/ptypes"
|
||||
)
|
||||
|
||||
// Chunk store a 256*16*16 area of blocks, sharded on the Y axis into 16
|
||||
// sections.
|
||||
type Chunk struct {
|
||||
Sections [16]Section
|
||||
TileEntities map[TilePosition]entity.BlockEntity
|
||||
}
|
||||
|
||||
// Section implements storage of blocks within a fixed 16*16*16 area.
|
||||
type Section interface {
|
||||
// GetBlock return block status, offset can be calculate by SectionOffset.
|
||||
GetBlock(offset uint) BlockStatus
|
||||
// SetBlock is the reverse operation of GetBlock.
|
||||
SetBlock(offset uint, s BlockStatus)
|
||||
}
|
||||
|
||||
func sectionIdx(x, y, z int) uint {
|
||||
return uint(((y & 15) << 8) | (z << 4) | x)
|
||||
}
|
||||
|
||||
type BlockStatus uint32
|
||||
|
||||
type ChunkLoc struct {
|
||||
X, Z int
|
||||
}
|
||||
|
||||
// Signs returns a list of signs on the server within viewing range.
|
||||
func (w *World) Signs() []entity.BlockEntity {
|
||||
w.chunkLock.RLock()
|
||||
defer w.chunkLock.RUnlock()
|
||||
|
||||
out := make([]entity.BlockEntity, 0, 4)
|
||||
for _, c := range w.Chunks {
|
||||
for _, e := range c.TileEntities {
|
||||
if e.ID == "minecraft:sign" {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// GetBlockStatus return the state ID of the block at the given position.
|
||||
func (w *World) GetBlockStatus(x, y, z int) BlockStatus {
|
||||
w.chunkLock.RLock()
|
||||
defer w.chunkLock.RUnlock()
|
||||
|
||||
c := w.Chunks[ChunkLoc{x >> 4, z >> 4}]
|
||||
if c != nil && y >= 0 {
|
||||
if sec := c.Sections[y>>4]; sec != nil {
|
||||
return sec.GetBlock(sectionIdx(x&15, y&15, z&15))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// UnloadChunk unloads a chunk from the world.
|
||||
func (w *World) UnloadChunk(loc ChunkLoc) {
|
||||
w.chunkLock.Lock()
|
||||
delete(w.Chunks, loc)
|
||||
w.chunkLock.Unlock()
|
||||
}
|
||||
|
||||
// UnaryBlockUpdate updates the block at the specified position with a new
|
||||
// state ID.
|
||||
func (w *World) UnaryBlockUpdate(pos pk.Position, bStateID BlockStatus) bool {
|
||||
w.chunkLock.Lock()
|
||||
defer w.chunkLock.Unlock()
|
||||
|
||||
c := w.Chunks[ChunkLoc{X: pos.X >> 4, Z: pos.Z >> 4}]
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
sIdx, bIdx := pos.Y>>4, sectionIdx(pos.X&15, pos.Y&15, pos.Z&15)
|
||||
|
||||
if sec := c.Sections[sIdx]; sec == nil {
|
||||
sec = newSectionWithSize(uint(block.BitsPerBlock))
|
||||
sec.SetBlock(bIdx, bStateID)
|
||||
c.Sections[sIdx] = sec
|
||||
} else {
|
||||
tp := ToTilePos(pos.X, pos.Y, pos.Z)
|
||||
if _, ok := c.TileEntities[tp]; ok && sec.GetBlock(bIdx) != bStateID {
|
||||
delete(c.TileEntities, tp)
|
||||
}
|
||||
sec.SetBlock(bIdx, bStateID)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MultiBlockUpdate updates a packed specification of blocks within a single
|
||||
// section of a chunk.
|
||||
func (w *World) MultiBlockUpdate(loc ChunkLoc, sectionY int, blocks []pk.VarLong) bool {
|
||||
w.chunkLock.Lock()
|
||||
defer w.chunkLock.Unlock()
|
||||
|
||||
c := w.Chunks[loc]
|
||||
if c == nil {
|
||||
return false // not loaded
|
||||
}
|
||||
|
||||
sec := c.Sections[sectionY]
|
||||
if sec == nil {
|
||||
sec = newSectionWithSize(uint(block.BitsPerBlock))
|
||||
c.Sections[sectionY] = sec
|
||||
}
|
||||
|
||||
for _, b := range blocks {
|
||||
var (
|
||||
bStateID = BlockStatus(b >> 12)
|
||||
x, z, y = (b >> 8) & 0xf, (b >> 4) & 0xf, b & 0xf
|
||||
bIdx = sectionIdx(int(x&15), int(y&15), int(z&15))
|
||||
tp = ToTilePos(int(x), int(y), int(z))
|
||||
)
|
||||
if _, ok := c.TileEntities[tp]; ok && sec.GetBlock(bIdx) != bStateID {
|
||||
delete(c.TileEntities, tp)
|
||||
}
|
||||
sec.SetBlock(bIdx, bStateID)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//LoadChunk adds the given chunk to the world.
|
||||
func (w *World) LoadChunk(x, z int, c *Chunk) {
|
||||
w.chunkLock.Lock()
|
||||
w.Chunks[ChunkLoc{X: x, Z: z}] = c
|
||||
w.chunkLock.Unlock()
|
||||
}
|
||||
|
||||
func (w *World) TileEntityUpdate(pkt ptypes.TileEntityData) error {
|
||||
w.chunkLock.Lock()
|
||||
defer w.chunkLock.Unlock()
|
||||
c := w.Chunks[ChunkLoc{X: pkt.Pos.X >> 4, Z: pkt.Pos.Z >> 4}]
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch pkt.Action {
|
||||
case 9: // Sign update
|
||||
c.TileEntities[ToTilePos(pkt.Pos.X, pkt.Pos.Y, pkt.Pos.Z)] = pkt.Data
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Tnze/go-mc/bot/world/entity"
|
||||
e "github.com/Tnze/go-mc/data/entity"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"github.com/Tnze/go-mc/net/ptypes"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// PlayerEntities returns a list of players on the server within viewing range.
|
||||
func (w *World) PlayerEntities() []entity.Entity {
|
||||
w.entityLock.RLock()
|
||||
defer w.entityLock.RUnlock()
|
||||
out := make([]entity.Entity, 0, 12)
|
||||
for _, ent := range w.Entities {
|
||||
if ent.Base.ID == e.Player.ID {
|
||||
out = append(out, *ent)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// OnSpawnEntity should be called when a SpawnEntity packet
|
||||
// is received.
|
||||
func (w *World) OnSpawnEntity(pkt ptypes.SpawnEntity) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
base, ok := e.ByID[e.ID(pkt.Type)]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown entity ID %v", pkt.Type)
|
||||
}
|
||||
|
||||
w.Entities[int32(pkt.ID)] = &entity.Entity{
|
||||
ID: int32(pkt.ID),
|
||||
Base: base,
|
||||
Data: int32(pkt.Data),
|
||||
UUID: uuid.UUID(pkt.UUID),
|
||||
X: float64(pkt.X),
|
||||
Y: float64(pkt.Y),
|
||||
Z: float64(pkt.Z),
|
||||
Pitch: int8(pkt.Pitch),
|
||||
Yaw: int8(pkt.Yaw),
|
||||
VelX: int16(pkt.VelX),
|
||||
VelY: int16(pkt.VelY),
|
||||
VelZ: int16(pkt.VelZ),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnSpawnLivingEntity should be called when a SpawnLivingEntity packet
|
||||
// is received.
|
||||
func (w *World) OnSpawnLivingEntity(pkt ptypes.SpawnLivingEntity) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
base, ok := e.ByID[e.ID(pkt.Type)]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown entity ID %v", pkt.Type)
|
||||
}
|
||||
|
||||
w.Entities[int32(pkt.ID)] = &entity.Entity{
|
||||
ID: int32(pkt.ID),
|
||||
Base: base,
|
||||
UUID: uuid.UUID(pkt.UUID),
|
||||
X: float64(pkt.X),
|
||||
Y: float64(pkt.Y),
|
||||
Z: float64(pkt.Z),
|
||||
Pitch: int8(pkt.Pitch),
|
||||
Yaw: int8(pkt.Yaw),
|
||||
VelX: int16(pkt.VelX),
|
||||
VelY: int16(pkt.VelY),
|
||||
VelZ: int16(pkt.VelZ),
|
||||
HeadPitch: int8(pkt.HeadPitch),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnSpawnPlayer should be called when a SpawnPlayer packet
|
||||
// is received.
|
||||
func (w *World) OnSpawnPlayer(pkt ptypes.SpawnPlayer) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
w.Entities[int32(pkt.ID)] = &entity.Entity{
|
||||
ID: int32(pkt.ID),
|
||||
Base: &e.Player,
|
||||
UUID: uuid.UUID(pkt.UUID),
|
||||
X: float64(pkt.X),
|
||||
Y: float64(pkt.Y),
|
||||
Z: float64(pkt.Z),
|
||||
Pitch: int8(pkt.Pitch),
|
||||
Yaw: int8(pkt.Yaw),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnEntityPosUpdate should be called when an EntityPosition packet
|
||||
// is received.
|
||||
func (w *World) OnEntityPosUpdate(pkt ptypes.EntityPosition) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
ent, ok := w.Entities[int32(pkt.ID)]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot handle position update for unknown entity %d", pkt.ID)
|
||||
}
|
||||
|
||||
ent.X += float64(pkt.X) / (128 * 32)
|
||||
ent.Y += float64(pkt.Y) / (128 * 32)
|
||||
ent.Z += float64(pkt.Z) / (128 * 32)
|
||||
ent.OnGround = bool(pkt.OnGround)
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnEntityPosLookUpdate should be called when an EntityPositionLook packet
|
||||
// is received.
|
||||
func (w *World) OnEntityPosLookUpdate(pkt ptypes.EntityPositionLook) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
ent, ok := w.Entities[int32(pkt.ID)]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot handle position look update for unknown entity %d", pkt.ID)
|
||||
}
|
||||
|
||||
ent.X += float64(pkt.X) / (128 * 32)
|
||||
ent.Y += float64(pkt.Y) / (128 * 32)
|
||||
ent.Z += float64(pkt.Z) / (128 * 32)
|
||||
ent.OnGround = bool(pkt.OnGround)
|
||||
ent.Pitch, ent.Yaw = int8(pkt.Pitch), int8(pkt.Yaw)
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnEntityLookUpdate should be called when an EntityRotation packet
|
||||
// is received.
|
||||
func (w *World) OnEntityLookUpdate(pkt ptypes.EntityRotation) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
ent, ok := w.Entities[int32(pkt.ID)]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot handle look update for unknown entity %d", pkt.ID)
|
||||
}
|
||||
|
||||
ent.Pitch, ent.Yaw = int8(pkt.Pitch), int8(pkt.Yaw)
|
||||
ent.OnGround = bool(pkt.OnGround)
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnEntityDestroy should be called when a DestroyEntities packet
|
||||
// is received.
|
||||
func (w *World) OnEntityDestroy(eIDs []pk.VarInt) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
for _, eID := range eIDs {
|
||||
delete(w.Entities, int32(eID))
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user