Skip to content

feat: Add GlobalNamespace::system_builder, World::add_system and allow dynamic system creation #335

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 4 commits into from
Feb 28, 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
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ features = ["lua54", "rhai"]
default = ["core_functions", "bevy_bindings"]

## lua
lua = ["bevy_mod_scripting_lua"]
lua = ["bevy_mod_scripting_lua", "bevy_mod_scripting_functions/lua_bindings"]
# one of these must be selected
lua51 = ["bevy_mod_scripting_lua/lua51", "lua"]
lua52 = ["bevy_mod_scripting_lua/lua52", "lua"]
Expand All @@ -45,7 +45,7 @@ mlua_async = ["bevy_mod_scripting_lua?/mlua_async"]


## rhai
rhai = ["bevy_mod_scripting_rhai"]
rhai = ["bevy_mod_scripting_rhai", "bevy_mod_scripting_functions/rhai_bindings"]

## rune
# rune = ["bevy_mod_scripting_rune"]
Expand Down
48 changes: 48 additions & 0 deletions assets/tests/add_system/adds_system_in_correct_order.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- add two systems, one before and one after the existing `on_test_post_update` callback, then assert all systems have run
-- in the `on_test_last` callback

local runs = {}

-- runs on `Update`
function on_test()
local post_update_schedule = world.get_schedule_by_name("PostUpdate")

local test_system = post_update_schedule:get_system_by_name("on_test_post_update")

local system_after = world.add_system(
post_update_schedule,
system_builder("custom_system_after", script_id)
:after(test_system)
)

local system_before = world.add_system(
post_update_schedule,
system_builder("custom_system_before", script_id)
:before(test_system)
)
end


