Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ local entity = world.spawn()

world.push_children(entity, {})

assert(#world.get_children(entity) == 0)
assert(#world.get_children(entity) == 0)
2 changes: 2 additions & 0 deletions crates/bevy_mod_scripting_asset/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ categories.workspace = true
readme.workspace = true

[dependencies]
bevy_mod_scripting_display = { workspace = true }
bevy_mod_scripting_derive = { workspace = true }
bevy_reflect = { workspace = true }
bevy_asset = { workspace = true }
bevy_log = { workspace = true }
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_mod_scripting_asset/src/language.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
//! Defines supported scripting languages and their file extensions.

use bevy_mod_scripting_derive::DebugWithTypeInfo;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;

/// Represents a scripting language. Languages which compile into another language should use the target language as their language.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
#[derive(
Clone, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize, DebugWithTypeInfo,
)]
#[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")]
pub enum Language {
/// The Rhai scripting language
Rhai,
Expand Down
20 changes: 17 additions & 3 deletions crates/bevy_mod_scripting_bindings/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! Error types for the bindings
use crate::{
Namespace, ReflectBaseType, ReflectReference, access_map::ReflectAccessId,
FunctionCallContext, Namespace, ReflectBaseType, ReflectReference, access_map::ReflectAccessId,
script_value::ScriptValue,
};
use bevy_ecs::entity::Entity;
use bevy_mod_scripting_asset::Language;
use bevy_mod_scripting_derive::DebugWithTypeInfo;
use bevy_mod_scripting_display::{
DebugWithTypeInfo, DisplayWithTypeInfo, GetTypeInfo, OrFakeId, PrintReflectAsDebug,
Expand Down Expand Up @@ -148,6 +149,8 @@ pub enum InteropError {
on: Box<Namespace>,
/// The error that occurred
error: Box<InteropError>,
/// The context at the time of the call
context: Box<Option<FunctionCallContext>>,
},
/// An error occurred when converting a function argument
FunctionArgConversionError {
Expand Down Expand Up @@ -353,11 +356,13 @@ impl InteropError {
function_name: impl Display,
on: Namespace,
error: InteropError,
context: Option<FunctionCallContext>,
) -> Self {
Self::FunctionInteropError {
function_name: Box::new(function_name.to_string()),
on: Box::new(on),
error: Box::new(error),
context: Box::new(context),
}
}

Expand Down Expand Up @@ -423,11 +428,16 @@ impl InteropError {
}

/// Creates a new missing function error.
pub fn missing_function(function_name: impl Display, on: Namespace) -> Self {
pub fn missing_function(
function_name: impl Display,
on: Namespace,
context: Option<FunctionCallContext>,
) -> Self {
Self::FunctionInteropError {
function_name: Box::new(function_name.to_string()),
on: Box::new(on),
error: Box::new(InteropError::str("Function not found")),
context: Box::new(context),
}
}
}
Expand Down Expand Up @@ -563,10 +573,14 @@ impl DisplayWithTypeInfo for InteropError {
function_name,
on,
error,
context,
} => {
write!(
f,
"Error in function {} on {}: {}",
"Error {} in function {} on {}: {}",
context
.clone()
.unwrap_or(FunctionCallContext::new(Language::Unknown)),
function_name,
WithTypeInfo::new_with_opt_info(on, type_info_provider),
error
Expand Down
56 changes: 52 additions & 4 deletions crates/bevy_mod_scripting_bindings/src/function/script_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,56 @@ pub trait ScriptFunctionMut<'env, Marker> {

/// The caller context when calling a script function.
/// Functions can choose to react to caller preferences such as converting 1-indexed numbers to 0-indexed numbers
#[derive(Clone, Debug, Reflect)]
#[derive(Clone, Reflect, DebugWithTypeInfo)]
#[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")]
#[reflect(opaque)]
pub struct FunctionCallContext {
language: Language,
location_context: Option<LocationContext>,
}

impl std::fmt::Display for FunctionCallContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("in language: ")?;
self.language.fmt(f)?;
if let Some(context) = &self.location_context {
f.write_str(", at line: ")?;
context.line.fmt(f)?;
f.write_str(", at column: ")?;
context.col.fmt(f)?;
}
Ok(())
}
}

#[derive(Clone, Reflect, DebugWithTypeInfo)]
#[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")]
/// Describes a location within a script
pub struct LocationContext {
/// The line number
pub line: u32,
/// The column number
pub col: u32,
}

impl FunctionCallContext {
/// Create a new FunctionCallContext with the given 1-indexing conversion preference
pub const fn new(language: Language) -> Self {
Self { language }
Self {
language,
location_context: None,
}
}

/// Creates a new function call context with location information
pub const fn new_with_location(
language: Language,
location_context: Option<LocationContext>,
) -> Self {
Self {
language,
location_context,
}
}

/// Tries to access the world, returning an error if the world is not available
Expand All @@ -68,6 +108,11 @@ impl FunctionCallContext {
pub fn language(&self) -> Language {
self.language.clone()
}

/// Returns call location inside the script if available
pub fn location(&self) -> Option<&LocationContext> {
self.location_context.as_ref()
}
}

#[derive(Reflect, Clone, DebugWithTypeInfo)]
Expand Down Expand Up @@ -158,12 +203,13 @@ impl DynamicScriptFunction {
profiling::scope!("Dynamic Call ", self.name().deref());
let args = args.into_iter().collect::<VecDeque<_>>();
// should we be inlining call errors into the return value?
let return_val = (self.func)(context, args);
let return_val = (self.func)(context.clone(), args);
match return_val {
ScriptValue::Error(e) => Err(InteropError::function_interop_error(
self.name(),
self.info.namespace,
e,
Some(context),
)),
v => Ok(v),
}
Expand Down Expand Up @@ -195,12 +241,13 @@ impl DynamicScriptFunctionMut {
let args = args.into_iter().collect::<VecDeque<_>>();
// should we be inlining call errors into the return value?
let mut write = self.func.write();
let return_val = (write)(context, args);
let return_val = (write)(context.clone(), args);
match return_val {
ScriptValue::Error(e) => Err(InteropError::function_interop_error(
self.name(),
self.info.namespace,
e,
Some(context),
)),
v => Ok(v),
}
Expand Down Expand Up @@ -737,6 +784,7 @@ mod test {
function_name,
on,
error,
..
} = gotten
{
assert_eq!(*function_name, expected_function_name);
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_mod_scripting_bindings/src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ impl<'w> WorldAccessGuard<'w> {
return Err(InteropError::missing_function(
name.to_string(),
Namespace::OnType(type_id),
Some(context.clone()),
));
}
};
Expand Down Expand Up @@ -660,6 +661,7 @@ impl WorldAccessGuard<'_> {
"field missing and no default provided: '{}'",
descriptor.into()
)),
None,
)
})?;
return Ok(default_data.default().into_partial_reflect());
Expand Down Expand Up @@ -820,6 +822,7 @@ impl WorldAccessGuard<'_> {
"construct",
Namespace::OnType(TypeId::of::<World>()),
InteropError::str("missing 'variant' field in enum constructor payload"),
None,
)
})?;

