Skip to content

Commit 346874f

Browse files
authored
Fix bug in Rhai Event Handler (makspll#76)
* mmm * same behaviour as for lua * some printing * setup an example that demonstrates the issue * fixed the bug lol * if a function isnt found, dont scream in the console * Update multiple_events_rhai.rhai * fix clippy?
1 parent 7590248 commit 346874f

File tree

6 files changed

+193
-47
lines changed

6 files changed

+193
-47
lines changed

Cargo.toml

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ homepage = "https://github.com/makspll/bevy_mod_scripting"
1010
keywords = ["bevy", "gamedev", "scripting", "lua"]
1111
categories = ["game-development"]
1212
readme = "readme.md"
13-
include= ["readme.md","/src","/examples","/assets","LICENSE"]
13+
include = ["readme.md", "/src", "/examples", "/assets", "LICENSE"]
1414

1515
[[bin]]
1616
name = "bevy_mod_scripting_doc_gen"
@@ -21,15 +21,15 @@ name = "bevy_mod_scripting"
2121
path = "src/lib.rs"
2222

2323
[package.metadata."docs.rs"]
24-
features = ["lua","lua54","rhai","lua_script_api","rhai_script_api","teal"]
24+
features = ["lua", "lua54", "rhai", "lua_script_api", "rhai_script_api", "teal"]
2525

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

3535
[features]
@@ -47,30 +47,30 @@ luajit = ["bevy_mod_scripting_lua/luajit", "lua"]
4747
luajit52 = ["bevy_mod_scripting_lua/luajit52", "lua"]
4848

4949
# optional
50-
lua_script_api=["bevy_script_api/lua"]
51-
unsafe_lua_modules=["bevy_mod_scripting_lua/unsafe_lua_modules"]
50+
lua_script_api = ["bevy_script_api/lua"]
51+
unsafe_lua_modules = ["bevy_mod_scripting_lua/unsafe_lua_modules"]
5252
teal = ["bevy_mod_scripting_lua/teal"]
5353
mlua_serialize = ["bevy_mod_scripting_lua/mlua_serialize"]
5454
mlua_macros = ["bevy_mod_scripting_lua/mlua_macros"]
5555
mlua_async = ["bevy_mod_scripting_lua/mlua_async"]
5656

5757
## rhai
5858
rhai = ["bevy_mod_scripting_rhai"]
59-
rhai_script_api=["bevy_script_api/rhai"]
59+
rhai_script_api = ["bevy_script_api/rhai"]
6060

6161
[dependencies]
62-
bevy = { version = "0.11", default-features = false}
62+
bevy = { version = "0.11", default-features = false }
6363
bevy_mod_scripting_core = { path = "bevy_mod_scripting_core", version = "0.3.0" }
6464
bevy_mod_scripting_lua = { path = "languages/bevy_mod_scripting_lua", version = "0.3.0", optional = true }
65-
bevy_mod_scripting_rhai = { path = "languages/bevy_mod_scripting_rhai", version = "0.3.0", optional = true}
65+
bevy_mod_scripting_rhai = { path = "languages/bevy_mod_scripting_rhai", version = "0.3.0", optional = true }
6666
bevy_script_api = { path = "bevy_script_api", version = "0.3.0", optional = true }
6767

6868
[dev-dependencies]
69-
bevy = { version = "0.11"}
70-
clap = { version = "4.1", features = ["derive"]}
69+
bevy = { version = "0.11" }
70+
clap = { version = "4.1", features = ["derive"] }
7171
rand = "0.8.5"
7272
bevy_console = "0.8.0"
73-
rhai-rand = "0.1"
73+
rhai-rand = "0.1"
7474

7575
[workspace]
7676
resolver = "2"
@@ -84,7 +84,7 @@ members = [
8484
"languages/bevy_mod_scripting_lua_derive",
8585
"languages/bevy_mod_scripting_rhai",
8686
"languages/bevy_mod_scripting_rhai_derive",
87-
"bevy_mod_scripting_common"
87+
"bevy_mod_scripting_common",
8888
]
8989

9090
[profile.dev]
@@ -97,60 +97,60 @@ opt-level = 3
9797
[[example]]
9898
name = "console_integration_lua"
9999
path = "examples/lua/console_integration.rs"
100-
required-features = ["lua54","lua_script_api"]
101-
100+
required-features = ["lua54", "lua_script_api"]
102101

103102
[[example]]
104103
name = "console_integration_rhai"
105104
path = "examples/rhai/console_integration.rs"
106-
required-features = ["rhai","rhai_script_api"]
105+
required-features = ["rhai", "rhai_script_api"]
107106

108107
[[example]]
109108
name = "complex_game_loop_lua"
110109
path = "examples/lua/complex_game_loop.rs"
111-
required-features=["lua54"]
110+
required-features = ["lua54"]
112111

113112
[[example]]
114113
name = "game_of_life_lua"
115114
path = "examples/lua/game_of_life.rs"
116-
required-features=["lua54","teal","lua_script_api"]
115+
required-features = ["lua54", "teal", "lua_script_api"]
117116

118117
[[example]]
119118
name = "game_of_life_rhai"
120119
path = "examples/rhai/game_of_life.rs"
121-
required-features=["rhai","rhai_script_api"]
120+
required-features = ["rhai", "rhai_script_api"]
122121

123122
[[example]]
124123
name = "event_recipients_lua"
125124
path = "examples/lua/event_recipients.rs"
126-
required-features=["lua54"]
125+
required-features = ["lua54"]
127126

128127
[[example]]
129128
name = "coroutines_lua"
130129
path = "examples/lua/coroutines.rs"
131-
required-features=["lua54"]
130+
required-features = ["lua54"]
132131

133132
[[example]]
134133
name = "documentation_gen_lua"
135134
path = "examples/lua/documentation_gen.rs"
136-
required-features=["lua54","teal","lua_script_api"]
135+
required-features = ["lua54", "teal", "lua_script_api"]
137136

138137

139138
[[example]]
140139
name = "bevy_api_lua"
141140
path = "examples/lua/bevy_api.rs"
142-
required-features=["lua54","lua_script_api"]
141+
required-features = ["lua54", "lua_script_api"]
143142

144143
[[example]]
145144
name = "bevy_api_rhai"
146145
path = "examples/rhai/bevy_api.rs"
147-
required-features=["rhai","rhai_script_api"]
146+
required-features = ["rhai", "rhai_script_api"]
148147

148+
[[example]]
149+
name = "multiple_events_rhai"
150+
path = "examples/rhai/multiple_events_rhai.rs"
151+
required-features = ["rhai", "rhai_script_api"]
149152

150153
[[example]]
151154
name = "wrappers"
152155
path = "examples/wrappers.rs"
153-
required-features=["lua54","lua_script_api"]
154-
155-
156-
156+
required-features = ["lua54", "lua_script_api"]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn on_init(name) {
2+
print(`Hello World! From "${name}" in Init`);
3+
}
4+
5+
fn on_update(name, delta) {
6+
print(`Hello World! From "${name}" in Update: ${delta}`);
7+
}

examples/rhai/multiple_events_rhai.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use bevy::{prelude::*, reflect::Reflect};
2+
use bevy_mod_scripting_core::{
3+
prelude::{APIProvider, PriorityEventWriter, Recipients, Script, ScriptCollection},
4+
AddScriptApiProvider, AddScriptHost, AddScriptHostHandler, ScriptingPlugin,
5+
};
6+
use bevy_mod_scripting_rhai::{
7+
prelude::{RhaiDocFragment, RhaiFile},
8+
rhai::{Engine, FuncArgs},
9+
RhaiContext, RhaiEvent, RhaiScriptHost,
10+
};
11+
use bevy_script_api::prelude::RhaiBevyAPIProvider;
12+
13+
fn main() {
14+
App::new()
15+
.add_plugins((DefaultPlugins, ScriptingPlugin))
16+
.add_systems(Startup, setup_entities)
17+
.add_systems(Update, (call_init, call_update))
18+
.add_script_host::<RhaiScriptHost<ScriptArgs>>(PostUpdate)
19+
.add_api_provider::<RhaiScriptHost<ScriptArgs>>(Box::new(RhaiBevyAPIProvider))
20+
.add_script_handler::<RhaiScriptHost<ScriptArgs>, 0, 1>(PostUpdate)
21+
.run();
22+
}
23+
24+
#[derive(Default)]
25+
pub struct MyCustomAPI;
26+
27+
impl APIProvider for MyCustomAPI {
28+
type APITarget = Engine;
29+
type ScriptContext = RhaiContext;
30+
type DocTarget = RhaiDocFragment;
31+
32+
fn attach_api(
33+
&mut self,
34+
api: &mut Self::APITarget,
35+
) -> Result<(), bevy_mod_scripting::prelude::ScriptError> {
36+
api.set_max_expr_depths(0, 0);
37+
38+
Ok(())
39+
}
40+
}
41+
42+
#[derive(Debug, Clone, Reflect, Default)]
43+
struct ScriptArgs {
44+
entity_name: Option<String>,
45+
delta_time: Option<f32>,
46+
}
47+
48+
impl FuncArgs for ScriptArgs {
49+
fn parse<ARGS: Extend<bevy_mod_scripting_rhai::rhai::Dynamic>>(self, args: &mut ARGS) {
50+
if let Some(entity_name) = self.entity_name {
51+
args.extend(vec![entity_name.into()]);
52+
}
53+
if let Some(delta_time) = self.delta_time {
54+
args.extend(vec![delta_time.to_string().into()]);
55+
}
56+
}
57+
}
58+
59+
fn setup_entities(mut commands: Commands, asset_server: Res<AssetServer>) {
60+
let script_path = "scripts/multiple_events_rhai.rhai";
61+
62+
for i in 0..10 {
63+
let entity_name = format!("Test Entity {}", i);
64+
commands.spawn((
65+
NewlyAddedEntityCallInit,
66+
Name::from(entity_name),
67+
ScriptCollection::<RhaiFile> {
68+
scripts: vec![Script::new(
69+
script_path.to_owned(),
70+
asset_server.load(script_path),
71+
)],
72+
},
73+
));
74+
}
75+
}
76+
77+
#[derive(Debug, Clone, Copy, Reflect, Default, Component)]
78+
#[reflect(Component)]
79+
pub struct NewlyAddedEntityCallInit;
80+
81+
fn call_update(
82+
mut events: PriorityEventWriter<RhaiEvent<ScriptArgs>>,
83+
time: Res<Time>,
84+
to_update: Query<
85+
(Entity, Option<&Name>),
86+
(
87+
With<ScriptCollection<RhaiFile>>,
88+
Without<NewlyAddedEntityCallInit>,
89+
),
90+
>,
91+
) {
92+
to_update.for_each(|(entity, name)| {
93+
events.send(
94+
RhaiEvent {
95+
hook_name: "on_update".to_owned(),
96+
args: ScriptArgs {
97+
delta_time: Some(time.delta_seconds()),
98+
entity_name: name.map(|n| n.to_string()),
99+
},
100+
recipients: Recipients::Entity(entity),
101+
},
102+
1,
103+
);
104+
});
105+
}
106+
107+
fn call_init(
108+
mut events: PriorityEventWriter<RhaiEvent<ScriptArgs>>,
109+
mut commands: Commands,
110+
entity_query: Query<
111+
(Entity, Option<&Name>, Option<&ScriptCollection<RhaiFile>>),
112+
Added<NewlyAddedEntityCallInit>,
113+
>,
114+
) {
115+
entity_query.for_each(|(entity, name, scripts)| {
116+
if let Some(_) = scripts {
117+
events.send(
118+
RhaiEvent {
119+
hook_name: "on_init".to_owned(),
120+
args: ScriptArgs {
121+
delta_time: None,
122+
entity_name: name.clone().map(|n| n.to_string()),
123+
},
124+
recipients: Recipients::Entity(entity),
125+
},
126+
0,
127+
);
128+
commands.entity(entity).remove::<NewlyAddedEntityCallInit>();
129+
} else {
130+
commands.entity(entity).remove::<NewlyAddedEntityCallInit>();
131+
}
132+
});
133+
}

languages/bevy_mod_scripting_lua_derive/src/derive_flags/auto_methods.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub(crate) fn make_methods(flag: &DeriveFlag, new_type: &Newtype, out: &mut Vec<
125125
let method_identifier_string = method_identifier.to_string();
126126
let self_ident = m.self_.as_ref()
127127
.map(|_| quote_spanned!(m.span()=>#receiver_argument_identifier,))
128-
.unwrap_or_else(Default::default);
128+
.unwrap_or_default();
129129
parse_quote_spanned!{m.span()=>
130130
#docstrings
131131
#static_ #mut_ #fn_ #method_identifier_string =>|_,#self_ident (#(#parameter_identifiers),*):(#(#parameter_types),*)| #body

languages/bevy_mod_scripting_rhai/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ path="src/lib.rs"
2323

2424
[dependencies]
2525
bevy= { version = "0.11", default-features = false}
26-
rhai = { version = "1.15.1", features = ["sync"] }
26+
rhai = { version = "1.16", features = ["sync"] }
2727
bevy_mod_scripting_core = {path="../../bevy_mod_scripting_core", version = "0.3.0" }

languages/bevy_mod_scripting_rhai/src/lib.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ impl<A: FuncArgs + Send> Default for RhaiScriptHost<A> {
3232
let mut e = Engine::new();
3333
// prevent shadowing of `state`,`world` and `entity` in variable in scripts
3434
e.on_def_var(|_, info, _| {
35-
Ok(info.name != "state" && info.name != "world" && info.name != "entity")
35+
Ok(info.name() != "state" && info.name() != "world" && info.name() != "entity")
3636
});
3737

3838
Self {
@@ -148,19 +148,20 @@ impl<A: FuncArgs + Send + Clone + Sync + 'static> ScriptHost for RhaiScriptHost<
148148
ctxs: impl Iterator<Item = (ScriptData<'a>, &'a mut Self::ScriptContext)>,
149149
providers: &mut APIProviders<Self>,
150150
) {
151+
// safety:
152+
// - we have &mut World access
153+
// - we do not use world_ptr after we use the original reference again anywhere in this function
154+
let world_ptr = unsafe { WorldPointer::new(world) };
155+
151156
ctxs.for_each(|(fd, ctx)| {
152-
// safety:
153-
// - we have &mut World access
154-
// - we do not use world_ptr after we use the original reference again anywhere in this function
155-
let world_ptr = unsafe { WorldPointer::new(world) };
156157
providers
157158
.setup_runtime_all(world_ptr.clone(), &fd, ctx)
158159
.expect("Failed to setup script runtime");
159160

160161
for event in events.iter() {
161162
// check if this script should handle this event
162163
if !event.recipients().is_recipient(&fd) {
163-
return;
164+
continue;
164165
};
165166

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

177-
let (_, mut error_wrt, _) = state.event_state.get_mut(&mut world);
178-
179-
let error = ScriptError::RuntimeError {
180-
script: fd.name.to_string(),
181-
msg: e.to_string(),
182-
};
183-
error!("{}", error);
184-
error_wrt.send(ScriptErrorEvent { error });
178+
match *e {
179+
EvalAltResult::ErrorFunctionNotFound(..) => {}
180+
_ => {
181+
let (_, mut error_wrt, _) = state.event_state.get_mut(&mut world);
182+
183+
let error = ScriptError::RuntimeError {
184+
script: fd.name.to_string(),
185+
msg: e.to_string(),
186+
};
187+
error!("{}", error);
188+
error_wrt.send(ScriptErrorEvent { error });
189+
}
190+
}
185191

186192
world.insert_resource(state);
187193
}

0 commit comments

Comments
 (0)