function custom_system_before()
print("custom_system_before")
runs[#runs + 1] = "custom_system_before"
end

-- runs on post_update
function on_test_post_update()
print("on_test_post_update")
runs[#runs + 1] = "on_test_post_update"
end

function custom_system_after()
print("custom_system_after")
runs[#runs + 1] = "custom_system_after"
end

-- runs in the `Last` bevy schedule
function on_test_last()
assert(#runs == 3, "Expected 3 runs, got: " .. #runs)
assert(runs[1] == "custom_system_before", "Expected custom_system_before to run first, got: " .. runs[1])
assert(runs[2] == "on_test_post_update", "Expected on_test_post_update to run second, got: " .. runs[2])
assert(runs[3] == "custom_system_after", "Expected custom_system_after to run third, got: " .. runs[3])
end
34 changes: 34 additions & 0 deletions assets/tests/add_system/adds_system_in_correct_order.rhai
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
let runs = [];

fn on_test() {
let post_update_schedule = world.get_schedule_by_name.call("PostUpdate");
let test_system = post_update_schedule.get_system_by_name.call("on_test_post_update");

let builder_after = system_builder.call("custom_system_after", script_id).after.call(test_system);
let system_after = world.add_system.call(post_update_schedule, builder_after);

let builder_before = system_builder.call("custom_system_before", script_id).before.call(test_system);
let system_before = world.add_system.call(post_update_schedule, builder_before);
}

fn custom_system_before() {
print("custom_system_before");
runs.push("custom_system_before");
}

fn on_test_post_update() {
print("on_test_post_update");
runs.push("on_test_post_update");
}

fn custom_system_after() {
print("custom_system_after");
runs.push("custom_system_after");
}

fn on_test_last() {
assert(runs.len() == 3, "Expected 3 runs, got: " + runs.len().to_string());
assert(runs[0] == "custom_system_before", "Expected custom_system_before to run first, got: " + runs[0]);
assert(runs[1] == "on_test_post_update", "Expected on_test_post_update to run second, got: " + runs[1]);
assert(runs[2] == "custom_system_after", "Expected custom_system_after to run third, got: " + runs[2]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
assert(world.get_schedule_by_name("Update") ~= nil, "Schedule not found under short identifier")
assert(world.get_schedule_by_name("bevy_app::main_schedule::Update") ~= nil, "Schedule not found under long identifier")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
assert(type_of(world.get_schedule_by_name.call("Update")) != "()", "Schedule not found under short identifier");
assert(type_of(world.get_schedule_by_name.call("bevy_app::main_schedule::Update")) != "()", "Schedule not found under long identifier");
144 changes: 0 additions & 144 deletions assets/tests/rhai_tests.rs

This file was deleted.

13 changes: 13 additions & 0 deletions assets/tests/schedule/gets_all_schedule_systems.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function on_test()
local startup_schedule = world.get_schedule_by_name("Startup")


local expected_systems = {
"dummy_startup_system",
}

for i, system in ipairs(expected_systems) do
local found_system = startup_schedule:get_system_by_name(system)
assert(found_system ~= nil, "Expected system not found: " .. system)
end
end
13 changes: 13 additions & 0 deletions assets/tests/schedule/gets_all_schedule_systems.rhai
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
fn on_test() {
let startup_schedule = world.get_schedule_by_name.call("Startup");


let expected_systems = [
"dummy_startup_system"
];

for system in expected_systems {
let found_system = startup_schedule.get_system_by_name.call(system);
assert(type_of(found_system) != "()", "Expected system not found: " + system);
}
}
1 change: 1 addition & 0 deletions crates/bevy_mod_scripting_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ derivative = "2.2"
profiling = { workspace = true }
bevy_mod_scripting_derive = { workspace = true }
fixedbitset = "0.5"
petgraph = "0.6"

[dev-dependencies]
test_utils = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Implementations of the [`ScriptFunction`] and [`ScriptFunctionMut`] traits for functions with up to 13 arguments.

use super::{from::FromScript, into::IntoScript, namespace::Namespace};
use crate::asset::Language;
use crate::bindings::function::arg_meta::ArgMeta;
use crate::docgen::info::{FunctionInfo, GetFunctionInfo};
use crate::{
Expand Down Expand Up @@ -38,24 +39,30 @@ 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, Copy, Debug, Reflect, Default)]
#[derive(Clone, Debug, Reflect)]
#[reflect(opaque)]
pub struct FunctionCallContext {
/// Whether the caller uses 1-indexing on all indexes and expects 0-indexing conversions to be performed.
pub convert_to_0_indexed: bool,
language: Language,
}
impl FunctionCallContext {
/// Create a new FunctionCallContext with the given 1-indexing conversion preference
pub fn new(convert_to_0_indexed: bool) -> Self {
Self {
convert_to_0_indexed,
}
pub const fn new(language: Language) -> Self {
Self { language }
}

/// Tries to access the world, returning an error if the world is not available
pub fn world<'l>(&self) -> Result<WorldGuard<'l>, InteropError> {
ThreadWorldContainer.try_get_world()
}
/// Whether the caller uses 1-indexing on all indexes and expects 0-indexing conversions to be performed.
pub fn convert_to_0_indexed(&self) -> bool {
matches!(&self.language, Language::Lua)
}

/// Gets the scripting language of the caller
pub fn language(&self) -> Language {
self.language.clone()
}
}

#[derive(Clone, Reflect)]
Expand Down Expand Up @@ -589,7 +596,7 @@ macro_rules! impl_script_function {
let received_args_len = args.len();
let expected_arg_count = count!($($param )*);

$( let $context = caller_context; )?
$( let $context = caller_context.clone(); )?
let world = caller_context.world()?;
// Safety: we're not holding any references to the world, the arguments which might have aliased will always be dropped
let ret: Result<ScriptValue, InteropError> = unsafe {
Expand Down Expand Up @@ -672,7 +679,10 @@ mod test {

with_local_world(|| {
let out = script_function
.call(vec![ScriptValue::from(1)], FunctionCallContext::default())
.call(
vec![ScriptValue::from(1)],
FunctionCallContext::new(Language::Lua),
)
.unwrap();

assert_eq!(out, ScriptValue::from(1));
Expand All @@ -685,8 +695,10 @@ mod test {
let script_function = fn_.into_dynamic_script_function().with_name("my_fn");

with_local_world(|| {
let out =
script_function.call(vec![ScriptValue::from(1)], FunctionCallContext::default());
let out = script_function.call(
vec![ScriptValue::from(1)],
FunctionCallContext::new(Language::Lua),
);

assert!(out.is_err());
assert_eq!(
Expand All @@ -709,11 +721,13 @@ mod test {
let script_function = fn_.into_dynamic_script_function().with_name("my_fn");

with_local_world(|| {
let out =
script_function.call(vec![ScriptValue::from(1)], FunctionCallContext::default());
let out = script_function.call(
vec![ScriptValue::from(1)],
FunctionCallContext::new(Language::Lua),
);

assert!(out.is_err());
let world = FunctionCallContext::default().world().unwrap();
let world = FunctionCallContext::new(Language::Lua).world().unwrap();
// assert no access is held
assert!(world.list_accesses().is_empty());
});
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_mod_scripting_core/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod function;
pub mod pretty_print;
pub mod query;
pub mod reference;
pub mod schedule;
pub mod script_value;
pub mod world;

Expand Down
Loading
Loading