Skip to content

feat: Add rhai support after re-write, including bevy bindings #222

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

Merged
merged 13 commits into from
Jan 24, 2025
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
23 changes: 13 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ path = "src/lib.rs"
features = ["lua54"]

[features]
default = ["core_functions", "bevy_bindings", "unsafe_lua_modules"]
default = ["core_functions", "bevy_bindings"]

## lua
lua = ["bevy_mod_scripting_lua"]
Expand All @@ -39,12 +39,12 @@ bevy_bindings = ["bevy_mod_scripting_functions/bevy_bindings"]

# optional
unsafe_lua_modules = ["bevy_mod_scripting_lua?/unsafe_lua_modules"]
mlua_serialize = ["bevy_mod_scripting_lua/mlua_serialize"]
mlua_macros = ["bevy_mod_scripting_lua/mlua_macros"]
mlua_async = ["bevy_mod_scripting_lua/mlua_async"]
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 = ["bevy_mod_scripting_rhai"]

## rune
# rune = ["bevy_mod_scripting_rune"]
Expand All @@ -53,16 +53,14 @@ mlua_async = ["bevy_mod_scripting_lua/mlua_async"]
bevy = { workspace = true }
bevy_mod_scripting_core = { workspace = true }
bevy_mod_scripting_lua = { path = "crates/languages/bevy_mod_scripting_lua", version = "0.9.0-alpha.7", optional = true }
# bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.2", optional = true }
bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.7", optional = true }
# bevy_mod_scripting_rune = { path = "crates/languages/bevy_mod_scripting_rune", version = "0.9.0-alpha.2", optional = true }
bevy_mod_scripting_functions = { workspace = true }

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

