Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod transpile;
use asset::JsScriptLoader;
use bevy::{asset::AssetStage, prelude::*, utils::HashSet};

pub use bevy_reflect_fns;
pub use asset::JsScript;
pub use bevy_ecs_dynamic;
pub use runtime::{
Expand Down
44 changes: 44 additions & 0 deletions src/runtime/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ fn op_bevy_mod_js_scripting(
args: serde_json::Value,
) -> Result<serde_json::Value, AnyError> {
with_state(state, |state, custom_op_state| {
let args = convert_safe_ints(args);
let script_info = state.borrow::<ScriptInfo>();
let ops = state.borrow::<Ops>();
let op_names = state.borrow::<OpNames>();
Expand Down Expand Up @@ -323,3 +324,46 @@ fn with_state<T: 'static, R, F: FnOnce(&mut OpState, &mut T) -> R>(state: &mut O

r
}

/// Takes a [`serde_json::Value`] and converts all floating point number types that are safe
/// integers, to integers.
///
/// This is important for deserializing numbers to integers, because of the way `serde_json` handles
/// them.
///
/// For example, `serde_json` will not deserialize `1.0` to a `u32` without an error, but it will
/// deserialize `1`. `serde_v8` seems to retun numbers with a decimal point, even when they are
/// valid integers, so this function makes the conversion of safe integers back to integers without
/// a decimal point.
fn convert_safe_ints(value: serde_json::Value) -> serde_json::Value {
match value {
serde_json::Value::Number(n) => {
let max_safe_int = (2u64.pow(53) - 1) as f64;

serde_json::Value::Number(if let Some(f) = n.as_f64() {
if f.abs() <= max_safe_int && f.fract() == 0.0 {
if f == 0.0 {
serde_json::Number::from(0u64)
} else if f.is_sign_negative() {
serde_json::Number::from(f as i64)
} else {
serde_json::Number::from(f as u64)
}
} else {
n
}
} else {
n
})
}
serde_json::Value::Array(arr) => {
serde_json::Value::Array(arr.into_iter().map(|x| convert_safe_ints(x)).collect())
}
serde_json::Value::Object(obj) => serde_json::Value::Object(
obj.into_iter()
.map(|(k, v)| (k, convert_safe_ints(v)))
.collect(),
),
other => other,
}
}
6 changes: 4 additions & 2 deletions src/runtime/ops/ecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ use crate::runtime::{JsRuntimeOp, OpMap};

use self::types::{JsReflectFunctions, JsValueRefs};

mod component;
mod info;
mod query;
mod resource;
pub mod types;
mod value;
mod world;

pub fn insert_ecs_ops(ops: &mut OpMap) {
ops.insert("ecs_js", Box::new(EcsJs));
Expand Down Expand Up @@ -41,9 +41,11 @@ pub fn insert_ecs_ops(ops: &mut OpMap) {
);
ops.insert("ecs_value_ref_patch", Box::new(value::ecs_value_ref_patch));
ops.insert("ecs_value_ref_cleanup", Box::new(value::EcsValueRefCleanup));
ops.insert("ecs_entity_spawn", Box::new(world::ecs_entity_spawn));
ops.insert("ecs_value_ref_cleanup", Box::new(value::EcsValueRefCleanup));
ops.insert(
"ecs_component_insert",
Box::new(component::ecs_component_insert),
Box::new(world::ecs_component_insert),
);
}

Expand Down
21 changes: 17 additions & 4 deletions src/runtime/ops/ecs/ecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
default:
const collected = collectedQuery(target);
const prop = collected[propName];
return prop.bind ? prop.bind(collected) : prop;
return prop && prop.bind ? prop.bind(collected) : prop;
}
}
})
Expand All @@ -97,6 +97,10 @@
Value.unwrapValueRef(component)
);
}

spawn() {
return Value.wrapValueRef(bevyModJsScriptingOpSync("ecs_entity_spawn"));
}
}

const VALUE_REF_GET_INNER = Symbol("value_ref_get_inner");
Expand All @@ -106,7 +110,16 @@
unwrapValueRef(valueRefProxy) {
if (valueRefProxy === null || valueRefProxy === undefined) return valueRefProxy;
const inner = valueRefProxy[VALUE_REF_GET_INNER]
return inner ? inner : valueRefProxy;
if (inner) {
return inner;
} else {
if (typeof valueRefProxy == 'object') {
for (const key of Reflect.ownKeys(valueRefProxy)) {
valueRefProxy[key] = Value.unwrapValueRef(valueRefProxy[key]);
}
}
return valueRefProxy;
}
},

// keep primitives, null and undefined as is, otherwise wraps the object
Expand Down Expand Up @@ -160,7 +173,7 @@
"ecs_value_ref_set",
target.valueRef,
p,
value
Value.unwrapValueRef(value)
);
},
apply: (target, thisArg, args) => {
Expand All @@ -179,7 +192,7 @@

// Instantiates the default value of a given bevy type
create(type, patch) {
return Value.wrapValueRef(bevyModJsScriptingOpSync("ecs_value_ref_default", type.typeName, patch));
return Value.wrapValueRef(bevyModJsScriptingOpSync("ecs_value_ref_default", type.typeName, Value.unwrapValueRef(patch)));
},

patch(value, patch) {
Expand Down
Loading