Skip to content

Commit cfa9a01

Browse files
WIP
1 parent 7f1c7f5 commit cfa9a01

File tree

23 files changed

+427
-277
lines changed

23 files changed

+427
-277
lines changed

assets/scripts/bevy_api.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function on_event()
2020

2121

2222
local my_component_type = world:get_type_by_name("MyComponent")
23-
print("MyComponent type: ", my_component_type:print_value())
23+
print("MyComponent type: ", my_component_type:short_name())
2424

2525
local comp = world:get_component(entity, my_component_type)
2626
print("Before script: ", comp:print_value())

crates/bevy_mod_scripting_core/src/bindings/function/from.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
error::InteropError,
44
prelude::ScriptValue,
55
};
6-
use bevy::reflect::FromReflect;
6+
use bevy::reflect::{FromReflect, GetTypeRegistration};
77
use std::{
88
any::TypeId,
99
ffi::OsString,
@@ -141,6 +141,16 @@ impl FromScript for ReflectReference {
141141
/// You can also use this to return values from a script function to be allocated directly as a [`ScriptValue::Reference`].
142142
pub struct Val<T>(pub T);
143143

144+
impl<T> Val<T> {
145+
pub fn new(value: T) -> Self {
146+
Val(value)
147+
}
148+
149+
pub fn into_inner(self) -> T {
150+
self.0
151+
}
152+
}
153+
144154
impl<T> Deref for Val<T> {
145155
type Target = T;
146156

@@ -165,7 +175,7 @@ impl<T: FromReflect> FromScript for Val<T> {
165175
T::from_reflect(r).ok_or_else(|| {
166176
InteropError::failed_from_reflect(
167177
Some(TypeId::of::<T>()),
168-
"from reflect failed to produce output when converting to Val<T>"
178+
format!("from reflect failed to produce output when converting to Val<T> from: {r:?}")
169179
.to_owned(),
170180
)
171181
})

crates/bevy_mod_scripting_core/src/bindings/function/into.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::{
2+
borrow::Cow,
23
ffi::OsString,
34
path::{Path, PathBuf},
45
};
56

6-
use bevy::reflect::{PartialReflect, ReflectRef};
7+
use bevy::reflect::{GetTypeRegistration, PartialReflect, ReflectRef};
78

89
use crate::{
910
bindings::{ReflectReference, WorldGuard},
@@ -75,6 +76,12 @@ impl_into_stringlike!(
7576
]
7677
);
7778

79+
impl IntoScript for &'static str {
80+
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
81+
Ok(ScriptValue::String(Cow::Borrowed(self)))
82+
}
83+
}
84+
7885
impl IntoScript for ReflectReference {
7986
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
8087
Ok(ScriptValue::Reference(self))

crates/bevy_mod_scripting_core/src/bindings/function/mod.rs

Lines changed: 0 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -33,122 +33,8 @@ pub trait CallScriptFunction {
3333
args: I,
3434
world: WorldGuard,
3535
) -> Result<ScriptValue, InteropError>;
36-
37-
// fn with_call<O, F: FnOnce(Return) -> O, I: IntoIterator<Item = ScriptValue>>(
38-
// &self,
39-
// args: I,
40-
// world: Arc<WorldAccessGuard>,
41-
// f: F,
42-
// ) -> Result<O, InteropError>;
43-
44-
// fn dynamic_call<I: IntoIterator<Item = ScriptValue>>(
45-
// &self,
46-
// args: I,
47-
// world: Arc<WorldAccessGuard>,
48-
// ) -> Result<ScriptValue, InteropError>;
4936
}
5037

51-
// impl CallAsScriptFunction for DynamicFunction<'_> {
52-
// fn with_call<O, F: FnOnce(Return) -> O, I: IntoIterator<Item = ScriptValue>>(
53-
// &self,
54-
// args: I,
55-
// world: Arc<WorldAccessGuard>,
56-
// f: F,
57-
// ) -> Result<O, InteropError> {
58-
// let info = self.info().args();
59-
60-
// let mut arg_iter = args.into_iter().peekable();
61-
62-
// // we also want to add the world arg if it's required as the first arg but not present
63-
// // let (mut args_list, mut accesses) = if self.first_arg_is_world()
64-
// // && !arg_iter
65-
// // .peek()
66-
// // .map(|a| a == &ScriptValue::World)
67-
// // .unwrap_or(false)
68-
// // {
69-
// // std::iter::once(ScriptValue::World)
70-
// // .chain(arg_iter)
71-
// // .a(self.info(), world.clone())?
72-
// // } else {
73-
// // arg_iter.into_args_list_with_access(self.info(), world.clone())?
74-
// // };
75-
76-
// // let mut final_args_list = ArgList::default();
77-
78-
// // // we sometimes want to use the boxed value in the arg instead of allocating and refing to it.
79-
// // // for this reason let's be lenient in calling functions. Allow passing owned values as refs
80-
// // for (arg, info) in args_list.iter_mut().zip(info.iter()) {
81-
// // let next_arg = match (arg, info.ownership()) {
82-
// // (ArgValue::Owned(r), Ownership::Ref) => {
83-
// // ArgValue::Ref((r as &Box<dyn PartialReflect>).as_ref())
84-
// // }
85-
// // (ArgValue::Owned(r), Ownership::Mut) => ArgValue::Mut(r.as_mut()),
86-
// // (v, _) => {
87-
// // // muahaha, shouldn't allocate due to ZST
88-
// // let a = std::mem::replace(v, ArgValue::Owned(Box::new(())));
89-
// // a
90-
// // }
91-
// // };
92-
// // final_args_list = final_args_list.push_arg(next_arg);
93-
// // }
94-
95-
// // bevy::log::trace!(
96-
// // "Calling function: {:?} with args: {:?}",
97-
// // self.info().name(),
98-
// // final_args_list
99-
// // );
100-
101-
// // let return_val = match self.call(final_args_list) {
102-
// // Ok(return_val) => return_val,
103-
// // Err(e) => {
104-
// // // Safety: we have not generated any unsafe aliases
105-
// // // - we are releasing only the access we have claimed
106-
// // accesses.drain(..).for_each(|(id, _)| {
107-
// // unsafe { world.release_access(id) };
108-
// // });
109-
110-
// // return Err(InteropError::function_call_error(e));
111-
// // }
112-
// // };
113-
114-
// bevy::log::trace!(
115-
// "Function: {:?} returned: {:?}",
116-
// self.info().name(),
117-
// return_val
118-
// );
119-
120-
// let out = f(return_val);
121-
// // Safety: we have not generated any unsafe aliases
122-
// // - we are releasing only the access we have claimed
123-
// accesses.drain(..).for_each(|(id, _)| {
124-
// unsafe { world.release_access(id) };
125-
// });
126-
127-
// Ok(out)
128-
// }
129-
130-
// fn dynamic_call<I: IntoIterator<Item = ScriptValue>>(
131-
// &self,
132-
// args: I,
133-
// world: Arc<WorldAccessGuard>,
134-
// ) -> Result<ScriptValue, InteropError> {
135-
// bevy::log::debug!("Dynamic call to function: {:?}", self.info().name());
136-
// self.with_call(args, world.clone(), |r| {
137-
// match r.try_into_or_boxed::<ScriptValue>() {
138-
// Ok(script_val) => Ok(script_val),
139-
// Err(e) => {
140-
// let allocator = world.allocator();
141-
// let mut allocator = allocator.write();
142-
143-
// Ok(ReflectReference::new_allocated_boxed(e, &mut allocator).into())
144-
// }
145-
// }
146-
// })
147-
// .flatten_interop_error()
148-
// .map_err(|e| InteropError::function_interop_error(self.info(), None, e))
149-
// }
150-
// }
151-
15238
impl CallScriptFunction for DynamicFunction<'_> {
15339
fn call_script_function<I: IntoIterator<Item = ScriptValue>>(
15440
&self,
@@ -207,23 +93,3 @@ impl DynamicFunctionExt for DynamicFunction<'_> {
20793
})
20894
}
20995
}
210-
211-
// pub trait ArgValueExt {
212-
// fn is_type_id(&self, type_id: std::any::TypeId) -> bool;
213-
// }
214-
215-
// impl ArgValueExt for ArgValue<'_> {
216-
// fn is_type_id(&self, type_id: std::any::TypeId) -> bool {
217-
// match self {
218-
// ArgValue::Owned(r) => r
219-
// .get_represented_type_info()
220-
// .map_or(false, |t| t.type_id() == type_id),
221-
// ArgValue::Ref(r) => r
222-
// .get_represented_type_info()
223-
// .map_or(false, |t| t.type_id() == type_id),
224-
// ArgValue::Mut(r) => r
225-
// .get_represented_type_info()
226-
// .map_or(false, |t| t.type_id() == type_id),
227-
// }
228-
// }
229-
// }

crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
use bevy::{
2-
prelude::{AppFunctionRegistry, IntoFunction, World},
3-
reflect::{func::DynamicFunction, PartialReflect},
4-
};
5-
1+
use super::{from::FromScript, into::IntoScript};
62
use crate::{
3+
bindings::{
4+
function::from::{Mut, Ref, Val},
5+
ReflectReference,
6+
},
77
error::InteropError,
88
prelude::{ScriptValue, WorldCallbackAccess},
99
};
10-
11-
use super::{from::FromScript, into::IntoScript};
10+
use bevy::{
11+
prelude::{AppFunctionRegistry, IntoFunction, World},
12+
reflect::{
13+
func::{DynamicFunction, FunctionInfo},
14+
GetTypeRegistration, PartialReflect, TypeRegistration, TypeRegistry,
15+
},
16+
};
17+
use std::sync::Arc;
1218

1319
#[diagnostic::on_unimplemented(
1420
message = "Only functions with all arguments impplementing FromScript and return values supporting IntoScript are supported. use assert_impls_into_script!(MyArg) and assert_impls_from_script!(MyReturnType) to verify yours do.",
@@ -18,6 +24,42 @@ pub trait ScriptFunction<'env, Marker> {
1824
fn into_dynamic_function(self) -> DynamicFunction<'static>;
1925
}
2026

