1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
use std::collections::HashMap;
use crate::sys::{registerSystem};
use crate::utils::*;
use serde::Serialize;
use wasm_bindgen::{JsCast, prelude::*};
/// Top-level macro to define systems. Usage resembles struct creation syntax.
/// The `js!` macro is available for writing inline javascript, and returns a
/// js_sys::Function object. This macro calls `into` on expressions passed into the
/// fields expecting function, allowing the `js!` macro to be used as a catch-all.
/// Takes the optional fields described in the table below.
///
/// | field | syntax explanation | description |
/// |-------|--------------------|-------------|
/// | schema | A hashmap containing string keys and ComponentProperty values. Recommend the maplit crate | Describes component properties |
/// | init | JsValue created from a js_sys::Function() | Called on initialization |
/// | pause | JsValue created from a js_sys::Function() | Called when the entity or scene pauses |
/// | play | JsValue created from a js_sys::Function() | Called when the entity or scene resumes |
/// | tick | JsValue created from a js_sys::Function(time, timeDelta) | Called on each tick or frame of the scene’s render loop |
/// | properties | name: JsValue, ... | Additional comma-separated functions or data with any valid name may be specified |
///
/// All parameteres are optional, although the order must be exactly as shown.
/// `schema` should be a HashMap with string keys and `AframeProperty`
/// values. The rest are strings containing javascript code. A `js!` macro
/// is provided to allow inline javascript code to be included in the Rust code
/// (See the docs for the `js!` macro for caveats and limitations). Here's an
/// example:
/// ```ignore
/// // Example:
/// let some_system = system_def!
/// (
/// schema: hashmap!
/// {
/// "some_float" => AframeProperty::float("number", None),
/// "some_text" => AframeProperty::string("string", Some(Cow::Borrowed("init")))
/// },
/// init: js!
/// (
/// this.data.some_float = 1.0;
/// this.data.some_text = "I'm a bit of text";
/// ),
/// tick: js!
/// (time, delta =>>
/// this.data.some_float = this.data.some_float + 1.0;
/// ),
/// pause: js!(this.data.some_text = "paused!";),
/// play: js!(this.data.some_text = "playing!";),
/// properties:
/// reset_me: js!(this.data.some_float = 0.0;)
/// );
/// unsafe
/// {
/// some_system.register("system_name");
/// }
/// ```
#[macro_export]
macro_rules! system_def
{
(
$(schema: $schema:expr,)?
$(init: $init:expr,)?
$(pause: $pause:expr,)?
$(play: $play:expr,)?
$(tick: $tick:expr,)?
$(properties: $($fn_name:ident: $func:expr),*)?
) =>
{
$crate::system::SystemReg
{
$(schema: $schema,)?
$(init: $init.into(),)?
$(pause: $pause.into(),)?
$(play: $play.into(),)?
$(tick: $tick.into(),)?
$(properties:
{
let mut props = std::collections::HashMap::new();
$(
props.insert(stringify!($fn_name), $func.into());
)*
props
},)?
..$crate::system::SystemReg::default()
}
}
}
/// System registration definition. All JsValues should be derived from [`js_sys::Function`]
#[derive(Serialize, Clone)]
pub struct SystemReg
{
pub schema: HashMap<&'static str, AframeProperty>,
#[serde(skip)] pub init: JsValue,
#[serde(skip)] pub pause: JsValue,
#[serde(skip)] pub play: JsValue,
#[serde(skip)] pub tick: JsValue,
#[serde(skip)] pub properties: HashMap<&'static str, JsValue>
}
impl Default for SystemReg
{
fn default() -> Self
{
let empty_fn: JsValue = js_sys::Function::default().into();
Self
{
schema: HashMap::new(),
init: empty_fn.clone(),
pause: empty_fn.clone(),
play: empty_fn.clone(),
tick: empty_fn,
properties: HashMap::new()
}
}
}
impl From<&SystemReg> for JsValue
{
fn from(sysr: &SystemReg) -> Self
{
let js_value = JsValue::from_serde(sysr).expect("Failed to convert SystemReg into JsObject");
define_property(js_value.unchecked_ref(), "init", (sysr.init).unchecked_ref());
define_property(js_value.unchecked_ref(), "pause", (sysr.pause).unchecked_ref());
define_property(js_value.unchecked_ref(), "play", (sysr.play).unchecked_ref());
define_property(js_value.unchecked_ref(), "tick", (sysr.tick).unchecked_ref());
for (k, v) in sysr.properties.iter()
{
define_property(js_value.unchecked_ref(), k, v.unchecked_ref());
}
js_value
}
}
impl SystemReg
{
/// Register a system in aframe. Warning: Aframe must be initialized before this is called.
pub unsafe fn register(self, name: &str)
{
registerSystem(name, (&self).into());
}
}