Expand All @@ -834,6 +837,7 @@ impl WorldAccessGuard<'_> {
variant_name,
enum_info.type_path()
)),
None,
)
})?;

Expand Down
20 changes: 15 additions & 5 deletions crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,11 @@ impl UserData for LuaReflectReference {
let iter_func = world
.lookup_function([TypeId::of::<ReflectReference>()], "iter")
.map_err(|f| {
InteropError::missing_function(f, TypeId::of::<ReflectReference>().into())
InteropError::missing_function(
f,
TypeId::of::<ReflectReference>().into(),
Some(LUA_CALLER_CONTEXT),
)
})
.map_err(IntoMluaError::to_lua_error)?;

Expand All @@ -347,7 +351,11 @@ impl UserData for LuaReflectReference {
let func = world
.lookup_function([TypeId::of::<ReflectReference>()], "display")
.map_err(|f| {
InteropError::missing_function(f, TypeId::of::<ReflectReference>().into())
InteropError::missing_function(
f,
TypeId::of::<ReflectReference>().into(),
Some(LUA_CALLER_CONTEXT),
)
})
.map_err(IntoMluaError::to_lua_error)?;
let out = func
Expand Down Expand Up @@ -389,10 +397,12 @@ impl UserData for LuaStaticReflectReference {
},
Err(key) => key,
};
Err(
InteropError::missing_function(format!("{key:#?}"), type_id.into())
.into_lua_err(),
Err(InteropError::missing_function(
format!("{key:#?}"),
type_id.into(),
Some(LUA_CALLER_CONTEXT),
)
.into_lua_err())
},
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::{

use bevy_mod_scripting_asset::Language;
use bevy_mod_scripting_bindings::{
error::InteropError, function::script_function::FunctionCallContext, script_value::ScriptValue,
LocationContext, error::InteropError, function::script_function::FunctionCallContext,
script_value::ScriptValue,
};
use bevy_platform::collections::HashMap;
use mlua::{FromLua, IntoLua, Value, Variadic};
Expand Down Expand Up @@ -140,18 +141,32 @@ impl IntoLua for LuaScriptValue {
ScriptValue::Reference(r) => LuaReflectReference::from(r).into_lua(lua)?,
ScriptValue::Error(script_error) => return Err(mlua::Error::external(script_error)),
ScriptValue::Function(function) => lua
.create_function(move |_lua, args: Variadic<LuaScriptValue>| {
.create_function(move |lua, args: Variadic<LuaScriptValue>| {
let loc = lua.inspect_stack(1).map(|debug| LocationContext {
line: debug.curr_line().try_into().unwrap_or_default(),
col: 0,
});
let out = function
.call(args.into_iter().map(Into::into), LUA_CALLER_CONTEXT)
.call(
args.into_iter().map(Into::into),
FunctionCallContext::new_with_location(Language::Lua, loc),
)
.map_err(IntoMluaError::to_lua_error)?;

Ok(LuaScriptValue::from(out))
})?
.into_lua(lua)?,
ScriptValue::FunctionMut(function) => lua
.create_function(move |_lua, args: Variadic<LuaScriptValue>| {
.create_function(move |lua, args: Variadic<LuaScriptValue>| {
let loc = lua.inspect_stack(0).map(|debug| LocationContext {
line: debug.curr_line() as u32,
col: 0,
});
let out = function
.call(args.into_iter().map(Into::into), LUA_CALLER_CONTEXT)
.call(
args.into_iter().map(Into::into),
FunctionCallContext::new_with_location(Language::Lua, loc),
)
.map_err(IntoMluaError::to_lua_error)?;

Ok(LuaScriptValue::from(out))
Expand Down
16 changes: 13 additions & 3 deletions crates/languages/bevy_mod_scripting_rhai/src/bindings/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,11 @@ impl IntoIterator for RhaiReflectReference {
let iter_func = world
.lookup_function([TypeId::of::<ReflectReference>()], "iter")
.map_err(|f| {
InteropError::missing_function(f, TypeId::of::<ReflectReference>().into())
InteropError::missing_function(
f,
TypeId::of::<ReflectReference>().into(),
Some(RHAI_CALLER_CONTEXT),
)
})?;

iter_func.call(
Expand Down Expand Up @@ -562,6 +566,7 @@ impl CustomType for RhaiReflectReference {
InteropError::missing_function(
f,
TypeId::of::<ReflectReference>().into(),
Some(RHAI_CALLER_CONTEXT),
)
})?;

Expand Down Expand Up @@ -592,6 +597,7 @@ impl CustomType for RhaiReflectReference {
InteropError::missing_function(
f,
TypeId::of::<ReflectReference>().into(),
Some(RHAI_CALLER_CONTEXT),
)
})?;

Expand Down Expand Up @@ -639,8 +645,12 @@ impl CustomType for RhaiStaticReflectReference {
};

Err::<_, Box<EvalAltResult>>(
InteropError::missing_function(format!("{key:#?}"), type_id.into())
.into_rhai_error(),
InteropError::missing_function(
format!("{key:#?}"),
type_id.into(),
Some(RHAI_CALLER_CONTEXT),
)
.into_rhai_error(),
)
});
}
Expand Down
Loading