Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug in Rhai Event Handler #76

Merged
merged 8 commits into from
Oct 8, 2023
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
62 changes: 31 additions & 31 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ homepage = "https://github.com/makspll/bevy_mod_scripting"
keywords = ["bevy", "gamedev", "scripting", "lua"]
categories = ["game-development"]
readme = "readme.md"
include= ["readme.md","/src","/examples","/assets","LICENSE"]
include = ["readme.md", "/src", "/examples", "/assets", "LICENSE"]

[[bin]]
name = "bevy_mod_scripting_doc_gen"
Expand All @@ -21,15 +21,15 @@ name = "bevy_mod_scripting"
path = "src/lib.rs"

[package.metadata."docs.rs"]
features = ["lua","lua54","rhai","lua_script_api","rhai_script_api","teal"]
features = ["lua", "lua54", "rhai", "lua_script_api", "rhai_script_api", "teal"]

[package.metadata.release]
pre-release-replacements = [
{file="Cargo.toml", search='^version\s*=\s*.*$', replace="version = \"{{version}}\"", exactly=1},
{file="Cargo.toml", search='^(?P<h>bevy_mod_scripting_core\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace="${h}version = \"{{version}}\"${t}", exactly=1},
{file="Cargo.toml", search='^(?P<h>bevy_mod_scripting_lua\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace="${h}version = \"{{version}}\"${t}", exactly=1},
{file="Cargo.toml", search='^(?P<h>bevy_mod_scripting_rhai\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace="${h}version = \"{{version}}\"${t}", exactly=1},
{file="Cargo.toml", search='^(?P<h>bevy_script_api\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace="${h}version = \"{{version}}\"${t}", exactly=1},
{ file = "Cargo.toml", search = '^version\s*=\s*.*$', replace = "version = \"{{version}}\"", exactly = 1 },
{ file = "Cargo.toml", search = '^(?P<h>bevy_mod_scripting_core\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace = "${h}version = \"{{version}}\"${t}", exactly = 1 },
{ file = "Cargo.toml", search = '^(?P<h>bevy_mod_scripting_lua\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace = "${h}version = \"{{version}}\"${t}", exactly = 1 },
{ file = "Cargo.toml", search = '^(?P<h>bevy_mod_scripting_rhai\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace = "${h}version = \"{{version}}\"${t}", exactly = 1 },
{ file = "Cargo.toml", search = '^(?P<h>bevy_script_api\s*=.*)version\s*=\s*".*"(?P<t>.*)$', replace = "${h}version = \"{{version}}\"${t}", exactly = 1 },
]

[features]
Expand All @@ -47,30 +47,30 @@ luajit = ["bevy_mod_scripting_lua/luajit", "lua"]
luajit52 = ["bevy_mod_scripting_lua/luajit52", "lua"]

# optional
lua_script_api=["bevy_script_api/lua"]
unsafe_lua_modules=["bevy_mod_scripting_lua/unsafe_lua_modules"]
lua_script_api = ["bevy_script_api/lua"]
unsafe_lua_modules = ["bevy_mod_scripting_lua/unsafe_lua_modules"]
teal = ["bevy_mod_scripting_lua/teal"]
mlua_serialize = ["bevy_mod_scripting_lua/mlua_serialize"]
mlua_macros = ["bevy_mod_scripting_lua/mlua_macros"]
mlua_async = ["bevy_mod_scripting_lua/mlua_async"]

## rhai
rhai = ["bevy_mod_scripting_rhai"]
rhai_script_api=["bevy_script_api/rhai"]
rhai_script_api = ["bevy_script_api/rhai"]

[dependencies]
bevy = { version = "0.11", default-features = false}
bevy = { version = "0.11", default-features = false }
bevy_mod_scripting_core = { path = "bevy_mod_scripting_core", version = "0.3.0" }
bevy_mod_scripting_lua = { path = "languages/bevy_mod_scripting_lua", version = "0.3.0", optional = true }
bevy_mod_scripting_rhai = { path = "languages/bevy_mod_scripting_rhai", version = "0.3.0", optional = true}
bevy_mod_scripting_rhai = { path = "languages/bevy_mod_scripting_rhai", version = "0.3.0", optional = true }
bevy_script_api = { path = "bevy_script_api", version = "0.3.0", optional = true }

[dev-dependencies]
bevy = { version = "0.11"}
clap = { version = "4.1", features = ["derive"]}
bevy = { version = "0.11" }
clap = { version = "4.1", features = ["derive"] }
rand = "0.8.5"
bevy_console = "0.8.0"
rhai-rand = "0.1"
rhai-rand = "0.1"

[workspace]
resolver = "2"
Expand All @@ -84,7 +84,7 @@ members = [
"languages/bevy_mod_scripting_lua_derive",
"languages/bevy_mod_scripting_rhai",
"languages/bevy_mod_scripting_rhai_derive",
"bevy_mod_scripting_common"
"bevy_mod_scripting_common",
]

[profile.dev]
Expand All @@ -97,60 +97,60 @@ opt-level = 3
[[example]]
name = "console_integration_lua"
path = "examples/lua/console_integration.rs"
required-features = ["lua54","lua_script_api"]

required-features = ["lua54", "lua_script_api"]

[[example]]
name = "console_integration_rhai"
path = "examples/rhai/console_integration.rs"
required-features = ["rhai","rhai_script_api"]
required-features = ["rhai", "rhai_script_api"]

[[example]]
name = "complex_game_loop_lua"
path = "examples/lua/complex_game_loop.rs"
required-features=["lua54"]
required-features = ["lua54"]

[[example]]
name = "game_of_life_lua"
path = "examples/lua/game_of_life.rs"
required-features=["lua54","teal","lua_script_api"]
required-features = ["lua54", "teal", "lua_script_api"]

[[example]]
name = "game_of_life_rhai"
path = "examples/rhai/game_of_life.rs"
required-features=["rhai","rhai_script_api"]
required-features = ["rhai", "rhai_script_api"]

[[example]]
name = "event_recipients_lua"
path = "examples/lua/event_recipients.rs"
required-features=["lua54"]
required-features = ["lua54"]

[[example]]
name = "coroutines_lua"
path = "examples/lua/coroutines.rs"
required-features=["lua54"]
required-features = ["lua54"]

[[example]]
name = "documentation_gen_lua"
path = "examples/lua/documentation_gen.rs"
required-features=["lua54","teal","lua_script_api"]
required-features = ["lua54", "teal", "lua_script_api"]


[[example]]
name = "bevy_api_lua"
path = "examples/lua/bevy_api.rs"
required-features=["lua54","lua_script_api"]
required-features = ["lua54", "lua_script_api"]

[[example]]
name = "bevy_api_rhai"
path = "examples/rhai/bevy_api.rs"
required-features=["rhai","rhai_script_api"]
required-features = ["rhai", "rhai_script_api"]

[[example]]
name = "multiple_events_rhai"
path = "examples/rhai/multiple_events_rhai.rs"
required-features = ["rhai", "rhai_script_api"]

[[example]]
name = "wrappers"
path = "examples/wrappers.rs"
required-features=["lua54","lua_script_api"]



required-features = ["lua54", "lua_script_api"]
7 changes: 7 additions & 0 deletions assets/scripts/multiple_events_rhai.rhai
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn on_init(name) {
print(`Hello World! From "${name}" in Init`);
}

fn on_update(name, delta) {
print(`Hello World! From "${name}" in Update: ${delta}`);
}
133 changes: 133 additions & 0 deletions examples/rhai/multiple_events_rhai.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use bevy::{prelude::*, reflect::Reflect};
use bevy_mod_scripting_core::{
prelude::{APIProvider, PriorityEventWriter, Recipients, Script, ScriptCollection},
AddScriptApiProvider, AddScriptHost, AddScriptHostHandler, ScriptingPlugin,
};
use bevy_mod_scripting_rhai::{
prelude::{RhaiDocFragment, RhaiFile},
rhai::{Engine, FuncArgs},
RhaiContext, RhaiEvent, RhaiScriptHost,
};
use bevy_script_api::prelude::RhaiBevyAPIProvider;

fn main() {
App::new()
.add_plugins((DefaultPlugins, ScriptingPlugin))
.add_systems(Startup, setup_entities)
.add_systems(Update, (call_init, call_update))
.add_script_host::<RhaiScriptHost<ScriptArgs>>(PostUpdate)
.add_api_provider::<RhaiScriptHost<ScriptArgs>>(Box::new(RhaiBevyAPIProvider))
.add_script_handler::<RhaiScriptHost<ScriptArgs>, 0, 1>(PostUpdate)
.run();
}

#[derive(Default)]
pub struct MyCustomAPI;

impl APIProvider for MyCustomAPI {
type APITarget = Engine;
type ScriptContext = RhaiContext;
type DocTarget = RhaiDocFragment;

fn attach_api(
&mut self,
api: &mut Self::APITarget,
) -> Result<(), bevy_mod_scripting::prelude::ScriptError> {
api.set_max_expr_depths(0, 0);

Ok(())
}
}

#[derive(Debug, Clone, Reflect, Default)]
struct ScriptArgs {
entity_name: Option<String>,
delta_time: Option<f32>,
}

impl FuncArgs for ScriptArgs {
fn parse<ARGS: Extend<bevy_mod_scripting_rhai::rhai::Dynamic>>(self, args: &mut ARGS) {
if let Some(entity_name) = self.entity_name {
args.extend(vec![entity_name.into()]);
}
if let Some(delta_time) = self.delta_time {
args.extend(vec![delta_time.to_string().into()]);
}
}
}

fn setup_entities(mut commands: Commands, asset_server: Res<AssetServer>) {
let script_path = "scripts/multiple_events_rhai.rhai";

for i in 0..10 {
let entity_name = format!("Test Entity {}", i);
commands.spawn((
NewlyAddedEntityCallInit,
Name::from(entity_name),
ScriptCollection::<RhaiFile> {
scripts: vec![Script::new(
script_path.to_owned(),
asset_server.load(script_path),
)],
},
));
}
}

#[derive(Debug, Clone, Copy, Reflect, Default, Component)]
#[reflect(Component)]
pub struct NewlyAddedEntityCallInit;

fn call_update(
mut events: PriorityEventWriter<RhaiEvent<ScriptArgs>>,
time: Res<Time>,
to_update: Query<
(Entity, Option<&Name>),
(
With<ScriptCollection<RhaiFile>>,
Without<NewlyAddedEntityCallInit>,
),
>,
) {
to_update.for_each(|(entity, name)| {
events.send(
RhaiEvent {
hook_name: "on_update".to_owned(),
args: ScriptArgs {
delta_time: Some(time.delta_seconds()),
entity_name: name.map(|n| n.to_string()),
},
recipients: Recipients::Entity(entity),
},
1,
);
});
}

fn call_init(
mut events: PriorityEventWriter<RhaiEvent<ScriptArgs>>,
mut commands: Commands,
entity_query: Query<
(Entity, Option<&Name>, Option<&ScriptCollection<RhaiFile>>),
Added<NewlyAddedEntityCallInit>,
>,
) {
entity_query.for_each(|(entity, name, scripts)| {
if let Some(_) = scripts {
events.send(
RhaiEvent {
hook_name: "on_init".to_owned(),
args: ScriptArgs {
delta_time: None,
entity_name: name.clone().map(|n| n.to_string()),
},
recipients: Recipients::Entity(entity),
},
0,
);
commands.entity(entity).remove::<NewlyAddedEntityCallInit>();
} else {
commands.entity(entity).remove::<NewlyAddedEntityCallInit>();
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub(crate) fn make_methods(flag: &DeriveFlag, new_type: &Newtype, out: &mut Vec<
let method_identifier_string = method_identifier.to_string();
let self_ident = m.self_.as_ref()
.map(|_| quote_spanned!(m.span()=>#receiver_argument_identifier,))
.unwrap_or_else(Default::default);
.unwrap_or_default();
parse_quote_spanned!{m.span()=>
#docstrings
#static_ #mut_ #fn_ #method_identifier_string =>|_,#self_ident (#(#parameter_identifiers),*):(#(#parameter_types),*)| #body
Expand Down
2 changes: 1 addition & 1 deletion languages/bevy_mod_scripting_rhai/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ path="src/lib.rs"

[dependencies]
bevy= { version = "0.11", default-features = false}
rhai = { version = "1.15.1", features = ["sync"] }
rhai = { version = "1.16", features = ["sync"] }
bevy_mod_scripting_core = {path="../../bevy_mod_scripting_core", version = "0.3.0" }
34 changes: 20 additions & 14 deletions languages/bevy_mod_scripting_rhai/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl<A: FuncArgs + Send> Default for RhaiScriptHost<A> {
let mut e = Engine::new();
// prevent shadowing of `state`,`world` and `entity` in variable in scripts
e.on_def_var(|_, info, _| {
Ok(info.name != "state" && info.name != "world" && info.name != "entity")
Ok(info.name() != "state" && info.name() != "world" && info.name() != "entity")
});

Self {
Expand Down Expand Up @@ -148,19 +148,20 @@ impl<A: FuncArgs + Send + Clone + Sync + 'static> ScriptHost for RhaiScriptHost<
ctxs: impl Iterator<Item = (ScriptData<'a>, &'a mut Self::ScriptContext)>,
providers: &mut APIProviders<Self>,
) {
// safety:
// - we have &mut World access
// - we do not use world_ptr after we use the original reference again anywhere in this function
let world_ptr = unsafe { WorldPointer::new(world) };

ctxs.for_each(|(fd, ctx)| {
// safety:
// - we have &mut World access
// - we do not use world_ptr after we use the original reference again anywhere in this function
let world_ptr = unsafe { WorldPointer::new(world) };
providers
.setup_runtime_all(world_ptr.clone(), &fd, ctx)
.expect("Failed to setup script runtime");

for event in events.iter() {
// check if this script should handle this event
if !event.recipients().is_recipient(&fd) {
return;
continue;
};

match self.engine.call_fn(
Expand All @@ -174,14 +175,19 @@ impl<A: FuncArgs + Send + Clone + Sync + 'static> ScriptHost for RhaiScriptHost<
let mut world = world_ptr.write();
let mut state: CachedScriptState<Self> = world.remove_resource().unwrap();

let (_, mut error_wrt, _) = state.event_state.get_mut(&mut world);

let error = ScriptError::RuntimeError {
script: fd.name.to_string(),
msg: e.to_string(),
};
error!("{}", error);
error_wrt.send(ScriptErrorEvent { error });
match *e {
EvalAltResult::ErrorFunctionNotFound(..) => {}
_ => {
let (_, mut error_wrt, _) = state.event_state.get_mut(&mut world);

let error = ScriptError::RuntimeError {
script: fd.name.to_string(),
msg: e.to_string(),
};
error!("{}", error);
error_wrt.send(ScriptErrorEvent { error });
}
}

world.insert_resource(state);
}
Expand Down
Loading