Basic entity tracking
This commit is contained in:
@ -23,10 +23,11 @@ type Client struct {
|
||||
abilities PlayerAbilities
|
||||
settings Settings
|
||||
|
||||
Wd world.World //the map data
|
||||
Inputs phy.Inputs
|
||||
Physics phy.State
|
||||
lastPosTx time.Time
|
||||
Wd world.World //the map data
|
||||
Inputs phy.Inputs
|
||||
Physics phy.State
|
||||
lastPosTx time.Time
|
||||
justTeleported bool
|
||||
|
||||
// Delegate allows you push a function to let HandleGame run.
|
||||
// Do not send at the same goroutine!
|
||||
@ -58,8 +59,8 @@ func NewClient() *Client {
|
||||
Auth: Auth{Name: "Steve"},
|
||||
Delegate: make(chan func() error),
|
||||
Wd: world.World{
|
||||
Entities: make(map[int32]entity.Entity),
|
||||
Chunks: make(map[world.ChunkLoc]*world.Chunk),
|
||||
Entities: make(map[int32]*entity.Entity, 8192),
|
||||
Chunks: make(map[world.ChunkLoc]*world.Chunk, 2048),
|
||||
},
|
||||
closing: make(chan struct{}),
|
||||
inbound: make(chan pk.Packet, 5),
|
||||
|
@ -71,6 +71,9 @@ type eventBroker struct {
|
||||
// ReceivePacket will be called when new packets arrive.
|
||||
// The default handler will run only if pass == false.
|
||||
ReceivePacket func(p pk.Packet) (pass bool, err error)
|
||||
|
||||
// PrePhysics will be called before a phyiscs tick.
|
||||
PrePhysics func() error
|
||||
}
|
||||
|
||||
func (b *eventBroker) updateSeenPackets(f seenPacketFlags) error {
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"github.com/Tnze/go-mc/bot/world/entity/player"
|
||||
"github.com/Tnze/go-mc/chat"
|
||||
"github.com/Tnze/go-mc/data"
|
||||
"github.com/Tnze/go-mc/data/entity"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"github.com/Tnze/go-mc/net/ptypes"
|
||||
)
|
||||
@ -38,7 +37,10 @@ func (c *Client) updateServerPos(pos player.Pos) error {
|
||||
pk.Boolean(pos.OnGround),
|
||||
),
|
||||
)
|
||||
case time.Now().Add(-time.Second).After(c.lastPosTx):
|
||||
}
|
||||
|
||||
if c.justTeleported || time.Now().Add(-time.Second).After(c.lastPosTx) {
|
||||
c.justTeleported = false
|
||||
c.lastPosTx = time.Now()
|
||||
sendPlayerPositionPacket(c)
|
||||
}
|
||||
@ -83,6 +85,11 @@ func (c *Client) HandleGame() error {
|
||||
case <-c.closing:
|
||||
return http.ErrServerClosed
|
||||
case <-cTick.C:
|
||||
if c.Events.PrePhysics != nil {
|
||||
if err := c.Events.PrePhysics(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := c.Physics.Tick(c.Inputs, &c.Wd); err != nil {
|
||||
c.disconnect()
|
||||
return err
|
||||
@ -209,12 +216,7 @@ func handleSpawnEntityPacket(c *Client, p pk.Packet) error {
|
||||
if err := se.Decode(p); err != nil {
|
||||
return err
|
||||
}
|
||||
if e := entity.ByID[entity.ID(se.Type)]; e != nil {
|
||||
fmt.Printf("Spawning %s at (%f,%f,%f)\n", e.DisplayName, se.X, se.Y, se.Z)
|
||||
} else {
|
||||
fmt.Printf("SpawnEntity: %+v\n", se)
|
||||
}
|
||||
return nil
|
||||
return c.Wd.OnSpawnEntity(se)
|
||||
}
|
||||
|
||||
func handleSpawnLivingEntityPacket(c *Client, p pk.Packet) error {
|
||||
@ -222,12 +224,7 @@ func handleSpawnLivingEntityPacket(c *Client, p pk.Packet) error {
|
||||
if err := se.Decode(p); err != nil {
|
||||
return err
|
||||
}
|
||||
if e := entity.ByID[entity.ID(se.Type)]; e != nil {
|
||||
fmt.Printf("Spawning %s at (%f,%f,%f)\n", e.DisplayName, se.X, se.Y, se.Z)
|
||||
} else {
|
||||
fmt.Printf("SpawnLivingEntity: %+v\n", se)
|
||||
}
|
||||
return nil
|
||||
return c.Wd.OnSpawnLivingEntity(se)
|
||||
}
|
||||
|
||||
func handleSpawnPlayerPacket(c *Client, p pk.Packet) error {
|
||||
@ -235,35 +232,32 @@ func handleSpawnPlayerPacket(c *Client, p pk.Packet) error {
|
||||
if err := se.Decode(p); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("SpawnPlayer: %+v\n", se)
|
||||
return nil
|
||||
fmt.Println(se)
|
||||
return c.Wd.OnSpawnPlayer(se)
|
||||
}
|
||||
|
||||
func handleEntityPositionPacket(c *Client, p pk.Packet) error {
|
||||
var se ptypes.EntityPosition
|
||||
if err := se.Decode(p); err != nil {
|
||||
var pu ptypes.EntityPosition
|
||||
if err := pu.Decode(p); err != nil {
|
||||
return err
|
||||
}
|
||||
// fmt.Printf("EntityPosition: %+v\n", se)
|
||||
return nil
|
||||
return c.Wd.OnEntityPosUpdate(pu)
|
||||
}
|
||||
|
||||
func handleEntityPositionLookPacket(c *Client, p pk.Packet) error {
|
||||
var se ptypes.EntityPositionLook
|
||||
if err := se.Decode(p); err != nil {
|
||||
var epr ptypes.EntityPositionLook
|
||||
if err := epr.Decode(p); err != nil {
|
||||
return err
|
||||
}
|
||||
// fmt.Printf("EntityPositionLook: %+v\n", se)
|
||||
return nil
|
||||
return c.Wd.OnEntityPosLookUpdate(epr)
|
||||
}
|
||||
|
||||
func handleEntityLookPacket(c *Client, p pk.Packet) error {
|
||||
var se ptypes.EntityRotation
|
||||
if err := se.Decode(p); err != nil {
|
||||
var er ptypes.EntityRotation
|
||||
if err := er.Decode(p); err != nil {
|
||||
return err
|
||||
}
|
||||
// fmt.Printf("EntityRotation: %+v\n", se)
|
||||
return nil
|
||||
return c.Wd.OnEntityLookUpdate(er)
|
||||
}
|
||||
|
||||
func handleEntityMovePacket(c *Client, p pk.Packet) error {
|
||||
@ -312,8 +306,7 @@ func handleDestroyEntitiesPacket(c *Client, p pk.Packet) error {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("DestroyEntities: %v\n", entities)
|
||||
return nil
|
||||
return c.Wd.OnEntityDestroy(entities)
|
||||
}
|
||||
|
||||
func handleSoundEffect(c *Client, p pk.Packet) error {
|
||||
@ -481,7 +474,7 @@ func handleJoinGamePacket(c *Client, p pk.Packet) error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.EntityID = int(pkt.PlayerEntity)
|
||||
c.Player.ID = int32(pkt.PlayerEntity)
|
||||
c.Gamemode = int(pkt.Gamemode & 0x7)
|
||||
c.Hardcore = pkt.Gamemode&0x8 != 0
|
||||
c.Dimension = int(pkt.Dimension)
|
||||
@ -671,6 +664,7 @@ func handlePlayerPositionAndLookPacket(c *Client, p pk.Packet) error {
|
||||
return err
|
||||
}
|
||||
c.Player.Pos = pp
|
||||
c.justTeleported = true
|
||||
|
||||
if c.Events.PositionChange != nil {
|
||||
if err := c.Events.PositionChange(pp); err != nil {
|
||||
@ -735,7 +729,6 @@ func handleSetExperience(c *Client, p pk.Packet) (err error) {
|
||||
}
|
||||
|
||||
func sendPlayerPositionAndLookPacket(c *Client) error {
|
||||
// fmt.Println("PPL")
|
||||
return c.conn.WritePacket(ptypes.PositionAndLookServerbound{
|
||||
X: pk.Double(c.Pos.X),
|
||||
Y: pk.Double(c.Pos.Y),
|
||||
@ -747,7 +740,6 @@ func sendPlayerPositionAndLookPacket(c *Client) error {
|
||||
}
|
||||
|
||||
func sendPlayerPositionPacket(c *Client) error {
|
||||
// fmt.Println("P")
|
||||
return c.conn.WritePacket(ptypes.Position{
|
||||
X: pk.Double(c.Pos.X),
|
||||
Y: pk.Double(c.Pos.Y),
|
||||
@ -757,7 +749,6 @@ func sendPlayerPositionPacket(c *Client) error {
|
||||
}
|
||||
|
||||
func sendPlayerLookPacket(c *Client) error {
|
||||
// fmt.Println("L")
|
||||
return c.conn.WritePacket(ptypes.Look{
|
||||
Yaw: pk.Float(c.Pos.Yaw),
|
||||
Pitch: pk.Float(c.Pos.Pitch),
|
||||
|
@ -10,6 +10,8 @@ type MinMax struct {
|
||||
Min, Max float64
|
||||
}
|
||||
|
||||
// Extends adjusts the bounds of the MinMax. A negative number will reduce the
|
||||
// minimum bound, whereas a positive number will increase the maximum bound.
|
||||
func (mm MinMax) Extend(delta float64) MinMax {
|
||||
if delta < 0 {
|
||||
return MinMax{
|
||||
@ -24,6 +26,8 @@ func (mm MinMax) Extend(delta float64) MinMax {
|
||||
}
|
||||
}
|
||||
|
||||
// Contract reduces both the minimum and maximum bound by the provided amount,
|
||||
// such that the difference between the bounds decreases for positive values.
|
||||
func (mm MinMax) Contract(amt float64) MinMax {
|
||||
return MinMax{
|
||||
Min: mm.Min + amt,
|
||||
@ -31,6 +35,8 @@ func (mm MinMax) Contract(amt float64) MinMax {
|
||||
}
|
||||
}
|
||||
|
||||
// Expand changes the minimum and maximum bounds by the provided amount, such
|
||||
// that the difference between the bounds increases for positive values.
|
||||
func (mm MinMax) Expand(amt float64) MinMax {
|
||||
return MinMax{
|
||||
Min: mm.Min - amt,
|
||||
@ -38,6 +44,7 @@ func (mm MinMax) Expand(amt float64) MinMax {
|
||||
}
|
||||
}
|
||||
|
||||
// Offset adds the provided value to both the minimum and maximum value.
|
||||
func (mm MinMax) Offset(amt float64) MinMax {
|
||||
return MinMax{
|
||||
Min: mm.Min + amt,
|
||||
@ -51,6 +58,8 @@ type AABB struct {
|
||||
Block world.BlockStatus
|
||||
}
|
||||
|
||||
// Extend adjusts the minimum (for negative values) or maximum bounds (for
|
||||
// positive values) by the provided scalar for each dimension.
|
||||
func (bb AABB) Extend(dx, dy, dz float64) AABB {
|
||||
return AABB{
|
||||
X: bb.X.Extend(dx),
|
||||
@ -60,6 +69,8 @@ func (bb AABB) Extend(dx, dy, dz float64) AABB {
|
||||
}
|
||||
}
|
||||
|
||||
// Contract reduces the difference between the min/max bounds (for positive
|
||||
// values) for each dimension.
|
||||
func (bb AABB) Contract(x, y, z float64) AABB {
|
||||
return AABB{
|
||||
X: bb.X.Contract(x),
|
||||
@ -69,6 +80,8 @@ func (bb AABB) Contract(x, y, z float64) AABB {
|
||||
}
|
||||
}
|
||||
|
||||
// Expand increases both the minimum and maximum bounds by the provided amount
|
||||
// (for positive values) for each dimension.
|
||||
func (bb AABB) Expand(x, y, z float64) AABB {
|
||||
return AABB{
|
||||
X: bb.X.Expand(x),
|
||||
@ -78,6 +91,8 @@ func (bb AABB) Expand(x, y, z float64) AABB {
|
||||
}
|
||||
}
|
||||
|
||||
// Offset moves both the minimum and maximum bound by the provided value for
|
||||
// each dimension.
|
||||
func (bb AABB) Offset(x, y, z float64) AABB {
|
||||
return AABB{
|
||||
X: bb.X.Offset(x),
|
||||
|
@ -2,7 +2,7 @@ package phy
|
||||
|
||||
// Inputs describes the desired movements of the player.
|
||||
type Inputs struct {
|
||||
Yaw, Pitch float32
|
||||
Yaw, Pitch float64
|
||||
|
||||
ThrottleX, ThrottleZ float64
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ const (
|
||||
playerHeight = 1.8
|
||||
resetVel = 0.003
|
||||
|
||||
yawSpeed = 3.0
|
||||
maxYawChange = 33
|
||||
maxPitchChange = 22
|
||||
|
||||
gravity = 0.08
|
||||
drag = 0.98
|
||||
@ -87,7 +88,12 @@ func (s *State) surroundings(query AABB, w World) Surrounds {
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *State) applyInputs(input Inputs, acceleration, inertia float64) {
|
||||
func (s *State) applyLookInputs(input Inputs) {
|
||||
errYaw := math.Min(math.Max(input.Yaw-s.Yaw, -maxYawChange), maxYawChange)
|
||||
s.Yaw += errYaw
|
||||
}
|
||||
|
||||
func (s *State) applyPosInputs(input Inputs, acceleration, inertia float64) {
|
||||
speed := math.Sqrt(input.ThrottleX*input.ThrottleX + input.ThrottleZ*input.ThrottleZ)
|
||||
if speed < 0.01 {
|
||||
return
|
||||
@ -111,7 +117,8 @@ func (s *State) Tick(input Inputs, w World) error {
|
||||
inertia *= slipperiness
|
||||
acceleration = 0.1 * (0.1627714 / (inertia * inertia * inertia))
|
||||
}
|
||||
s.applyInputs(input, acceleration, inertia)
|
||||
s.applyLookInputs(input)
|
||||
s.applyPosInputs(input, acceleration, inertia)
|
||||
|
||||
// Deadzone velocities when they get too low.
|
||||
if math.Abs(s.Vel.X) < resetVel {
|
||||
|
@ -2,13 +2,27 @@ package entity
|
||||
|
||||
import (
|
||||
"github.com/Tnze/go-mc/data"
|
||||
"github.com/Tnze/go-mc/data/entity"
|
||||
"github.com/Tnze/go-mc/nbt"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
//Entity is the entity of minecraft
|
||||
//Entity represents an instance of an entity.
|
||||
type Entity struct {
|
||||
EntityID int //实体ID
|
||||
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
|
||||
|
@ -1,120 +1,15 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Tnze/go-mc/bot/world/entity"
|
||||
"github.com/Tnze/go-mc/data/block"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
)
|
||||
|
||||
// World record all of the things in the world where player at
|
||||
type World struct {
|
||||
Entities map[int32]entity.Entity
|
||||
Chunks map[ChunkLoc]*Chunk
|
||||
}
|
||||
|
||||
// Chunk store a 256*16*16 column blocks
|
||||
type Chunk struct {
|
||||
Sections [16]Section
|
||||
}
|
||||
|
||||
// Section store a 16*16*16 cube blocks
|
||||
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 {
|
||||
// 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).
|
||||
return uint(((y & 15) << 8) | (z << 4) | x)
|
||||
}
|
||||
|
||||
type BlockStatus uint32
|
||||
|
||||
type ChunkLoc struct {
|
||||
X, Z int
|
||||
}
|
||||
|
||||
// //Entity 表示一个实体
|
||||
// type Entity interface {
|
||||
// EntityID() int32
|
||||
// }
|
||||
|
||||
// //Face is a face of a block
|
||||
// type Face byte
|
||||
|
||||
// // All six faces in a block
|
||||
// const (
|
||||
// Bottom Face = iota
|
||||
// Top
|
||||
// North
|
||||
// South
|
||||
// West
|
||||
// East
|
||||
// )
|
||||
|
||||
// getBlock return the block in the position (x, y, z)
|
||||
func (w *World) GetBlockStatus(x, y, z int) BlockStatus {
|
||||
// Use n>>4 rather then n/16. It acts wrong if n<0.
|
||||
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
|
||||
}
|
||||
|
||||
func (w *World) UnloadChunk(loc ChunkLoc) {
|
||||
delete(w.Chunks, loc)
|
||||
}
|
||||
|
||||
func (w *World) UnaryBlockUpdate(pos pk.Position, bStateID BlockStatus) bool {
|
||||
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 {
|
||||
sec.SetBlock(bIdx, bStateID)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *World) MultiBlockUpdate(loc ChunkLoc, sectionY int, blocks []pk.VarLong) bool {
|
||||
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 {
|
||||
bStateID := b >> 12
|
||||
x, z, y := (b>>8)&0xf, (b>>4)&0xf, b&0xf
|
||||
sec.SetBlock(SectionIdx(int(x&15), int(y&15), int(z&15)), BlockStatus(bStateID))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// func (b Block) String() string {
|
||||
// return blockNameByID[b.id]
|
||||
// }
|
||||
|
||||
//LoadChunk load chunk at (x, z)
|
||||
func (w *World) LoadChunk(x, z int, c *Chunk) {
|
||||
w.Chunks[ChunkLoc{X: x, Z: z}] = c
|
||||
entityLock sync.RWMutex
|
||||
Entities map[int32]*entity.Entity
|
||||
chunkLock sync.RWMutex
|
||||
Chunks map[ChunkLoc]*Chunk
|
||||
}
|
||||
|
106
bot/world/world_chunk.go
Normal file
106
bot/world/world_chunk.go
Normal file
@ -0,0 +1,106 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"github.com/Tnze/go-mc/data/block"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
)
|
||||
|
||||
// Chunk store a 256*16*16 area of blocks, sharded on the Y axis into 16
|
||||
// sections.
|
||||
type Chunk struct {
|
||||
Sections [16]Section
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
bStateID := b >> 12
|
||||
x, z, y := (b>>8)&0xf, (b>>4)&0xf, b&0xf
|
||||
sec.SetBlock(sectionIdx(int(x&15), int(y&15), int(z&15)), BlockStatus(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()
|
||||
}
|
167
bot/world/world_entity.go
Normal file
167
bot/world/world_entity.go
Normal file
@ -0,0 +1,167 @@
|
||||
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 recieved.
|
||||
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 recieved.
|
||||
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)
|
||||
}
|
||||
|
||||
// fmt.Printf("SpawnLivingEntity %q\n", base.Name)
|
||||
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 recieved.
|
||||
func (w *World) OnSpawnPlayer(pkt ptypes.SpawnPlayer) error {
|
||||
w.entityLock.Lock()
|
||||
defer w.entityLock.Unlock()
|
||||
|
||||
// fmt.Printf("SpawnPlayer %v\n", pkt)
|
||||
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 recieved.
|
||||
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 recieved.
|
||||
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 recieved.
|
||||
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 recieved.
|
||||
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
|
||||
}
|
@ -79,7 +79,7 @@ type Look struct {
|
||||
|
||||
func (p Look) Encode() pk.Packet {
|
||||
return pk.Marshal(
|
||||
data.PositionLook,
|
||||
data.Look,
|
||||
pk.Float(p.Yaw),
|
||||
pk.Float(p.Pitch),
|
||||
pk.Boolean(p.OnGround),
|
||||
|
Reference in New Issue
Block a user