Skip to content

Commit 01de445

Browse files
zicklagjakobhellermann
authored andcommitted
Implement Patches in Value.default()
1 parent 9e451d0 commit 01de445

File tree

4 files changed

+160
-27
lines changed

4 files changed

+160
-27
lines changed

assets/scripts/breakout.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ function run() {
3131
// Query the entity that has the NotABallYet component
3232
for (const item of world.query(NotABallYet)) {
3333
// Insert the ball component on that entity
34-
world.insert(item.entity, Value.default(Ball));
34+
world.insert(item.entity, Value.create(Ball));
3535

3636
// Create a velocity component
37-
let vel = Value.default(Velocity);
38-
// Set the velocity speed
39-
vel[0].x = -200.0;
40-
vel[0].y = 300.0;
37+
let vel = Value.create(Velocity, [
38+
{
39+
x: -200,
40+
y: 200,
41+
},
42+
]);
4143

4244
// Add the velocity to the ball
4345
world.insert(item.entity, vel);

src/runtime/ops/ecs/ecs.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@
184184
},
185185

186186
// Instantiates the default value of a given bevy type
187-
default(type) {
188-
return Value.wrapValueRef(bevyModJsScriptingOpSync("ecs_value_ref_default", type.typeName));
187+
create(type, patch) {
188+
return Value.wrapValueRef(bevyModJsScriptingOpSync("ecs_value_ref_default", type.typeName, patch));
189189
},
190190
}
191191

src/runtime/ops/ecs/value.rs

Lines changed: 150 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::any::TypeId;
22

3-
use anyhow::{format_err, Context};
3+
use anyhow::{bail, format_err, Context};
44
use bevy::prelude::{default, ReflectDefault, World};
55
use bevy_ecs_dynamic::reflect_value_ref::ReflectValueRef;
6-
use bevy_reflect::{ReflectRef, TypeRegistryArc};
6+
use bevy_reflect::{Reflect, ReflectRef, TypeRegistryArc};
77
use bevy_reflect_fns::{PassMode, ReflectArg, ReflectMethods};
88

99
use crate::runtime::OpContext;
@@ -17,22 +17,141 @@ use super::{
1717

1818
macro_rules! try_downcast_leaf_get {
1919
($value:ident for $($ty:ty $(,)?),*) => {
20-
$(if let Some(value) = $value.downcast_ref::<$ty>() {
21-
let value = serde_json::to_value(value)?;
22-
return Ok(value);
23-
})*
20+
(|| {
21+
$(if let Some(value) = $value.downcast_ref::<$ty>() {
22+
let value = serde_json::to_value(value)?;
23+
return Ok(Some(value));
24+
})*
25+
26+
Ok::<_, anyhow::Error>(None)
27+
})()
2428
};
2529
}
2630

2731
macro_rules! try_downcast_leaf_set {
2832
($value:ident <- $new_value:ident for $($ty:ty $(,)?),*) => {
29-
$(if let Some(value) = $value.downcast_mut::<$ty>() {
30-
*value = serde_json::from_value($new_value)?;
31-
return Ok(serde_json::Value::Null);
32-
})*
33+
(|| {
34+
$(if let Some(value) = $value.downcast_mut::<$ty>() {
35+
*value = serde_json::from_value($new_value)?;
36+
return Ok(());
37+
})*
38+
39+
Ok::<(), anyhow::Error>(())
40+
})()
3341
};
3442
}
3543

44+
/// Converts a JSON value to a dynamic reflect struct or list
45+
pub fn patch_reflect_with_json(
46+
value: &mut dyn Reflect,
47+
patch: serde_json::Value,
48+
) -> anyhow::Result<()> {
49+
match patch {
50+
serde_json::Value::Null => {
51+
bail!("Can't patch values with null");
52+
}
53+
patch @ (serde_json::Value::Bool(_)
54+
| serde_json::Value::Number(_)
55+
| serde_json::Value::String(_)) => {
56+
try_downcast_leaf_set!(value <- patch for
57+
u8, u16, u32, u64, u128, usize,
58+
i8, i16, i32, i64, i128, isize,
59+
String, char, bool, f32, f64
60+
)?;
61+
}
62+
serde_json::Value::Array(array) => match value.reflect_mut() {
63+
bevy_reflect::ReflectMut::Struct(_) => todo!(),
64+
bevy_reflect::ReflectMut::List(target) => {
65+
let target_len = target.len();
66+
let patch_len = array.len();
67+
if target_len < patch_len {
68+
bail!("Cannot patch list with {target_len} elements with patch with {patch_len} elements");
69+
}
70+
71+
for (i, patch) in array.into_iter().enumerate() {
72+
let target = target.get_mut(i).unwrap();
73+
patch_reflect_with_json(target, patch)?;
74+
}
75+
}
76+
bevy_reflect::ReflectMut::Tuple(target) => {
77+
let target_len = target.field_len();
78+
let patch_len = array.len();
79+
if target_len != patch_len {
80+
bail!("Cannot patch tuple with {target_len} elements with patch with {patch_len} elements");
81+
}
82+
83+
for (i, patch) in array.into_iter().enumerate() {
84+
let target = target.field_mut(i).unwrap();
85+
patch_reflect_with_json(target, patch)?;
86+
}
87+
}
88+
bevy_reflect::ReflectMut::TupleStruct(target) => {
89+
let target_len = target.field_len();
90+
let patch_len = array.len();
91+
if target_len != patch_len {
92+
bail!("Cannot patch tuple with {target_len} elements with patch with {patch_len} elements");
93+
}
94+
95+
for (i, patch) in array.into_iter().enumerate() {
96+
let target = target.field_mut(i).unwrap();
97+
patch_reflect_with_json(target, patch)?;
98+
}
99+
}
100+
bevy_reflect::ReflectMut::Array(target) => {
101+
let target_len = target.len();
102+
let patch_len = array.len();
103+
if target_len != patch_len {
104+
bail!("Cannot patch array with {target_len} elements with patch with {patch_len} elements");
105+
}
106+
107+
for (i, patch) in array.into_iter().enumerate() {
108+
let target = target.get_mut(i).unwrap();
109+
patch_reflect_with_json(target, patch)?;
110+
}
111+
}
112+
bevy_reflect::ReflectMut::Map(_) => bail!("Cannot patch map with array"),
113+
bevy_reflect::ReflectMut::Value(_) => bail!("Cannot patch primitive value with array"),
114+
},
115+
serde_json::Value::Object(map) => match value.reflect_mut() {
116+
bevy_reflect::ReflectMut::Struct(target) => {
117+
for (key, value) in map {
118+
let field = target.field_mut(&key).ok_or_else(|| {
119+
format_err!("Field `{key}` in patch does not exist on target struct")
120+
})?;
121+
122+
patch_reflect_with_json(field, value)?;
123+
}
124+
}
125+
bevy_reflect::ReflectMut::Map(_) => {
126+
bail!("Patching maps are not yet supported");
127+
// TODO: The code would be something like below, but we have to figure out how to
128+
// insert new values of the right type, or find out that it isn't actually a concern.
129+
130+
// for (key, value) in map {
131+
// let key = Box::new(key) as Box<dyn Reflect>;
132+
// if let Some(field) = target.get_mut(key.as_reflect()) {
133+
// patch_reflect_with_json(field, value)?;
134+
// } else {
135+
// target.insert_boxed(
136+
// key,
137+
// /* How do we know what the expected value type for the map is? */
138+
// );
139+
// }
140+
// }
141+
}
142+
bevy_reflect::ReflectMut::Tuple(_) | bevy_reflect::ReflectMut::TupleStruct(_) => {
143+
bail!("Cannot patch tuple struct with object")
144+
}
145+
bevy_reflect::ReflectMut::List(_) | bevy_reflect::ReflectMut::Array(_) => {
146+
bail!("Cannot patch list or array with object")
147+
}
148+
bevy_reflect::ReflectMut::Value(_) => bail!("Cannot patch primitive value with object"),
149+
},
150+
}
151+
152+
Ok(())
153+
}
154+
36155
pub fn ecs_value_ref_get(
37156
context: OpContext,
38157
world: &mut bevy::prelude::World,
@@ -81,11 +200,15 @@ pub fn ecs_value_ref_get(
81200
{
82201
let value = value_ref.get(world)?;
83202

84-
try_downcast_leaf_get!(value for
203+
let value = try_downcast_leaf_get!(value for
85204
u8, u16, u32, u64, u128, usize,
86205
i8, i16, i32, i64, i128, isize,
87206
String, char, bool, f32, f64
88207
);
208+
209+
if let Some(value) = value? {
210+
return Ok(value);
211+
}
89212
}
90213

91214
// If not a primitive, just return a new value ref
@@ -129,12 +252,14 @@ pub fn ecs_value_ref_set(
129252
u8, u16, u32, u64, u128, usize,
130253
i8, i16, i32, i64, i128, isize,
131254
String, char, bool, f32, f64
132-
);
133-
134-
anyhow::bail!(
135-
"could not set value reference: type `{}` is not a primitive type",
136-
reflect.type_name(),
137-
);
255+
)
256+
.map(|_| serde_json::Value::Null)
257+
.map_err(|e| {
258+
format_err!(
259+
"could not set value reference: type `{typename}` is not a primitive type: {e}",
260+
typename = reflect.type_name(),
261+
)
262+
})
138263
}
139264

140265
pub fn ecs_value_ref_keys(
@@ -206,7 +331,8 @@ pub fn ecs_value_ref_default(
206331
args: serde_json::Value,
207332
) -> anyhow::Result<serde_json::Value> {
208333
// Parse args
209-
let (type_name,): (String,) = serde_json::from_value(args).context("parse args")?;
334+
let (type_name, patch): (String, Option<serde_json::Value>) =
335+
serde_json::from_value(args).context("parse args")?;
210336

211337
let value_refs = context
212338
.op_state
@@ -226,7 +352,12 @@ pub fn ecs_value_ref_default(
226352
let reflect_default = type_registration
227353
.data::<ReflectDefault>()
228354
.ok_or_else(|| format_err!("Type does not have ReflectDefault: {type_name}"))?;
229-
let value = reflect_default.default();
355+
let mut value = reflect_default.default();
356+
357+
// Patch the default value if a patch is provided
358+
if let Some(patch) = patch {
359+
patch_reflect_with_json(value.as_reflect_mut(), patch)?;
360+
}
230361

231362
// Return the value ref to the new object
232363
let value_ref = JsValueRef::new_free(value, value_refs);

types/lib.bevy.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ declare interface BevyScript {
2323
declare type RawValueRef = unknown;
2424

2525
declare interface ValueGlobal {
26-
default<T>(t: BevyType<T>): T;
26+
create<T>(t: BevyType<T>, patch?: any): T;
2727
}
2828

2929
declare let Value: ValueGlobal;

0 commit comments

Comments
 (0)