153 lines
3.7 KiB
Go
153 lines
3.7 KiB
Go
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
|
|
}
|