oxygengine_script_web/
web_api.rs1use 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 #[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 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 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}