# test utilities
script_integration_test_harness = { path = "crates/script_integration_test_harness" }
Expand All @@ -80,7 +78,7 @@ ansi-parser = "0.9"
members = [
"crates/bevy_mod_scripting_core",
"crates/languages/bevy_mod_scripting_lua",
# "crates/languages/bevy_mod_scripting_rhai",
"crates/languages/bevy_mod_scripting_rhai",
# "crates/languages/bevy_mod_scripting_rune",
"crates/test_utils",
"crates/bevy_mod_scripting_functions",
Expand Down Expand Up @@ -116,7 +114,12 @@ debug = true
[[example]]
name = "game_of_life"
path = "examples/game_of_life.rs"
required-features = ["lua54", "bevy/file_watcher", "bevy/multi_threaded"]
required-features = [
"lua54",
"rhai",
"bevy/file_watcher",
"bevy/multi_threaded",
]

[workspace.lints.clippy]
panic = "deny"
Expand Down
11 changes: 5 additions & 6 deletions assets/scripts/game_of_life.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
LifeState = world.get_type_by_name("LifeState")
Settings = world.get_type_by_name("Settings")

world.info("Lua: The game_of_life.lua script just got loaded")
info("Lua: The game_of_life.lua script just got loaded")

math.randomseed(os.time())

Expand All @@ -12,8 +12,8 @@ function fetch_life_state()
end

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

local life_state = fetch_life_state()
local cells = life_state.cells
Expand All @@ -27,8 +27,7 @@ end

function on_click(x,y)
-- get the settings
world.info("Lua: Clicked at x: " .. x .. " y: " .. y)
print(entity)
info("Lua: Clicked at x: " .. x .. " y: " .. y)
local life_state = fetch_life_state()
local cells = life_state.cells

Expand Down Expand Up @@ -110,7 +109,7 @@ function on_update()
end

function on_script_unloaded()
world.info("Lua: I am being unloaded, goodbye!")
info("Lua: I am being unloaded, goodbye!")

-- set state to 0's
local life_state = fetch_life_state()
Expand Down
114 changes: 91 additions & 23 deletions assets/scripts/game_of_life.rhai
Original file line number Diff line number Diff line change
@@ -1,45 +1,100 @@
info.call("Rhai: the game_of_life.rhai script just got loaded");


fn fetch_life_state() {
let LifeState = world.get_type_by_name.call("LifeState");
let Settings = world.get_type_by_name.call("Settings");
for (v,i) in world.query.call().component.call(LifeState).build.call(){
return v.components.call()[0]
}
}


fn on_script_loaded() {
world.info("Game of Life script loaded");
// let LifeState = world.get_type_by_name("LifeState");
// let life_state = world.get_component(entity,LifeState);
// let cells = life_state.cells;

// // set some cells alive
// for x in 1..10000 {
// let index = rand(0..cells.len());
// cells[index] = 255;
// }
let LifeState = world.get_type_by_name.call("LifeState");
let Settings = world.get_type_by_name.call("Settings");

info.call("Rhai: Hello! I am initiating the game of life simulation state with randomness!");
info.call("Rhai: Click on the screen to set cells alive after running the `gol start` command");

let life_state = fetch_life_state!();
let cells = life_state.cells;
let cells_len = cells.len.call();
let x = 0;
while x < 1000 {
let index = to_int(floor(rand.call()*cells_len)) ;
cells[index] = 255;
x += 1;
}
}

fn on_update() {
fn on_click(x,y) {
let Settings = world.get_type_by_name.call("Settings");
let LifeState = world.get_type_by_name.call("LifeState");

info.call("Rhai: Clicked at x: "+ x + ", y: " + y );
let life_state = fetch_life_state!();
let cells = life_state.cells;

let settings = world.get_resource.call(Settings);
let dimensions = settings.physical_grid_dimensions;
let screen = settings.display_grid_dimensions;

let dimension_x = dimensions["_0"];
let dimension_y = dimensions["_1"];

let LifeState = world.get_type_by_name("LifeState");
let Settings = world.get_type_by_name("Settings");
let screen_x = screen["_0"];
let screen_y = screen["_1"];

let life_state = world.get_component(entity,LifeState);
let cell_width = screen_x / dimension_x;
let cell_height = screen_y / dimension_y;

let cell_x = to_int(x / cell_width);
let cell_y = to_int(y / cell_height);

let index = cell_y * dimension_x + cell_x;
let cell_offsets_x = [0, 1, 0, 1, -1, 0, -1, 1, -1];
let cell_offsets_y = [0, 0, 1, 1, 0, -1, -1, -1, 1];
for (v,i) in cell_offsets_x {
let offset_x = cell_offsets_x[i];
let offset_y = cell_offsets_y[i];
let new_index = index + offset_x + offset_y * dimension_x;
if new_index >= 0 && new_index < (dimension_x * dimension_y) {
cells[new_index] = 255;
}
}

}

fn on_update() {
let LifeState = world.get_type_by_name.call("LifeState");
let Settings = world.get_type_by_name.call("Settings");

let life_state = fetch_life_state!();
let cells = life_state.cells;


// note that here we do not make use of RhaiProxyable and just go off pure reflection
let settings = world.get_resource(Settings);
let settings = world.get_resource.call(Settings);
let dimensions = settings.physical_grid_dimensions;

let dimension_x = dimensions["_0"];
let dimension_y = dimensions["_1"];

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

for i in 0..(dimensions[0] * dimensions[1]) {
let north = prev_state.get(i - dimensions[0]);
let south = prev_state.get(i + dimensions[0]);
for i in 0..(dimension_x * dimension_y) {
let north = prev_state.get(i - dimension_x);
let south = prev_state.get(i + dimension_x);
let east = prev_state.get(i + 1);
let west = prev_state.get(i - 1);
let northeast = prev_state.get(i - dimensions[0] + 1);
let southeast = prev_state.get(i + dimensions[0] + 1);
let northwest = prev_state.get(i - dimensions[0] - 1);
let southwest = prev_state.get(i + dimensions[0] - 1);
let northeast = prev_state.get(i - dimension_x + 1);
let southeast = prev_state.get(i + dimension_x + 1);
let northwest = prev_state.get(i - dimension_x - 1);
let southwest = prev_state.get(i + dimension_x - 1);

let neighbours = 0;
if north == () || north {neighbours+=1}
Expand All @@ -60,5 +115,18 @@ fn on_update() {
cells[i] = 0;
}
}
}

fn on_script_unloaded() {
let LifeState = world.get_type_by_name.call("LifeState");
let Settings = world.get_type_by_name.call("Settings");

info.call("Rhai: I am being unloaded, goodbye!");

// set state to 0's
let life_state = fetch_life_state!();
let cells = life_state.cells;
for i in 0..cells.len.call() {
cells[i] = 0;
}
}
9 changes: 6 additions & 3 deletions crates/bevy_mod_scripting_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ name = "bevy_mod_scripting_core"
path = "src/lib.rs"

[features]
default = []
# if enabled enables documentation updating in optimized builds
doc_always = []

# if enabled enables some common mlua trait implementations
mlua_impls = ["mlua"]
# rhai_impls = ["rhai"]
rhai_impls = ["rhai"]

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

bevy = { workspace = true, default-features = false, features = [
"bevy_asset",
Expand Down
14 changes: 12 additions & 2 deletions crates/bevy_mod_scripting_core/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,20 @@ pub(crate) fn sync_script_data<P: IntoScriptPluginParams>(
mut commands: Commands,
) {
for event in events.read() {
let metadata = match event {
ScriptAssetEvent::Added(script_metadata)
| ScriptAssetEvent::Removed(script_metadata)
| ScriptAssetEvent::Modified(script_metadata) => script_metadata,
};

if metadata.language != P::LANGUAGE {
continue;
}

trace!("{}: Received script asset event: {:?}", P::LANGUAGE, event);
match event {
// emitted when a new script asset is loaded for the first time
ScriptAssetEvent::Added(metadata) | ScriptAssetEvent::Modified(metadata) => {
ScriptAssetEvent::Added(_) | ScriptAssetEvent::Modified(_) => {
if metadata.language != P::LANGUAGE {
trace!(
"{}: Script asset with id: {} is for a different langauge than this sync system. Skipping.",
Expand All @@ -275,7 +285,7 @@ pub(crate) fn sync_script_data<P: IntoScriptPluginParams>(
));
}
}
ScriptAssetEvent::Removed(metadata) => {
ScriptAssetEvent::Removed(_) => {
info!("{}: Deleting Script: {:?}", P::LANGUAGE, metadata.script_id,);
commands.queue(DeleteScript::<P>::new(metadata.script_id.clone()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ impl ScriptFunctionRegistryArc {
}
}

#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct FunctionKey {
pub name: Cow<'static, str>,
pub namespace: Namespace,
Expand Down Expand Up @@ -504,6 +504,23 @@ impl ScriptFunctionRegistry {
pub fn iter_all(&self) -> impl Iterator<Item = (&FunctionKey, &DynamicScriptFunction)> {
self.functions.iter()
}

/// Insert a function into the registry with the given key, this will not perform any overloading logic.
/// Do not use unless you really need to.
pub fn raw_insert(
&mut self,
namespace: Namespace,
name: impl Into<Cow<'static, str>>,
func: DynamicScriptFunction,
) {
self.functions.insert(
FunctionKey {
name: name.into(),
namespace,
},
func,
);
}
}

macro_rules! count {
Expand Down
1 change: 0 additions & 1 deletion crates/bevy_mod_scripting_core/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ impl<P: IntoScriptPluginParams> Command for DeleteScript<P> {
script.context_id,
self.id
);
return;
}
};
} else {
Expand Down
Loading
Loading