Skip to content

Commit d1f37a8

Browse files
committed
register option lua types for ALL THE TYPES BABY
1 parent 1f329aa commit d1f37a8

File tree

22 files changed

+1093
-274
lines changed

22 files changed

+1093
-274
lines changed

assets/scripts/bevy_api.lua

Lines changed: 117 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -7,122 +7,135 @@ function table_to_string(t)
77
end
88

99

10-
-- the api provides us with 3 globals
11-
print(entity)
12-
print(script)
13-
print(world)
14-
15-
-- we first retrieve ID's for our component and resource by their short name (long name/full path also work)
16-
local my_component_type = world:get_type_by_name("MyComponent")
17-
18-
-- then ask the world to give us a reference to `MyComponent` on the entity we just spawned
19-
-- resources work the same way, but we use `get_resource` instead of `get_component`
20-
-- the comp object is resolved to a `bevy_script_api::script_ref::ReflectValue` which implements UserData.
21-
-- we can use a custom proxy instead (by implementing LuaProxyable), but this is the simplest way to get started.
22-
local comp = world:get_component(entity, my_component_type)
23-
print("Before script: ", comp)
24-
25-
print("============")
26-
27-
-- the index metamethod on ReflectValue's uses bevy's reflection mechanism on top of some custom sub-reflection logic to
28-
-- allow reflecting inside Options, Vectors etc.
29-
-- when we index into ReflectValue's we either get back a custom proxy or another ReflectValue
30-
31-
-- the LuaBevyAPIProvider provides us custom proxies for many bevy types as well as std types.
32-
-- all of these implementations can be overridden via the bevy TypeRegistry
33-
comp.usize = 2
34-
print("comp.usize after assigning to 2: ", comp.usize)
35-
36-
-- vec's and matrices have custom __index and __newindex overrides
37-
print("comp.vec2 before: ", comp.vec2)
38-
comp.vec2[1] = 69
39-
print("comp.vec2 after: ", comp.vec2)
40-
41-
-- Option's get converted to nil or the value inside
42-
print("comp.option_vec3 before: ", comp.option_vec3)
43-
comp.option_vec3 = Vec3.new(2,1,3)
44-
print("comp.option_vec3 after: ", comp.option_vec3)
45-
46-
-- reflection via index is indexed starting at 1, unlike in Rust to match Lua's indexing
47-
print("comp.option_vec3[1] before: ", comp.option_vec3[1])
48-
comp.option_vec3[1] = 5
49-
print("comp.option_vec3[1] after: ", comp.option_vec3[1])
50-
51-
print("============")
52-
53-
-- Vec<T> references get converted to a custom proxy `LuaVec<T>` which is
54-
-- also assignable via lua tables
55-
56-
print("comp.vec_of_option_bools before: ", table_to_string(comp.vec_of_option_bools))
57-
comp.vec_of_option_bools = {true,false,true}
58-
print("comp.vec_of_option_bools after assignment: ", table_to_string(comp.vec_of_option_bools))
59-
60-
print("comp.vec_of_option_bools[1] before: ", comp.vec_of_option_bools[1])
61-
comp.vec_of_option_bools[1] = false
62-
print("comp.vec_of_option_bools[1] after: ", comp.vec_of_option_bools[1])
63-
64-
-- there are some additional methods available on LuaVec proxies imitating the Vec<T> api
65-
print("comp.vec_of_option_bools before insert: ", table_to_string(comp.vec_of_option_bools))
66-
comp.vec_of_option_bools:insert(1,nil)
67-
print("comp.vec_of_option_bools after insert: ", table_to_string(comp.vec_of_option_bools))
68-
69-
print("comp.vec_of_option_bools before push: ", table_to_string(comp.vec_of_option_bools))
70-
comp.vec_of_option_bools:push(false)
71-
print("comp.vec_of_option_bools after push: ", table_to_string(comp.vec_of_option_bools))
72-
73-
print("comp.vec_of_option_bools len after push: ", #comp.vec_of_option_bools)
74-
75-
print("comp.vec_of_option_bools before pop: ", table_to_string(comp.vec_of_option_bools))
76-
print(comp.vec_of_option_bools:pop())
77-
print("comp.vec_of_option_bools after pop: ", table_to_string(comp.vec_of_option_bools))
78-
79-
print("the pairs inside comp.vec_of_option_bools: ")
80-
for k,v in pairs(comp.vec_of_option_bools) do
81-
print(string.format(" - %s:%s",k,v))
82-
end
10+
function on_event()
8311

84-
comp.vec_of_option_bools:clear()
85-
print("comp.vec_of_option_bools after clear: ", table_to_string(comp.vec_of_option_bools))
12+
print(entity)
13+
print(script)
14+
print(world)
8615

87-
print("comp.vec_of_option_bools len after clear: ", #comp.vec_of_option_bools)
88-
print("============")
8916

90-
print("comp.option_vec_of_bools before: ", table_to_string(comp.option_vec_of_bools))
91-
print(comp.option_vec_of_bools:pop())
92-
print("comp.option_vec_of_bools after pop: ", table_to_string(comp.option_vec_of_bools))
17+
local my_component_type = world:get_type_by_name("MyComponent")
9318

19+
local comp = world:get_component(entity, my_component_type)
20+
print("Before script: ", comp)
9421

95-
print("comp.option_vec_of_bools len after pop: ", #comp.option_vec_of_bools)
9622

97-
print("the pairs inside comp.option_vec_of_bools: ")
98-
for k,v in pairs(comp.option_vec_of_bools) do
99-
print(string.format(" - %s:%s",k,v))
100-
end
23+
print(comp.option_usize)
24+
comp.option_usize = 69
25+
print(comp.option_usize)
26+
comp.option_usize = nil
27+
print(comp.option_usize)
28+
world:exit()
29+
30+
print(comp.vec_of_usize)
31+
print(comp.vec_of_usize[2])
32+
comp.vec_of_usize[2] = 69
33+
print(comp.vec_of_usize[2])
34+
world:exit()
35+
36+
print("============")
37+
38+
-- the index metamethod on ReflectValue's uses bevy's reflection mechanism on top of some custom sub-reflection logic to
39+
-- allow reflecting inside Options, Vectors etc.
40+
-- when we index into ReflectValue's we either get back a custom proxy or another ReflectValue
41+
42+
-- the LuaBevyAPIProvider provides us custom proxies for many bevy types as well as std types.
43+
-- all of these implementations can be overridden via the bevy TypeRegistry
44+
print("Hello:", comp.usize._1)
45+
comp.usize[1] = 2
46+
print("comp.usize after assigning to 2: ", comp.usize._1)
47+
48+
-- vec's and matrices have custom __index and __newindex overrides
49+
print("comp.vec2 before: ", comp.vec2)
50+
comp.vec2[1] = 69
51+
print("comp.vec2 after: ", comp.vec2)
52+
53+
-- Option's get converted to nil or the value inside
54+
print("comp.option_vec3 before: ", comp.option_vec3)
55+
comp.option_vec3 = Vec3.new(2,1,3)
56+
print("comp.option_vec3 after: ", comp.option_vec3)
57+
58+
-- reflection via index is indexed starting at 1, unlike in Rust to match Lua's indexing
59+
print("comp.option_vec3[1] before: ", comp.option_vec3[1])
60+
comp.option_vec3[1] = 5
61+
print("comp.option_vec3[1] after: ", comp.option_vec3[1])
62+
63+
print("============")
64+
65+
-- Vec<T> references get converted to a custom proxy `LuaVec<T>` which is
66+
-- also assignable via lua tables
67+
68+
print("comp.vec_of_option_bools before: ", table_to_string(comp.vec_of_option_bools))
69+
comp.vec_of_option_bools = {true,false,true}
70+
print("comp.vec_of_option_bools after assignment: ", table_to_string(comp.vec_of_option_bools))
71+
72+
print("comp.vec_of_option_bools[1] before: ", comp.vec_of_option_bools[1])
73+
comp.vec_of_option_bools[1] = false
74+
print("comp.vec_of_option_bools[1] after: ", comp.vec_of_option_bools[1])
75+
76+
-- there are some additional methods available on LuaVec proxies imitating the Vec<T> api
77+
print("comp.vec_of_option_bools before insert: ", table_to_string(comp.vec_of_option_bools))
78+
comp.vec_of_option_bools:insert(1,nil)
79+
print("comp.vec_of_option_bools after insert: ", table_to_string(comp.vec_of_option_bools))
80+
81+
print("comp.vec_of_option_bools before push: ", table_to_string(comp.vec_of_option_bools))
82+
comp.vec_of_option_bools:push(false)
83+
print("comp.vec_of_option_bools after push: ", table_to_string(comp.vec_of_option_bools))
84+
85+
print("comp.vec_of_option_bools len after push: ", #comp.vec_of_option_bools)
86+
87+
print("comp.vec_of_option_bools before pop: ", table_to_string(comp.vec_of_option_bools))
88+
print(comp.vec_of_option_bools:pop())
89+
print("comp.vec_of_option_bools after pop: ", table_to_string(comp.vec_of_option_bools))
90+
91+
print("the pairs inside comp.vec_of_option_bools: ")
92+
for k,v in pairs(comp.vec_of_option_bools) do
93+
print(string.format(" - %s:%s",k,v))
94+
end
95+
96+
comp.vec_of_option_bools:clear()
97+
print("comp.vec_of_option_bools after clear: ", table_to_string(comp.vec_of_option_bools))
98+
99+
print("comp.vec_of_option_bools len after clear: ", #comp.vec_of_option_bools)
100+
print("============")
101+
102+
print("comp.option_vec_of_bools before: ", table_to_string(comp.option_vec_of_bools))
103+
print(comp.option_vec_of_bools:pop())
104+
print("comp.option_vec_of_bools after pop: ", table_to_string(comp.option_vec_of_bools))
105+
106+
107+
print("comp.option_vec_of_bools len after pop: ", #comp.option_vec_of_bools)
108+
109+
print("the pairs inside comp.option_vec_of_bools: ")
110+
for k,v in pairs(comp.option_vec_of_bools) do
111+
print(string.format(" - %s:%s",k,v))
112+
end
101113

102-
print("============")
114+
print("============")
103115

104-
local complex_vec_op = Vec3.new(0,1,0):any_orthonormal_vector() + comp.mat3.x_axis
105-
print("(0,1,0).any_orthonormal_vector() + mat3.x_axis is: ", complex_vec_op)
116+
local complex_vec_op = Vec3.new(0,1,0):any_orthonormal_vector() + comp.mat3.x_axis
117+
print("(0,1,0).any_orthonormal_vector() + mat3.x_axis is: ", complex_vec_op)
106118

107-
local new_mat3 = Mat3.from_cols(Vec3.new(1,0,0),Vec3.new(0,1,0),Vec3.new(0,0,-1))
108-
print("new_mat3 is:", new_mat3)
119+
local new_mat3 = Mat3.from_cols(Vec3.new(1,0,0),Vec3.new(0,1,0),Vec3.new(0,0,-1))
120+
print("new_mat3 is:", new_mat3)
109121

110-
comp.vec2 = comp.vec2 + comp.vec2
111-
comp.usize = comp.vec2:min_element()
112-
comp.f32 = comp.f32 + comp.f32 + comp.vec2:min_element()
113-
comp.vec2 = Vec2.new(2,1)
114-
comp.quat = Quat.from_xyzw(3,2,1,4)
115-
comp.mat3.x_axis = Vec3.new(69,69,69)
122+
comp.vec2 = comp.vec2 + comp.vec2
123+
comp.usize = comp.vec2:min_element()
124+
comp.f32 = comp.f32 + comp.f32 + comp.vec2:min_element()
125+
comp.vec2 = Vec2.new(2,1)
126+
comp.quat = Quat.from_xyzw(3,2,1,4)
127+
comp.mat3.x_axis = Vec3.new(69,69,69)
116128

117-
print("============")
129+
print("============")
118130

119-
-- this is an example of something impossible to achieve with plain bevy reflection under the hood
120-
comp.mat3[1][1] = 42
131+
-- this is an example of something impossible to achieve with plain bevy reflection under the hood
132+
comp.mat3[1][1] = 42
121133

122-
-- now let's retrieve these again to see if we actually changed their values permanently
123-
comp = world:get_component(entity,my_component_type)
134+
-- now let's retrieve these again to see if we actually changed their values permanently
135+
comp = world:get_component(entity,my_component_type)
124136

125-
print("After script:")
126-
print(comp)
137+
print("After script:")
138+
print(comp)
127139

128-
world:exit()
140+
world:exit()
141+
end

crates/bevy_api_gen/templates/header.tera

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use super::{{crate}}::*;
1212
use bevy_mod_scripting_core::{AddContextInitializer, StoreDocumentation, bindings::ReflectReference};
1313

1414
{% if args.self_is_bms_lua %}
15-
use crate::{bindings::proxy::{LuaReflectRefProxy,LuaReflectRefMutProxy,LuaReflectValProxy,LuaValProxy,LuaIdentityProxy},RegisterLua, tealr::mlu::mlua::IntoLua};
15+
use crate::{bindings::proxy::{LuaReflectRefProxy,LuaReflectRefMutProxy,LuaReflectValProxy,LuaValProxy,LuaIdentityProxy},type_data::RegisterLua, tealr::mlu::mlua::IntoLua};
1616
{% else %}
17-
use bevy_mod_scripting::{lua::bindings::proxy::{LuaReflectRefProxy,LuaReflectRefMutProxy,LuaReflectValProxy,LuaValProxy,LuaIdentityProxy}, RegisterLua, tealr::mlu::mlua::IntoLua};
17+
use bevy_mod_scripting::{lua::bindings::proxy::{LuaReflectRefProxy,LuaReflectRefMutProxy,LuaReflectValProxy,LuaValProxy,LuaIdentityProxy}, type_data::RegisterLua, tealr::mlu::mlua::IntoLua};
1818
{% endif %}

crates/bevy_mod_scripting_core/src/bindings/allocator.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bevy::ecs::system::Resource;
22
use bevy::reflect::{PartialReflect, Reflect};
3-
use std::any::TypeId;
3+
use std::any::{Any, TypeId};
44
use std::cell::UnsafeCell;
55
use std::collections::HashMap;
66
use std::fmt::{Display, Formatter};
@@ -45,18 +45,46 @@ pub struct ReflectAllocator {
4545
}
4646

4747
impl ReflectAllocator {
48-
/// Allocates a new [`Reflect`] value and returns an [`AllocationId`] which can be used to access it later
48+
/// Allocates a new [`Reflect`] value and returns an [`AllocationId`] which can be used to access it later.
49+
/// Use [`Self::allocate_boxed`] if you already have an allocated boxed value.
4950
pub fn allocate<T: PartialReflect>(
5051
&mut self,
5152
value: T,
5253
) -> (ReflectAllocationId, ReflectAllocation) {
54+
let type_id = value.get_represented_type_info().map(|i| i.type_id());
55+
5356
let id = ReflectAllocationId(self.allocations.len());
5457
let value = ReflectAllocation::new(Arc::new(UnsafeCell::new(value)));
5558
self.allocations.insert(id, value.clone());
56-
self.types.insert(id, TypeId::of::<T>());
59+
if let Some(type_id) = type_id {
60+
self.types.insert(id, type_id);
61+
}
5762
(id, value)
5863
}
5964

65+
/// Moves the given boxed [`PartialReflect`] value into the allocator, returning an [`AllocationId`] which can be used to access it later
66+
pub fn allocate_boxed(
67+
&mut self,
68+
existing: Box<dyn PartialReflect>,
69+
) -> (ReflectAllocationId, ReflectAllocation) {
70+
let type_id = existing.get_represented_type_info().map(|i| i.type_id());
71+
let id = ReflectAllocationId(self.allocations.len());
72+
73+
let raw_ptr = Box::into_raw(existing);
74+
// Safety:
75+
// - we are the only ones to have access to this value since we have the Box
76+
// - UnsafeCell is repr(transparent), meaning we can safely transmute between it and the trait object
77+
// TODO: I don't think we can use this, because from_raw has a pre-condition that requires the pointer to have been an arc before
78+
let arc: Arc<UnsafeCell<dyn PartialReflect>> =
79+
unsafe { Arc::from_raw(raw_ptr as *const _) };
80+
let allocation = ReflectAllocation::new(arc);
81+
self.allocations.insert(id, allocation.clone());
82+
if let Some(type_id) = type_id {
83+
self.types.insert(id, type_id);
84+
}
85+
(id, allocation)
86+
}
87+
6088
pub fn get(&self, id: ReflectAllocationId) -> Option<ReflectAllocation> {
6189
self.allocations.get(&id).cloned()
6290
}

crates/bevy_mod_scripting_core/src/bindings/reference.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,17 @@ pub struct ReflectReference {
5959

6060
impl ReflectReference {
6161

62+
/// Prints the reference using the world to resolve type names.
6263
pub fn print_with_world(&self, world: &WorldAccessGuard) -> String {
63-
let base = world.with_resource(|_, type_registry: Mut<AppTypeRegistry>| {
64-
self.base.display_with_type_name(&type_registry.read())
65-
});
64+
world.with_resource(|_, type_registry: Mut<AppTypeRegistry>| {
65+
let type_registry = type_registry.read();
66+
self.print_with_type_registry(&type_registry)
67+
})
68+
}
6669

70+
/// Prints the reference using the type registry to resolve type names. Prefer this over [`Self::print_with_world`] if you have a type registry available.
71+
pub fn print_with_type_registry(&self, type_registry: &TypeRegistry) -> String {
72+
let base = self.base.display_with_type_name(type_registry);
6773
format!("Reference(base: {}, path: {:?})", base, self.reflect_path)
6874
}
6975

@@ -90,7 +96,7 @@ impl ReflectReference {
9096
})
9197
}
9298

93-
pub fn new_allocated<T: PartialReflect>(
99+
pub fn new_allocated<T: Reflect>(
94100
value: T,
95101
allocator: &mut ReflectAllocator,
96102
) -> ReflectReference {
@@ -104,6 +110,21 @@ impl ReflectReference {
104110
}
105111
}
106112

113+
pub fn new_allocated_boxed(
114+
value: Box<dyn PartialReflect>,
115+
allocator: &mut ReflectAllocator,
116+
) -> ReflectReference {
117+
let type_id = value.get_represented_type_info().map(|i| i.type_id()).expect("Expected type info for boxed value");
118+
let (id, _) = allocator.allocate_boxed(value);
119+
ReflectReference {
120+
base: ReflectBaseType {
121+
type_id,
122+
base_id: ReflectBase::Owned(id),
123+
},
124+
reflect_path: Vec::default(),
125+
}
126+
}
127+
107128
/// Indexes into the reflect path inside this reference.
108129
/// You can use [`Self::reflect`] and [`Self::reflect_mut`] to get the actual value.
109130
pub fn index_path<T: Into<ReflectionPathElem>>(&mut self, index: T) {

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,21 @@ impl<'w> WorldAccessGuard<'w> {
472472
out
473473
}
474474

475+
/// Convenience to get commonly used resources at the same time. Internally identical to [`Self::with_resource`]
476+
pub fn with_allocator_and_type_registry<
477+
O,
478+
F: FnOnce(&Self, Mut<AppTypeRegistry>, Mut<ReflectAllocator>) -> O,
479+
>(
480+
&self,
481+
f: F,
482+
) -> O {
483+
self.with_resource(|world, registry: Mut<AppTypeRegistry>| {
484+
world.with_resource(|world, allocator: Mut<ReflectAllocator>| {
485+
f(world, registry, allocator)
486+
})
487+
})
488+
}
489+
475490
/// Call a function on a type which can be proxied, first by unproxying the input with world access,
476491
/// then calling the function and finally proxying the output with the allocator.
477492
pub fn proxy_call<'i, O: Proxy, T: Unproxy, F: Fn(T::Output<'_>) -> O::Input<'i>>(

crates/bevy_mod_scripting_core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub mod docs;
2626
pub mod error;
2727
pub mod event;
2828
pub mod handler;
29+
pub mod reflection_extensions;
2930
pub mod runtime;
3031
pub mod script;
3132
pub mod systems;

0 commit comments

Comments
 (0)