diff --git a/bot/provider/basic.go b/bot/provider/basic.go index 3395f394..c71f5de4 100644 --- a/bot/provider/basic.go +++ b/bot/provider/basic.go @@ -123,8 +123,6 @@ func RunTransactions(c *Client, cancel context.CancelFunc) error { // I don't have to code all the physic by myself // thx minecrossoft 🙏 func Step(cl *Client, cancel context.CancelFunc) error { - cancel() - return nil feetBlock, err := cl.Player.World.GetBlock(cl.Player.EntityPlayer.Position) if err != nil { return err diff --git a/bot/provider/events.go b/bot/provider/events.go index f15b4a8f..e73802d2 100644 --- a/bot/provider/events.go +++ b/bot/provider/events.go @@ -10,8 +10,6 @@ import ( "github.com/Edouard127/go-mc/data/grids" "github.com/Edouard127/go-mc/level" "github.com/Edouard127/go-mc/maths" - "github.com/Edouard127/go-mc/save" - "github.com/Edouard127/go-mc/save/region" "time" "unsafe" @@ -359,24 +357,6 @@ func SetContainerSlot(c *Client, p pk.Packet, cancel context.CancelFunc) error { return fmt.Errorf("failed to scan SetSlot: %w", err) } - var chunk save.Chunk - for i := range c.Player.World.Columns { - r, err := region.Create(fmt.Sprintf("r.%d.%d.mca", i[0], i[1])) - if err != nil { - return err - } - - err = level.ChunkToSave(c.Player.World.Columns[i], &chunk) - if err != nil { - return err - } - data, err := chunk.Data(2) - if err != nil { - return err - } - r.WriteSector(int(i[0]), int(i[1]), data) - } - c.Player.Manager.StateID = int32(stateId) if containerId == -1 && slotId == -1 { @@ -933,7 +913,7 @@ func SelectAdvancementTab(c *Client, p pk.Packet, cancel context.CancelFunc) err return nil } -func WorldBorder(c *Client, p pk.Packet, cancel context.CancelFunc) error { +func InitializeBorder(c *Client, p pk.Packet, cancel context.CancelFunc) error { var ( action pk.Byte radius pk.Double @@ -950,6 +930,9 @@ func WorldBorder(c *Client, p pk.Packet, cancel context.CancelFunc) error { return fmt.Errorf("unable to read WorldBorder packet: %w", err) } + // When we receive this packet, we know that the client has fully loaded the world. + c.World.SafeToAccess = true + return nil } diff --git a/bot/provider/example_test.go b/bot/provider/example_test.go index 24cf60bc..ea1ceaa6 100644 --- a/bot/provider/example_test.go +++ b/bot/provider/example_test.go @@ -36,6 +36,7 @@ func TestExampleClient_JoinServer_online(t *testing.T) { /* Physic */ PacketHandler[Client]{ID: packetid.CPacketChunkData, Priority: 50, F: ChunkData}, + PacketHandler[Client]{ID: packetid.CPacketInitializeBorder, Priority: 50, F: InitializeBorder}, PacketHandler[Client]{ID: packetid.CPacketUnloadChunk, Priority: 50, F: UnloadChunk}, PacketHandler[Client]{ID: packetid.CPacketExplosion, Priority: 50, F: Explosion}, diff --git a/bot/world/chunks.go b/bot/world/chunks.go index 127768c7..6bd3342e 100644 --- a/bot/world/chunks.go +++ b/bot/world/chunks.go @@ -1,6 +1,7 @@ package world import ( + "context" "fmt" "github.com/Edouard127/go-mc/bot/core" . "github.com/Edouard127/go-mc/level" @@ -8,11 +9,13 @@ import ( "github.com/Edouard127/go-mc/maths" "math" "sync" + "time" ) type World struct { - Columns map[ChunkPos]*Chunk - Entities map[int32]core.Entity + Columns map[ChunkPos]*Chunk + Entities map[int32]core.Entity + SafeToAccess bool worldSync sync.Mutex entityMutex sync.Mutex @@ -25,9 +28,32 @@ func NewWorld() *World { } } +func (w *World) safeLock() { + if w.SafeToAccess { + return + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + for { + if w.SafeToAccess { + return + } + select { + case <-ctx.Done(): + panic("world not safe to access") + default: + time.Sleep(time.Millisecond * 10) + } + } +} + func (w *World) GetBlock(pos maths.Vec3d) (*block.Block, error) { w.worldSync.Lock() defer w.worldSync.Unlock() + w.safeLock() // As long as the world is empty, we can't get any blocks + if len(w.Columns) == 0 { + return block.Air, fmt.Errorf("no chunks loaded") + } chunk, ok := w.Columns[ChunkPos{int32(pos.X) >> 4, int32(pos.Z) >> 4}] if ok { return chunk.GetBlock(pos)