1use crate::archetype_world::ArchetypeWorld;
11use crate::component::ComponentTrait;
12use crate::query::QueryStorage;
13use crate::simple_world::{EntityId, SimpleWorld};
14use soroban_sdk::{Env, Symbol, Vec};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum WorldBackend {
19 Simple,
20 Archetype,
21}
22
23pub trait RuntimeWorld {
29 fn backend(&self) -> WorldBackend;
30 fn entity_count(&self) -> usize;
31 fn version(&self) -> u64;
32 fn has_component(&self, entity_id: EntityId, component_type: &Symbol) -> bool;
33 fn entities_with_component(
34 &self,
35 component_type: &Symbol,
36 storage: QueryStorage,
37 env: &Env,
38 ) -> Vec<EntityId>;
39}
40
41pub trait RuntimeWorldMut: RuntimeWorld {
47 fn spawn_entity(&mut self) -> EntityId;
48 fn despawn_entity(&mut self, entity_id: EntityId, env: &Env);
49 fn get_component(
50 &self,
51 entity_id: EntityId,
52 component_type: &Symbol,
53 ) -> Option<soroban_sdk::Bytes>;
54 fn add_component(
55 &mut self,
56 entity_id: EntityId,
57 component_type: Symbol,
58 data: soroban_sdk::Bytes,
59 env: &Env,
60 );
61 fn remove_component(&mut self, entity_id: EntityId, component_type: &Symbol, env: &Env)
62 -> bool;
63
64 fn get_typed<T: ComponentTrait>(&self, env: &Env, entity_id: EntityId) -> Option<T> {
65 let bytes = self.get_component(entity_id, &T::component_type())?;
66 T::deserialize(env, &bytes)
67 }
68
69 fn set_typed<T: ComponentTrait>(&mut self, env: &Env, entity_id: EntityId, component: &T) {
70 self.add_component(
71 entity_id,
72 T::component_type(),
73 component.serialize(env),
74 env,
75 );
76 }
77
78 fn has_typed<T: ComponentTrait>(&self, entity_id: EntityId) -> bool {
79 self.has_component(entity_id, &T::component_type())
80 }
81
82 fn remove_typed<T: ComponentTrait>(&mut self, env: &Env, entity_id: EntityId) -> bool {
83 self.remove_component(entity_id, &T::component_type(), env)
84 }
85}
86
87impl RuntimeWorld for SimpleWorld {
88 fn backend(&self) -> WorldBackend {
89 WorldBackend::Simple
90 }
91
92 fn entity_count(&self) -> usize {
93 self.entity_components.len().try_into().unwrap()
94 }
95
96 fn version(&self) -> u64 {
97 self.version()
98 }
99
100 fn has_component(&self, entity_id: EntityId, component_type: &Symbol) -> bool {
101 self.has_component(entity_id, component_type)
102 }
103
104 fn entities_with_component(
105 &self,
106 component_type: &Symbol,
107 storage: QueryStorage,
108 env: &Env,
109 ) -> Vec<EntityId> {
110 match storage {
111 QueryStorage::Table => self.get_table_entities_with_component(component_type, env),
112 QueryStorage::Any => self.get_all_entities_with_component(component_type, env),
113 }
114 }
115}
116
117impl RuntimeWorldMut for SimpleWorld {
118 fn spawn_entity(&mut self) -> EntityId {
119 SimpleWorld::spawn_entity(self)
120 }
121
122 fn despawn_entity(&mut self, entity_id: EntityId, _env: &Env) {
123 SimpleWorld::despawn_entity(self, entity_id);
124 }
125
126 fn get_component(
127 &self,
128 entity_id: EntityId,
129 component_type: &Symbol,
130 ) -> Option<soroban_sdk::Bytes> {
131 SimpleWorld::get_component(self, entity_id, component_type)
132 }
133
134 fn add_component(
135 &mut self,
136 entity_id: EntityId,
137 component_type: Symbol,
138 data: soroban_sdk::Bytes,
139 _env: &Env,
140 ) {
141 SimpleWorld::add_component(self, entity_id, component_type, data);
142 }
143
144 fn remove_component(
145 &mut self,
146 entity_id: EntityId,
147 component_type: &Symbol,
148 _env: &Env,
149 ) -> bool {
150 SimpleWorld::remove_component(self, entity_id, component_type)
151 }
152}
153
154impl RuntimeWorld for ArchetypeWorld {
155 fn backend(&self) -> WorldBackend {
156 WorldBackend::Archetype
157 }
158
159 fn entity_count(&self) -> usize {
160 self.entity_archetype.len().try_into().unwrap()
161 }
162
163 fn version(&self) -> u64 {
164 self.version()
165 }
166
167 fn has_component(&self, entity_id: EntityId, component_type: &Symbol) -> bool {
168 self.has_component(entity_id, component_type)
169 }
170
171 fn entities_with_component(
172 &self,
173 component_type: &Symbol,
174 _storage: QueryStorage,
175 env: &Env,
176 ) -> Vec<EntityId> {
177 self.query(core::slice::from_ref(component_type), env)
178 }
179}
180
181impl RuntimeWorldMut for ArchetypeWorld {
182 fn spawn_entity(&mut self) -> EntityId {
183 ArchetypeWorld::spawn_entity(self)
184 }
185
186 fn despawn_entity(&mut self, entity_id: EntityId, env: &Env) {
187 ArchetypeWorld::despawn_entity(self, entity_id, env);
188 }
189
190 fn get_component(
191 &self,
192 entity_id: EntityId,
193 component_type: &Symbol,
194 ) -> Option<soroban_sdk::Bytes> {
195 ArchetypeWorld::get_component(self, entity_id, component_type)
196 }
197
198 fn add_component(
199 &mut self,
200 entity_id: EntityId,
201 component_type: Symbol,
202 data: soroban_sdk::Bytes,
203 env: &Env,
204 ) {
205 ArchetypeWorld::add_component(self, entity_id, component_type, data, env);
206 }
207
208 fn remove_component(
209 &mut self,
210 entity_id: EntityId,
211 component_type: &Symbol,
212 env: &Env,
213 ) -> bool {
214 ArchetypeWorld::remove_component(self, entity_id, component_type, env)
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::component::Position;
222
223 fn exercise_runtime_mut<W: RuntimeWorldMut>(world: &mut W, env: &Env) {
224 let entity = world.spawn_entity();
225 world.set_typed(env, entity, &Position::new(4, 5));
226 assert!(world.has_typed::<Position>(entity));
227 let pos: Position = world.get_typed(env, entity).unwrap();
228 assert_eq!(pos.x, 4);
229 assert!(world.remove_typed::<Position>(env, entity));
230 world.despawn_entity(entity, env);
231 }
232
233 #[test]
234 fn runtime_world_mut_supports_simple_world() {
235 let env = Env::default();
236 let mut world = SimpleWorld::new(&env);
237 exercise_runtime_mut(&mut world, &env);
238 assert_eq!(world.backend(), WorldBackend::Simple);
239 }
240
241 #[test]
242 fn runtime_world_mut_supports_archetype_world() {
243 let env = Env::default();
244 let mut world = ArchetypeWorld::new(&env);
245 exercise_runtime_mut(&mut world, &env);
246 assert_eq!(world.backend(), WorldBackend::Archetype);
247 }
248}