27+
pub trait GetInnerTypeDependencies {
28+
fn register_type_dependencies(registry: &mut TypeRegistry);
29+
}
30+
31+
impl<T: GetTypeRegistration> GetInnerTypeDependencies for T {
32+
fn register_type_dependencies(registry: &mut TypeRegistry) {
33+
registry.register::<T>();
34+
}
35+
}
36+
37+
macro_rules! recursive_type_dependencies {
38+
($(T: $path:path),*) => {
39+
$(
40+
impl<T: GetInnerTypeDependencies> GetInnerTypeDependencies for $path {
41+
fn register_type_dependencies(registry: &mut TypeRegistry) {
42+
T::register_type_dependencies(registry);
43+
}
44+
}
45+
)*
46+
};
47+
}
48+
49+
recursive_type_dependencies!(T: Val<T>, T: Ref<'_, T>, T: Mut<'_, T>);
50+
51+
pub trait GetFunctionTypeDependencies<Marker> {
52+
fn register_type_dependencies(registry: &mut TypeRegistry);
53+
}
54+
55+
/// The Script Function equivalent for dynamic functions
56+
/// TODO: have a separate function registry to avoid the need for boxing script args every time
57+
pub struct DynamicScriptFunction {
58+
pub info: FunctionInfo,
59+
pub func:
60+
Arc<dyn Fn(WorldCallbackAccess, Vec<ScriptValue>) -> Result<ScriptValue, InteropError>>,
61+
}
62+
2163
macro_rules! impl_script_function {
2264

2365
($( $param:ident ),* ) => {
@@ -50,7 +92,7 @@ macro_rules! impl_script_function {
5092
(move |world: WorldCallbackAccess, $( $param: ScriptValue ),* | {
5193
let res: Result<ScriptValue, InteropError> = (|| {
5294
$( let $callback = world.clone(); )?
53-
let world = world.read().ok_or_else(|| InteropError::stale_world_access())?;
95+
let world = world.try_read()?;
5496
// TODO: snapshot the accesses and release them after
5597
$( let $param = <$param>::from_script($param, world.clone())?; )*
5698
let out = self( $( $callback, )? $( $param.into(), )* );
@@ -68,7 +110,24 @@ macro_rules! impl_script_function {
68110
};
69111
}
70112

113+
macro_rules! impl_script_function_type_dependencies{
114+
($( $param:ident ),* ) => {
115+
impl<F, $( $param: GetInnerTypeDependencies ,)* O: GetInnerTypeDependencies> GetFunctionTypeDependencies<fn($($param),*) -> O> for F
116+
where F: Fn( $( $param ),* ) -> O
117+
{
118+
fn register_type_dependencies(registry: &mut TypeRegistry) {
119+
$(
120+
$param::register_type_dependencies(registry);
121+
)*
122+
123+
O::register_type_dependencies(registry);
124+
}
125+
}
126+
};
127+
}
128+
71129
bevy::utils::all_tuples!(impl_script_function, 0, 14, T);
130+
bevy::utils::all_tuples!(impl_script_function_type_dependencies, 0, 14, T);
72131

73132
/// Utility for quickly checking your type can be used as an argument in a script function
74133
///

crates/bevy_mod_scripting_core/src/bindings/query.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{ReflectReference, WorldAccessGuard, WorldCallbackAccess};
22
use crate::{
33
bindings::{CONCURRENT_WORLD_ACCESS_MSG, STALE_WORLD_MSG},
4-
prelude::ScriptResult,
4+
error::InteropError,
55
with_global_access,
66
};
77
use bevy::{
@@ -36,7 +36,7 @@ impl ScriptTypeRegistration {
3636
}
3737

3838
#[inline(always)]
39-
pub fn short_name(&self) -> &str {
39+
pub fn short_name(&self) -> &'static str {
4040
self.registration.type_info().type_path_table().short_path()
4141
}
4242

@@ -50,11 +50,17 @@ impl ScriptTypeRegistration {
5050
self.registration.type_info().type_id()
5151
}
5252

53-
/// Returns the [`ComponentId`] for this type, if it is a component or a resource.
53+
/// Returns the [`ComponentId`] for this type, if it is a component.
5454
#[inline(always)]
5555
pub fn component_id(&self) -> Option<ComponentId> {
5656
self.component_id
5757
}
58+
59+
/// Returns the [`ComponentId`] for this type, if it is a resource.
60+
#[inline(always)]
61+
pub fn resource_id(&self) -> Option<ComponentId> {
62+
self.resource_id
63+
}
5864
}
5965

6066
impl std::fmt::Debug for ScriptTypeRegistration {
@@ -71,7 +77,8 @@ impl std::fmt::Display for ScriptTypeRegistration {
7177
}
7278
}
7379

74-
#[derive(Clone, Default)]
80+
#[derive(Clone, Default, Reflect)]
81+
#[reflect(opaque)]
7582
pub struct ScriptQueryBuilder {
7683
components: Vec<ScriptTypeRegistration>,
7784
with: Vec<ScriptTypeRegistration>,
@@ -95,20 +102,25 @@ impl ScriptQueryBuilder {
95102
}
96103
}
97104

98-
#[derive(Clone)]
105+
#[derive(Clone, Reflect)]
106+
#[reflect(opaque)]
99107
pub struct ScriptQueryResult(pub Entity, pub Vec<ReflectReference>);
100108

101109
impl WorldCallbackAccess {
102-
pub fn query(&self, query: ScriptQueryBuilder) -> ScriptResult<VecDeque<ScriptQueryResult>> {
110+
pub fn query(
111+
&self,
112+
query: ScriptQueryBuilder,
113+
) -> Result<VecDeque<ScriptQueryResult>, InteropError> {
103114
// find the set of components
104-
self.read()
105-
.unwrap_or_else(|| panic!("{STALE_WORLD_MSG}"))
106-
.query(query)
115+
self.try_read().and_then(|world| world.query(query))
107116
}
108117
}
109118

110119
impl<'w> WorldAccessGuard<'w> {
111-
pub fn query(&self, query: ScriptQueryBuilder) -> ScriptResult<VecDeque<ScriptQueryResult>> {
120+
pub fn query(
121+
&self,
122+
query: ScriptQueryBuilder,
123+
) -> Result<VecDeque<ScriptQueryResult>, InteropError> {
112124
with_global_access!(self.0.accesses, "Could not query", {
113125
let world = unsafe { self.as_unsafe_world_cell().world_mut() };
114126
let mut dynamic_query = QueryBuilder::<EntityRef>::new(world);

0 commit comments

Comments
 (0)