Skip to content

Commit 361ca78

Browse files
authored
feat: re-implement rhai again (#222)
* initial rhai work * LETS GOO * convert more tests * pass the whole scripting suite * some cleanup * add rhai to ci matrix * fix fmt * re-arrange dependencies, and don't run exclusive deps in ci by themselves * rearrange * finalize game of life for both languages * fix resource issues * clippy * correct script unloading handling
1 parent b2c39d0 commit 361ca78

File tree

91 files changed

+1387
-371
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+1387
-371
lines changed

Cargo.toml

+13-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ path = "src/lib.rs"
2020
features = ["lua54"]
2121

2222
[features]
23-
default = ["core_functions", "bevy_bindings", "unsafe_lua_modules"]
23+
default = ["core_functions", "bevy_bindings"]
2424

2525
## lua
2626
lua = ["bevy_mod_scripting_lua"]
@@ -39,12 +39,12 @@ bevy_bindings = ["bevy_mod_scripting_functions/bevy_bindings"]
3939

4040
# optional
4141
unsafe_lua_modules = ["bevy_mod_scripting_lua?/unsafe_lua_modules"]
42-
mlua_serialize = ["bevy_mod_scripting_lua/mlua_serialize"]
43-
mlua_macros = ["bevy_mod_scripting_lua/mlua_macros"]
44-
mlua_async = ["bevy_mod_scripting_lua/mlua_async"]
42+
mlua_serialize = ["bevy_mod_scripting_lua?/mlua_serialize"]
43+
mlua_macros = ["bevy_mod_scripting_lua?/mlua_macros"]
44+
mlua_async = ["bevy_mod_scripting_lua?/mlua_async"]
4545

4646
## rhai
47-
# rhai = ["bevy_mod_scripting_rhai"]
47+
rhai = ["bevy_mod_scripting_rhai"]
4848

4949
## rune
5050
# rune = ["bevy_mod_scripting_rune"]
@@ -53,16 +53,14 @@ mlua_async = ["bevy_mod_scripting_lua/mlua_async"]
5353
bevy = { workspace = true }
5454
bevy_mod_scripting_core = { workspace = true }
5555
bevy_mod_scripting_lua = { path = "crates/languages/bevy_mod_scripting_lua", version = "0.9.0-alpha.7", optional = true }
56-
# bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.2", optional = true }
56+
bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.7", optional = true }
5757
# bevy_mod_scripting_rune = { path = "crates/languages/bevy_mod_scripting_rune", version = "0.9.0-alpha.2", optional = true }
5858
bevy_mod_scripting_functions = { workspace = true }
5959

6060
[workspace.dependencies]
6161
bevy = { version = "0.15.0", default-features = false }
6262
bevy_mod_scripting_core = { path = "crates/bevy_mod_scripting_core", version = "0.9.0-alpha.7" }
6363
bevy_mod_scripting_functions = { path = "crates/bevy_mod_scripting_functions", version = "0.9.0-alpha.7", default-features = false }
64-
mlua = { version = "0.10" }
65-
# rhai = { version = "1.20.1" }
6664

6765
# test utilities
6866
script_integration_test_harness = { path = "crates/script_integration_test_harness" }
@@ -80,7 +78,7 @@ ansi-parser = "0.9"
8078
members = [
8179
"crates/bevy_mod_scripting_core",
8280
"crates/languages/bevy_mod_scripting_lua",
83-
# "crates/languages/bevy_mod_scripting_rhai",
81+
"crates/languages/bevy_mod_scripting_rhai",
8482
# "crates/languages/bevy_mod_scripting_rune",
8583
"crates/test_utils",
8684
"crates/bevy_mod_scripting_functions",
@@ -116,7 +114,12 @@ debug = true
116114
[[example]]
117115
name = "game_of_life"
118116
path = "examples/game_of_life.rs"
119-
required-features = ["lua54", "bevy/file_watcher", "bevy/multi_threaded"]
117+
required-features = [
118+
"lua54",
119+
"rhai",
120+
"bevy/file_watcher",
121+
"bevy/multi_threaded",
122+
]
120123

121124
[workspace.lints.clippy]
122125
panic = "deny"

assets/scripts/game_of_life.lua

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
LifeState = world.get_type_by_name("LifeState")
22
Settings = world.get_type_by_name("Settings")
33

4-
world.info("Lua: The game_of_life.lua script just got loaded")
4+
info("Lua: The game_of_life.lua script just got loaded")
55

66
math.randomseed(os.time())
77

@@ -12,8 +12,8 @@ function fetch_life_state()
1212
end
1313

1414
function on_script_loaded()
15-
world.info("Lua: Hello! I am initiating the game of life simulation state with randomness!")
16-
world.info("Lua: Click on the screen to set cells alive after running the `gol start` command")
15+
info("Lua: Hello! I am initiating the game of life simulation state with randomness!")
16+
info("Lua: Click on the screen to set cells alive after running the `gol start` command")
1717

1818
local life_state = fetch_life_state()
1919
local cells = life_state.cells
@@ -27,8 +27,7 @@ end
2727

2828
function on_click(x,y)
2929
-- get the settings
30-
world.info("Lua: Clicked at x: " .. x .. " y: " .. y)
31-
print(entity)
30+
info("Lua: Clicked at x: " .. x .. " y: " .. y)
3231
local life_state = fetch_life_state()
3332
local cells = life_state.cells
3433

@@ -110,7 +109,7 @@ function on_update()
110109
end
111110

112111
function on_script_unloaded()
113-
world.info("Lua: I am being unloaded, goodbye!")
112+
info("Lua: I am being unloaded, goodbye!")
114113

115114
-- set state to 0's
116115
local life_state = fetch_life_state()

assets/scripts/game_of_life.rhai

+91-23
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,100 @@
1+
info.call("Rhai: the game_of_life.rhai script just got loaded");
2+
3+
4+
fn fetch_life_state() {
5+
let LifeState = world.get_type_by_name.call("LifeState");
6+
let Settings = world.get_type_by_name.call("Settings");
7+
for (v,i) in world.query.call().component.call(LifeState).build.call(){
8+
return v.components.call()[0]
9+
}
10+
}
11+
12+
113
fn on_script_loaded() {
2-
world.info("Game of Life script loaded");
3-
// let LifeState = world.get_type_by_name("LifeState");
4-
// let life_state = world.get_component(entity,LifeState);
5-
// let cells = life_state.cells;
6-
7-
// // set some cells alive
8-
// for x in 1..10000 {
9-
// let index = rand(0..cells.len());
10-
// cells[index] = 255;
11-
// }
14+
let LifeState = world.get_type_by_name.call("LifeState");
15+
let Settings = world.get_type_by_name.call("Settings");
16+
17+
info.call("Rhai: Hello! I am initiating the game of life simulation state with randomness!");
18+
info.call("Rhai: Click on the screen to set cells alive after running the `gol start` command");
19+
20+
let life_state = fetch_life_state!();
21+
let cells = life_state.cells;
22+
let cells_len = cells.len.call();
23+
let x = 0;
24+
while x < 1000 {
25+
let index = to_int(floor(rand.call()*cells_len)) ;
26+
cells[index] = 255;
27+
x += 1;
28+
}
1229
}
1330

14-
fn on_update() {
31+
fn on_click(x,y) {
32+
let Settings = world.get_type_by_name.call("Settings");
33+
let LifeState = world.get_type_by_name.call("LifeState");
34+
35+
info.call("Rhai: Clicked at x: "+ x + ", y: " + y );
36+
let life_state = fetch_life_state!();
37+
let cells = life_state.cells;
38+
39+
let settings = world.get_resource.call(Settings);
40+
let dimensions = settings.physical_grid_dimensions;
41+
let screen = settings.display_grid_dimensions;
42+
43+
let dimension_x = dimensions["_0"];
44+
let dimension_y = dimensions["_1"];
1545

16-
let LifeState = world.get_type_by_name("LifeState");
17-
let Settings = world.get_type_by_name("Settings");
46+
let screen_x = screen["_0"];
47+
let screen_y = screen["_1"];
1848

19-
let life_state = world.get_component(entity,LifeState);
49+
let cell_width = screen_x / dimension_x;
50+
let cell_height = screen_y / dimension_y;
51+
52+
let cell_x = to_int(x / cell_width);
53+
let cell_y = to_int(y / cell_height);
54+
55+
let index = cell_y * dimension_x + cell_x;
56+
let cell_offsets_x = [0, 1, 0, 1, -1, 0, -1, 1, -1];
57+
let cell_offsets_y = [0, 0, 1, 1, 0, -1, -1, -1, 1];
58+
for (v,i) in cell_offsets_x {
59+
let offset_x = cell_offsets_x[i];
60+
let offset_y = cell_offsets_y[i];
61+
let new_index = index + offset_x + offset_y * dimension_x;
62+
if new_index >= 0 && new_index < (dimension_x * dimension_y) {
63+
cells[new_index] = 255;
64+
}
65+
}
66+
67+
}
68+
69+
fn on_update() {
70+
let LifeState = world.get_type_by_name.call("LifeState");
71+
let Settings = world.get_type_by_name.call("Settings");
72+
73+
let life_state = fetch_life_state!();
2074
let cells = life_state.cells;
2175

2276

2377
// note that here we do not make use of RhaiProxyable and just go off pure reflection
24-
let settings = world.get_resource(Settings);
78+
let settings = world.get_resource.call(Settings);
2579
let dimensions = settings.physical_grid_dimensions;
26-
80+
let dimension_x = dimensions["_0"];
81+
let dimension_y = dimensions["_1"];
2782

2883
// primitives are passed by value to rhai, keep a hold of old state but turn 255's into 1's
2984
let prev_state = [];
3085
for (v,k) in life_state.cells {
3186
prev_state.push(life_state.cells[k] != 0);
3287
}
3388

34-
for i in 0..(dimensions[0] * dimensions[1]) {
35-
let north = prev_state.get(i - dimensions[0]);
36-
let south = prev_state.get(i + dimensions[0]);
89+
for i in 0..(dimension_x * dimension_y) {
90+
let north = prev_state.get(i - dimension_x);
91+
let south = prev_state.get(i + dimension_x);
3792
let east = prev_state.get(i + 1);
3893
let west = prev_state.get(i - 1);
39-
let northeast = prev_state.get(i - dimensions[0] + 1);
40-
let southeast = prev_state.get(i + dimensions[0] + 1);
41-
let northwest = prev_state.get(i - dimensions[0] - 1);
42-
let southwest = prev_state.get(i + dimensions[0] - 1);
94+
let northeast = prev_state.get(i - dimension_x + 1);
95+
let southeast = prev_state.get(i + dimension_x + 1);
96+
let northwest = prev_state.get(i - dimension_x - 1);
97+
let southwest = prev_state.get(i + dimension_x - 1);
4398

4499
let neighbours = 0;
45100
if north == () || north {neighbours+=1}
@@ -60,5 +115,18 @@ fn on_update() {
60115
cells[i] = 0;
61116
}
62117
}
118+
}
119+
120+
fn on_script_unloaded() {
121+
let LifeState = world.get_type_by_name.call("LifeState");
122+
let Settings = world.get_type_by_name.call("Settings");
63123

124+
info.call("Rhai: I am being unloaded, goodbye!");
125+
126+
// set state to 0's
127+
let life_state = fetch_life_state!();
128+
let cells = life_state.cells;
129+
for i in 0..cells.len.call() {
130+
cells[i] = 0;
131+
}
64132
}

crates/bevy_mod_scripting_core/Cargo.toml

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ name = "bevy_mod_scripting_core"
1616
path = "src/lib.rs"
1717

1818
[features]
19+
default = []
1920
# if enabled enables documentation updating in optimized builds
2021
doc_always = []
2122

2223
# if enabled enables some common mlua trait implementations
2324
mlua_impls = ["mlua"]
24-
# rhai_impls = ["rhai"]
25+
rhai_impls = ["rhai"]
2526

2627
[dependencies]
27-
mlua = { optional = true, workspace = true }
28-
# rhai = { optional = true, workspace = true }
28+
mlua = { version = "0.10", default-features = false, optional = true }
29+
rhai = { git = "https://github.com/rhaiscript/rhai", rev = "4ead53eb40f4a18d6f827609041ef1c742f04799", default-features = false, features = [
30+
"sync",
31+
], optional = true }
2932

3033
bevy = { workspace = true, default-features = false, features = [
3134
"bevy_asset",

crates/bevy_mod_scripting_core/src/asset.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,20 @@ pub(crate) fn sync_script_data<P: IntoScriptPluginParams>(
252252
mut commands: Commands,
253253
) {
254254
for event in events.read() {
255+
let metadata = match event {
256+
ScriptAssetEvent::Added(script_metadata)
257+
| ScriptAssetEvent::Removed(script_metadata)
258+
| ScriptAssetEvent::Modified(script_metadata) => script_metadata,
259+
};
260+
261+
if metadata.language != P::LANGUAGE {
262+
continue;
263+
}
264+
255265
trace!("{}: Received script asset event: {:?}", P::LANGUAGE, event);
256266
match event {
257267
// emitted when a new script asset is loaded for the first time
258-
ScriptAssetEvent::Added(metadata) | ScriptAssetEvent::Modified(metadata) => {
268+
ScriptAssetEvent::Added(_) | ScriptAssetEvent::Modified(_) => {
259269
if metadata.language != P::LANGUAGE {
260270
trace!(
261271
"{}: Script asset with id: {} is for a different langauge than this sync system. Skipping.",
@@ -275,7 +285,7 @@ pub(crate) fn sync_script_data<P: IntoScriptPluginParams>(
275285
));
276286
}
277287
}
278-
ScriptAssetEvent::Removed(metadata) => {
288+
ScriptAssetEvent::Removed(_) => {
279289
info!("{}: Deleting Script: {:?}", P::LANGUAGE, metadata.script_id,);
280290
commands.queue(DeleteScript::<P>::new(metadata.script_id.clone()));
281291
}

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ impl ScriptFunctionRegistryArc {
353353
}
354354
}
355355

356-
#[derive(Debug, PartialEq, Eq, Hash)]
356+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
357357
pub struct FunctionKey {
358358
pub name: Cow<'static, str>,
359359
pub namespace: Namespace,
@@ -504,6 +504,23 @@ impl ScriptFunctionRegistry {
504504
pub fn iter_all(&self) -> impl Iterator<Item = (&FunctionKey, &DynamicScriptFunction)> {
505505
self.functions.iter()
506506
}
507+
508+
/// Insert a function into the registry with the given key, this will not perform any overloading logic.
509+
/// Do not use unless you really need to.
510+
pub fn raw_insert(
511+
&mut self,
512+
namespace: Namespace,
513+
name: impl Into<Cow<'static, str>>,
514+
func: DynamicScriptFunction,
515+
) {
516+
self.functions.insert(
517+
FunctionKey {
518+
name: name.into(),
519+
namespace,
520+
},
521+
func,
522+
);
523+
}
507524
}
508525

509526
macro_rules! count {

crates/bevy_mod_scripting_core/src/commands.rs

-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ impl<P: IntoScriptPluginParams> Command for DeleteScript<P> {
8585
script.context_id,
8686
self.id
8787
);
88-
return;
8988
}
9089
};
9190
} else {

0 commit comments

Comments
 (0)