Skip to main content

basalt_api/world/
handle.rs

1//! Long-lived handle to the world runtime.
2//!
3//! [`WorldHandle`] is the abstract interface plugins use to access world
4//! state from anywhere -- captured in system closures, stored in plugin
5//! state. The trait itself does not require `Send + Sync` -- concrete
6//! implementors that need to be shared across threads (e.g. the runtime
7//! `World` wrapped in `Arc`) satisfy these bounds independently.
8//! Distinct from [`WorldContext`](crate::context::WorldContext) which
9//! extends this trait with dispatch-only methods (response queueing).
10//!
11//! Implemented by the runtime `World` in `basalt-world` (production)
12//! and mock types in tests. Plugins receive an `Arc<dyn WorldHandle>`
13//! from [`PluginRegistrar::world`](crate::PluginRegistrar::world).
14
15use crate::world::block_entity::BlockEntity;
16use crate::world::collision::{Aabb, RayHit};
17
18/// Long-lived handle to the world runtime.
19///
20/// Pure read/write operations only -- no response queueing (see
21/// [`WorldContext`](crate::context::WorldContext) for that).
22///
23/// Concrete types stored in `Arc` for cross-thread sharing (e.g.
24/// `Arc<World>`) already implement `Send + Sync`. The trait itself
25/// does not require them so that per-dispatch types like
26/// `ServerContext` (which uses `RefCell`) can implement it too.
27pub trait WorldHandle {
28    /// Returns the block state at the given position.
29    ///
30    /// Generates or loads the chunk if it is not cached. Returns `0`
31    /// (air) for positions outside the valid Y range.
32    fn get_block(&self, x: i32, y: i32, z: i32) -> u16;
33
34    /// Sets a block state at the given position.
35    ///
36    /// Generates or loads the chunk if it is not cached. Marks the
37    /// containing chunk as dirty for persistence.
38    fn set_block(&self, x: i32, y: i32, z: i32, state: u16);
39
40    /// Returns a cloned block entity at the given position, if any.
41    fn get_block_entity(&self, x: i32, y: i32, z: i32) -> Option<BlockEntity>;
42
43    /// Sets a block entity at the given position. Marks the chunk dirty.
44    fn set_block_entity(&self, x: i32, y: i32, z: i32, entity: BlockEntity);
45
46    /// Marks a chunk as dirty so the persistence system flushes it
47    /// on the next batch.
48    fn mark_chunk_dirty(&self, cx: i32, cz: i32);
49
50    /// Forces immediate persistence of the chunk to disk (synchronous).
51    ///
52    /// Most callers should prefer [`mark_chunk_dirty`](Self::mark_chunk_dirty)
53    /// to let the batch persistence path handle it.
54    fn persist_chunk(&self, cx: i32, cz: i32);
55
56    /// Returns the coordinates of all chunks currently marked dirty.
57    fn dirty_chunks(&self) -> Vec<(i32, i32)>;
58
59    /// Returns `true` if the AABB overlaps any solid block.
60    fn check_overlap(&self, aabb: &Aabb) -> bool;
61
62    /// Casts a ray through solid blocks. Returns the first hit within
63    /// `max_distance`, or `None`.
64    fn ray_cast(
65        &self,
66        origin: (f64, f64, f64),
67        direction: (f64, f64, f64),
68        max_distance: f64,
69    ) -> Option<RayHit>;
70
71    /// Resolves desired AABB movement against solid block collisions.
72    /// Returns the actual `(dx, dy, dz)` after clamping.
73    fn resolve_movement(&self, aabb: &Aabb, dx: f64, dy: f64, dz: f64) -> (f64, f64, f64);
74}