Skip to main content

openstranded_common_wasmcontract/
game_api.rs

1use crate::{RegistryEntry, Service, ServiceError, Value};
2
3/// Log severity level.
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum LogLevel {
6    Debug,
7    Info,
8    Warn,
9    Error,
10}
11
12/// Host-side API surface provided to WASM plugins.
13///
14/// During the build/ready/finish lifecycle phases, the plugin receives
15/// a `&mut dyn GameAPI` to interact with the engine.
16///
17/// # Implementations
18///
19/// - **Host (engine):** [`GameAPIHost`] — real implementation backed by ECS, Registry, ServiceRegistry.
20/// - **Test:** `MockGameAPI` (in `openstranded-plugin-api` behind `test-utils` feature)
21///   for unit-testing plugins natively.
22///
23/// # Object safety
24///
25/// This trait is not object-safe due to `get_service()` returning `&dyn Service`.
26/// In practice, callers use `&mut dyn GameAPI` or concrete types.
27/// WASM plugins access the API through host import functions, not through this trait.
28pub trait GameAPI {
29    // ── Registry (content pack data) ──────────────────────────────
30
31    /// Get all file entries for a domain.
32    fn registry_domain(&self, name: &str) -> Result<Vec<RegistryEntry>, ServiceError>;
33
34    /// Get raw bytes of a specific file within a domain.
35    fn registry_file(&self, domain: &str, filename: &str) -> Result<Vec<u8>, ServiceError>;
36
37    // ── Content file access ──────────────────────────────────────
38
39    /// Read any file from the active Content Pack by relative path.
40    ///
41    /// Useful for loading assets (`.glb`, `.png`, `.wav`) that are not in
42    /// the Registry but are referenced by path.
43    fn read_content_file(&self, path: &str) -> Result<Vec<u8>, ServiceError>;
44
45    // ── Services (cross-plugin communication) ────────────────────
46
47    /// Register a service under the given domain name.
48    fn register_service(
49        &mut self,
50        domain: &str,
51        service: Box<dyn Service>,
52    ) -> Result<(), ServiceError>;
53
54    /// Get a reference to a registered service (native use only).
55    fn get_service(&self, domain: &str) -> Result<&dyn Service, ServiceError>;
56
57    /// Call a method on another plugin's service (WASM-friendly).
58    ///
59    /// This is the primary way for WASM plugins to communicate.
60    /// The call is serialized, sent to the host, and dispatched to the
61    /// target plugin.
62    fn call_service(
63        &self,
64        domain: &str,
65        method: &str,
66        args: &[Value],
67    ) -> Result<Value, ServiceError>;
68
69    /// Check whether a service domain is registered.
70    fn has_service(&self, domain: &str) -> bool;
71
72    // ── ECS bridge (buffered writes) ─────────────────────────────
73
74    /// Read a component from an entity as raw serialised bytes.
75    fn get_component(&self, entity: u64, type_name: &str) -> Result<Vec<u8>, ServiceError>;
76
77    /// Buffer a component write. Applied atomically at the flush point.
78    fn set_component(
79        &mut self,
80        entity: u64,
81        type_name: &str,
82        data: &[u8],
83    ) -> Result<(), ServiceError>;
84
85    /// Find all entities that have a component with the given type name.
86    fn query_entities(&self, component_name: &str) -> Result<Vec<u64>, ServiceError>;
87
88    /// Buffer a spawn request. Returns the new entity ID after flush.
89    fn spawn_entity(&mut self, archetype: &str) -> Result<u64, ServiceError>;
90
91    /// Buffer a despawn request.
92    fn despawn_entity(&mut self, entity: u64) -> Result<(), ServiceError>;
93
94    // ── Configuration ────────────────────────────────────────────
95
96    /// Read an engine config value by dotted key path.
97    ///
98    /// Examples: `"audio.master_volume"`, `"window.width"`.
99    /// Returns `Null` if the key does not exist (not an error).
100    fn read_config(&self, key: &str) -> Result<Value, ServiceError>;
101
102    /// Read the full keybinds table as a `Map<String, String>`.
103    fn read_keybinds(&self) -> Result<Value, ServiceError>;
104
105    // ── Save / Load ──────────────────────────────────────────────
106
107    /// Register a named blob of save data for the current save slot.
108    ///
109    /// Called in response to a `"save"` event. The engine collects
110    /// all domains and writes them to disk.
111    fn register_save_data(
112        &mut self,
113        domain: &str,
114        data: Vec<u8>,
115    ) -> Result<(), ServiceError>;
116
117    /// Load a previously saved data blob by domain name.
118    ///
119    /// Returns `None` if no data was saved under this domain.
120    fn load_save_data(&self, domain: &str) -> Result<Option<Vec<u8>>, ServiceError>;
121
122    // ── Events ───────────────────────────────────────────────────
123
124    /// Emit a named event with attached data.
125    ///
126    /// The event is broadcast to all registered handlers:
127    /// - WASM plugins with a matching `on_event` export are called
128    /// - Lua hooks registered via `core_api.on()` are triggered
129    fn emit_event(&self, name: &str, data: &Value) -> Result<(), ServiceError>;
130
131    // ── Logging ──────────────────────────────────────────────────
132
133    /// Log a message at the given level.
134    fn log(&self, level: LogLevel, message: &str);
135}