oxygengine_script_web/
web_api.rs

1use crate::{component::WebScriptComponent, interface::WebScriptInterface};
2use core::ecs::WorldExt;
3use js_sys::{Array, Function, JsString};
4use serde::{Deserialize, Serialize};
5use wasm_bindgen::{prelude::*, JsCast};
6
7#[wasm_bindgen]
8#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct EntityId {
10    pub(crate) index: u64,
11    pub(crate) generation: u32,
12}
13
14#[wasm_bindgen]
15impl EntityId {
16    #[wasm_bindgen(constructor)]
17    pub fn new_invalid() -> Result<(), JsValue> {
18        Err(JsValue::from_str(
19            "Tried to create EntityId from constructor!",
20        ))
21    }
22
23    pub(crate) fn new(index: u64, generation: u32) -> Self {
24        Self { index, generation }
25    }
26
27    pub fn is_valid(&self) -> bool {
28        self.generation > 0
29    }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub(crate) enum Constrain {
34    None,
35    Entities,
36    Components(String),
37    ExcludeComponents(String),
38}
39
40#[wasm_bindgen]
41#[derive(Debug, Clone)]
42pub struct WebScriptFetch {
43    index: usize,
44    constrains: Vec<Constrain>,
45}
46
47impl WebScriptFetch {
48    pub(crate) fn new(constrains: Vec<Constrain>) -> Self {
49        Self {
50            index: 0,
51            constrains,
52        }
53    }
54}
55
56#[wasm_bindgen]
57impl WebScriptFetch {
58    #[wasm_bindgen(constructor)]
59    pub fn new_invalid() -> Result<(), JsValue> {
60        Err(JsValue::from_str("Tried to create WebScriptFetch from constructor! Use WebScriptApi::fetch() to create valid fetch object."))
61    }
62
63    // TODO: refactor this shit, please.
64    #[allow(clippy::should_implement_trait)]
65    pub fn next(&mut self) -> bool {
66        if let Some(world) = WebScriptInterface::world() {
67            if let Some(world) = unsafe { world.as_mut() } {
68                'main: while let Some(entity) = WebScriptInterface::get_entity(self.index) {
69                    self.index += 1;
70                    if let Some(c) = world.read_storage::<WebScriptComponent>().get(entity) {
71                        for constrain in &self.constrains {
72                            match constrain {
73                                Constrain::Entities => {}
74                                Constrain::Components(name) => {
75                                    if !c.has_component(name) {
76                                        continue 'main;
77                                    }
78                                }
79                                Constrain::ExcludeComponents(name) => {
80                                    if c.has_component(name) {
81                                        continue 'main;
82                                    }
83                                }
84                                Constrain::None => {
85                                    continue 'main;
86                                }
87                            };
88                        }
89                        return true;
90                    }
91                }
92            }
93        }
94        false
95    }
96
97    // TODO: refactor this shit, please.
98    pub fn read(&mut self, item: usize) -> JsValue {
99        if self.index == 0 {
100            return JsValue::UNDEFINED;
101        }
102        if let Some(constrain) = self.constrains.get(item) {
103            let index = self.index - 1;
104            if let Some(entity) = WebScriptInterface::get_entity(index) {
105                if let Some(world) = WebScriptInterface::world() {
106                    if let Some(world) = unsafe { world.as_ref() } {
107                        if let Some(c) = world.read_storage::<WebScriptComponent>().get(entity) {
108                            match constrain {
109                                Constrain::Entities => return c.id().into(),
110                                Constrain::Components(name) => {
111                                    if let Some(component) = c.get_component(name) {
112                                        return component;
113                                    } else if let Some(component) =
114                                        WebScriptInterface::read_component_bridge(name, entity)
115                                    {
116                                        return component;
117                                    }
118                                }
119                                _ => {}
120                            }
121                        }
122                    }
123                }
124            }
125        }
126        JsValue::UNDEFINED
127    }
128
129    // TODO: refactor this shit, please.
130    pub fn write(&mut self, item: usize, value: JsValue) {
131        if self.index == 0 {
132            return;
133        }
134        if let Some(constrain) = self.constrains.get(item) {
135            let index = self.index - 1;
136            if let Some(entity) = WebScriptInterface::get_entity(index) {
137                if let Some(world) = WebScriptInterface::world() {
138                    if let Some(world) = unsafe { world.as_mut() } {
139                        if let Some(c) = world.write_storage::<WebScriptComponent>().get_mut(entity)
140                        {
141                            if let Constrain::Components(name) = constrain {
142                                if !c.set_component(name, value.clone()) {
143                                    WebScriptInterface::write_component_bridge(name, entity, value);
144                                }
145                            }
146                        }
147                    }
148                }
149            }
150        }
151    }
152
153    #[wasm_bindgen(js_name = "readResource")]
154    pub fn read_resource(&self, name: &str) -> JsValue {
155        if let Some(resource) = WebScriptInterface::get_resource(name) {
156            resource
157        } else if let Some(resource) = WebScriptInterface::read_resource_bridge(name) {
158            resource
159        } else {
160            JsValue::UNDEFINED
161        }
162    }
163
164    #[wasm_bindgen(js_name = "writeResource")]
165    pub fn write_resource(&self, name: &str, value: JsValue) {
166        if !WebScriptInterface::set_resource(name, value.clone()) {
167            WebScriptInterface::write_resource_bridge(name, value);
168        }
169    }
170
171    #[wasm_bindgen(js_name = "accessResource")]
172    pub fn access_resource(&self, name: &str, value: JsValue) -> JsValue {
173        if !WebScriptInterface::set_resource(name, value.clone()) {
174            WebScriptInterface::access_resource_bridge(name, value)
175        } else {
176            JsValue::UNDEFINED
177        }
178    }
179}
180
181#[wasm_bindgen]
182#[derive(Debug, Default, Copy, Clone)]
183pub struct WebScriptApi;
184
185#[wasm_bindgen]
186impl WebScriptApi {
187    #[wasm_bindgen(js_name = "start")]
188    pub fn start() {
189        WebScriptInterface::start();
190    }
191
192    #[wasm_bindgen(js_name = "registerResource")]
193    pub fn register_resource(name: &str, resource: JsValue) {
194        WebScriptInterface::register_resource(name, resource);
195    }
196
197    #[wasm_bindgen(js_name = "registerStateFactory")]
198    pub fn register_state_factory(name: &str, factory: Function) {
199        WebScriptInterface::register_state_factory(name, factory);
200    }
201
202    #[wasm_bindgen(js_name = "registerComponentFactory")]
203    pub fn register_component_factory(name: &str, factory: Function) {
204        WebScriptInterface::register_component_factory(name, factory);
205    }
206
207    #[wasm_bindgen(js_name = "registerSystem")]
208    pub fn register_system(name: &str, system: JsValue) {
209        WebScriptInterface::register_system(name, system);
210    }
211
212    #[wasm_bindgen(js_name = "createEntity")]
213    pub fn create_entity(data: JsValue) -> EntityId {
214        WebScriptInterface::create_entity(data)
215    }
216
217    #[wasm_bindgen(js_name = "destroyEntity")]
218    pub fn destroy_entity(id: EntityId) {
219        WebScriptInterface::destroy_entity(id);
220    }
221
222    pub fn fetch(constrains: &Array) -> WebScriptFetch {
223        let constrains = constrains
224            .iter()
225            .map(|c| {
226                if let Some(s) = c.dyn_ref::<JsString>() {
227                    let s = String::from(s);
228                    if s.starts_with('@') {
229                        return Constrain::Entities;
230                    } else if s.starts_with('+') {
231                        return Constrain::Components(s[1..].to_owned());
232                    } else if s.starts_with('-') {
233                        return Constrain::ExcludeComponents(s[1..].to_owned());
234                    }
235                }
236                Constrain::None
237            })
238            .collect::<Vec<_>>();
239        WebScriptFetch::new(constrains)
240    }
241}