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}