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}