1use crate::{
2 component::WebScriptComponent,
3 scriptable::{
4 scriptable_js_to_value, scriptable_value_merge, scriptable_value_to_js, Scriptable,
5 ScriptableValue,
6 },
7 state::WebScriptStateScripted,
8 web_api::EntityId,
9};
10use core::ecs::{Builder, Component, Entity, EntityBuilder, Resource, World, WorldExt};
11use js_sys::{Function, JsString, Object, Reflect};
12use std::{
13 collections::{HashMap, HashSet},
14 sync::Mutex,
15};
16use wasm_bindgen::{JsCast, JsValue};
17
18lazy_static! {
19 static ref INTERFACE: Mutex<WebScriptInterface> = Mutex::new(WebScriptInterface::default());
20}
21
22pub trait ComponentModify<R> {
23 fn modify_component(&mut self, source: R);
24}
25
26impl<T, R> ComponentModify<R> for T
27where
28 T: Component + From<R>,
29{
30 fn modify_component(&mut self, source: R) {
31 *self = source.into();
32 }
33}
34
35struct ComponentBridge {
36 add_to_entity: Box<dyn FnMut(EntityBuilder, JsValue) -> EntityBuilder>,
37 read_data: Box<dyn FnMut(&World, Entity) -> JsValue>,
38 write_data: Box<dyn FnMut(&mut World, Entity, JsValue)>,
39}
40
41impl ComponentBridge {
42 fn on_add_to_entity<'a>(
43 &mut self,
44 data: JsValue,
45 builder: EntityBuilder<'a>,
46 ) -> EntityBuilder<'a> {
47 (self.add_to_entity)(builder, data)
48 }
49
50 fn on_read_data(&mut self, world: &World, entity: Entity) -> JsValue {
51 (self.read_data)(world, entity)
52 }
53
54 fn on_write_data(&mut self, world: &mut World, entity: Entity, value: JsValue) {
55 (self.write_data)(world, entity, value)
56 }
57}
58
59pub trait ResourceModify<R> {
60 fn modify_resource(&mut self, source: R);
61}
62
63pub trait ResourceAccess {
64 fn access_resource(&mut self, value: ScriptableValue) -> ScriptableValue;
65}
66
67struct ResourceBridge {
68 read_data: Box<dyn FnMut(&World) -> JsValue>,
69 write_data: Box<dyn FnMut(&mut World, JsValue)>,
70 access_data: Box<dyn FnMut(&World, JsValue) -> JsValue>,
71}
72
73impl ResourceBridge {
74 fn on_read_data(&mut self, world: &World) -> JsValue {
75 (self.read_data)(world)
76 }
77
78 fn on_write_data(&mut self, world: &mut World, value: JsValue) {
79 (self.write_data)(world, value)
80 }
81
82 fn on_access_data(&mut self, world: &World, value: JsValue) -> JsValue {
83 (self.access_data)(world, value)
84 }
85}
86
87pub struct WebScriptInterface {
88 ready: bool,
89 world_ptr: Option<*mut World>,
91 resources: HashMap<String, JsValue>,
92 resources_bridge: HashMap<String, ResourceBridge>,
93 component_factory: HashMap<String, Function>,
94 scriptable_components: HashMap<String, Box<dyn Scriptable>>,
95 components_bridge: HashMap<String, ComponentBridge>,
96 state_factory: HashMap<String, Function>,
97 scriptable_state_factory: HashMap<String, Box<dyn FnMut() -> Box<dyn WebScriptStateScripted>>>,
98 systems: HashMap<String, (JsValue, Function)>,
99 systems_cache: Option<Vec<(JsValue, Function)>>,
100 index_generator: u64,
101 generation: u32,
102 entities_to_create: Vec<(JsValue, EntityId)>,
103 entities_to_destroy: HashSet<EntityId>,
104 entities_map: HashMap<EntityId, Entity>,
105 entities_cache: Vec<Entity>,
106}
107
108unsafe impl Send for WebScriptInterface {}
109unsafe impl Sync for WebScriptInterface {}
110
111impl Default for WebScriptInterface {
112 fn default() -> Self {
113 Self {
114 ready: false,
115 world_ptr: None,
116 resources: HashMap::new(),
117 resources_bridge: HashMap::new(),
118 component_factory: HashMap::new(),
119 scriptable_components: HashMap::new(),
120 components_bridge: HashMap::new(),
121 state_factory: HashMap::new(),
122 scriptable_state_factory: HashMap::new(),
123 systems: HashMap::new(),
124 systems_cache: None,
125 index_generator: 0,
126 generation: 1,
127 entities_to_create: vec![],
128 entities_to_destroy: HashSet::new(),
129 entities_map: HashMap::new(),
130 entities_cache: vec![],
131 }
132 }
133}
134
135impl WebScriptInterface {
136 pub fn is_ready() -> bool {
137 if let Ok(interface) = INTERFACE.lock() {
138 interface.ready
139 } else {
140 false
141 }
142 }
143
144 pub fn is_invalid() -> bool {
145 if let Ok(interface) = INTERFACE.lock() {
146 interface.world_ptr.is_none()
147 } else {
148 true
149 }
150 }
151
152 pub fn register_scriptable_resource<S>(&mut self, name: &str, resource: S)
153 where
154 S: 'static + Scriptable,
155 {
156 if !self.ready {
157 if let Ok(resource) = resource.to_js() {
158 self.resources.insert(name.to_owned(), resource);
159 }
160 }
161 }
162
163 pub fn register_resource_bridge<'a, S, M>(&mut self, name: &str)
164 where
165 S: Resource + Send + Sync + ResourceModify<M> + ResourceAccess,
166 M: Scriptable + From<&'a S>,
167 {
168 self.resources_bridge.insert(
169 name.to_owned(),
170 ResourceBridge {
171 read_data: Box::new(|world| {
172 let r: &S = &world.read_resource::<S>();
173 let data = M::from(unsafe { std::mem::transmute(r) });
177 if let Ok(data) = data.to_js() {
178 data
179 } else {
180 JsValue::UNDEFINED
181 }
182 }),
183 write_data: Box::new(|world, value| {
184 if let Ok(data) = M::from_js(value) {
185 let r: &mut S = &mut world.write_resource::<S>();
186 r.modify_resource(data);
187 }
188 }),
189 access_data: Box::new(|world, value| {
190 if let Ok(data) = scriptable_js_to_value(value) {
191 let r: &mut S = &mut world.write_resource::<S>();
192 let v = r.access_resource(data);
193 if let Ok(v) = scriptable_value_to_js(&v) {
194 return v;
195 }
196 }
197 JsValue::UNDEFINED
198 }),
199 },
200 );
201 }
202
203 pub fn register_scriptable_component<S>(&mut self, name: &str, component: S)
204 where
205 S: 'static + Scriptable,
206 {
207 if !self.ready {
208 self.scriptable_components
209 .insert(name.to_owned(), Box::new(component));
210 }
211 }
212
213 pub fn register_component_bridge<S, M>(&mut self, name: &str, template: S)
214 where
215 S: Scriptable + Component + Clone + Send + Sync + ComponentModify<M>,
216 S::Storage: Default,
217 M: Scriptable + From<S>,
218 {
219 self.components_bridge.insert(
220 name.to_owned(),
221 ComponentBridge {
222 add_to_entity: Box::new(move |builder, data| {
223 let template_medium: M = template.clone().into();
224 if let Ok(template_scriptable) = template_medium.to_scriptable() {
225 if let Ok(data_scriptable) = scriptable_js_to_value(data) {
226 let merged_scriptable =
227 scriptable_value_merge(&template_scriptable, &data_scriptable);
228 if let Ok(data) = M::from_scriptable(&merged_scriptable) {
229 let mut template = template.clone();
230 template.modify_component(data);
231 return builder.with(template);
232 }
233 }
234 }
235 builder
236 }),
237 read_data: Box::new(|world, entity| {
238 if let Some(data) = world.read_storage::<S>().get(entity) {
239 let data: M = data.clone().into();
240 if let Ok(data) = data.to_js() {
241 return data;
242 }
243 }
244 JsValue::UNDEFINED
245 }),
246 write_data: Box::new(|world, entity, value| {
247 if let Ok(data) = M::from_js(value) {
248 if let Some(component) = world.write_storage::<S>().get_mut(entity) {
249 component.modify_component(data);
250 }
251 }
252 }),
253 },
254 );
255 }
256
257 pub fn register_scriptable_state_factory<S>(&mut self, name: &str, factory: S)
258 where
259 S: 'static + FnMut() -> Box<dyn WebScriptStateScripted>,
260 {
261 if !self.ready {
262 self.scriptable_state_factory
263 .insert(name.to_owned(), Box::new(factory));
264 }
265 }
266
267 pub fn read_scriptable_resource<T>(name: &str) -> Option<T>
268 where
269 T: Scriptable,
270 {
271 if let Some(resource) = Self::get_resource(name) {
272 if let Ok(resource) = T::from_js(resource) {
273 return Some(resource);
274 }
275 }
276 None
277 }
278
279 pub fn read_js_resource(name: &str) -> Option<ScriptableValue> {
280 if let Some(resource) = Self::get_resource(name) {
281 if let Ok(resource) = scriptable_js_to_value(resource) {
282 return Some(resource);
283 }
284 }
285 None
286 }
287
288 pub fn write_scriptable_resource<T>(name: &str, value: &T)
289 where
290 T: Scriptable,
291 {
292 if let Ok(resource) = value.to_js() {
293 Self::set_resource(name, resource);
294 }
295 }
296
297 pub fn write_js_resource(name: &str, value: &ScriptableValue) {
298 if let Ok(resource) = scriptable_value_to_js(value) {
299 Self::set_resource(name, resource);
300 }
301 }
302
303 pub(crate) fn register_resource(name: &str, resource: JsValue) {
304 if let Ok(mut interface) = INTERFACE.lock() {
305 if !interface.ready {
306 interface.resources.insert(name.to_owned(), resource);
307 }
308 }
309 }
310
311 pub(crate) fn register_component_factory(name: &str, factory: Function) {
312 if let Ok(mut interface) = INTERFACE.lock() {
313 if !interface.ready {
314 interface.component_factory.insert(name.to_owned(), factory);
315 }
316 }
317 }
318
319 pub(crate) fn register_system(name: &str, system: JsValue) {
320 if let Ok(mut interface) = INTERFACE.lock() {
321 if !interface.ready {
322 if let Ok(m) = Reflect::get(&system, &JsValue::from_str("onRun")) {
323 if let Some(m) = m.dyn_ref::<Function>() {
324 interface
325 .systems
326 .insert(name.to_owned(), (system, m.clone()));
327 }
328 }
329 }
330 }
331 }
332
333 pub(crate) fn register_state_factory(name: &str, factory: Function) {
334 if let Ok(mut interface) = INTERFACE.lock() {
335 if !interface.ready {
336 interface.state_factory.insert(name.to_owned(), factory);
337 }
338 }
339 }
340
341 pub(crate) fn with<F, R>(mut f: F) -> R
342 where
343 F: FnMut(&mut Self) -> R,
344 R: Default,
345 {
346 if let Ok(mut interface) = INTERFACE.lock() {
347 f(&mut interface)
348 } else {
349 R::default()
350 }
351 }
352
353 pub(crate) fn start() {
354 if let Ok(mut interface) = INTERFACE.lock() {
355 interface.ready = true;
356 interface.systems_cache = Some(interface.systems.values().cloned().collect::<Vec<_>>());
357 }
358 }
359
360 pub(crate) fn set_world(world: &mut World) {
361 if let Ok(mut interface) = INTERFACE.lock() {
362 interface.world_ptr = Some(world as *mut World);
363 }
364 }
365
366 pub(crate) fn unset_world() {
367 if let Ok(mut interface) = INTERFACE.lock() {
368 interface.world_ptr = None;
369 }
370 }
371
372 pub(crate) fn world() -> Option<*mut World> {
373 if let Ok(interface) = INTERFACE.lock() {
374 interface.world_ptr
375 } else {
376 None
377 }
378 }
379
380 pub(crate) fn get_entity(index: usize) -> Option<Entity> {
381 if let Ok(interface) = INTERFACE.lock() {
382 interface.entities_cache.get(index).copied()
383 } else {
384 None
385 }
386 }
387
388 pub(crate) fn get_resource(name: &str) -> Option<JsValue> {
389 if let Ok(interface) = INTERFACE.lock() {
390 if let Some(resource) = interface.resources.get(name) {
391 return Some(resource.clone());
392 }
393 }
394 None
395 }
396
397 pub(crate) fn set_resource(name: &str, value: JsValue) -> bool {
398 if let Ok(mut interface) = INTERFACE.lock() {
399 if interface.ready && interface.resources.contains_key(name) {
400 interface.resources.insert(name.to_owned(), value);
401 return true;
402 }
403 }
404 false
405 }
406
407 pub(crate) fn read_resource_bridge(name: &str) -> Option<JsValue> {
408 if let Ok(mut interface) = INTERFACE.lock() {
409 if !interface.ready || interface.world_ptr.is_none() {
410 return None;
411 }
412 let world = interface.world_ptr.unwrap();
413 if let Some(bridge) = interface.resources_bridge.get_mut(name) {
414 return Some(bridge.on_read_data(unsafe { world.as_ref().unwrap() }));
415 }
416 }
417 None
418 }
419
420 pub(crate) fn write_resource_bridge(name: &str, value: JsValue) {
421 if let Ok(mut interface) = INTERFACE.lock() {
422 if !interface.ready || interface.world_ptr.is_none() {
423 return;
424 }
425 let world = interface.world_ptr.unwrap();
426 if let Some(bridge) = interface.resources_bridge.get_mut(name) {
427 bridge.on_write_data(unsafe { world.as_mut().unwrap() }, value);
428 }
429 }
430 }
431
432 pub(crate) fn access_resource_bridge(name: &str, value: JsValue) -> JsValue {
433 if let Ok(mut interface) = INTERFACE.lock() {
434 if !interface.ready || interface.world_ptr.is_none() {
435 return JsValue::UNDEFINED;
436 }
437 let world = interface.world_ptr.unwrap();
438 if let Some(bridge) = interface.resources_bridge.get_mut(name) {
439 return bridge.on_access_data(unsafe { world.as_mut().unwrap() }, value);
440 }
441 }
442 JsValue::UNDEFINED
443 }
444
445 pub(crate) fn read_component_bridge(name: &str, entity: Entity) -> Option<JsValue> {
446 if let Ok(mut interface) = INTERFACE.lock() {
447 interface.world_ptr?;
448 let world = interface.world_ptr.unwrap();
449 if let Some(bridge) = interface.components_bridge.get_mut(name) {
450 return Some(bridge.on_read_data(unsafe { world.as_ref().unwrap() }, entity));
451 }
452 }
453 None
454 }
455
456 pub(crate) fn write_component_bridge(name: &str, entity: Entity, value: JsValue) {
457 if let Ok(mut interface) = INTERFACE.lock() {
458 if interface.world_ptr.is_none() {
459 return;
460 }
461 let world = interface.world_ptr.unwrap();
462 if let Some(bridge) = interface.components_bridge.get_mut(name) {
463 bridge.on_write_data(unsafe { world.as_mut().unwrap() }, entity, value);
464 }
465 }
466 }
467
468 pub(crate) fn build_state(name: &str) -> Option<JsValue> {
469 if let Ok(interface) = INTERFACE.lock() {
470 if let Some(factory) = interface.state_factory.get(name) {
471 if let Ok(result) = factory.call0(&JsValue::UNDEFINED) {
472 return Some(result);
473 }
474 }
475 }
476 None
477 }
478
479 pub(crate) fn build_state_scripted(name: &str) -> Option<Box<dyn WebScriptStateScripted>> {
480 if let Ok(mut interface) = INTERFACE.lock() {
481 if let Some(factory) = interface.scriptable_state_factory.get_mut(name) {
482 return Some(factory());
483 }
484 }
485 None
486 }
487
488 pub(crate) fn create_entity(data: JsValue) -> EntityId {
489 if let Ok(mut interface) = INTERFACE.lock() {
490 if interface.index_generator == std::u64::MAX {
491 interface.index_generator = 0;
492 interface.generation += 1;
493 }
494 let index = interface.index_generator;
495 interface.index_generator += 1;
496 let id = EntityId::new(index, interface.generation);
497 interface.entities_to_create.push((data, id));
498 id
499 } else {
500 EntityId::default()
501 }
502 }
503
504 pub(crate) fn destroy_entity(id: EntityId) {
505 if let Ok(mut interface) = INTERFACE.lock() {
506 interface.entities_to_destroy.insert(id);
507 }
508 }
509
510 pub(crate) fn run_systems() {
511 let meta = if let Ok(mut interface) = INTERFACE.lock() {
512 std::mem::replace(&mut interface.systems_cache, None)
513 } else {
514 return;
515 };
516 if let Some(meta) = &meta {
517 for (context, on_run) in meta {
518 drop(on_run.call0(&context));
519 }
520 }
521 if let Ok(mut interface) = INTERFACE.lock() {
522 interface.systems_cache = meta;
523 }
524 }
525
526 pub(crate) fn maintain_entities(world: &mut World) {
527 if let Ok(mut interface) = INTERFACE.lock() {
528 let entities_to_destroy =
529 std::mem::replace(&mut interface.entities_to_destroy, HashSet::new());
530 for id in entities_to_destroy {
531 if let Some(entity) = interface.entities_map.remove(&id) {
532 interface.entities_cache.retain(|e| *e != entity);
533 drop(world.delete_entity(entity));
534 }
535 }
536
537 let entities_to_create = std::mem::replace(&mut interface.entities_to_create, vec![]);
538 for (data, id) in entities_to_create {
539 interface.build_entity(world, id, data);
540 }
541 }
542 }
543
544 fn build_entity(&mut self, world: &mut World, id: EntityId, data: JsValue) {
545 let mut builder = world.create_entity();
546 let mut components = HashMap::new();
547 if !data.is_null() && !data.is_undefined() {
548 if let Some(object) = Object::try_from(&data) {
549 let keys = Object::keys(&object)
550 .iter()
551 .map(|key| key.dyn_ref::<JsString>().map(String::from))
552 .collect::<Vec<_>>();
553 let values = Object::values(&object).iter().collect::<Vec<_>>();
554 for (key, value) in keys.into_iter().zip(values.into_iter()) {
555 if let Some(key) = key {
556 if let Some(factory) = self.component_factory.get(&key) {
557 if let Ok(v) = factory.call0(&JsValue::UNDEFINED) {
558 if let Some(d) = Object::try_from(&v) {
559 let v = if let Some(o) = Object::try_from(&value) {
560 Object::assign(d, o).into()
561 } else {
562 v
563 };
564 components.insert(key, Some(v));
565 }
566 }
567 } else if let Some(scriptable) = self.scriptable_components.get(&key) {
568 if let Ok(v) = scriptable.to_js() {
569 if let Some(d) = Object::try_from(&v) {
570 let v = if let Some(o) = Object::try_from(&value) {
571 Object::assign(d, o).into()
572 } else {
573 v
574 };
575 components.insert(key, Some(v));
576 }
577 }
578 } else if let Some(bridge) = self.components_bridge.get_mut(&key) {
579 builder = bridge.on_add_to_entity(value, builder);
580 components.insert(key, None);
581 }
582 }
583 }
584 }
585 }
586 builder = builder.with(WebScriptComponent::new(id, components));
587 let entity = builder.build();
588 self.entities_map.insert(id, entity);
589 self.entities_cache.push(entity);
590 }
591}