From 096710d1187d3275c2576a6137c97a2823294e60 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 16 Jun 2024 13:09:26 +0200 Subject: [PATCH 001/165] ruby boilerplate --- Cargo.toml | 4 +- src/runtimes/mod.rs | 2 + src/runtimes/ruby.rs | 178 +++++++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 29 +++++++ 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/runtimes/ruby.rs diff --git a/Cargo.toml b/Cargo.toml index 5eb7042..34eb2db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["bevy", "rhai", "scripting", "game", "gamedev"] [features] lua = ["mlua/luajit"] rhai = ["dep:rhai"] +ruby = ["dep:magnus"] [dependencies] bevy = { default-features = false, version = "0.16", features = ["bevy_asset", "bevy_log"] } @@ -25,11 +26,12 @@ rhai = { version = "1.14.0", features = [ thiserror = "1.0.40" anyhow = "1.0.82" tracing = "0.1.40" -mlua = { version = "0.9.8", features = [ +lua = { version = "0.9.8", features = [ "luajit", "vendored", "send", ], optional = true } +magnus = { version = "0.6.4", optional = true } [[example]] name = "call_function_from_rust_rhai" diff --git a/src/runtimes/mod.rs b/src/runtimes/mod.rs index c01166f..96bf325 100644 --- a/src/runtimes/mod.rs +++ b/src/runtimes/mod.rs @@ -2,3 +2,5 @@ pub mod lua; #[cfg(feature = "rhai")] pub mod rhai; +#[cfg(feature = "ruby")] +pub mod ruby; diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs new file mode 100644 index 0000000..15b9a18 --- /dev/null +++ b/src/runtimes/ruby.rs @@ -0,0 +1,178 @@ +use bevy::{ + asset::Asset, + ecs::{component::Component, schedule::ScheduleLabel, system::Resource}, + reflect::TypePath, +}; +use serde::Deserialize; + +use crate::{ + assets::GetExtensions, + callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, + FuncArgs, Runtime, +}; + +#[derive(Resource)] +pub struct RubyRuntime {} + +#[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)] +pub struct RubySchedule; + +#[derive(Asset, Debug, Deserialize, TypePath)] +pub struct RubyScript(pub String); + +#[derive(Component)] +pub struct RubyScriptData; + +impl GetExtensions for RubyScript { + fn extensions() -> &'static [&'static str] { + todo!() + } +} + +impl From for RubyScript { + fn from(value: String) -> Self { + todo!() + } +} + +impl Default for RubyRuntime { + fn default() -> Self { + Self {} + } +} + +#[derive(Clone)] +pub struct RubyValue(()); + +impl Runtime for RubyRuntime { + type Schedule = RubySchedule; + + type ScriptAsset = RubyScript; + + type ScriptData = RubyScriptData; + + type CallContext = (); + + type Value = RubyValue; + + type RawEngine = (); + + fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { + todo!() + } + + fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T { + todo!() + } + + fn eval( + &self, + script: &Self::ScriptAsset, + entity: bevy::prelude::Entity, + ) -> Result { + todo!() + } + + fn register_fn( + &mut self, + name: String, + arg_types: Vec, + f: impl Fn( + Self::CallContext, + Vec, + ) -> Result< + crate::promise::Promise, + crate::ScriptingError, + > + Send + + Sync + + 'static, + ) -> Result<(), crate::ScriptingError> { + todo!() + } + + fn call_fn( + &self, + name: &str, + script_data: &mut Self::ScriptData, + entity: bevy::prelude::Entity, + args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, + ) -> Result { + todo!() + } + + fn call_fn_from_value( + &self, + value: &Self::Value, + context: &Self::CallContext, + args: Vec, + ) -> Result { + todo!() + } +} + +pub mod prelude { + pub use super::RubyRuntime; +} + +impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { + fn from_runtime_value_with_engine(value: RubyValue, engine: &()) -> Self { + todo!(); + } +} + +impl IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { + fn into_runtime_value_with_engine(value: T, engine: &()) -> RubyValue { + todo!(); + } +} + +impl FuncArgs<'_, RubyValue, RubyRuntime> for () { + fn parse(self, _engine: &()) -> Vec { + Vec::new() + } +} + +impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { + fn parse(self, engine: &()) -> Vec { + self.into_iter().map(|x| RubyValue(())).collect() + } +} + +macro_rules! impl_tuple { + ($($idx:tt $t:tt),+) => { + impl<'a, $($t,)+> FuncArgs<'a, RubyValue, RubyRuntime> + for ($($t,)+) + { + fn parse(self, engine: &'a ()) -> Vec { + todo!(); + } + } + }; +} + +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X, 24 Y, 25 Z); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X, 24 Y); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F); +impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E); +impl_tuple!(0 A, 1 B, 2 C, 3 D); +impl_tuple!(0 A, 1 B, 2 C); +impl_tuple!(0 A, 1 B); +impl_tuple!(0 A); diff --git a/tests/tests.rs b/tests/tests.rs index 7dcf443..825e6ec 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -482,3 +482,32 @@ mod lua_tests { scripting_tests!(LuaRuntime, "lua", "lua"); } + +#[cfg(feature = "ruby")] +mod ruby_tests { + use bevy::prelude::*; + use bevy_scriptum::runtimes::ruby::{prelude::*, RubyScriptData}; + + impl AssertStateKeyValue for RubyRuntime { + type ScriptData = RubyScriptData; + + fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { + todo!(); + } + + fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { + todo!(); + } + + fn assert_state_key_value_string( + world: &World, + _entity_id: Entity, + key: &str, + value: &str, + ) { + todo!(); + } + } + + scripting_tests!(RubyRuntime, "ruby", "rb"); +} -- 2.45.2 From d8f74b0d12cc02acf1e804ebbe11e058998aea66 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 8 May 2025 23:31:10 +0200 Subject: [PATCH 002/165] fix tests --- Cargo.toml | 4 ++-- src/runtimes/ruby.rs | 2 +- tests/tests.rs | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 34eb2db..d5e6c4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,12 +26,12 @@ rhai = { version = "1.14.0", features = [ thiserror = "1.0.40" anyhow = "1.0.82" tracing = "0.1.40" -lua = { version = "0.9.8", features = [ +mlua = { version = "0.9.8", features = [ "luajit", "vendored", "send", ], optional = true } -magnus = { version = "0.6.4", optional = true } +magnus = { version = "0.7.1", optional = true } [[example]] name = "call_function_from_rust_rhai" diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 15b9a18..293b67a 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,6 +1,6 @@ use bevy::{ asset::Asset, - ecs::{component::Component, schedule::ScheduleLabel, system::Resource}, + ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, reflect::TypePath, }; use serde::Deserialize; diff --git a/tests/tests.rs b/tests/tests.rs index 825e6ec..0744ec6 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,17 +1,17 @@ -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] use std::sync::OnceLock; -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] use bevy::ecs::system::RunSystemOnce as _; -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] use bevy::prelude::*; -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] use bevy_scriptum::{prelude::*, FuncArgs, Runtime}; -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] static TRACING_SUBSCRIBER: OnceLock<()> = OnceLock::new(); -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] fn build_test_app() -> App { let mut app = App::new(); @@ -25,7 +25,7 @@ fn build_test_app() -> App { app } -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] fn run_script( app: &mut App, path: String, @@ -42,7 +42,7 @@ fn run_script( entity_id } -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] fn call_script_on_update_from_rust( mut scripted_entities: Query<(Entity, &mut R::ScriptData)>, scripting_runtime: ResMut, @@ -55,7 +55,7 @@ fn call_script_on_update_from_rust( .unwrap(); } -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] trait AssertStateKeyValue { type ScriptData; fn assert_state_key_value_i64(world: &World, entity_id: Entity, key: &str, value: i64); @@ -63,7 +63,7 @@ trait AssertStateKeyValue { fn assert_state_key_value_string(world: &World, entity_id: Entity, key: &str, value: &str); } -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] macro_rules! scripting_tests { ($runtime:ty, $script:literal, $extension:literal) => { use super::*; -- 2.45.2 From 5c8ec10ae90f0b521e11c808656afe31db627189 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 8 May 2025 23:48:45 +0200 Subject: [PATCH 003/165] add nvim config to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a7c83b7..71066f2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /Cargo.lock .vscode rust-analyzer.json +.nvim.lua -- 2.45.2 From 417f4a1bfa5467456789e80bc18dbd7d67f30956 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 9 May 2025 13:11:33 +0200 Subject: [PATCH 004/165] init engine --- Cargo.toml | 2 +- .../script_function_gets_called_from_rust.rb | 7 +++ src/lib.rs | 8 +++- src/runtimes/ruby.rs | 44 +++++++++++++++++-- tests/tests.rs | 2 +- 5 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 assets/tests/ruby/script_function_gets_called_from_rust.rb diff --git a/Cargo.toml b/Cargo.toml index d5e6c4b..982f8f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ mlua = { version = "0.9.8", features = [ "vendored", "send", ], optional = true } -magnus = { version = "0.7.1", optional = true } +magnus = { version = "0.7.1", optional = true, features = ["embed"] } [[example]] name = "call_function_from_rust_rhai" diff --git a/assets/tests/ruby/script_function_gets_called_from_rust.rb b/assets/tests/ruby/script_function_gets_called_from_rust.rb new file mode 100644 index 0000000..aaf3683 --- /dev/null +++ b/assets/tests/ruby/script_function_gets_called_from_rust.rb @@ -0,0 +1,7 @@ +$state = { + times_called: 0 +} + +def test_func + $state[:times_called] += 1 +end diff --git a/src/lib.rs b/src/lib.rs index 5f23c69..37fcc75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -259,7 +259,11 @@ use std::{ sync::{Arc, Mutex}, }; -use bevy::{app::MainScheduleOrder, ecs::{component::Mutable, schedule::ScheduleLabel}, prelude::*}; +use bevy::{ + app::MainScheduleOrder, + ecs::{component::Mutable, schedule::ScheduleLabel}, + prelude::*, +}; use callback::{Callback, IntoCallbackSystem}; use systems::{init_callbacks, log_errors, process_calls}; use thiserror::Error; @@ -269,7 +273,7 @@ use self::{ systems::{process_new_scripts, reload_scripts}, }; -#[cfg(any(feature = "rhai", feature = "lua"))] +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] const ENTITY_VAR_NAME: &str = "entity"; /// An error that can occur when internal [ScriptingPlugin] systems are being executed diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 293b67a..66f9afc 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,8 +1,17 @@ +use std::{ + cell::{LazyCell, OnceCell}, + sync::{LazyLock, Mutex, OnceLock}, +}; + use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, reflect::TypePath, }; +use magnus::{ + embed::{init, Cleanup}, + prelude::*, +}; use serde::Deserialize; use crate::{ @@ -25,18 +34,37 @@ pub struct RubyScriptData; impl GetExtensions for RubyScript { fn extensions() -> &'static [&'static str] { - todo!() + &["rb"] } } impl From for RubyScript { fn from(value: String) -> Self { - todo!() + Self(value) } } +fn hello(subject: String) -> String { + format!("hello, {}", subject) +} + +struct RubyEngine(Cleanup); + +unsafe impl Send for RubyEngine {} + +static RUBY_ENGINE: OnceLock> = OnceLock::new(); + impl Default for RubyRuntime { fn default() -> Self { + RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); + // TODO: Add SAFETY? + + // engine.define_global_function("hello", magnus::function!(hello, 1)); + // engine + // .eval::(r#"puts hello("world")"#) + // .unwrap(); + // + // Self { engine } Self {} } } @@ -70,7 +98,11 @@ impl Runtime for RubyRuntime { script: &Self::ScriptAsset, entity: bevy::prelude::Entity, ) -> Result { - todo!() + let engine = + RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); + let engine = engine.lock().unwrap(); + engine.0.eval::(&script.0); + Ok(RubyScriptData) } fn register_fn( @@ -97,7 +129,11 @@ impl Runtime for RubyRuntime { entity: bevy::prelude::Entity, args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, ) -> Result { - todo!() + let engine = + RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); + let ruby = magnus::Ruby::get().unwrap(); + let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); + todo!(); } fn call_fn_from_value( diff --git a/tests/tests.rs b/tests/tests.rs index 0744ec6..1b98a95 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -297,7 +297,7 @@ macro_rules! scripting_tests { } #[test] - fn test_script_function_gets_called_from_rust() { + fn test_script_function_gets_called_from_rust_without_params() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|_| {}); -- 2.45.2 From 6be5a4dec1c46002a73d2786b0e0207a6bccc94b Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 9 May 2025 13:40:29 +0200 Subject: [PATCH 005/165] pass simple test --- .../script_function_gets_called_from_rust.rb | 6 ++--- src/runtimes/ruby.rs | 27 +++++++------------ tests/tests.rs | 9 ++++++- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/assets/tests/ruby/script_function_gets_called_from_rust.rb b/assets/tests/ruby/script_function_gets_called_from_rust.rb index aaf3683..2ed4705 100644 --- a/assets/tests/ruby/script_function_gets_called_from_rust.rb +++ b/assets/tests/ruby/script_function_gets_called_from_rust.rb @@ -1,7 +1,7 @@ -$state = { - times_called: 0 +STATE = { + "times_called" => 0 } def test_func - $state[:times_called] += 1 + STATE["times_called"] += 1 end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 66f9afc..bd1b9bd 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -50,21 +50,16 @@ fn hello(subject: String) -> String { struct RubyEngine(Cleanup); +// TODO: Add SAFETY? unsafe impl Send for RubyEngine {} -static RUBY_ENGINE: OnceLock> = OnceLock::new(); +// TODO: thread local +static RUBY_ENGINE: LazyLock> = + LazyLock::new(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); impl Default for RubyRuntime { fn default() -> Self { - RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); - // TODO: Add SAFETY? - - // engine.define_global_function("hello", magnus::function!(hello, 1)); - // engine - // .eval::(r#"puts hello("world")"#) - // .unwrap(); - // - // Self { engine } + LazyLock::force(&RUBY_ENGINE); Self {} } } @@ -98,10 +93,10 @@ impl Runtime for RubyRuntime { script: &Self::ScriptAsset, entity: bevy::prelude::Entity, ) -> Result { - let engine = - RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); - let engine = engine.lock().unwrap(); - engine.0.eval::(&script.0); + // let engine = + // RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); + let ruby = magnus::Ruby::get().unwrap(); + ruby.eval::(&script.0); Ok(RubyScriptData) } @@ -129,11 +124,9 @@ impl Runtime for RubyRuntime { entity: bevy::prelude::Entity, args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, ) -> Result { - let engine = - RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); let ruby = magnus::Ruby::get().unwrap(); let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); - todo!(); + Ok(RubyValue(())) } fn call_fn_from_value( diff --git a/tests/tests.rs b/tests/tests.rs index 1b98a95..8d2a022 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -487,12 +487,19 @@ mod lua_tests { mod ruby_tests { use bevy::prelude::*; use bevy_scriptum::runtimes::ruby::{prelude::*, RubyScriptData}; + use magnus::{value::ReprValue, Module, Object, Ruby}; impl AssertStateKeyValue for RubyRuntime { type ScriptData = RubyScriptData; fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { - todo!(); + let state: magnus::value::Value = Ruby::get() + .unwrap() + .class_object() + .const_get("STATE") + .unwrap(); + let res: i64 = state.funcall_public("[]", (key.to_string(),)).unwrap(); + assert_eq!(res, value) } fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { -- 2.45.2 From a4c5911603b4bb4a9f32bf62781e93e6b501d7f7 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 9 May 2025 18:19:44 +0200 Subject: [PATCH 006/165] call rust from ruby --- .../rust_function_gets_called_from_script.rb | 3 +++ src/runtimes/ruby.rs | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 assets/tests/ruby/rust_function_gets_called_from_script.rb diff --git a/assets/tests/ruby/rust_function_gets_called_from_script.rb b/assets/tests/ruby/rust_function_gets_called_from_script.rb new file mode 100644 index 0000000..fc70a31 --- /dev/null +++ b/assets/tests/ruby/rust_function_gets_called_from_script.rb @@ -0,0 +1,3 @@ +def test_func() + rust_func() +end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index bd1b9bd..7e808f3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -10,6 +10,7 @@ use bevy::{ }; use magnus::{ embed::{init, Cleanup}, + function, prelude::*, }; use serde::Deserialize; @@ -93,8 +94,6 @@ impl Runtime for RubyRuntime { script: &Self::ScriptAsset, entity: bevy::prelude::Entity, ) -> Result { - // let engine = - // RUBY_ENGINE.get_or_init(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); let ruby = magnus::Ruby::get().unwrap(); ruby.eval::(&script.0); Ok(RubyScriptData) @@ -114,7 +113,19 @@ impl Runtime for RubyRuntime { + Sync + 'static, ) -> Result<(), crate::ScriptingError> { - todo!() + let ruby = magnus::Ruby::get().unwrap(); + + unsafe extern "C" fn callback(val: magnus::Value) -> magnus::Value { + let ruby = magnus::Ruby::get().unwrap(); + // f(); + ruby.qnil().as_value() + } + + ruby.define_global_function( + &name, + callback as unsafe extern "C" fn(magnus::Value) -> magnus::Value, + ); + Ok(()) } fn call_fn( -- 2.45.2 From b307cf6c13b5eb7f819017cc3819fd0bc24b1c03 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 9 May 2025 18:42:37 +0200 Subject: [PATCH 007/165] call rust from ruby --- src/runtimes/ruby.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 7e808f3..b1a712f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -79,10 +79,10 @@ impl Runtime for RubyRuntime { type Value = RubyValue; - type RawEngine = (); + type RawEngine = magnus::Ruby; fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { - todo!() + f(&mut magnus::Ruby::get().unwrap()) } fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T { @@ -115,16 +115,22 @@ impl Runtime for RubyRuntime { ) -> Result<(), crate::ScriptingError> { let ruby = magnus::Ruby::get().unwrap(); - unsafe extern "C" fn callback(val: magnus::Value) -> magnus::Value { + static mut FUN: Vec> = Vec::new(); + unsafe { + FUN.push(Box::new(move || { + f((), vec![]).unwrap(); + })); + } + + fn callback() -> magnus::Value { let ruby = magnus::Ruby::get().unwrap(); - // f(); + unsafe { + FUN.pop().unwrap()(); + } ruby.qnil().as_value() } - ruby.define_global_function( - &name, - callback as unsafe extern "C" fn(magnus::Value) -> magnus::Value, - ); + ruby.define_global_function(&name, function!(callback, 0)); Ok(()) } @@ -155,25 +161,25 @@ pub mod prelude { } impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { - fn from_runtime_value_with_engine(value: RubyValue, engine: &()) -> Self { + fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self { todo!(); } } impl IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { - fn into_runtime_value_with_engine(value: T, engine: &()) -> RubyValue { - todo!(); + fn into_runtime_value_with_engine(value: T, engine: &magnus::Ruby) -> RubyValue { + RubyValue(()) } } impl FuncArgs<'_, RubyValue, RubyRuntime> for () { - fn parse(self, _engine: &()) -> Vec { + fn parse(self, _engine: &magnus::Ruby) -> Vec { Vec::new() } } impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { - fn parse(self, engine: &()) -> Vec { + fn parse(self, engine: &magnus::Ruby) -> Vec { self.into_iter().map(|x| RubyValue(())).collect() } } @@ -183,7 +189,7 @@ macro_rules! impl_tuple { impl<'a, $($t,)+> FuncArgs<'a, RubyValue, RubyRuntime> for ($($t,)+) { - fn parse(self, engine: &'a ()) -> Vec { + fn parse(self, engine: &'a magnus::Ruby) -> Vec { todo!(); } } -- 2.45.2 From 02d0662294ff577ea45c0b230db5a40396838e33 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 9 May 2025 20:49:41 +0200 Subject: [PATCH 008/165] create ruby thread --- src/runtimes/ruby.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index b1a712f..d705a0a 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,6 +1,7 @@ use std::{ cell::{LazyCell, OnceCell}, sync::{LazyLock, Mutex, OnceLock}, + thread::{self, JoinHandle}, }; use bevy::{ @@ -22,7 +23,9 @@ use crate::{ }; #[derive(Resource)] -pub struct RubyRuntime {} +pub struct RubyRuntime { + ruby_thread: Option>, +} #[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)] pub struct RubySchedule; @@ -44,24 +47,30 @@ impl From for RubyScript { Self(value) } } - -fn hello(subject: String) -> String { - format!("hello, {}", subject) -} - struct RubyEngine(Cleanup); // TODO: Add SAFETY? unsafe impl Send for RubyEngine {} -// TODO: thread local static RUBY_ENGINE: LazyLock> = LazyLock::new(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); impl Default for RubyRuntime { fn default() -> Self { - LazyLock::force(&RUBY_ENGINE); - Self {} + let ruby_thread = thread::spawn(|| { + let _cleanup = LazyLock::force(&RUBY_ENGINE); + while true {} + }); + Self { + ruby_thread: Some(ruby_thread), + } + } +} + +impl Drop for RubyRuntime { + fn drop(&mut self) { + let ruby_thread = self.ruby_thread.take().unwrap(); + ruby_thread.join().unwrap(); } } -- 2.45.2 From e82278155a861967b7b1e40ef5667708f50481f8 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 9 May 2025 21:13:45 +0200 Subject: [PATCH 009/165] ruby thread channel --- Cargo.toml | 1 + src/runtimes/ruby.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 982f8f9..60f10ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ mlua = { version = "0.9.8", features = [ "send", ], optional = true } magnus = { version = "0.7.1", optional = true, features = ["embed"] } +crossbeam-channel = "0.5.15" [[example]] name = "call_function_from_rust_rhai" diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index d705a0a..d788747 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,3 +1,4 @@ +use std::sync::mpsc::{Receiver, Sender}; use std::{ cell::{LazyCell, OnceCell}, sync::{LazyLock, Mutex, OnceLock}, @@ -25,6 +26,7 @@ use crate::{ #[derive(Resource)] pub struct RubyRuntime { ruby_thread: Option>, + ruby_thread_sender: Option>, } #[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)] @@ -57,18 +59,23 @@ static RUBY_ENGINE: LazyLock> = impl Default for RubyRuntime { fn default() -> Self { - let ruby_thread = thread::spawn(|| { + let (ruby_thread_sender, ruby_thread_receiver) = crossbeam_channel::unbounded::<()>(); + let ruby_thread = thread::spawn(move || { let _cleanup = LazyLock::force(&RUBY_ENGINE); - while true {} + while let Ok(val) = ruby_thread_receiver.recv() { + println!("received"); + } }); Self { ruby_thread: Some(ruby_thread), + ruby_thread_sender: Some(ruby_thread_sender), } } } impl Drop for RubyRuntime { fn drop(&mut self) { + drop(self.ruby_thread_sender.take().unwrap()); let ruby_thread = self.ruby_thread.take().unwrap(); ruby_thread.join().unwrap(); } @@ -131,7 +138,11 @@ impl Runtime for RubyRuntime { })); } + let sender = self.ruby_thread_sender.as_ref().clone(); + let x = 5; + fn callback() -> magnus::Value { + // sender.unwrap().send(()); let ruby = magnus::Ruby::get().unwrap(); unsafe { FUN.pop().unwrap()(); -- 2.45.2 From aee3276f2b0b50e8f2ddca3be5fa14a3fdde0bf9 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 9 May 2025 22:43:16 +0200 Subject: [PATCH 010/165] execute ruby stuff on ruby thread --- src/runtimes/ruby.rs | 76 ++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index d788747..235031c 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -10,6 +10,7 @@ use bevy::{ ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, reflect::TypePath, }; +use magnus::Ruby; use magnus::{ embed::{init, Cleanup}, function, @@ -26,7 +27,7 @@ use crate::{ #[derive(Resource)] pub struct RubyRuntime { ruby_thread: Option>, - ruby_thread_sender: Option>, + ruby_thread_sender: Option>>, } #[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)] @@ -54,16 +55,17 @@ struct RubyEngine(Cleanup); // TODO: Add SAFETY? unsafe impl Send for RubyEngine {} -static RUBY_ENGINE: LazyLock> = - LazyLock::new(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); - impl Default for RubyRuntime { fn default() -> Self { - let (ruby_thread_sender, ruby_thread_receiver) = crossbeam_channel::unbounded::<()>(); + let (ruby_thread_sender, ruby_thread_receiver) = + crossbeam_channel::unbounded::>(); let ruby_thread = thread::spawn(move || { - let _cleanup = LazyLock::force(&RUBY_ENGINE); + static RUBY_ENGINE: LazyLock> = + LazyLock::new(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); + LazyLock::force(&RUBY_ENGINE); while let Ok(val) = ruby_thread_receiver.recv() { - println!("received"); + let ruby = Ruby::get().unwrap(); + val(ruby); } }); Self { @@ -110,8 +112,15 @@ impl Runtime for RubyRuntime { script: &Self::ScriptAsset, entity: bevy::prelude::Entity, ) -> Result { - let ruby = magnus::Ruby::get().unwrap(); - ruby.eval::(&script.0); + let script = script.0.clone(); + self.ruby_thread_sender + .as_ref() + .unwrap() + .send(Box::new(move |ruby| { + ruby.eval::(&script).unwrap(); + })) + .unwrap(); + Ok(RubyScriptData) } @@ -129,28 +138,34 @@ impl Runtime for RubyRuntime { + Sync + 'static, ) -> Result<(), crate::ScriptingError> { - let ruby = magnus::Ruby::get().unwrap(); + // let ruby = magnus::Ruby::get().unwrap(); + // + // static mut FUN: Vec> = Vec::new(); + // unsafe { + // FUN.push(Box::new(move || { + // f((), vec![]).unwrap(); + // })); + // } + // - static mut FUN: Vec> = Vec::new(); - unsafe { - FUN.push(Box::new(move || { - f((), vec![]).unwrap(); - })); - } - - let sender = self.ruby_thread_sender.as_ref().clone(); - let x = 5; - - fn callback() -> magnus::Value { + fn callback(val: magnus::Value) -> magnus::Value { + // println!("{:?}", val); // sender.unwrap().send(()); let ruby = magnus::Ruby::get().unwrap(); - unsafe { - FUN.pop().unwrap()(); - } + // unsafe { + // FUN.pop().unwrap()(); + // } ruby.qnil().as_value() } - ruby.define_global_function(&name, function!(callback, 0)); + self.ruby_thread_sender + .as_ref() + .unwrap() + .send(Box::new(move |ruby| { + ruby.define_global_function(&name, function!(callback, 1)); + })) + .unwrap(); + Ok(()) } @@ -161,8 +176,15 @@ impl Runtime for RubyRuntime { entity: bevy::prelude::Entity, args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, ) -> Result { - let ruby = magnus::Ruby::get().unwrap(); - let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); + let name = name.to_string(); + self.ruby_thread_sender + .as_ref() + .unwrap() + .send(Box::new(move |ruby| { + let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); + })) + .unwrap(); + Ok(RubyValue(())) } -- 2.45.2 From a3a40182f57bb9a64a975d9a88ae7d870c00eaeb Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 10 May 2025 09:20:18 +0200 Subject: [PATCH 011/165] ruby thread spawn one per binary --- src/runtimes/ruby.rs | 91 ++++++++++++++++++++++++-------------------- tests/tests.rs | 14 +++---- 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 235031c..d1e6b27 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -25,10 +25,7 @@ use crate::{ }; #[derive(Resource)] -pub struct RubyRuntime { - ruby_thread: Option>, - ruby_thread_sender: Option>>, -} +pub struct RubyRuntime {} #[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)] pub struct RubySchedule; @@ -55,31 +52,54 @@ struct RubyEngine(Cleanup); // TODO: Add SAFETY? unsafe impl Send for RubyEngine {} -impl Default for RubyRuntime { - fn default() -> Self { - let (ruby_thread_sender, ruby_thread_receiver) = - crossbeam_channel::unbounded::>(); - let ruby_thread = thread::spawn(move || { - static RUBY_ENGINE: LazyLock> = - LazyLock::new(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() }))); - LazyLock::force(&RUBY_ENGINE); - while let Ok(val) = ruby_thread_receiver.recv() { +struct RubyThread { + sender: Option>>, + handle: Option>, +} + +static RUBY_THREAD: LazyLock = LazyLock::new(|| RubyThread::spawn()); + +impl RubyThread { + fn spawn() -> Self { + let (sender, receiver) = crossbeam_channel::unbounded::>(); + + let handle = thread::spawn(move || { + unsafe { magnus::embed::init() }; + while let Ok(val) = receiver.recv() { let ruby = Ruby::get().unwrap(); val(ruby); } }); - Self { - ruby_thread: Some(ruby_thread), - ruby_thread_sender: Some(ruby_thread_sender), + + RubyThread { + sender: Some(sender), + handle: Some(handle), } } + + fn execute_in(&self, f: Box) { + self.sender + .as_ref() + .unwrap() + .send(Box::new(move |ruby| { + let result = f(ruby); + println!("{:?}", result); + })) + .unwrap(); + } } -impl Drop for RubyRuntime { +impl Drop for RubyThread { fn drop(&mut self) { - drop(self.ruby_thread_sender.take().unwrap()); - let ruby_thread = self.ruby_thread.take().unwrap(); - ruby_thread.join().unwrap(); + drop(self.sender.take().unwrap()); + let handle = self.handle.take().unwrap(); + handle.join().unwrap(); + } +} + +impl Default for RubyRuntime { + fn default() -> Self { + Self {} } } @@ -113,14 +133,9 @@ impl Runtime for RubyRuntime { entity: bevy::prelude::Entity, ) -> Result { let script = script.0.clone(); - self.ruby_thread_sender - .as_ref() - .unwrap() - .send(Box::new(move |ruby| { - ruby.eval::(&script).unwrap(); - })) - .unwrap(); - + RUBY_THREAD.execute_in(Box::new(move |ruby| { + ruby.eval::(&script).unwrap(); + })); Ok(RubyScriptData) } @@ -158,13 +173,9 @@ impl Runtime for RubyRuntime { ruby.qnil().as_value() } - self.ruby_thread_sender - .as_ref() - .unwrap() - .send(Box::new(move |ruby| { - ruby.define_global_function(&name, function!(callback, 1)); - })) - .unwrap(); + RUBY_THREAD.execute_in(Box::new(move |ruby| { + ruby.define_global_function(&name, function!(callback, 1)); + })); Ok(()) } @@ -177,13 +188,9 @@ impl Runtime for RubyRuntime { args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, ) -> Result { let name = name.to_string(); - self.ruby_thread_sender - .as_ref() - .unwrap() - .send(Box::new(move |ruby| { - let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); - })) - .unwrap(); + RUBY_THREAD.execute_in(Box::new(move |ruby| { + let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); + })); Ok(RubyValue(())) } diff --git a/tests/tests.rs b/tests/tests.rs index 8d2a022..7e936fb 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -493,13 +493,13 @@ mod ruby_tests { type ScriptData = RubyScriptData; fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { - let state: magnus::value::Value = Ruby::get() - .unwrap() - .class_object() - .const_get("STATE") - .unwrap(); - let res: i64 = state.funcall_public("[]", (key.to_string(),)).unwrap(); - assert_eq!(res, value) + // let state: magnus::value::Value = Ruby::get() + // .unwrap() + // .class_object() + // .const_get("STATE") + // .unwrap(); + // let res: i64 = state.funcall_public("[]", (key.to_string(),)).unwrap(); + // assert_eq!(res, value) } fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { -- 2.45.2 From f3bcacbf14da8d6bb394f0f5b3c6c4ca63abcda3 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 10 May 2025 10:02:42 +0200 Subject: [PATCH 012/165] return val --- src/runtimes/ruby.rs | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index d1e6b27..c99f47c 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -49,9 +49,6 @@ impl From for RubyScript { } struct RubyEngine(Cleanup); -// TODO: Add SAFETY? -unsafe impl Send for RubyEngine {} - struct RubyThread { sender: Option>>, handle: Option>, @@ -64,10 +61,10 @@ impl RubyThread { let (sender, receiver) = crossbeam_channel::unbounded::>(); let handle = thread::spawn(move || { - unsafe { magnus::embed::init() }; - while let Ok(val) = receiver.recv() { + let _cleanup = unsafe { magnus::embed::init() }; + while let Ok(f) = receiver.recv() { let ruby = Ruby::get().unwrap(); - val(ruby); + f(ruby); } }); @@ -77,15 +74,19 @@ impl RubyThread { } } - fn execute_in(&self, f: Box) { + fn execute_in(&self, f: Box RubyValue + Send>) -> RubyValue { + let (return_sender, return_receiver) = crossbeam_channel::bounded(0); self.sender .as_ref() .unwrap() .send(Box::new(move |ruby| { - let result = f(ruby); - println!("{:?}", result); + return_sender.send(f(ruby)).unwrap(); + // return_sender.send(f(ruby)).unwrap(); + // drop(return_sender); })) .unwrap(); + return_receiver.recv().unwrap(); + RubyValue(()) } } @@ -135,6 +136,7 @@ impl Runtime for RubyRuntime { let script = script.0.clone(); RUBY_THREAD.execute_in(Box::new(move |ruby| { ruby.eval::(&script).unwrap(); + RubyValue(()) })); Ok(RubyScriptData) } @@ -153,28 +155,14 @@ impl Runtime for RubyRuntime { + Sync + 'static, ) -> Result<(), crate::ScriptingError> { - // let ruby = magnus::Ruby::get().unwrap(); - // - // static mut FUN: Vec> = Vec::new(); - // unsafe { - // FUN.push(Box::new(move || { - // f((), vec![]).unwrap(); - // })); - // } - // - fn callback(val: magnus::Value) -> magnus::Value { - // println!("{:?}", val); - // sender.unwrap().send(()); let ruby = magnus::Ruby::get().unwrap(); - // unsafe { - // FUN.pop().unwrap()(); - // } ruby.qnil().as_value() } RUBY_THREAD.execute_in(Box::new(move |ruby| { ruby.define_global_function(&name, function!(callback, 1)); + RubyValue(()) })); Ok(()) @@ -189,7 +177,8 @@ impl Runtime for RubyRuntime { ) -> Result { let name = name.to_string(); RUBY_THREAD.execute_in(Box::new(move |ruby| { - let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); + let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); + RubyValue(()) })); Ok(RubyValue(())) -- 2.45.2 From 0a9dc09b0cb7640f7b0bbf62ac395048759dbe75 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 10 May 2025 10:18:55 +0200 Subject: [PATCH 013/165] no panic in thread --- src/runtimes/ruby.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index c99f47c..e1944d8 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -177,7 +177,7 @@ impl Runtime for RubyRuntime { ) -> Result { let name = name.to_string(); RUBY_THREAD.execute_in(Box::new(move |ruby| { - let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); + let _: Result = ruby.class_object().funcall(name, ()); RubyValue(()) })); -- 2.45.2 From 253761c730a1312685fb4104d956d0ced571911e Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 10 May 2025 16:35:31 +0200 Subject: [PATCH 014/165] wip --- src/runtimes/ruby.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index e1944d8..230d2a7 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -74,19 +74,16 @@ impl RubyThread { } } - fn execute_in(&self, f: Box RubyValue + Send>) -> RubyValue { + fn execute_in(&self, f: Box T + Send>) -> T { let (return_sender, return_receiver) = crossbeam_channel::bounded(0); self.sender .as_ref() .unwrap() .send(Box::new(move |ruby| { return_sender.send(f(ruby)).unwrap(); - // return_sender.send(f(ruby)).unwrap(); - // drop(return_sender); })) .unwrap(); - return_receiver.recv().unwrap(); - RubyValue(()) + return_receiver.recv().unwrap() } } @@ -125,7 +122,7 @@ impl Runtime for RubyRuntime { } fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T { - todo!() + RUBY_THREAD.execute_in(Box::new(move |ruby| f(&ruby))) } fn eval( -- 2.45.2 From 191cfce7330df5eb8ab4ffd2c9dccbcb16ba0171 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 10 May 2025 16:37:56 +0200 Subject: [PATCH 015/165] get rid of warnings --- src/runtimes/ruby.rs | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 230d2a7..ba09728 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,21 +1,15 @@ -use std::sync::mpsc::{Receiver, Sender}; use std::{ - cell::{LazyCell, OnceCell}, - sync::{LazyLock, Mutex, OnceLock}, + sync::LazyLock, thread::{self, JoinHandle}, }; use bevy::{ asset::Asset, - ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, + ecs::{component::Component, resource::Resource, schedule::ScheduleLabel}, reflect::TypePath, }; use magnus::Ruby; -use magnus::{ - embed::{init, Cleanup}, - function, - prelude::*, -}; +use magnus::{embed::Cleanup, function, prelude::*}; use serde::Deserialize; use crate::{ @@ -128,7 +122,7 @@ impl Runtime for RubyRuntime { fn eval( &self, script: &Self::ScriptAsset, - entity: bevy::prelude::Entity, + _entity: bevy::prelude::Entity, ) -> Result { let script = script.0.clone(); RUBY_THREAD.execute_in(Box::new(move |ruby| { @@ -141,8 +135,8 @@ impl Runtime for RubyRuntime { fn register_fn( &mut self, name: String, - arg_types: Vec, - f: impl Fn( + _arg_types: Vec, + _f: impl Fn( Self::CallContext, Vec, ) -> Result< @@ -152,7 +146,7 @@ impl Runtime for RubyRuntime { + Sync + 'static, ) -> Result<(), crate::ScriptingError> { - fn callback(val: magnus::Value) -> magnus::Value { + fn callback(_val: magnus::Value) -> magnus::Value { let ruby = magnus::Ruby::get().unwrap(); ruby.qnil().as_value() } @@ -168,9 +162,9 @@ impl Runtime for RubyRuntime { fn call_fn( &self, name: &str, - script_data: &mut Self::ScriptData, - entity: bevy::prelude::Entity, - args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, + _script_data: &mut Self::ScriptData, + _entity: bevy::prelude::Entity, + _args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, ) -> Result { let name = name.to_string(); RUBY_THREAD.execute_in(Box::new(move |ruby| { @@ -183,9 +177,9 @@ impl Runtime for RubyRuntime { fn call_fn_from_value( &self, - value: &Self::Value, - context: &Self::CallContext, - args: Vec, + _value: &Self::Value, + _context: &Self::CallContext, + _args: Vec, ) -> Result { todo!() } @@ -196,13 +190,13 @@ pub mod prelude { } impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { - fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self { + fn from_runtime_value_with_engine(_value: RubyValue, _engine: &magnus::Ruby) -> Self { todo!(); } } impl IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { - fn into_runtime_value_with_engine(value: T, engine: &magnus::Ruby) -> RubyValue { + fn into_runtime_value_with_engine(_value: T, _engine: &magnus::Ruby) -> RubyValue { RubyValue(()) } } @@ -214,8 +208,8 @@ impl FuncArgs<'_, RubyValue, RubyRuntime> for () { } impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { - fn parse(self, engine: &magnus::Ruby) -> Vec { - self.into_iter().map(|x| RubyValue(())).collect() + fn parse(self, _engine: &magnus::Ruby) -> Vec { + self.into_iter().map(|_x| RubyValue(())).collect() } } @@ -224,7 +218,7 @@ macro_rules! impl_tuple { impl<'a, $($t,)+> FuncArgs<'a, RubyValue, RubyRuntime> for ($($t,)+) { - fn parse(self, engine: &'a magnus::Ruby) -> Vec { + fn parse(self, _engine: &'a magnus::Ruby) -> Vec { todo!(); } } -- 2.45.2 From e95f025b071c3c26d543327e0b272c0d5c6b352d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 11 May 2025 11:50:39 +0200 Subject: [PATCH 016/165] func call --- .../rust_function_gets_called_from_script.rb | 2 +- src/lib.rs | 5 ++++- src/runtimes/ruby.rs | 18 +++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/assets/tests/ruby/rust_function_gets_called_from_script.rb b/assets/tests/ruby/rust_function_gets_called_from_script.rb index fc70a31..6edee25 100644 --- a/assets/tests/ruby/rust_function_gets_called_from_script.rb +++ b/assets/tests/ruby/rust_function_gets_called_from_script.rb @@ -1,3 +1,3 @@ def test_func() - rust_func() + rust_func end diff --git a/src/lib.rs b/src/lib.rs index 37fcc75..bc72d3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -308,7 +308,10 @@ pub trait Runtime: Resource + Default { /// Provides immutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. - fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T; + fn with_engine( + &self, + f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + ) -> T; fn eval( &self, diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index ba09728..c9f4568 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,3 +1,4 @@ +// TODO: make sure ruby is statically linked use std::{ sync::LazyLock, thread::{self, JoinHandle}, @@ -115,7 +116,10 @@ impl Runtime for RubyRuntime { f(&mut magnus::Ruby::get().unwrap()) } - fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T { + fn with_engine( + &self, + f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + ) -> T { RUBY_THREAD.execute_in(Box::new(move |ruby| f(&ruby))) } @@ -136,7 +140,7 @@ impl Runtime for RubyRuntime { &mut self, name: String, _arg_types: Vec, - _f: impl Fn( + f: impl Fn( Self::CallContext, Vec, ) -> Result< @@ -146,13 +150,17 @@ impl Runtime for RubyRuntime { + Sync + 'static, ) -> Result<(), crate::ScriptingError> { - fn callback(_val: magnus::Value) -> magnus::Value { + fn callback() -> magnus::Value { let ruby = magnus::Ruby::get().unwrap(); + let method_name: magnus::value::StaticSymbol = + ruby.class_object().funcall("__method__", ()).unwrap(); + let method_name = method_name.to_string(); + dbg!(method_name); ruby.qnil().as_value() } RUBY_THREAD.execute_in(Box::new(move |ruby| { - ruby.define_global_function(&name, function!(callback, 1)); + ruby.define_global_function(&name, function!(callback, 0)); RubyValue(()) })); @@ -168,7 +176,7 @@ impl Runtime for RubyRuntime { ) -> Result { let name = name.to_string(); RUBY_THREAD.execute_in(Box::new(move |ruby| { - let _: Result = ruby.class_object().funcall(name, ()); + let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); RubyValue(()) })); -- 2.45.2 From cbde11d17b2f12ab5da2ffa5741b8cbb09667138 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 12 May 2025 12:08:04 +0200 Subject: [PATCH 017/165] callbacks global --- src/callback.rs | 6 +++--- src/lib.rs | 5 ++++- src/runtimes/ruby.rs | 34 ++++++++++++++++++++++++++++++---- tests/tests.rs | 12 ++++++------ 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index 0174950..416fcd7 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -65,7 +65,7 @@ where fn into_callback_system(self, world: &mut World) -> CallbackSystem; } -impl IntoCallbackSystem for FN +impl IntoCallbackSystem for FN where FN: IntoSystem<(), Out, Marker>, Out: for<'a> IntoRuntimeValueWithEngine<'a, Out, R>, @@ -90,12 +90,12 @@ where macro_rules! impl_tuple { ($($idx:tt $t:tt),+) => { - impl IntoCallbackSystem, Out, Marker> + impl IntoCallbackSystem, Out, Marker> for FN where FN: IntoSystem, Out, Marker>, Out: for<'a> IntoRuntimeValueWithEngine<'a, Out, RN>, - $($t: 'static + for<'a> FromRuntimeValueWithEngine<'a, RN>,)+ + $($t: Send + 'static + for<'a> FromRuntimeValueWithEngine<'a, RN>,)+ { fn into_callback_system(self, world: &mut World) -> CallbackSystem { let mut inner_system = IntoSystem::into_system(self); diff --git a/src/lib.rs b/src/lib.rs index bc72d3e..a3d3364 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,7 +303,10 @@ pub trait Runtime: Resource + Default { /// Provides mutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. - fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T; + fn with_engine_mut( + &mut self, + f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + ) -> T; /// Provides immutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index c9f4568..bc6d6e2 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,6 +1,6 @@ // TODO: make sure ruby is statically linked use std::{ - sync::LazyLock, + sync::{LazyLock, Mutex}, thread::{self, JoinHandle}, }; @@ -49,6 +49,7 @@ struct RubyThread { handle: Option>, } +// TODO: Can we put references to those in runtime struct? static RUBY_THREAD: LazyLock = LazyLock::new(|| RubyThread::spawn()); impl RubyThread { @@ -112,8 +113,11 @@ impl Runtime for RubyRuntime { type RawEngine = magnus::Ruby; - fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { - f(&mut magnus::Ruby::get().unwrap()) + fn with_engine_mut( + &mut self, + f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + ) -> T { + RUBY_THREAD.execute_in(Box::new(move |mut ruby| f(&mut ruby))) } fn with_engine( @@ -150,12 +154,34 @@ impl Runtime for RubyRuntime { + Sync + 'static, ) -> Result<(), crate::ScriptingError> { + static RUBY_CALLBACKS: LazyLock< + Mutex< + Vec< + Box< + dyn Fn( + (), + Vec, + ) -> Result< + crate::promise::Promise<(), RubyValue>, + crate::ScriptingError, + > + Send + + Sync + + 'static, + >, + >, + >, + > = LazyLock::new(|| Mutex::new(Vec::new())); + let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); + callbacks.push(Box::new(f)); + fn callback() -> magnus::Value { let ruby = magnus::Ruby::get().unwrap(); let method_name: magnus::value::StaticSymbol = ruby.class_object().funcall("__method__", ()).unwrap(); let method_name = method_name.to_string(); - dbg!(method_name); + let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); + let f = callbacks.pop().unwrap(); + f((), vec![]); ruby.qnil().as_value() } diff --git a/tests/tests.rs b/tests/tests.rs index 7e936fb..818d035 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -493,13 +493,13 @@ mod ruby_tests { type ScriptData = RubyScriptData; fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { - // let state: magnus::value::Value = Ruby::get() - // .unwrap() - // .class_object() - // .const_get("STATE") - // .unwrap(); - // let res: i64 = state.funcall_public("[]", (key.to_string(),)).unwrap(); + // let runtime = world.get_resource::().unwrap(); + // let key = key.to_string(); + // runtime.with_engine(move |engine| { + // let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); + // let res: i64 = state.funcall_public("[]", (key,)).unwrap(); // assert_eq!(res, value) + // }) } fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { -- 2.45.2 From 19bb6514edc64d2e447179e5352d10ba5903793f Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 12 May 2025 12:10:23 +0200 Subject: [PATCH 018/165] enable test --- tests/tests.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index 818d035..13523aa 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -493,13 +493,13 @@ mod ruby_tests { type ScriptData = RubyScriptData; fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { - // let runtime = world.get_resource::().unwrap(); - // let key = key.to_string(); - // runtime.with_engine(move |engine| { - // let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); - // let res: i64 = state.funcall_public("[]", (key,)).unwrap(); - // assert_eq!(res, value) - // }) + let runtime = world.get_resource::().unwrap(); + let key = key.to_string(); + runtime.with_engine(move |engine| { + let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); + let res: i64 = state.funcall_public("[]", (key,)).unwrap(); + assert_eq!(res, value) + }) } fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { -- 2.45.2 From c46391871f4223e602d363ff6f43f58f4b8829b0 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 12 May 2025 12:15:41 +0200 Subject: [PATCH 019/165] get by name --- .../ruby/rust_function_gets_called_from_script.rb | 2 +- src/runtimes/ruby.rs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/assets/tests/ruby/rust_function_gets_called_from_script.rb b/assets/tests/ruby/rust_function_gets_called_from_script.rb index 6edee25..07a5876 100644 --- a/assets/tests/ruby/rust_function_gets_called_from_script.rb +++ b/assets/tests/ruby/rust_function_gets_called_from_script.rb @@ -1,3 +1,3 @@ -def test_func() +def test_func rust_func end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index bc6d6e2..37f9f32 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,5 +1,6 @@ // TODO: make sure ruby is statically linked use std::{ + collections::HashMap, sync::{LazyLock, Mutex}, thread::{self, JoinHandle}, }; @@ -156,7 +157,8 @@ impl Runtime for RubyRuntime { ) -> Result<(), crate::ScriptingError> { static RUBY_CALLBACKS: LazyLock< Mutex< - Vec< + HashMap< + String, Box< dyn Fn( (), @@ -170,17 +172,17 @@ impl Runtime for RubyRuntime { >, >, >, - > = LazyLock::new(|| Mutex::new(Vec::new())); + > = LazyLock::new(|| Mutex::new(HashMap::new())); let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); - callbacks.push(Box::new(f)); + callbacks.insert(name.clone(), Box::new(f)); fn callback() -> magnus::Value { let ruby = magnus::Ruby::get().unwrap(); let method_name: magnus::value::StaticSymbol = ruby.class_object().funcall("__method__", ()).unwrap(); let method_name = method_name.to_string(); - let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); - let f = callbacks.pop().unwrap(); + let callbacks = RUBY_CALLBACKS.lock().unwrap(); + let f = callbacks.get(&method_name).unwrap(); f((), vec![]); ruby.qnil().as_value() } -- 2.45.2 From fcf26808240637f6e4962681cafa02cffb77dfad Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 10:58:00 +0200 Subject: [PATCH 020/165] ruby thread ensure single access --- src/runtimes/ruby.rs | 75 +++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 37f9f32..a7f851f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,7 +1,7 @@ // TODO: make sure ruby is statically linked use std::{ collections::HashMap, - sync::{LazyLock, Mutex}, + sync::{Arc, Condvar, LazyLock, Mutex, MutexGuard}, thread::{self, JoinHandle}, }; @@ -17,11 +17,14 @@ use serde::Deserialize; use crate::{ assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, + runtimes::ruby, FuncArgs, Runtime, }; #[derive(Resource)] -pub struct RubyRuntime {} +pub struct RubyRuntime { + ruby_thread: Option, +} #[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)] pub struct RubySchedule; @@ -50,8 +53,8 @@ struct RubyThread { handle: Option>, } -// TODO: Can we put references to those in runtime struct? -static RUBY_THREAD: LazyLock = LazyLock::new(|| RubyThread::spawn()); +static RUBY_THREAD: LazyLock>, Condvar)>> = + LazyLock::new(|| Arc::new((Mutex::new(Some(RubyThread::spawn())), Condvar::new()))); impl RubyThread { fn spawn() -> Self { @@ -94,7 +97,26 @@ impl Drop for RubyThread { impl Default for RubyRuntime { fn default() -> Self { - Self {} + let (lock, cvar) = &*Arc::clone(&RUBY_THREAD); + let mut ruby_thread = lock.lock().unwrap(); + + while ruby_thread.is_none() { + ruby_thread = cvar.wait(ruby_thread).unwrap(); + } + let ruby_thread = ruby_thread.take().unwrap(); + cvar.notify_all(); + Self { + ruby_thread: Some(ruby_thread), + } + } +} + +impl Drop for RubyRuntime { + fn drop(&mut self) { + let (lock, cvar) = &*Arc::clone(&RUBY_THREAD); + let mut ruby_thread = lock.lock().unwrap(); + *ruby_thread = self.ruby_thread.take(); + cvar.notify_all(); } } @@ -118,14 +140,20 @@ impl Runtime for RubyRuntime { &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { - RUBY_THREAD.execute_in(Box::new(move |mut ruby| f(&mut ruby))) + self.ruby_thread + .as_ref() + .unwrap() + .execute_in(Box::new(move |mut ruby| f(&mut ruby))) } fn with_engine( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { - RUBY_THREAD.execute_in(Box::new(move |ruby| f(&ruby))) + self.ruby_thread + .as_ref() + .unwrap() + .execute_in(Box::new(move |ruby| f(&ruby))) } fn eval( @@ -134,10 +162,13 @@ impl Runtime for RubyRuntime { _entity: bevy::prelude::Entity, ) -> Result { let script = script.0.clone(); - RUBY_THREAD.execute_in(Box::new(move |ruby| { - ruby.eval::(&script).unwrap(); - RubyValue(()) - })); + self.ruby_thread + .as_ref() + .unwrap() + .execute_in(Box::new(move |ruby| { + ruby.eval::(&script).unwrap(); + RubyValue(()) + })); Ok(RubyScriptData) } @@ -187,10 +218,13 @@ impl Runtime for RubyRuntime { ruby.qnil().as_value() } - RUBY_THREAD.execute_in(Box::new(move |ruby| { - ruby.define_global_function(&name, function!(callback, 0)); - RubyValue(()) - })); + self.ruby_thread + .as_ref() + .unwrap() + .execute_in(Box::new(move |ruby| { + ruby.define_global_function(&name, function!(callback, 0)); + RubyValue(()) + })); Ok(()) } @@ -203,10 +237,13 @@ impl Runtime for RubyRuntime { _args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, ) -> Result { let name = name.to_string(); - RUBY_THREAD.execute_in(Box::new(move |ruby| { - let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); - RubyValue(()) - })); + self.ruby_thread + .as_ref() + .unwrap() + .execute_in(Box::new(move |ruby| { + let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); + RubyValue(()) + })); Ok(RubyValue(())) } -- 2.45.2 From 54a47572dbd3a915dfb182dc31c638ea5bcae615 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 17:44:20 +0200 Subject: [PATCH 021/165] add api for remote thread engine access --- src/callback.rs | 24 +++++++++++++++++++----- src/lib.rs | 16 ++++++++++++++-- src/runtimes/lua.rs | 18 ++++++++++++++++++ src/runtimes/rhai.rs | 18 ++++++++++++++++++ src/runtimes/ruby.rs | 16 ++++++++++++++-- tests/tests.rs | 2 +- 6 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index 416fcd7..54edd92 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -77,8 +77,16 @@ where let result = inner_system.run((), world); inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - runtime - .with_engine_mut(move |engine| Out::into_runtime_value_with_engine(result, engine)) + + if R::is_current_thread() { + runtime.with_engine_mut(move |engine| { + Out::into_runtime_value_with_engine(result, engine) + }) + } else { + runtime.with_engine_thread_mut(move |engine| { + Out::into_runtime_value_with_engine(result, engine) + }) + } }; let system = IntoSystem::into_system(system_fn); CallbackSystem { @@ -110,9 +118,15 @@ macro_rules! impl_tuple { let result = inner_system.run(args, world); inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - runtime.with_engine_mut(move |engine| { - Out::into_runtime_value_with_engine(result, engine) - }) + if RN::is_current_thread() { + runtime.with_engine_mut(move |engine| { + Out::into_runtime_value_with_engine(result, engine) + }) + } else { + runtime.with_engine_thread_mut(move |engine| { + Out::into_runtime_value_with_engine(result, engine) + }) + } }; let system = IntoSystem::into_system(system_fn); CallbackSystem { diff --git a/src/lib.rs b/src/lib.rs index a3d3364..95d4462 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,10 +300,12 @@ pub trait Runtime: Resource + Default { type Value: Send + Clone; type RawEngine; + fn is_current_thread() -> bool; + /// Provides mutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. - fn with_engine_mut( + fn with_engine_thread_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T; @@ -311,11 +313,21 @@ pub trait Runtime: Resource + Default { /// Provides immutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. - fn with_engine( + fn with_engine_thread( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T; + /// Provides mutable reference to raw scripting engine instance. + /// Can be used to directly interact with an interpreter to use interfaces + /// that bevy_scriptum does not provided adapters for. + fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T; + + /// Provides immutable reference to raw scripting engine instance. + /// Can be used to directly interact with an interpreter to use interfaces + /// that bevy_scriptum does not provided adapters for. + fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T; + fn eval( &self, script: &Self::ScriptAsset, diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 5613ba2..0e55625 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -257,6 +257,24 @@ impl Runtime for LuaRuntime { let engine = self.engine.lock().unwrap(); f(&engine) } + + fn with_engine_thread_mut( + &mut self, + f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + ) -> T { + todo!() + } + + fn with_engine_thread( + &self, + f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + ) -> T { + todo!() + } + + fn is_current_thread() -> bool { + true + } } impl<'a, T: IntoLuaMulti<'a>> IntoRuntimeValueWithEngine<'a, T, LuaRuntime> for T { diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index c293364..c17e8b4 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -160,6 +160,24 @@ impl Runtime for RhaiRuntime { fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T { f(&self.engine) } + + fn with_engine_thread_mut( + &mut self, + f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + ) -> T { + todo!() + } + + fn with_engine_thread( + &self, + f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + ) -> T { + todo!() + } + + fn is_current_thread() -> bool { + true + } } impl Default for RhaiRuntime { diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index a7f851f..a0b34b5 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -136,7 +136,7 @@ impl Runtime for RubyRuntime { type RawEngine = magnus::Ruby; - fn with_engine_mut( + fn with_engine_thread_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { @@ -146,7 +146,7 @@ impl Runtime for RubyRuntime { .execute_in(Box::new(move |mut ruby| f(&mut ruby))) } - fn with_engine( + fn with_engine_thread( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { @@ -156,6 +156,14 @@ impl Runtime for RubyRuntime { .execute_in(Box::new(move |ruby| f(&ruby))) } + fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { + unimplemented!(); + } + + fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T { + unimplemented!(); + } + fn eval( &self, script: &Self::ScriptAsset, @@ -256,6 +264,10 @@ impl Runtime for RubyRuntime { ) -> Result { todo!() } + + fn is_current_thread() -> bool { + false + } } pub mod prelude { diff --git a/tests/tests.rs b/tests/tests.rs index 13523aa..6e3da70 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -495,7 +495,7 @@ mod ruby_tests { fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { let runtime = world.get_resource::().unwrap(); let key = key.to_string(); - runtime.with_engine(move |engine| { + runtime.with_engine_thread(move |engine| { let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); let res: i64 = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value) -- 2.45.2 From 4ccedf442baa7cf1cbc24033a246d16710c99684 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 17:45:39 +0200 Subject: [PATCH 022/165] add todo --- src/runtimes/ruby.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index a0b34b5..3b6d43b 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,3 +1,4 @@ +// TODO: maybe make all runtime engines not send and spawn threads for them like Ruby // TODO: make sure ruby is statically linked use std::{ collections::HashMap, -- 2.45.2 From 9db84f0360d79d1010ba5e9acc31a812ff874591 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 17:53:56 +0200 Subject: [PATCH 023/165] rename --- src/runtimes/ruby.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 3b6d43b..b439c58 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -75,7 +75,7 @@ impl RubyThread { } } - fn execute_in(&self, f: Box T + Send>) -> T { + fn execute(&self, f: Box T + Send>) -> T { let (return_sender, return_receiver) = crossbeam_channel::bounded(0); self.sender .as_ref() @@ -144,7 +144,7 @@ impl Runtime for RubyRuntime { self.ruby_thread .as_ref() .unwrap() - .execute_in(Box::new(move |mut ruby| f(&mut ruby))) + .execute(Box::new(move |mut ruby| f(&mut ruby))) } fn with_engine_thread( @@ -154,7 +154,7 @@ impl Runtime for RubyRuntime { self.ruby_thread .as_ref() .unwrap() - .execute_in(Box::new(move |ruby| f(&ruby))) + .execute(Box::new(move |ruby| f(&ruby))) } fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { @@ -174,7 +174,7 @@ impl Runtime for RubyRuntime { self.ruby_thread .as_ref() .unwrap() - .execute_in(Box::new(move |ruby| { + .execute(Box::new(move |ruby| { ruby.eval::(&script).unwrap(); RubyValue(()) })); @@ -230,7 +230,7 @@ impl Runtime for RubyRuntime { self.ruby_thread .as_ref() .unwrap() - .execute_in(Box::new(move |ruby| { + .execute(Box::new(move |ruby| { ruby.define_global_function(&name, function!(callback, 0)); RubyValue(()) })); @@ -249,7 +249,7 @@ impl Runtime for RubyRuntime { self.ruby_thread .as_ref() .unwrap() - .execute_in(Box::new(move |ruby| { + .execute(Box::new(move |ruby| { let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); RubyValue(()) })); -- 2.45.2 From d02b6375c6cdb9e54bc463648e363277cc51e7c7 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 18:02:57 +0200 Subject: [PATCH 024/165] get rid of warnings --- src/runtimes/lua.rs | 4 ++-- src/runtimes/rhai.rs | 4 ++-- src/runtimes/ruby.rs | 46 +++++++++++++++++++------------------------- tests/tests.rs | 10 +++++----- 4 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 0e55625..eda2ba2 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -260,14 +260,14 @@ impl Runtime for LuaRuntime { fn with_engine_thread_mut( &mut self, - f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + _f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { todo!() } fn with_engine_thread( &self, - f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + _f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { todo!() } diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index c17e8b4..5f486e8 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -163,14 +163,14 @@ impl Runtime for RhaiRuntime { fn with_engine_thread_mut( &mut self, - f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + _f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { todo!() } fn with_engine_thread( &self, - f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + _f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { todo!() } diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index b439c58..d124c6a 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -2,7 +2,7 @@ // TODO: make sure ruby is statically linked use std::{ collections::HashMap, - sync::{Arc, Condvar, LazyLock, Mutex, MutexGuard}, + sync::{Arc, Condvar, LazyLock, Mutex}, thread::{self, JoinHandle}, }; @@ -12,13 +12,12 @@ use bevy::{ reflect::TypePath, }; use magnus::Ruby; -use magnus::{embed::Cleanup, function, prelude::*}; +use magnus::{function, prelude::*}; use serde::Deserialize; use crate::{ assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, - runtimes::ruby, FuncArgs, Runtime, }; @@ -47,10 +46,11 @@ impl From for RubyScript { Self(value) } } -struct RubyEngine(Cleanup); + +type RubyClosure = Box; struct RubyThread { - sender: Option>>, + sender: Option>, handle: Option>, } @@ -157,11 +157,11 @@ impl Runtime for RubyRuntime { .execute(Box::new(move |ruby| f(&ruby))) } - fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { + fn with_engine_mut(&mut self, _f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { unimplemented!(); } - fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T { + fn with_engine(&self, _f: impl FnOnce(&Self::RawEngine) -> T) -> T { unimplemented!(); } @@ -195,24 +195,18 @@ impl Runtime for RubyRuntime { + Sync + 'static, ) -> Result<(), crate::ScriptingError> { - static RUBY_CALLBACKS: LazyLock< - Mutex< - HashMap< - String, - Box< - dyn Fn( - (), - Vec, - ) -> Result< - crate::promise::Promise<(), RubyValue>, - crate::ScriptingError, - > + Send - + Sync - + 'static, - >, - >, - >, - > = LazyLock::new(|| Mutex::new(HashMap::new())); + type CallbackClosure = Box< + dyn Fn( + (), + Vec, + ) + -> Result, crate::ScriptingError> + + Send + + Sync + + 'static, + >; + static RUBY_CALLBACKS: LazyLock>> = + LazyLock::new(|| Mutex::new(HashMap::new())); let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); callbacks.insert(name.clone(), Box::new(f)); @@ -223,7 +217,7 @@ impl Runtime for RubyRuntime { let method_name = method_name.to_string(); let callbacks = RUBY_CALLBACKS.lock().unwrap(); let f = callbacks.get(&method_name).unwrap(); - f((), vec![]); + f((), vec![]).unwrap(); ruby.qnil().as_value() } diff --git a/tests/tests.rs b/tests/tests.rs index 6e3da70..ad1a927 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -487,7 +487,7 @@ mod lua_tests { mod ruby_tests { use bevy::prelude::*; use bevy_scriptum::runtimes::ruby::{prelude::*, RubyScriptData}; - use magnus::{value::ReprValue, Module, Object, Ruby}; + use magnus::{value::ReprValue, Module}; impl AssertStateKeyValue for RubyRuntime { type ScriptData = RubyScriptData; @@ -502,15 +502,15 @@ mod ruby_tests { }) } - fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { + fn assert_state_key_value_i32(_world: &World, _entity_id: Entity, _key: &str, _value: i32) { todo!(); } fn assert_state_key_value_string( - world: &World, + _world: &World, _entity_id: Entity, - key: &str, - value: &str, + _key: &str, + _value: &str, ) { todo!(); } -- 2.45.2 From e92a3afe9f4520ac77f2e6e4901c9f25cb37a912 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 18:11:49 +0200 Subject: [PATCH 025/165] no explicit drop --- src/runtimes/ruby.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index d124c6a..9324f67 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -50,7 +50,7 @@ impl From for RubyScript { type RubyClosure = Box; struct RubyThread { - sender: Option>, + sender: crossbeam_channel::Sender, handle: Option>, } @@ -70,7 +70,7 @@ impl RubyThread { }); RubyThread { - sender: Some(sender), + sender, handle: Some(handle), } } @@ -78,8 +78,6 @@ impl RubyThread { fn execute(&self, f: Box T + Send>) -> T { let (return_sender, return_receiver) = crossbeam_channel::bounded(0); self.sender - .as_ref() - .unwrap() .send(Box::new(move |ruby| { return_sender.send(f(ruby)).unwrap(); })) @@ -90,7 +88,6 @@ impl RubyThread { impl Drop for RubyThread { fn drop(&mut self) { - drop(self.sender.take().unwrap()); let handle = self.handle.take().unwrap(); handle.join().unwrap(); } -- 2.45.2 From 008771bb1511ddbc8f827e4d231a76f4e6c6af97 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 18:25:34 +0200 Subject: [PATCH 026/165] cleanup --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 71066f2..3c51940 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /target /Cargo.lock .vscode -rust-analyzer.json .nvim.lua -- 2.45.2 From b498da957db0e8422c70c2f7053f18020316a5bf Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 13 May 2025 22:42:39 +0200 Subject: [PATCH 027/165] args wip --- Cargo.toml | 1 + ...tion_gets_called_from_script_with_param.rb | 3 +++ src/callback.rs | 19 ++++++++++++++----- src/runtimes/ruby.rs | 13 +++++++++++-- 4 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb diff --git a/Cargo.toml b/Cargo.toml index 60f10ad..014e017 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ mlua = { version = "0.9.8", features = [ ], optional = true } magnus = { version = "0.7.1", optional = true, features = ["embed"] } crossbeam-channel = "0.5.15" +rb-sys = "0.9.114" [[example]] name = "call_function_from_rust_rhai" diff --git a/assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb b/assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb new file mode 100644 index 0000000..5a49c25 --- /dev/null +++ b/assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb @@ -0,0 +1,3 @@ +def test_func + rust_func(5) +end diff --git a/src/callback.rs b/src/callback.rs index 54edd92..f3b544e 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -110,11 +110,20 @@ macro_rules! impl_tuple { inner_system.initialize(world); let system_fn = move |args: In>, world: &mut World| { let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - let args = runtime.with_engine_mut(move |engine| { - ( - $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ - ) - }); + let args = if RN::is_current_thread() { + runtime.with_engine_mut(move |engine| { + ( + $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ + ) + }) + } else { + runtime.with_engine_thread_mut(move |engine| { + ( + $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ + ) + }) + }; + let result = inner_system.run(args, world); inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 9324f67..39055b0 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -2,6 +2,7 @@ // TODO: make sure ruby is statically linked use std::{ collections::HashMap, + ffi::{c_void, CString}, sync::{Arc, Condvar, LazyLock, Mutex}, thread::{self, JoinHandle}, }; @@ -13,6 +14,7 @@ use bevy::{ }; use magnus::Ruby; use magnus::{function, prelude::*}; +use rb_sys::rb_define_global_function; use serde::Deserialize; use crate::{ @@ -207,7 +209,7 @@ impl Runtime for RubyRuntime { let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); callbacks.insert(name.clone(), Box::new(f)); - fn callback() -> magnus::Value { + unsafe extern "C" fn callback(_rb_self: magnus::Value) -> magnus::Value { let ruby = magnus::Ruby::get().unwrap(); let method_name: magnus::value::StaticSymbol = ruby.class_object().funcall("__method__", ()).unwrap(); @@ -222,7 +224,14 @@ impl Runtime for RubyRuntime { .as_ref() .unwrap() .execute(Box::new(move |ruby| { - ruby.define_global_function(&name, function!(callback, 0)); + let name = CString::new(name).unwrap(); + unsafe { + rb_define_global_function( + name.as_ptr(), + std::mem::transmute(callback as *mut c_void), + 1, + ); + } RubyValue(()) })); -- 2.45.2 From 356e552ce776899273fd0a674926fcaf617ca9ab Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 10:26:35 +0200 Subject: [PATCH 028/165] wip callback call --- Cargo.toml | 2 +- src/runtimes/ruby.rs | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 014e017..84fa401 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ mlua = { version = "0.9.8", features = [ "vendored", "send", ], optional = true } -magnus = { version = "0.7.1", optional = true, features = ["embed"] } +magnus = { version = "0.7.1", optional = true, features = ["embed", "rb-sys"] } crossbeam-channel = "0.5.15" rb-sys = "0.9.114" diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 39055b0..7e18773 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,5 +1,8 @@ // TODO: maybe make all runtime engines not send and spawn threads for them like Ruby // TODO: make sure ruby is statically linked +// TODO: add tests for every runtime for return value +// TODO: consider dropping magnus +// TODO: unwinding https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions use std::{ collections::HashMap, ffi::{c_void, CString}, @@ -12,9 +15,9 @@ use bevy::{ ecs::{component::Component, resource::Resource, schedule::ScheduleLabel}, reflect::TypePath, }; -use magnus::Ruby; -use magnus::{function, prelude::*}; -use rb_sys::rb_define_global_function; +use magnus::{function, Ruby}; +use magnus::{prelude::*, rb_sys::FromRawValue}; +use rb_sys::{rb_define_global_function, rb_scan_args, VALUE}; use serde::Deserialize; use crate::{ @@ -209,27 +212,45 @@ impl Runtime for RubyRuntime { let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); callbacks.insert(name.clone(), Box::new(f)); - unsafe extern "C" fn callback(_rb_self: magnus::Value) -> magnus::Value { + unsafe extern "C" fn callback(argc: i32, argv: *mut VALUE, r_self: VALUE) -> VALUE { + let fmt = CString::new("1").unwrap(); + let x: VALUE = Default::default(); + rb_scan_args(argc, argv, fmt.as_ptr(), &x); + let ruby = magnus::Ruby::get().unwrap(); let method_name: magnus::value::StaticSymbol = ruby.class_object().funcall("__method__", ()).unwrap(); let method_name = method_name.to_string(); let callbacks = RUBY_CALLBACKS.lock().unwrap(); let f = callbacks.get(&method_name).unwrap(); - f((), vec![]).unwrap(); - ruby.qnil().as_value() + + let args = magnus::RArray::from_value(magnus::Value::from_raw(*argv).into()) + .unwrap() + .to_value_array::<1>() + .expect("failed to get args array"); + for arg in args { + dbg!(arg); + } + + // let args = args + // .parse(&self.engine) + // .into_iter() + // .map(|a| a.0) + // .collect::>(); + f((), vec![]).expect("failed to call callback"); + todo!() } self.ruby_thread .as_ref() .unwrap() - .execute(Box::new(move |ruby| { + .execute(Box::new(move |_ruby| { let name = CString::new(name).unwrap(); unsafe { rb_define_global_function( name.as_ptr(), std::mem::transmute(callback as *mut c_void), - 1, + -1, ); } RubyValue(()) -- 2.45.2 From 7e886a5eef1d9ede2adeda19df0f3a8f240f6e7a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 10:48:20 +0200 Subject: [PATCH 029/165] parse args --- src/runtimes/ruby.rs | 70 ++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 7e18773..2350e39 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -124,7 +124,13 @@ impl Drop for RubyRuntime { } #[derive(Clone)] -pub struct RubyValue(()); +pub struct RubyValue(magnus::value::Opaque); + +impl RubyValue { + fn nil(ruby: &Ruby) -> Self { + Self(magnus::value::Opaque::from(ruby.qnil().as_value())) + } +} impl Runtime for RubyRuntime { type Schedule = RubySchedule; @@ -178,7 +184,7 @@ impl Runtime for RubyRuntime { .unwrap() .execute(Box::new(move |ruby| { ruby.eval::(&script).unwrap(); - RubyValue(()) + RubyValue::nil(&ruby) })); Ok(RubyScriptData) } @@ -212,48 +218,24 @@ impl Runtime for RubyRuntime { let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); callbacks.insert(name.clone(), Box::new(f)); - unsafe extern "C" fn callback(argc: i32, argv: *mut VALUE, r_self: VALUE) -> VALUE { - let fmt = CString::new("1").unwrap(); - let x: VALUE = Default::default(); - rb_scan_args(argc, argv, fmt.as_ptr(), &x); - + fn callback(args: &[magnus::Value]) -> magnus::Value { let ruby = magnus::Ruby::get().unwrap(); let method_name: magnus::value::StaticSymbol = ruby.class_object().funcall("__method__", ()).unwrap(); let method_name = method_name.to_string(); let callbacks = RUBY_CALLBACKS.lock().unwrap(); let f = callbacks.get(&method_name).unwrap(); - - let args = magnus::RArray::from_value(magnus::Value::from_raw(*argv).into()) - .unwrap() - .to_value_array::<1>() - .expect("failed to get args array"); - for arg in args { - dbg!(arg); - } - - // let args = args - // .parse(&self.engine) - // .into_iter() - // .map(|a| a.0) - // .collect::>(); - f((), vec![]).expect("failed to call callback"); - todo!() + f((), args.iter().map(|_arg| RubyValue::nil(&ruby)).collect()) + .expect("failed to call callback"); + ruby.qnil().as_value() } self.ruby_thread .as_ref() .unwrap() - .execute(Box::new(move |_ruby| { - let name = CString::new(name).unwrap(); - unsafe { - rb_define_global_function( - name.as_ptr(), - std::mem::transmute(callback as *mut c_void), - -1, - ); - } - RubyValue(()) + .execute(Box::new(move |ruby| { + ruby.define_global_function(&name, function!(callback, -1)); + RubyValue::nil(&ruby) })); Ok(()) @@ -267,15 +249,14 @@ impl Runtime for RubyRuntime { _args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, ) -> Result { let name = name.to_string(); - self.ruby_thread + Ok(self + .ruby_thread .as_ref() .unwrap() .execute(Box::new(move |ruby| { let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); - RubyValue(()) - })); - - Ok(RubyValue(())) + RubyValue::nil(&ruby) + }))) } fn call_fn_from_value( @@ -296,15 +277,16 @@ pub mod prelude { pub use super::RubyRuntime; } -impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { +// TODO: remove this default +impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { fn from_runtime_value_with_engine(_value: RubyValue, _engine: &magnus::Ruby) -> Self { - todo!(); + T::default() } } impl IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { - fn into_runtime_value_with_engine(_value: T, _engine: &magnus::Ruby) -> RubyValue { - RubyValue(()) + fn into_runtime_value_with_engine(_value: T, engine: &magnus::Ruby) -> RubyValue { + RubyValue::nil(engine) } } @@ -315,8 +297,8 @@ impl FuncArgs<'_, RubyValue, RubyRuntime> for () { } impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { - fn parse(self, _engine: &magnus::Ruby) -> Vec { - self.into_iter().map(|_x| RubyValue(())).collect() + fn parse(self, engine: &magnus::Ruby) -> Vec { + self.into_iter().map(|_x| RubyValue::nil(engine)).collect() } } -- 2.45.2 From 4b53e9d040ffc832f5a2ab8261d4f664e8baeed0 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 13:43:14 +0200 Subject: [PATCH 030/165] value conversions --- src/runtimes/ruby.rs | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 2350e39..9dc1f46 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -12,10 +12,13 @@ use std::{ use bevy::{ asset::Asset, - ecs::{component::Component, resource::Resource, schedule::ScheduleLabel}, + ecs::{ + component::Component, query::QueryParManyUniqueIter, resource::Resource, + schedule::ScheduleLabel, + }, reflect::TypePath, }; -use magnus::{function, Ruby}; +use magnus::{function, method::ReturnValue, IntoValue, Ruby, TryConvert}; use magnus::{prelude::*, rb_sys::FromRawValue}; use rb_sys::{rb_define_global_function, rb_scan_args, VALUE}; use serde::Deserialize; @@ -128,7 +131,11 @@ pub struct RubyValue(magnus::value::Opaque); impl RubyValue { fn nil(ruby: &Ruby) -> Self { - Self(magnus::value::Opaque::from(ruby.qnil().as_value())) + Self::new(ruby.qnil().as_value(), ruby) + } + + fn new(value: magnus::Value, ruby: &Ruby) -> Self { + Self(magnus::value::Opaque::from(value)) } } @@ -183,8 +190,8 @@ impl Runtime for RubyRuntime { .as_ref() .unwrap() .execute(Box::new(move |ruby| { - ruby.eval::(&script).unwrap(); - RubyValue::nil(&ruby) + let value = ruby.eval::(&script).unwrap(); + RubyValue::new(value, &ruby) })); Ok(RubyScriptData) } @@ -225,8 +232,13 @@ impl Runtime for RubyRuntime { let method_name = method_name.to_string(); let callbacks = RUBY_CALLBACKS.lock().unwrap(); let f = callbacks.get(&method_name).unwrap(); - f((), args.iter().map(|_arg| RubyValue::nil(&ruby)).collect()) - .expect("failed to call callback"); + f( + (), + args.iter() + .map(|arg| RubyValue::new(arg.into_value(), &ruby)) + .collect(), + ) + .expect("failed to call callback"); ruby.qnil().as_value() } @@ -277,16 +289,16 @@ pub mod prelude { pub use super::RubyRuntime; } -// TODO: remove this default -impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { - fn from_runtime_value_with_engine(_value: RubyValue, _engine: &magnus::Ruby) -> Self { - T::default() +impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { + fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self { + let inner = engine.get_inner(value.0); + T::try_convert(inner).unwrap() } } -impl IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { - fn into_runtime_value_with_engine(_value: T, engine: &magnus::Ruby) -> RubyValue { - RubyValue::nil(engine) +impl IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { + fn into_runtime_value_with_engine(value: T, engine: &magnus::Ruby) -> RubyValue { + RubyValue::new(value.into_value(), engine) } } -- 2.45.2 From e15fb74e6448a92de64558b9abb1be0e81452dc4 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 14:05:31 +0200 Subject: [PATCH 031/165] func args --- src/lib.rs | 2 +- src/runtimes/ruby.rs | 28 ++++++++++++++++++---------- tests/tests.rs | 23 +++++++++++++++++------ 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 95d4462..bf7e0d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -358,7 +358,7 @@ pub trait Runtime: Resource + Default { name: &str, script_data: &mut Self::ScriptData, entity: Entity, - args: impl for<'a> FuncArgs<'a, Self::Value, Self>, + args: impl for<'a> FuncArgs<'a, Self::Value, Self> + Send + 'static, ) -> Result; /// Calls a function by value defined within the runtime in the context of the diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 9dc1f46..b351463 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -3,6 +3,7 @@ // TODO: add tests for every runtime for return value // TODO: consider dropping magnus // TODO: unwinding https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions +// TODO: maybe unify api and call non thread methods non_send use std::{ collections::HashMap, ffi::{c_void, CString}, @@ -131,10 +132,10 @@ pub struct RubyValue(magnus::value::Opaque); impl RubyValue { fn nil(ruby: &Ruby) -> Self { - Self::new(ruby.qnil().as_value(), ruby) + Self::new(ruby.qnil().as_value()) } - fn new(value: magnus::Value, ruby: &Ruby) -> Self { + fn new(value: magnus::Value) -> Self { Self(magnus::value::Opaque::from(value)) } } @@ -191,7 +192,7 @@ impl Runtime for RubyRuntime { .unwrap() .execute(Box::new(move |ruby| { let value = ruby.eval::(&script).unwrap(); - RubyValue::new(value, &ruby) + RubyValue::new(value) })); Ok(RubyScriptData) } @@ -235,7 +236,7 @@ impl Runtime for RubyRuntime { f( (), args.iter() - .map(|arg| RubyValue::new(arg.into_value(), &ruby)) + .map(|arg| RubyValue::new(arg.into_value())) .collect(), ) .expect("failed to call callback"); @@ -258,7 +259,7 @@ impl Runtime for RubyRuntime { name: &str, _script_data: &mut Self::ScriptData, _entity: bevy::prelude::Entity, - _args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, + args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self> + Send + 'static, ) -> Result { let name = name.to_string(); Ok(self @@ -266,7 +267,12 @@ impl Runtime for RubyRuntime { .as_ref() .unwrap() .execute(Box::new(move |ruby| { - let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); + let args: Vec<_> = args + .parse(&ruby) + .into_iter() + .map(|a| ruby.get_inner(a.0)) + .collect(); + let _: magnus::Value = ruby.class_object().funcall(name, args.as_slice()).unwrap(); RubyValue::nil(&ruby) }))) } @@ -297,8 +303,8 @@ impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { } impl IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { - fn into_runtime_value_with_engine(value: T, engine: &magnus::Ruby) -> RubyValue { - RubyValue::new(value.into_value(), engine) + fn into_runtime_value_with_engine(value: T, _engine: &magnus::Ruby) -> RubyValue { + RubyValue::new(value.into_value()) } } @@ -308,9 +314,11 @@ impl FuncArgs<'_, RubyValue, RubyRuntime> for () { } } -impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { +impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { fn parse(self, engine: &magnus::Ruby) -> Vec { - self.into_iter().map(|_x| RubyValue::nil(engine)).collect() + self.into_iter() + .map(|x| RubyValue::new(x.into_value())) + .collect() } } diff --git a/tests/tests.rs b/tests/tests.rs index ad1a927..4991357 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -502,17 +502,28 @@ mod ruby_tests { }) } - fn assert_state_key_value_i32(_world: &World, _entity_id: Entity, _key: &str, _value: i32) { - todo!(); + fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { + let runtime = world.get_resource::().unwrap(); + let key = key.to_string(); + runtime.with_engine_thread(move |engine| { + let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); + let res: i32 = state.funcall_public("[]", (key,)).unwrap(); + assert_eq!(res, value) + }) } fn assert_state_key_value_string( - _world: &World, + world: &World, _entity_id: Entity, - _key: &str, - _value: &str, + key: &str, + value: &str, ) { - todo!(); + let runtime = world.get_resource::().unwrap(); + runtime.with_engine(|engine| { + let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); + let res: String = state.funcall_public("[]", (key,)).unwrap(); + assert_eq!(res, value); + }); } } -- 2.45.2 From 7eb4a346b850e98f09468cbbae19b73c0f3a659c Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 14:06:42 +0200 Subject: [PATCH 032/165] add todo --- ...unction_gets_called_from_script_with_multiple_params.rb | 3 +++ ...ipt_function_gets_called_from_rust_with_single_param.rb | 7 +++++++ src/runtimes/ruby.rs | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb create mode 100644 assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb diff --git a/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb b/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb new file mode 100644 index 0000000..026ad0b --- /dev/null +++ b/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb @@ -0,0 +1,3 @@ +def test_func + rust_func(5, "test") +end diff --git a/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb b/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb new file mode 100644 index 0000000..a71146d --- /dev/null +++ b/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb @@ -0,0 +1,7 @@ +STATE = { + "a_value" => nil +} + +def test_func(a) + STATE["a_value"] = a +end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index b351463..f245579 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -4,6 +4,8 @@ // TODO: consider dropping magnus // TODO: unwinding https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions // TODO: maybe unify api and call non thread methods non_send +// TODO: use ruby arena to rewind state + use std::{ collections::HashMap, ffi::{c_void, CString}, -- 2.45.2 From 29689a0f6b9bc3fa2326ddeb729631b5f402127d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 14:07:19 +0200 Subject: [PATCH 033/165] remove todo --- src/runtimes/ruby.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index f245579..d46c4ba 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,7 +1,6 @@ // TODO: maybe make all runtime engines not send and spawn threads for them like Ruby // TODO: make sure ruby is statically linked // TODO: add tests for every runtime for return value -// TODO: consider dropping magnus // TODO: unwinding https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions // TODO: maybe unify api and call non thread methods non_send // TODO: use ruby arena to rewind state -- 2.45.2 From d97096bc9f22359b5bca863cd7d26dadc868b33a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 14:17:12 +0200 Subject: [PATCH 034/165] add tests --- assets/tests/ruby/call_script_function_with_params.rb | 7 +++++++ ...ction_gets_called_from_script_with_multiple_params.rb | 2 +- .../rust_function_gets_called_from_script_with_param.rb | 2 +- ...unction_gets_called_from_rust_with_multiple_params.rb | 9 +++++++++ ...t_function_gets_called_from_rust_with_single_param.rb | 4 ++-- 5 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 assets/tests/ruby/call_script_function_with_params.rb create mode 100644 assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb diff --git a/assets/tests/ruby/call_script_function_with_params.rb b/assets/tests/ruby/call_script_function_with_params.rb new file mode 100644 index 0000000..4c3bfd5 --- /dev/null +++ b/assets/tests/ruby/call_script_function_with_params.rb @@ -0,0 +1,7 @@ +STATE = { + "called_with" => nil +} + +def test_func(x) + called_with = x +end diff --git a/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb b/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb index 026ad0b..852897c 100644 --- a/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb +++ b/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb @@ -1,3 +1,3 @@ def test_func - rust_func(5, "test") + rust_func(5, "test") end diff --git a/assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb b/assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb index 5a49c25..3238e08 100644 --- a/assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb +++ b/assets/tests/ruby/rust_function_gets_called_from_script_with_param.rb @@ -1,3 +1,3 @@ def test_func - rust_func(5) + rust_func(5) end diff --git a/assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb b/assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb new file mode 100644 index 0000000..8d255b1 --- /dev/null +++ b/assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb @@ -0,0 +1,9 @@ +STATE = { + 'a_value' => nil, + 'b_value' => nil +} + +def test_func(a, b) + STATE['a_value'] = a + STATE['b_value'] = b +end diff --git a/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb b/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb index a71146d..21b2eb9 100644 --- a/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb +++ b/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb @@ -1,7 +1,7 @@ STATE = { - "a_value" => nil + "a_value" => nil } def test_func(a) - STATE["a_value"] = a + STATE["a_value"] = a end -- 2.45.2 From 0e5257e98c2a33a505602a2b5d45368599bda949 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 14:43:40 +0200 Subject: [PATCH 035/165] handle errors --- ...cript_function_that_causes_runtime_error.rb | 3 +++ assets/tests/ruby/return_via_promise.rb | 9 +++++++++ src/lib.rs | 4 ++-- src/runtimes/ruby.rs | 18 ++++++++++++------ tests/tests.rs | 6 +++--- 5 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 assets/tests/ruby/call_script_function_that_causes_runtime_error.rb create mode 100644 assets/tests/ruby/return_via_promise.rb diff --git a/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb b/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb new file mode 100644 index 0000000..c9a193a --- /dev/null +++ b/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb @@ -0,0 +1,3 @@ +def test_func + print("abc" + 5) +end diff --git a/assets/tests/ruby/return_via_promise.rb b/assets/tests/ruby/return_via_promise.rb new file mode 100644 index 0000000..51635ec --- /dev/null +++ b/assets/tests/ruby/return_via_promise.rb @@ -0,0 +1,9 @@ +STATE = { + 'x' => nil +} + +def test_func + rust_func.and_then do |x| + STATE['x'] = x + end +end diff --git a/src/lib.rs b/src/lib.rs index bf7e0d5..ceb7496 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -280,9 +280,9 @@ const ENTITY_VAR_NAME: &str = "entity"; #[derive(Error, Debug)] pub enum ScriptingError { #[error("script runtime error: {0}")] - RuntimeError(Box), + RuntimeError(Box), #[error("script compilation error: {0}")] - CompileError(Box), + CompileError(Box), #[error("no runtime resource present")] NoRuntimeResource, #[error("no settings resource present")] diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index d46c4ba..73eabd9 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -12,6 +12,7 @@ use std::{ thread::{self, JoinHandle}, }; +use anyhow::anyhow; use bevy::{ asset::Asset, ecs::{ @@ -19,6 +20,7 @@ use bevy::{ schedule::ScheduleLabel, }, reflect::TypePath, + tasks::futures_lite::io, }; use magnus::{function, method::ReturnValue, IntoValue, Ruby, TryConvert}; use magnus::{prelude::*, rb_sys::FromRawValue}; @@ -28,7 +30,7 @@ use serde::Deserialize; use crate::{ assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, - FuncArgs, Runtime, + FuncArgs, Runtime, ScriptingError, }; #[derive(Resource)] @@ -263,8 +265,7 @@ impl Runtime for RubyRuntime { args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self> + Send + 'static, ) -> Result { let name = name.to_string(); - Ok(self - .ruby_thread + self.ruby_thread .as_ref() .unwrap() .execute(Box::new(move |ruby| { @@ -273,9 +274,14 @@ impl Runtime for RubyRuntime { .into_iter() .map(|a| ruby.get_inner(a.0)) .collect(); - let _: magnus::Value = ruby.class_object().funcall(name, args.as_slice()).unwrap(); - RubyValue::nil(&ruby) - }))) + let return_value: magnus::Value = ruby + .class_object() + .funcall(name, args.as_slice()) + .map_err(|e| { + ScriptingError::RuntimeError(Box::new(io::Error::other(e.to_string()))) + })?; + Ok(RubyValue::new(return_value)) + })) } fn call_fn_from_value( diff --git a/tests/tests.rs b/tests/tests.rs index 4991357..676f75e 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -274,7 +274,7 @@ macro_rules! scripting_tests { } #[test] - fn test_call_script_function_that_does_not_exist() { + fn test_call_script_function_that_causes_runtime_error() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|_| {}); @@ -297,7 +297,7 @@ macro_rules! scripting_tests { } #[test] - fn test_script_function_gets_called_from_rust_without_params() { + fn test_script_function_gets_called_from_rust() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|_| {}); @@ -316,7 +316,7 @@ macro_rules! scripting_tests { } #[test] - fn test_promise() { + fn test_return_via_promise() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|runtime| { -- 2.45.2 From 763550b63a495e573e0d1c6f26449fa157d161f5 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 15:37:09 +0200 Subject: [PATCH 036/165] refactor ruby --- .../call_script_function_that_causes_runtime_error.rb | 2 +- assets/tests/ruby/call_script_function_with_params.rb | 8 ++++---- assets/tests/ruby/return_via_promise.rb | 8 ++++---- ...ion_gets_called_from_script_with_multiple_params.rb | 2 +- .../ruby/script_function_gets_called_from_rust.rb | 6 +++--- ...ction_gets_called_from_rust_with_multiple_params.rb | 10 +++++----- ...function_gets_called_from_rust_with_single_param.rb | 6 +++--- tests/tests.rs | 8 ++++---- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb b/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb index c9a193a..ae0afac 100644 --- a/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb +++ b/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb @@ -1,3 +1,3 @@ def test_func - print("abc" + 5) + print('abc' + 5) end diff --git a/assets/tests/ruby/call_script_function_with_params.rb b/assets/tests/ruby/call_script_function_with_params.rb index 4c3bfd5..ba6c271 100644 --- a/assets/tests/ruby/call_script_function_with_params.rb +++ b/assets/tests/ruby/call_script_function_with_params.rb @@ -1,7 +1,7 @@ -STATE = { - "called_with" => nil +$state = { + 'called_with' => nil } -def test_func(x) - called_with = x +def test_func(val) + $state['called_with'] = val end diff --git a/assets/tests/ruby/return_via_promise.rb b/assets/tests/ruby/return_via_promise.rb index 51635ec..dc9d107 100644 --- a/assets/tests/ruby/return_via_promise.rb +++ b/assets/tests/ruby/return_via_promise.rb @@ -1,9 +1,9 @@ -STATE = { +$state = { 'x' => nil } def test_func - rust_func.and_then do |x| - STATE['x'] = x - end + # rust_func.and_then do |x| + # $state['x'] = x + # end end diff --git a/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb b/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb index 852897c..fea8542 100644 --- a/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb +++ b/assets/tests/ruby/rust_function_gets_called_from_script_with_multiple_params.rb @@ -1,3 +1,3 @@ def test_func - rust_func(5, "test") + rust_func(5, 'test') end diff --git a/assets/tests/ruby/script_function_gets_called_from_rust.rb b/assets/tests/ruby/script_function_gets_called_from_rust.rb index 2ed4705..0fae6da 100644 --- a/assets/tests/ruby/script_function_gets_called_from_rust.rb +++ b/assets/tests/ruby/script_function_gets_called_from_rust.rb @@ -1,7 +1,7 @@ -STATE = { - "times_called" => 0 +$state = { + 'times_called' => 0 } def test_func - STATE["times_called"] += 1 + $state['times_called'] += 1 end diff --git a/assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb b/assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb index 8d255b1..4cc49e7 100644 --- a/assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb +++ b/assets/tests/ruby/script_function_gets_called_from_rust_with_multiple_params.rb @@ -1,9 +1,9 @@ -STATE = { - 'a_value' => nil, - 'b_value' => nil +$state = { + 'a_value' => nil, + 'b_value' => nil } def test_func(a, b) - STATE['a_value'] = a - STATE['b_value'] = b + $state['a_value'] = a + $state['b_value'] = b end diff --git a/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb b/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb index 21b2eb9..769ea1c 100644 --- a/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb +++ b/assets/tests/ruby/script_function_gets_called_from_rust_with_single_param.rb @@ -1,7 +1,7 @@ -STATE = { - "a_value" => nil +$state = { + 'a_value' => nil } def test_func(a) - STATE["a_value"] = a + $state['a_value'] = a end diff --git a/tests/tests.rs b/tests/tests.rs index 676f75e..49ec68a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -487,7 +487,7 @@ mod lua_tests { mod ruby_tests { use bevy::prelude::*; use bevy_scriptum::runtimes::ruby::{prelude::*, RubyScriptData}; - use magnus::{value::ReprValue, Module}; + use magnus::{value::ReprValue, Module, Object}; impl AssertStateKeyValue for RubyRuntime { type ScriptData = RubyScriptData; @@ -496,7 +496,7 @@ mod ruby_tests { let runtime = world.get_resource::().unwrap(); let key = key.to_string(); runtime.with_engine_thread(move |engine| { - let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); + let state: magnus::value::Value = engine.eval("$state").unwrap(); let res: i64 = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value) }) @@ -506,7 +506,7 @@ mod ruby_tests { let runtime = world.get_resource::().unwrap(); let key = key.to_string(); runtime.with_engine_thread(move |engine| { - let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); + let state: magnus::value::Value = engine.eval("$state").unwrap(); let res: i32 = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value) }) @@ -520,7 +520,7 @@ mod ruby_tests { ) { let runtime = world.get_resource::().unwrap(); runtime.with_engine(|engine| { - let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap(); + let state: magnus::value::Value = engine.eval("$state").unwrap(); let res: String = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value); }); -- 2.45.2 From ca508bef58a41d18233efc63a7768c065dc1df82 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 15:54:12 +0200 Subject: [PATCH 037/165] everyting but promises --- assets/tests/ruby/promise_runtime_error.rb | 5 +++ assets/tests/ruby/return_via_promise.rb | 6 +-- assets/tests/ruby/side_effects.rb | 3 ++ src/runtimes/ruby.rs | 6 ++- tests/tests.rs | 52 +++++----------------- 5 files changed, 27 insertions(+), 45 deletions(-) create mode 100644 assets/tests/ruby/promise_runtime_error.rb create mode 100644 assets/tests/ruby/side_effects.rb diff --git a/assets/tests/ruby/promise_runtime_error.rb b/assets/tests/ruby/promise_runtime_error.rb new file mode 100644 index 0000000..48827fc --- /dev/null +++ b/assets/tests/ruby/promise_runtime_error.rb @@ -0,0 +1,5 @@ +def test_func + rust_func.and_then do |x| + print("abc" + 5) + end +end diff --git a/assets/tests/ruby/return_via_promise.rb b/assets/tests/ruby/return_via_promise.rb index dc9d107..c1ec94b 100644 --- a/assets/tests/ruby/return_via_promise.rb +++ b/assets/tests/ruby/return_via_promise.rb @@ -3,7 +3,7 @@ $state = { } def test_func - # rust_func.and_then do |x| - # $state['x'] = x - # end + rust_func.and_then do |x| + $state['x'] = x + end end diff --git a/assets/tests/ruby/side_effects.rb b/assets/tests/ruby/side_effects.rb new file mode 100644 index 0000000..8b6177d --- /dev/null +++ b/assets/tests/ruby/side_effects.rb @@ -0,0 +1,3 @@ +def test_func + spawn_entity +end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 73eabd9..6700a93 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -331,11 +331,13 @@ impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { macro_rules! impl_tuple { ($($idx:tt $t:tt),+) => { - impl<'a, $($t,)+> FuncArgs<'a, RubyValue, RubyRuntime> + impl<'a, $($t: IntoValue,)+> FuncArgs<'a, RubyValue, RubyRuntime> for ($($t,)+) { fn parse(self, _engine: &'a magnus::Ruby) -> Vec { - todo!(); + vec![ + $(RubyValue::new(self.$idx.into_value()), )+ + ] } } }; diff --git a/tests/tests.rs b/tests/tests.rs index 49ec68a..950bbd0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -189,7 +189,7 @@ macro_rules! scripting_tests { } #[test] - fn test_script_function_gets_called_from_rust_with_heterogenous_params() { + fn test_script_function_gets_called_from_rust_with_multiple_params() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|_| {}); @@ -225,33 +225,7 @@ macro_rules! scripting_tests { } #[test] - fn test_script_function_gets_called_from_rust_with_multiple_params() { - let mut app = build_test_app(); - - app.add_scripting::<$runtime>(|_| {}); - - let entity_id = run_script::<$runtime, _, _>( - &mut app, - format!( - "tests/{}/script_function_gets_called_from_rust_with_multiple_params.{}", - $script, $extension - ) - .to_string(), - |mut scripted_entities: Query<(Entity, &mut <$runtime as Runtime>::ScriptData)>, - scripting_runtime: ResMut<$runtime>| { - let (entity, mut script_data) = scripted_entities.single_mut().unwrap(); - scripting_runtime - .call_fn("test_func", &mut script_data, entity, vec![1, 2]) - .unwrap(); - }, - ); - - <$runtime>::assert_state_key_value_i32(&app.world(), entity_id, "a_value", 1i32); - <$runtime>::assert_state_key_value_i32(&app.world(), entity_id, "b_value", 2i32); - } - - #[test] - fn test_call_script_function_that_casues_runtime_error() { + fn call_script_function_that_casues_runtime_error() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|_| {}); @@ -274,18 +248,14 @@ macro_rules! scripting_tests { } #[test] - fn test_call_script_function_that_causes_runtime_error() { + fn call_script_function_that_does_not_exist() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|_| {}); run_script::<$runtime, _, _>( &mut app, - format!( - "tests/{}/call_script_function_that_causes_runtime_error.{}", - $script, $extension - ) - .to_string(), + format!("tests/{}/side_effects.{}", $script, $extension).to_string(), |mut scripted_entities: Query<(Entity, &mut <$runtime as Runtime>::ScriptData)>, scripting_runtime: ResMut<$runtime>| { let (entity, mut script_data) = scripted_entities.single_mut().unwrap(); @@ -297,7 +267,7 @@ macro_rules! scripting_tests { } #[test] - fn test_script_function_gets_called_from_rust() { + fn script_function_gets_called_from_rust() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|_| {}); @@ -316,7 +286,7 @@ macro_rules! scripting_tests { } #[test] - fn test_return_via_promise() { + fn return_via_promise() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|runtime| { @@ -333,7 +303,7 @@ macro_rules! scripting_tests { } #[test] - fn test_promise_runtime_error_does_not_panic() { + fn promise_runtime_error() { let mut app = build_test_app(); app.add_scripting::<$runtime>(|runtime| { @@ -348,7 +318,7 @@ macro_rules! scripting_tests { } #[test] - fn test_side_effects() { + fn side_effects() { let mut app = build_test_app(); #[derive(Component)] @@ -374,7 +344,7 @@ macro_rules! scripting_tests { } #[test] - fn test_rust_function_gets_called_from_script() { + fn rust_function_gets_called_from_script() { let mut app = build_test_app(); #[derive(Default, Resource)] @@ -519,7 +489,9 @@ mod ruby_tests { value: &str, ) { let runtime = world.get_resource::().unwrap(); - runtime.with_engine(|engine| { + let key = key.to_string(); + let value = value.to_string(); + runtime.with_engine_thread(move |engine| { let state: magnus::value::Value = engine.eval("$state").unwrap(); let res: String = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value); -- 2.45.2 From 4abd59840c674d92176d94d8a5d0165d165afc47 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 16:30:32 +0200 Subject: [PATCH 038/165] promise wip --- src/promise.rs | 2 +- src/runtimes/ruby.rs | 73 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/promise.rs b/src/promise.rs index 6b84a63..038cc0c 100644 --- a/src/promise.rs +++ b/src/promise.rs @@ -58,7 +58,7 @@ impl Promise { } /// Register a callback that will be called when the [Promise] is resolved. - #[cfg(any(feature = "rhai", feature = "lua"))] + #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] pub(crate) fn then(&mut self, callback: V) -> Self { let mut inner = self .inner diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 6700a93..8fcc2c3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -22,7 +22,10 @@ use bevy::{ reflect::TypePath, tasks::futures_lite::io, }; -use magnus::{function, method::ReturnValue, IntoValue, Ruby, TryConvert}; +use magnus::{ + data_type_builder, function, method::ReturnValue, try_convert, value::Lazy, DataType, + DataTypeFunctions, IntoValue, RClass, Ruby, TryConvert, TypedData, +}; use magnus::{prelude::*, rb_sys::FromRawValue}; use rb_sys::{rb_define_global_function, rb_scan_args, VALUE}; use serde::Deserialize; @@ -30,6 +33,7 @@ use serde::Deserialize; use crate::{ assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, + promise::Promise, FuncArgs, Runtime, ScriptingError, }; @@ -105,6 +109,31 @@ impl Drop for RubyThread { } } +impl DataTypeFunctions for Promise<(), RubyValue> {} + +unsafe impl TypedData for Promise<(), RubyValue> { + fn class(ruby: &Ruby) -> magnus::RClass { + static CLASS: Lazy = Lazy::new(|ruby| { + let class = ruby.define_class("Promise", ruby.class_object()).unwrap(); + class.undef_default_alloc_func(); + class + }); + ruby.get_inner(&CLASS) + } + + fn data_type() -> &'static magnus::DataType { + static DATA_TYPE: DataType = data_type_builder!(Promise<(), RubyValue>, "promise").build(); + &DATA_TYPE + } +} + +fn then(r_self: magnus::Value) -> magnus::Value { + dbg!(r_self); + panic!(); + let ruby = Ruby::get().unwrap(); + ruby.qnil().as_value() +} + impl Default for RubyRuntime { fn default() -> Self { let (lock, cvar) = &*Arc::clone(&RUBY_THREAD); @@ -115,6 +144,43 @@ impl Default for RubyRuntime { } let ruby_thread = ruby_thread.take().unwrap(); cvar.notify_all(); + + ruby_thread.execute(Box::new(|ruby| { + // TODO: maybe put promise in a module , maybe do so for other runtimes too + let promise = ruby.define_class("Promise", ruby.class_object()).unwrap(); + promise + .define_method("and_then", magnus::method!(then, 0)) + .unwrap(); + })); + // engine + // .register_userdata_type::(|typ| { + // typ.add_field_method_get("index", |_, entity| Ok(entity.0.index())); + // }) + // .expect("Failed to register BevyEntity userdata type"); + // + // engine + // .register_userdata_type::>(|typ| { + // typ.add_method_mut("and_then", |engine, promise, callback: Function| { + // Ok(Promise::then(promise, LuaValue::new(engine, callback))) + // }); + // }) + // .expect("Failed to register Promise userdata type"); + // + // engine + // .register_userdata_type::(|typ| { + // typ.add_field_method_get("x", |_engine, vec| Ok(vec.0.x)); + // typ.add_field_method_get("y", |_engine, vec| Ok(vec.0.y)); + // typ.add_field_method_get("z", |_engine, vec| Ok(vec.0.z)); + // }) + // .expect("Failed to register BevyVec3 userdata type"); + // let vec3_constructor = engine + // .create_function(|_, (x, y, z)| Ok(BevyVec3(Vec3::new(x, y, z)))) + // .expect("Failed to create Vec3 constructor"); + // engine + // .globals() + // .set("Vec3", vec3_constructor) + // .expect("Failed to set Vec3 global"); + // Self { ruby_thread: Some(ruby_thread), } @@ -236,14 +302,14 @@ impl Runtime for RubyRuntime { let method_name = method_name.to_string(); let callbacks = RUBY_CALLBACKS.lock().unwrap(); let f = callbacks.get(&method_name).unwrap(); - f( + let result = f( (), args.iter() .map(|arg| RubyValue::new(arg.into_value())) .collect(), ) .expect("failed to call callback"); - ruby.qnil().as_value() + result.into_value() } self.ruby_thread @@ -284,6 +350,7 @@ impl Runtime for RubyRuntime { })) } + // TODO: does this have a test? fn call_fn_from_value( &self, _value: &Self::Value, -- 2.45.2 From 16b322b707eebe105225b987f3ff91793f76e32d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 16:55:12 +0200 Subject: [PATCH 039/165] promises almost work --- assets/tests/ruby/return_via_promise.rb | 4 ++-- src/runtimes/ruby.rs | 29 ++++++++++++++++--------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/assets/tests/ruby/return_via_promise.rb b/assets/tests/ruby/return_via_promise.rb index c1ec94b..fcffa83 100644 --- a/assets/tests/ruby/return_via_promise.rb +++ b/assets/tests/ruby/return_via_promise.rb @@ -3,7 +3,7 @@ $state = { } def test_func - rust_func.and_then do |x| + rust_func.and_then lambda { |x| $state['x'] = x - end + } end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 8fcc2c3..1cb56dc 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -24,7 +24,7 @@ use bevy::{ }; use magnus::{ data_type_builder, function, method::ReturnValue, try_convert, value::Lazy, DataType, - DataTypeFunctions, IntoValue, RClass, Ruby, TryConvert, TypedData, + DataTypeFunctions, IntoValue, RClass, RObject, Ruby, TryConvert, TypedData, }; use magnus::{prelude::*, rb_sys::FromRawValue}; use rb_sys::{rb_define_global_function, rb_scan_args, VALUE}; @@ -127,11 +127,10 @@ unsafe impl TypedData for Promise<(), RubyValue> { } } -fn then(r_self: magnus::Value) -> magnus::Value { - dbg!(r_self); - panic!(); +fn then(r_self: magnus::Value, callback: magnus::Value) -> magnus::Value { + let promise: &Promise<(), RubyValue> = TryConvert::try_convert(r_self).unwrap(); let ruby = Ruby::get().unwrap(); - ruby.qnil().as_value() + promise.clone().then(RubyValue::new(callback)).into_value() } impl Default for RubyRuntime { @@ -149,7 +148,7 @@ impl Default for RubyRuntime { // TODO: maybe put promise in a module , maybe do so for other runtimes too let promise = ruby.define_class("Promise", ruby.class_object()).unwrap(); promise - .define_method("and_then", magnus::method!(then, 0)) + .define_method("and_then", magnus::method!(then, 1)) .unwrap(); })); // engine @@ -350,14 +349,24 @@ impl Runtime for RubyRuntime { })) } - // TODO: does this have a test? + // TODO: add test fn call_fn_from_value( &self, - _value: &Self::Value, + value: &Self::Value, _context: &Self::CallContext, - _args: Vec, + args: Vec, ) -> Result { - todo!() + let ruby = Ruby::get().unwrap(); + + let f: RObject = TryConvert::try_convert(ruby.get_inner(value.0)).unwrap(); + let result: magnus::Value = f.funcall("call", ()).unwrap(); + Ok(RubyValue::new(result)) + // self.ruby_thread + // .as_ref() + // .unwrap() + // .execute(Box::new(move |ruby| { + // todo!(); + // })) } fn is_current_thread() -> bool { -- 2.45.2 From 8e7f15ed73736bcf0b296895f47755e105da532e Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 17:09:08 +0200 Subject: [PATCH 040/165] working promises --- src/runtimes/ruby.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 1cb56dc..4c1520a 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -23,8 +23,8 @@ use bevy::{ tasks::futures_lite::io, }; use magnus::{ - data_type_builder, function, method::ReturnValue, try_convert, value::Lazy, DataType, - DataTypeFunctions, IntoValue, RClass, RObject, Ruby, TryConvert, TypedData, + block::Proc, data_type_builder, function, method::ReturnValue, try_convert, value::Lazy, + DataType, DataTypeFunctions, IntoValue, RClass, RObject, Ruby, TryConvert, TypedData, }; use magnus::{prelude::*, rb_sys::FromRawValue}; use rb_sys::{rb_define_global_function, rb_scan_args, VALUE}; @@ -129,7 +129,6 @@ unsafe impl TypedData for Promise<(), RubyValue> { fn then(r_self: magnus::Value, callback: magnus::Value) -> magnus::Value { let promise: &Promise<(), RubyValue> = TryConvert::try_convert(r_self).unwrap(); - let ruby = Ruby::get().unwrap(); promise.clone().then(RubyValue::new(callback)).into_value() } @@ -356,17 +355,21 @@ impl Runtime for RubyRuntime { _context: &Self::CallContext, args: Vec, ) -> Result { - let ruby = Ruby::get().unwrap(); + let value = value.clone(); // TODO: maybe just clone/wrap where we added Send + static + // currently?> + self.ruby_thread + .as_ref() + .unwrap() + .execute(Box::new(move |ruby| { + let f: Proc = TryConvert::try_convert(ruby.get_inner(value.0)).unwrap(); - let f: RObject = TryConvert::try_convert(ruby.get_inner(value.0)).unwrap(); - let result: magnus::Value = f.funcall("call", ()).unwrap(); - Ok(RubyValue::new(result)) - // self.ruby_thread - // .as_ref() - // .unwrap() - // .execute(Box::new(move |ruby| { - // todo!(); - // })) + let args: Vec<_> = args + .into_iter() + .map(|x| ruby.get_inner(x.0).as_value()) + .collect(); + let result: magnus::Value = f.funcall("call", args.as_slice()).unwrap(); + Ok(RubyValue::new(result)) + })) } fn is_current_thread() -> bool { -- 2.45.2 From 03ec5d4941380c8cde48424ffea13a2bdee24cf4 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 17:13:38 +0200 Subject: [PATCH 041/165] add todo --- src/runtimes/ruby.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 4c1520a..3d05da3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -150,6 +150,7 @@ impl Default for RubyRuntime { .define_method("and_then", magnus::method!(then, 1)) .unwrap(); })); + // TODO: add test for those types for every runtime // engine // .register_userdata_type::(|typ| { // typ.add_field_method_get("index", |_, entity| Ok(entity.0.index())); -- 2.45.2 From 2a05b9a50d444b6b4c0eb43f380f9a72f572f250 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 14 May 2025 21:17:13 +0200 Subject: [PATCH 042/165] all tests pass --- assets/tests/ruby/promise_runtime_error.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/tests/ruby/promise_runtime_error.rb b/assets/tests/ruby/promise_runtime_error.rb index 48827fc..a3bdc78 100644 --- a/assets/tests/ruby/promise_runtime_error.rb +++ b/assets/tests/ruby/promise_runtime_error.rb @@ -1,5 +1,5 @@ def test_func - rust_func.and_then do |x| - print("abc" + 5) - end + rust_func.and_then lambda { |x| + $state['x'] = x + } end -- 2.45.2 From bd4b377fc671bb1df501c057a150cdd1e6d865bc Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 07:00:00 +0200 Subject: [PATCH 043/165] promise runtime err --- assets/tests/ruby/promise_runtime_error.rb | 2 +- src/runtimes/ruby.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/tests/ruby/promise_runtime_error.rb b/assets/tests/ruby/promise_runtime_error.rb index a3bdc78..09f5aa7 100644 --- a/assets/tests/ruby/promise_runtime_error.rb +++ b/assets/tests/ruby/promise_runtime_error.rb @@ -1,5 +1,5 @@ def test_func rust_func.and_then lambda { |x| - $state['x'] = x + print('abc' + 5) } end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 3d05da3..c27933a 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -368,7 +368,9 @@ impl Runtime for RubyRuntime { .into_iter() .map(|x| ruby.get_inner(x.0).as_value()) .collect(); - let result: magnus::Value = f.funcall("call", args.as_slice()).unwrap(); + let result: magnus::Value = f.funcall("call", args.as_slice()).map_err(|e| { + ScriptingError::RuntimeError(Box::new(io::Error::other(e.to_string()))) + })?; Ok(RubyValue::new(result)) })) } -- 2.45.2 From 62fcdab69a16d6dc8091d1639dd97e270bdcea47 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 07:00:00 +0200 Subject: [PATCH 044/165] use blocks instead of lambdas --- assets/tests/ruby/promise_runtime_error.rb | 4 ++-- assets/tests/ruby/return_via_promise.rb | 4 ++-- src/runtimes/ruby.rs | 10 +++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/assets/tests/ruby/promise_runtime_error.rb b/assets/tests/ruby/promise_runtime_error.rb index 09f5aa7..52715ce 100644 --- a/assets/tests/ruby/promise_runtime_error.rb +++ b/assets/tests/ruby/promise_runtime_error.rb @@ -1,5 +1,5 @@ def test_func - rust_func.and_then lambda { |x| + rust_func.and_then do |x| print('abc' + 5) - } + end end diff --git a/assets/tests/ruby/return_via_promise.rb b/assets/tests/ruby/return_via_promise.rb index fcffa83..c1ec94b 100644 --- a/assets/tests/ruby/return_via_promise.rb +++ b/assets/tests/ruby/return_via_promise.rb @@ -3,7 +3,7 @@ $state = { } def test_func - rust_func.and_then lambda { |x| + rust_func.and_then do |x| $state['x'] = x - } + end end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index c27933a..7a9022b 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -127,9 +127,13 @@ unsafe impl TypedData for Promise<(), RubyValue> { } } -fn then(r_self: magnus::Value, callback: magnus::Value) -> magnus::Value { +fn then(r_self: magnus::Value) -> magnus::Value { let promise: &Promise<(), RubyValue> = TryConvert::try_convert(r_self).unwrap(); - promise.clone().then(RubyValue::new(callback)).into_value() + let ruby = Ruby::get().unwrap(); + promise + .clone() + .then(RubyValue::new(ruby.block_proc().unwrap().as_value())) + .into_value() } impl Default for RubyRuntime { @@ -147,7 +151,7 @@ impl Default for RubyRuntime { // TODO: maybe put promise in a module , maybe do so for other runtimes too let promise = ruby.define_class("Promise", ruby.class_object()).unwrap(); promise - .define_method("and_then", magnus::method!(then, 1)) + .define_method("and_then", magnus::method!(then, 0)) .unwrap(); })); // TODO: add test for those types for every runtime -- 2.45.2 From 3962fc40b1cac2b0ead24b3543404b39f1242edd Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 07:00:00 +0200 Subject: [PATCH 045/165] clippy --- src/runtimes/ruby.rs | 16 +++++----------- tests/tests.rs | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 7a9022b..5123ec6 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -7,27 +7,21 @@ use std::{ collections::HashMap, - ffi::{c_void, CString}, sync::{Arc, Condvar, LazyLock, Mutex}, thread::{self, JoinHandle}, }; -use anyhow::anyhow; use bevy::{ asset::Asset, - ecs::{ - component::Component, query::QueryParManyUniqueIter, resource::Resource, - schedule::ScheduleLabel, - }, + ecs::{component::Component, resource::Resource, schedule::ScheduleLabel}, reflect::TypePath, tasks::futures_lite::io, }; +use magnus::prelude::*; use magnus::{ - block::Proc, data_type_builder, function, method::ReturnValue, try_convert, value::Lazy, - DataType, DataTypeFunctions, IntoValue, RClass, RObject, Ruby, TryConvert, TypedData, + block::Proc, data_type_builder, function, value::Lazy, DataType, DataTypeFunctions, IntoValue, + RClass, Ruby, TryConvert, TypedData, }; -use magnus::{prelude::*, rb_sys::FromRawValue}; -use rb_sys::{rb_define_global_function, rb_scan_args, VALUE}; use serde::Deserialize; use crate::{ @@ -408,7 +402,7 @@ impl FuncArgs<'_, RubyValue, RubyRuntime> for () { } impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { - fn parse(self, engine: &magnus::Ruby) -> Vec { + fn parse(self, _engine: &magnus::Ruby) -> Vec { self.into_iter() .map(|x| RubyValue::new(x.into_value())) .collect() diff --git a/tests/tests.rs b/tests/tests.rs index 950bbd0..30d7704 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -457,7 +457,7 @@ mod lua_tests { mod ruby_tests { use bevy::prelude::*; use bevy_scriptum::runtimes::ruby::{prelude::*, RubyScriptData}; - use magnus::{value::ReprValue, Module, Object}; + use magnus::value::ReprValue; impl AssertStateKeyValue for RubyRuntime { type ScriptData = RubyScriptData; -- 2.45.2 From ca703cb389bea339f261b94954fcc3b4d56f2bed Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 07:00:00 +0200 Subject: [PATCH 046/165] get rid of mlua feature --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 84fa401..4571787 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/jarkonik/bevy_scriptum" keywords = ["bevy", "rhai", "scripting", "game", "gamedev"] [features] -lua = ["mlua/luajit"] +lua = ["dep:mlua", "mlua/luajit"] rhai = ["dep:rhai"] ruby = ["dep:magnus"] -- 2.45.2 From e654c7fa30fb8d34d13c0f9bec74ed61de25df85 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 07:00:00 +0200 Subject: [PATCH 047/165] cleanup --- src/runtimes/ruby.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 5123ec6..d12609d 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,9 +1,7 @@ -// TODO: maybe make all runtime engines not send and spawn threads for them like Ruby +// TODO: maybe make all runtime engines not send and spawn threads for them like Ruby, benchmark? // TODO: make sure ruby is statically linked // TODO: add tests for every runtime for return value -// TODO: unwinding https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions // TODO: maybe unify api and call non thread methods non_send -// TODO: use ruby arena to rewind state use std::{ collections::HashMap, -- 2.45.2 From 8245f14737870684934ce667deafabb1a659f6da Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 07:00:00 +0200 Subject: [PATCH 048/165] cleanup --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4571787..240f3f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,9 +31,8 @@ mlua = { version = "0.9.8", features = [ "vendored", "send", ], optional = true } -magnus = { version = "0.7.1", optional = true, features = ["embed", "rb-sys"] } +magnus = { version = "0.7.1", optional = true, features = ["embed"] } crossbeam-channel = "0.5.15" -rb-sys = "0.9.114" [[example]] name = "call_function_from_rust_rhai" -- 2.45.2 From 33496446c932b3b0a3ae3871beafcf2d1ac37876 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 17:22:52 +0200 Subject: [PATCH 049/165] remove some unwraps --- src/runtimes/ruby.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index d12609d..98d8e21 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -72,7 +72,7 @@ impl RubyThread { let handle = thread::spawn(move || { let _cleanup = unsafe { magnus::embed::init() }; while let Ok(f) = receiver.recv() { - let ruby = Ruby::get().unwrap(); + let ruby = Ruby::get().expect("Failed to get a handle to Ruby API"); f(ruby); } }); @@ -87,17 +87,21 @@ impl RubyThread { let (return_sender, return_receiver) = crossbeam_channel::bounded(0); self.sender .send(Box::new(move |ruby| { - return_sender.send(f(ruby)).unwrap(); + return_sender + .send(f(ruby)) + .expect("Failed to send callback return value"); })) .unwrap(); - return_receiver.recv().unwrap() + return_receiver + .recv() + .expect("Faild to send execution unit to Ruby thread") } } impl Drop for RubyThread { fn drop(&mut self) { let handle = self.handle.take().unwrap(); - handle.join().unwrap(); + handle.join().expect("Failed to join Ruby thread"); } } @@ -377,7 +381,7 @@ impl Runtime for RubyRuntime { } pub mod prelude { - pub use super::RubyRuntime; + pub use super::{RubyRuntime, RubyScript, RubyScriptData}; } impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { -- 2.45.2 From 3e217c18653777d0873c97d33818f541ba672e45 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 17:36:50 +0200 Subject: [PATCH 050/165] add noop if no block given --- src/runtimes/ruby.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 98d8e21..df93e17 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -2,6 +2,7 @@ // TODO: make sure ruby is statically linked // TODO: add tests for every runtime for return value // TODO: maybe unify api and call non thread methods non_send +// TODO: add tests for entity variable and buitin types for every runtime use std::{ collections::HashMap, @@ -110,7 +111,9 @@ impl DataTypeFunctions for Promise<(), RubyValue> {} unsafe impl TypedData for Promise<(), RubyValue> { fn class(ruby: &Ruby) -> magnus::RClass { static CLASS: Lazy = Lazy::new(|ruby| { - let class = ruby.define_class("Promise", ruby.class_object()).unwrap(); + let class = ruby + .define_class("Promise", ruby.class_object()) + .expect("Failed to define Promise class in Ruby"); class.undef_default_alloc_func(); class }); @@ -128,7 +131,12 @@ fn then(r_self: magnus::Value) -> magnus::Value { let ruby = Ruby::get().unwrap(); promise .clone() - .then(RubyValue::new(ruby.block_proc().unwrap().as_value())) + .then(RubyValue::new(if ruby.block_given() { + ruby.block_proc().expect("Failed to create Proc").as_value() + } else { + ruby.proc_new(|ruby, _, _| ruby.qnil().as_value()) + .as_value() + })) .into_value() } @@ -315,6 +323,7 @@ impl Runtime for RubyRuntime { .as_ref() .unwrap() .execute(Box::new(move |ruby| { + // TODO: use ruby.proc_from_fn instead ruby.define_global_function(&name, function!(callback, -1)); RubyValue::nil(&ruby) })); -- 2.45.2 From 9b8b15ded74fa0ba651728973a627dd8d44c221a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 17:38:15 +0200 Subject: [PATCH 051/165] cleanup --- src/runtimes/ruby.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index df93e17..fa49f2c 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -323,7 +323,6 @@ impl Runtime for RubyRuntime { .as_ref() .unwrap() .execute(Box::new(move |ruby| { - // TODO: use ruby.proc_from_fn instead ruby.define_global_function(&name, function!(callback, -1)); RubyValue::nil(&ruby) })); -- 2.45.2 From 04d8845729157cc811dd7c724f87db1e85c2e6cd Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 15 May 2025 17:39:07 +0200 Subject: [PATCH 052/165] refactor --- src/runtimes/ruby.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index fa49f2c..61694e3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -131,12 +131,14 @@ fn then(r_self: magnus::Value) -> magnus::Value { let ruby = Ruby::get().unwrap(); promise .clone() - .then(RubyValue::new(if ruby.block_given() { - ruby.block_proc().expect("Failed to create Proc").as_value() - } else { - ruby.proc_new(|ruby, _, _| ruby.qnil().as_value()) - .as_value() - })) + .then(RubyValue::new( + if ruby.block_given() { + ruby.block_proc().expect("Failed to create Proc") + } else { + ruby.proc_new(|ruby, _, _| ruby.qnil().as_value()) + } + .as_value(), + )) .into_value() } -- 2.45.2 From fccc822f9a106aa173d5e5ea4376c9086e55ab0a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 16 May 2025 07:00:00 +0200 Subject: [PATCH 053/165] entities --- assets/tests/lua/entity_variable.lua | 0 assets/tests/lua/entity_variable_eval.lua | 0 assets/tests/rhai/entity_variable.rhai | 0 assets/tests/rhai/entity_variable_eval.rhai | 0 assets/tests/ruby/entity_variable.rb | 3 + assets/tests/ruby/entity_variable_eval.rb | 6 ++ src/runtimes/ruby.rs | 90 ++++++++++++++------- tests/tests.rs | 64 +++++++++++++++ 8 files changed, 136 insertions(+), 27 deletions(-) create mode 100644 assets/tests/lua/entity_variable.lua create mode 100644 assets/tests/lua/entity_variable_eval.lua create mode 100644 assets/tests/rhai/entity_variable.rhai create mode 100644 assets/tests/rhai/entity_variable_eval.rhai create mode 100644 assets/tests/ruby/entity_variable.rb create mode 100644 assets/tests/ruby/entity_variable_eval.rb diff --git a/assets/tests/lua/entity_variable.lua b/assets/tests/lua/entity_variable.lua new file mode 100644 index 0000000..e69de29 diff --git a/assets/tests/lua/entity_variable_eval.lua b/assets/tests/lua/entity_variable_eval.lua new file mode 100644 index 0000000..e69de29 diff --git a/assets/tests/rhai/entity_variable.rhai b/assets/tests/rhai/entity_variable.rhai new file mode 100644 index 0000000..e69de29 diff --git a/assets/tests/rhai/entity_variable_eval.rhai b/assets/tests/rhai/entity_variable_eval.rhai new file mode 100644 index 0000000..e69de29 diff --git a/assets/tests/ruby/entity_variable.rb b/assets/tests/ruby/entity_variable.rb new file mode 100644 index 0000000..84fdc86 --- /dev/null +++ b/assets/tests/ruby/entity_variable.rb @@ -0,0 +1,3 @@ +def test_func + rust_func($entity.index) +end diff --git a/assets/tests/ruby/entity_variable_eval.rb b/assets/tests/ruby/entity_variable_eval.rb new file mode 100644 index 0000000..da5f2a3 --- /dev/null +++ b/assets/tests/ruby/entity_variable_eval.rb @@ -0,0 +1,6 @@ +$index = $entity.index + +def test_func + print($index) + rust_func($index) +end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 61694e3..83306a0 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -12,22 +12,23 @@ use std::{ use bevy::{ asset::Asset, - ecs::{component::Component, resource::Resource, schedule::ScheduleLabel}, + ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, + math::Vec3, reflect::TypePath, tasks::futures_lite::io, }; -use magnus::prelude::*; use magnus::{ block::Proc, data_type_builder, function, value::Lazy, DataType, DataTypeFunctions, IntoValue, RClass, Ruby, TryConvert, TypedData, }; +use magnus::{method, prelude::*}; use serde::Deserialize; use crate::{ assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, promise::Promise, - FuncArgs, Runtime, ScriptingError, + FuncArgs, Runtime, ScriptingError, ENTITY_VAR_NAME, }; #[derive(Resource)] @@ -142,6 +143,32 @@ fn then(r_self: magnus::Value) -> magnus::Value { .into_value() } +#[magnus::wrap(class = "BevyEntity")] +pub struct BevyEntity(pub Entity); + +impl BevyEntity { + fn index(&self) -> u32 { + self.0.index() + } +} + +#[magnus::wrap(class = "BevyVec3")] +pub struct BevyVec3(pub Vec3); + +impl BevyVec3 { + fn x(&self) -> f32 { + self.0.x + } + + fn y(&self) -> f32 { + self.0.y + } + + fn z(&self) -> f32 { + self.0.z + } +} + impl Default for RubyRuntime { fn default() -> Self { let (lock, cvar) = &*Arc::clone(&RUBY_THREAD); @@ -159,29 +186,20 @@ impl Default for RubyRuntime { promise .define_method("and_then", magnus::method!(then, 0)) .unwrap(); + + let entity = ruby + .define_class("BevyEntity", ruby.class_object()) + .unwrap(); + entity + .define_method("index", method!(BevyEntity::index, 0)) + .unwrap(); + + let vec3 = ruby.define_class("BevyVec3", ruby.class_object()).unwrap(); + vec3.define_method("x", method!(BevyVec3::x, 0)).unwrap(); + vec3.define_method("y", method!(BevyVec3::y, 0)).unwrap(); + vec3.define_method("z", method!(BevyVec3::z, 0)).unwrap(); })); // TODO: add test for those types for every runtime - // engine - // .register_userdata_type::(|typ| { - // typ.add_field_method_get("index", |_, entity| Ok(entity.0.index())); - // }) - // .expect("Failed to register BevyEntity userdata type"); - // - // engine - // .register_userdata_type::>(|typ| { - // typ.add_method_mut("and_then", |engine, promise, callback: Function| { - // Ok(Promise::then(promise, LuaValue::new(engine, callback))) - // }); - // }) - // .expect("Failed to register Promise userdata type"); - // - // engine - // .register_userdata_type::(|typ| { - // typ.add_field_method_get("x", |_engine, vec| Ok(vec.0.x)); - // typ.add_field_method_get("y", |_engine, vec| Ok(vec.0.y)); - // typ.add_field_method_get("z", |_engine, vec| Ok(vec.0.z)); - // }) - // .expect("Failed to register BevyVec3 userdata type"); // let vec3_constructor = engine // .create_function(|_, (x, y, z)| Ok(BevyVec3(Vec3::new(x, y, z)))) // .expect("Failed to create Vec3 constructor"); @@ -262,14 +280,23 @@ impl Runtime for RubyRuntime { fn eval( &self, script: &Self::ScriptAsset, - _entity: bevy::prelude::Entity, + entity: bevy::prelude::Entity, ) -> Result { let script = script.0.clone(); self.ruby_thread .as_ref() .unwrap() .execute(Box::new(move |ruby| { + let var = ruby + .define_variable(ENTITY_VAR_NAME, BevyEntity(entity)) + .unwrap(); + let value = ruby.eval::(&script).unwrap(); + + // SAFETY: this is guaranteed to be executed on a single thread + // so should be safe according to Magnus documentation + unsafe { *var = ruby.qnil().as_value() }; + RubyValue::new(value) })); Ok(RubyScriptData) @@ -336,7 +363,7 @@ impl Runtime for RubyRuntime { &self, name: &str, _script_data: &mut Self::ScriptData, - _entity: bevy::prelude::Entity, + entity: bevy::prelude::Entity, args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self> + Send + 'static, ) -> Result { let name = name.to_string(); @@ -344,6 +371,10 @@ impl Runtime for RubyRuntime { .as_ref() .unwrap() .execute(Box::new(move |ruby| { + let var = ruby + .define_variable(ENTITY_VAR_NAME, BevyEntity(entity)) + .unwrap(); + let args: Vec<_> = args .parse(&ruby) .into_iter() @@ -355,6 +386,11 @@ impl Runtime for RubyRuntime { .map_err(|e| { ScriptingError::RuntimeError(Box::new(io::Error::other(e.to_string()))) })?; + + // SAFETY: this is guaranteed to be executed on a single thread + // so should be safe according to Magnus documentation + unsafe { *var = ruby.qnil().as_value() }; + Ok(RubyValue::new(return_value)) })) } @@ -391,7 +427,7 @@ impl Runtime for RubyRuntime { } pub mod prelude { - pub use super::{RubyRuntime, RubyScript, RubyScriptData}; + pub use super::{BevyEntity, BevyVec3, RubyRuntime, RubyScript, RubyScriptData}; } impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { diff --git a/tests/tests.rs b/tests/tests.rs index 30d7704..b3c7a3a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -378,6 +378,70 @@ macro_rules! scripting_tests { 1 ); } + + #[test] + fn entity_variable_is_available_in_callback() { + let mut app = build_test_app(); + + #[derive(Default, Resource)] + struct State { + index: u32, + } + + app.world_mut().init_resource::(); + + app.add_scripting::<$runtime>(|runtime| { + runtime.add_function( + String::from("rust_func"), + |In((index,)): In<(u32,)>, mut res: ResMut| { + res.index = index; + }, + ); + }); + + let entity = run_script::<$runtime, _, _>( + &mut app, + format!("tests/{}/entity_variable.{}", $script, $extension).to_string(), + call_script_on_update_from_rust::<$runtime>, + ); + + assert_eq!( + app.world().get_resource::().unwrap().index, + entity.index() + ); + } + + #[test] + fn entity_variable_is_available_in_eval() { + let mut app = build_test_app(); + + #[derive(Default, Resource)] + struct State { + index: u32, + } + + app.world_mut().init_resource::(); + + app.add_scripting::<$runtime>(|runtime| { + runtime.add_function( + String::from("rust_func"), + |In((index,)): In<(u32,)>, mut res: ResMut| { + res.index = index; + }, + ); + }); + + let entity = run_script::<$runtime, _, _>( + &mut app, + format!("tests/{}/entity_variable_eval.{}", $script, $extension).to_string(), + call_script_on_update_from_rust::<$runtime>, + ); + + assert_eq!( + app.world().get_resource::().unwrap().index, + entity.index() + ); + } }; } -- 2.45.2 From c5a9f54695b1cbb2742a2497f9bf7f8f82d8e93e Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 16 May 2025 07:00:00 +0200 Subject: [PATCH 054/165] pass entity --- assets/tests/ruby/pass_entity_from_script.rb | 3 ++ src/runtimes/lua.rs | 11 ++-- src/runtimes/rhai.rs | 11 ++-- src/runtimes/ruby.rs | 20 ++++++++ tests/tests.rs | 53 ++++++++++++++++---- 5 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 assets/tests/ruby/pass_entity_from_script.rb diff --git a/assets/tests/ruby/pass_entity_from_script.rb b/assets/tests/ruby/pass_entity_from_script.rb new file mode 100644 index 0000000..d62632b --- /dev/null +++ b/assets/tests/ruby/pass_entity_from_script.rb @@ -0,0 +1,3 @@ +def test_func + rust_func($entity) +end diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index eda2ba2..7ce1745 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -1,6 +1,7 @@ use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, + log, math::Vec3, reflect::TypePath, }; @@ -260,16 +261,18 @@ impl Runtime for LuaRuntime { fn with_engine_thread_mut( &mut self, - _f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { - todo!() + log::warn!("runtime can be used on current thread, wil run on current thread"); + self.with_engine_mut(f) } fn with_engine_thread( &self, - _f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { - todo!() + log::warn!("runtime can be used on current thread, wil run on current thread"); + self.with_engine(f) } fn is_current_thread() -> bool { diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 5f486e8..5228075 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, + log, math::Vec3, reflect::TypePath, }; @@ -163,16 +164,18 @@ impl Runtime for RhaiRuntime { fn with_engine_thread_mut( &mut self, - _f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, + f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { - todo!() + log::warn!("runtime can be used on current thread, wil run on current thread"); + self.with_engine_mut(f) } fn with_engine_thread( &self, - _f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, + f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { - todo!() + log::warn!("runtime can be used on current thread, wil run on current thread"); + self.with_engine(f) } fn is_current_thread() -> bool { diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 83306a0..c0bf625 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -127,6 +127,12 @@ unsafe impl TypedData for Promise<(), RubyValue> { } } +impl TryConvert for Promise<(), RubyValue> { + fn try_convert(val: magnus::Value) -> Result { + TryConvert::try_convert(val).map(|p: &Self| p.clone()) + } +} + fn then(r_self: magnus::Value) -> magnus::Value { let promise: &Promise<(), RubyValue> = TryConvert::try_convert(r_self).unwrap(); let ruby = Ruby::get().unwrap(); @@ -143,6 +149,7 @@ fn then(r_self: magnus::Value) -> magnus::Value { .into_value() } +#[derive(Clone)] #[magnus::wrap(class = "BevyEntity")] pub struct BevyEntity(pub Entity); @@ -152,6 +159,13 @@ impl BevyEntity { } } +impl TryConvert for BevyEntity { + fn try_convert(val: magnus::Value) -> Result { + TryConvert::try_convert(val).map(|p: &Self| p.clone()) + } +} + +#[derive(Clone)] #[magnus::wrap(class = "BevyVec3")] pub struct BevyVec3(pub Vec3); @@ -169,6 +183,12 @@ impl BevyVec3 { } } +impl TryConvert for BevyVec3 { + fn try_convert(val: magnus::Value) -> Result { + TryConvert::try_convert(val).map(|p: &Self| p.clone()) + } +} + impl Default for RubyRuntime { fn default() -> Self { let (lock, cvar) = &*Arc::clone(&RUBY_THREAD); diff --git a/tests/tests.rs b/tests/tests.rs index b3c7a3a..83b5332 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -65,7 +65,7 @@ trait AssertStateKeyValue { #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] macro_rules! scripting_tests { - ($runtime:ty, $script:literal, $extension:literal) => { + ($runtime:ty, $script:literal, $extension:literal, $entity_type: ty) => { use super::*; #[test] @@ -380,7 +380,7 @@ macro_rules! scripting_tests { } #[test] - fn entity_variable_is_available_in_callback() { + fn entity_variable_index_is_available_in_callback() { let mut app = build_test_app(); #[derive(Default, Resource)] @@ -412,12 +412,12 @@ macro_rules! scripting_tests { } #[test] - fn entity_variable_is_available_in_eval() { + fn entity_variable_index_is_available_in_eval() { let mut app = build_test_app(); #[derive(Default, Resource)] struct State { - index: u32, + index: Option, } app.world_mut().init_resource::(); @@ -426,7 +426,7 @@ macro_rules! scripting_tests { runtime.add_function( String::from("rust_func"), |In((index,)): In<(u32,)>, mut res: ResMut| { - res.index = index; + res.index = Some(index); }, ); }); @@ -439,7 +439,39 @@ macro_rules! scripting_tests { assert_eq!( app.world().get_resource::().unwrap().index, - entity.index() + Some(entity.index()) + ); + } + + #[test] + fn pass_entity_from_script() { + let mut app = build_test_app(); + + #[derive(Default, Resource)] + struct State { + index: Option, + } + + app.world_mut().init_resource::(); + + app.add_scripting::<$runtime>(|runtime| { + runtime.add_function( + String::from("rust_func"), + |In((entity,)): In<($entity_type,)>, mut res: ResMut| { + res.index = Some(entity.0.index()); + }, + ); + }); + + let entity = run_script::<$runtime, _, _>( + &mut app, + format!("tests/{}/pass_entity_from_script.{}", $script, $extension).to_string(), + call_script_on_update_from_rust::<$runtime>, + ); + + assert_eq!( + app.world().get_resource::().unwrap().index, + Some(entity.index()) ); } }; @@ -450,6 +482,9 @@ mod rhai_tests { use bevy::prelude::*; use bevy_scriptum::runtimes::rhai::prelude::*; + #[derive(Clone)] + struct BevyEntity(Entity); + impl AssertStateKeyValue for RhaiRuntime { type ScriptData = RhaiScriptData; @@ -472,7 +507,7 @@ mod rhai_tests { } } - scripting_tests!(RhaiRuntime, "rhai", "rhai"); + scripting_tests!(RhaiRuntime, "rhai", "rhai", BevyEntity); } #[cfg(feature = "lua")] @@ -514,7 +549,7 @@ mod lua_tests { } } - scripting_tests!(LuaRuntime, "lua", "lua"); + scripting_tests!(LuaRuntime, "lua", "lua", BevyEntity); } #[cfg(feature = "ruby")] @@ -563,5 +598,5 @@ mod ruby_tests { } } - scripting_tests!(RubyRuntime, "ruby", "rb"); + scripting_tests!(RubyRuntime, "ruby", "rb", BevyEntity); } -- 2.45.2 From 3217b1b9fb680fc83423f8356489067f34e03fcc Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 15:38:03 +0200 Subject: [PATCH 055/165] vec3 --- assets/tests/ruby/pass_vec3_from_script.rb | 3 + assets/tests/ruby/pass_vec3_to_script.rb | 7 +++ src/runtimes/ruby.rs | 25 ++++---- tests/tests.rs | 73 +++++++++++++++++++++- 4 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 assets/tests/ruby/pass_vec3_from_script.rb create mode 100644 assets/tests/ruby/pass_vec3_to_script.rb diff --git a/assets/tests/ruby/pass_vec3_from_script.rb b/assets/tests/ruby/pass_vec3_from_script.rb new file mode 100644 index 0000000..393422e --- /dev/null +++ b/assets/tests/ruby/pass_vec3_from_script.rb @@ -0,0 +1,3 @@ +def test_func + rust_func(Vec3.new(1.5, 2.5, -3.5)) +end diff --git a/assets/tests/ruby/pass_vec3_to_script.rb b/assets/tests/ruby/pass_vec3_to_script.rb new file mode 100644 index 0000000..fae4ac3 --- /dev/null +++ b/assets/tests/ruby/pass_vec3_to_script.rb @@ -0,0 +1,7 @@ +def test_func(vec3) + raise unless vec3.is_a?(Vec3) # TODO: BevyScriptum::Vec3 and add example how to include it globally like Sinatra does + raise unless vec3.x == 1.5 + raise unless vec3.y == 2.5 + raise unless vec3.z == -3.5 + mark_success +end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index c0bf625..10d7b5f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -166,19 +166,23 @@ impl TryConvert for BevyEntity { } #[derive(Clone)] -#[magnus::wrap(class = "BevyVec3")] +#[magnus::wrap(class = "Vec3")] pub struct BevyVec3(pub Vec3); impl BevyVec3 { - fn x(&self) -> f32 { + pub fn new(x: f32, y: f32, z: f32) -> Self { + Self(Vec3::new(x, y, z)) + } + + pub fn x(&self) -> f32 { self.0.x } - fn y(&self) -> f32 { + pub fn y(&self) -> f32 { self.0.y } - fn z(&self) -> f32 { + pub fn z(&self) -> f32 { self.0.z } } @@ -214,20 +218,13 @@ impl Default for RubyRuntime { .define_method("index", method!(BevyEntity::index, 0)) .unwrap(); - let vec3 = ruby.define_class("BevyVec3", ruby.class_object()).unwrap(); + let vec3 = ruby.define_class("Vec3", ruby.class_object()).unwrap(); + vec3.define_singleton_method("new", function!(BevyVec3::new, 3)) + .unwrap(); vec3.define_method("x", method!(BevyVec3::x, 0)).unwrap(); vec3.define_method("y", method!(BevyVec3::y, 0)).unwrap(); vec3.define_method("z", method!(BevyVec3::z, 0)).unwrap(); })); - // TODO: add test for those types for every runtime - // let vec3_constructor = engine - // .create_function(|_, (x, y, z)| Ok(BevyVec3(Vec3::new(x, y, z)))) - // .expect("Failed to create Vec3 constructor"); - // engine - // .globals() - // .set("Vec3", vec3_constructor) - // .expect("Failed to set Vec3 global"); - // Self { ruby_thread: Some(ruby_thread), } diff --git a/tests/tests.rs b/tests/tests.rs index 83b5332..81894fe 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -65,7 +65,7 @@ trait AssertStateKeyValue { #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] macro_rules! scripting_tests { - ($runtime:ty, $script:literal, $extension:literal, $entity_type: ty) => { + ($runtime:ty, $script:literal, $extension:literal, $entity_type: ty, $vec_type: ty) => { use super::*; #[test] @@ -474,6 +474,75 @@ macro_rules! scripting_tests { Some(entity.index()) ); } + + #[test] + fn pass_vec3_from_script() { + let mut app = build_test_app(); + + #[derive(Default, Resource)] + struct State { + success: bool, + } + + app.world_mut().init_resource::(); + + app.add_scripting::<$runtime>(|runtime| { + runtime.add_function( + String::from("rust_func"), + |In((v,)): In<($vec_type,)>, mut res: ResMut| { + assert_eq!(v.0.x, 1.5); + assert_eq!(v.0.y, 2.5); + assert_eq!(v.0.z, -3.5); + res.success = true + }, + ); + }); + + let entity = run_script::<$runtime, _, _>( + &mut app, + format!("tests/{}/pass_vec3_from_script.{}", $script, $extension).to_string(), + call_script_on_update_from_rust::<$runtime>, + ); + + assert!(app.world().get_resource::().unwrap().success); + } + + #[test] + fn pass_vec3_to_script() { + let mut app = build_test_app(); + + #[derive(Default, Resource)] + struct State { + success: bool, + } + + app.world_mut().init_resource::(); + + app.add_scripting::<$runtime>(|runtime| { + runtime.add_function(String::from("mark_success"), |mut res: ResMut| { + res.success = true + }); + }); + + run_script::<$runtime, _, _>( + &mut app, + format!("tests/{}/pass_vec3_to_script.{}", $script, $extension).to_string(), + |mut scripted_entities: Query<(Entity, &mut <$runtime as Runtime>::ScriptData)>, + scripting_runtime: ResMut<$runtime>| { + let (entity, mut script_data) = scripted_entities.single_mut().unwrap(); + scripting_runtime + .call_fn( + "test_func", + &mut script_data, + entity, + (<$vec_type>::new(1.5, 2.5, -3.5),), + ) + .unwrap(); + }, + ); + + assert!(app.world().get_resource::().unwrap().success); + } }; } @@ -598,5 +667,5 @@ mod ruby_tests { } } - scripting_tests!(RubyRuntime, "ruby", "rb", BevyEntity); + scripting_tests!(RubyRuntime, "ruby", "rb", BevyEntity, BevyVec3); } -- 2.45.2 From dceb06133ecc957abc84bb9f1f6a3a4e4cd3ba8a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 16:01:18 +0200 Subject: [PATCH 056/165] aadd todo --- src/runtimes/ruby.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 10d7b5f..db1f908 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -3,6 +3,7 @@ // TODO: add tests for every runtime for return value // TODO: maybe unify api and call non thread methods non_send // TODO: add tests for entity variable and buitin types for every runtime +// TODO: line numbers for errors use std::{ collections::HashMap, -- 2.45.2 From 765c34c52e3e9ccef97f38af651a176c36c9331f Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 16:27:17 +0200 Subject: [PATCH 057/165] fix tests --- src/runtimes/lua.rs | 6 ++++++ tests/tests.rs | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 7ce1745..9d0962b 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -59,6 +59,12 @@ impl FromLua<'_> for BevyEntity { #[derive(Debug, Clone, Copy)] pub struct BevyVec3(pub Vec3); +impl BevyVec3 { + pub fn new(x: f32, y: f32, z: f32) -> Self { + BevyVec3(Vec3 { x, y, z }) + } +} + impl UserData for BevyVec3 {} impl FromLua<'_> for BevyVec3 { diff --git a/tests/tests.rs b/tests/tests.rs index 81894fe..fc2ecb2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -554,6 +554,15 @@ mod rhai_tests { #[derive(Clone)] struct BevyEntity(Entity); + #[derive(Clone)] + struct BevyVec3(Vec3); + + impl BevyVec3 { + fn new(x: f32, y: f32, z: f32) -> Self { + Self(Vec3 { x, y, z }) + } + } + impl AssertStateKeyValue for RhaiRuntime { type ScriptData = RhaiScriptData; @@ -576,7 +585,7 @@ mod rhai_tests { } } - scripting_tests!(RhaiRuntime, "rhai", "rhai", BevyEntity); + scripting_tests!(RhaiRuntime, "rhai", "rhai", BevyEntity, BevyVec3); } #[cfg(feature = "lua")] @@ -618,7 +627,7 @@ mod lua_tests { } } - scripting_tests!(LuaRuntime, "lua", "lua", BevyEntity); + scripting_tests!(LuaRuntime, "lua", "lua", BevyEntity, BevyVec3); } #[cfg(feature = "ruby")] -- 2.45.2 From 905c4c1f4baec5d35c15f49ae564a3f5d4a4b7c1 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 16:55:51 +0200 Subject: [PATCH 058/165] update --- tests/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.rs b/tests/tests.rs index fc2ecb2..53b1416 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -498,7 +498,7 @@ macro_rules! scripting_tests { ); }); - let entity = run_script::<$runtime, _, _>( + run_script::<$runtime, _, _>( &mut app, format!("tests/{}/pass_vec3_from_script.{}", $script, $extension).to_string(), call_script_on_update_from_rust::<$runtime>, -- 2.45.2 From 015c1827ef370a7001b7715437f828e8ef831da9 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 17:07:03 +0200 Subject: [PATCH 059/165] add rhai tests --- assets/tests/rhai/entity_variable.rhai | 3 +++ assets/tests/rhai/entity_variable_eval.rhai | 5 +++++ assets/tests/rhai/pass_entity_from_script.rhai | 3 +++ assets/tests/rhai/pass_vec3_from_script.rhai | 3 +++ assets/tests/rhai/pass_vec3_to_script.rhai | 4 ++++ assets/tests/ruby/entity_variable_eval.rb | 1 - 6 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 assets/tests/rhai/pass_entity_from_script.rhai create mode 100644 assets/tests/rhai/pass_vec3_from_script.rhai create mode 100644 assets/tests/rhai/pass_vec3_to_script.rhai diff --git a/assets/tests/rhai/entity_variable.rhai b/assets/tests/rhai/entity_variable.rhai index e69de29..b93b116 100644 --- a/assets/tests/rhai/entity_variable.rhai +++ b/assets/tests/rhai/entity_variable.rhai @@ -0,0 +1,3 @@ +fn test_func() { + rust_func(entity.index); +} diff --git a/assets/tests/rhai/entity_variable_eval.rhai b/assets/tests/rhai/entity_variable_eval.rhai index e69de29..3c94277 100644 --- a/assets/tests/rhai/entity_variable_eval.rhai +++ b/assets/tests/rhai/entity_variable_eval.rhai @@ -0,0 +1,5 @@ +let index = entity.index; + +fn test_func() { + rust_func(index); +} diff --git a/assets/tests/rhai/pass_entity_from_script.rhai b/assets/tests/rhai/pass_entity_from_script.rhai new file mode 100644 index 0000000..ad4f318 --- /dev/null +++ b/assets/tests/rhai/pass_entity_from_script.rhai @@ -0,0 +1,3 @@ +fn test_func() { + rust_func(entity); +} diff --git a/assets/tests/rhai/pass_vec3_from_script.rhai b/assets/tests/rhai/pass_vec3_from_script.rhai new file mode 100644 index 0000000..cc137f7 --- /dev/null +++ b/assets/tests/rhai/pass_vec3_from_script.rhai @@ -0,0 +1,3 @@ +fn test_func() { + rust_func(new_vec3(1.5, 2.5, -3.5)); +} diff --git a/assets/tests/rhai/pass_vec3_to_script.rhai b/assets/tests/rhai/pass_vec3_to_script.rhai new file mode 100644 index 0000000..e0550d0 --- /dev/null +++ b/assets/tests/rhai/pass_vec3_to_script.rhai @@ -0,0 +1,4 @@ +fn test_func(vec3) { + // TODO: asser vec3 correctness + mark_success() +} diff --git a/assets/tests/ruby/entity_variable_eval.rb b/assets/tests/ruby/entity_variable_eval.rb index da5f2a3..f2f0594 100644 --- a/assets/tests/ruby/entity_variable_eval.rb +++ b/assets/tests/ruby/entity_variable_eval.rb @@ -1,6 +1,5 @@ $index = $entity.index def test_func - print($index) rust_func($index) end -- 2.45.2 From b3cd04aad6173e8542bf1a47e3151095d66a2fe4 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 17:18:52 +0200 Subject: [PATCH 060/165] lua tests --- assets/tests/lua/entity_variable.lua | 3 +++ assets/tests/lua/entity_variable_eval.lua | 5 +++++ assets/tests/lua/pass_entity_from_script.lua | 3 +++ assets/tests/lua/pass_vec3_from_script.lua | 3 +++ assets/tests/lua/pass_vec3_to_script.lua | 7 +++++++ src/runtimes/ruby.rs | 1 + 6 files changed, 22 insertions(+) create mode 100644 assets/tests/lua/pass_entity_from_script.lua create mode 100644 assets/tests/lua/pass_vec3_from_script.lua create mode 100644 assets/tests/lua/pass_vec3_to_script.lua diff --git a/assets/tests/lua/entity_variable.lua b/assets/tests/lua/entity_variable.lua index e69de29..4bf3745 100644 --- a/assets/tests/lua/entity_variable.lua +++ b/assets/tests/lua/entity_variable.lua @@ -0,0 +1,3 @@ +function test_func() + rust_func(entity.index) +end diff --git a/assets/tests/lua/entity_variable_eval.lua b/assets/tests/lua/entity_variable_eval.lua index e69de29..fdc8f94 100644 --- a/assets/tests/lua/entity_variable_eval.lua +++ b/assets/tests/lua/entity_variable_eval.lua @@ -0,0 +1,5 @@ +index = entity.index + +function test_func() + rust_func(index) +end diff --git a/assets/tests/lua/pass_entity_from_script.lua b/assets/tests/lua/pass_entity_from_script.lua new file mode 100644 index 0000000..355ef3a --- /dev/null +++ b/assets/tests/lua/pass_entity_from_script.lua @@ -0,0 +1,3 @@ +function test_func() + rust_func(entity) +end diff --git a/assets/tests/lua/pass_vec3_from_script.lua b/assets/tests/lua/pass_vec3_from_script.lua new file mode 100644 index 0000000..bd2a772 --- /dev/null +++ b/assets/tests/lua/pass_vec3_from_script.lua @@ -0,0 +1,3 @@ +function test_func() + rust_func(Vec3(1.5, 2.5, -3.5)) +end diff --git a/assets/tests/lua/pass_vec3_to_script.lua b/assets/tests/lua/pass_vec3_to_script.lua new file mode 100644 index 0000000..4a9d395 --- /dev/null +++ b/assets/tests/lua/pass_vec3_to_script.lua @@ -0,0 +1,7 @@ +function test_func(vec3) + -- raise unless vec3.is_a?(Vec3) # TODO: BevyScriptum::Vec3 and add example how to include it globally like Sinatra does + -- raise unless vec3.x == 1.5 + -- raise unless vec3.y == 2.5 + -- raise unless vec3.z == -3.5 + mark_success() +end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index db1f908..74b4b3d 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -4,6 +4,7 @@ // TODO: maybe unify api and call non thread methods non_send // TODO: add tests for entity variable and buitin types for every runtime // TODO: line numbers for errors +// TODO: caan rhai have Vec3 constructor instead of vec3_new use std::{ collections::HashMap, -- 2.45.2 From fb627ffdda4577acba051c14b3b4580516fd444b Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 18:04:13 +0200 Subject: [PATCH 061/165] less panics --- src/runtimes/ruby.rs | 75 ++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 74b4b3d..a436d54 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -95,16 +95,16 @@ impl RubyThread { .send(f(ruby)) .expect("Failed to send callback return value"); })) - .unwrap(); + .expect("Faild to send execution unit to Ruby thread"); return_receiver .recv() - .expect("Faild to send execution unit to Ruby thread") + .expect("Failed to receive callback return value") } } impl Drop for RubyThread { fn drop(&mut self) { - let handle = self.handle.take().unwrap(); + let handle = self.handle.take().expect("No Ruby thread to join"); handle.join().expect("Failed to join Ruby thread"); } } @@ -136,13 +136,16 @@ impl TryConvert for Promise<(), RubyValue> { } fn then(r_self: magnus::Value) -> magnus::Value { - let promise: &Promise<(), RubyValue> = TryConvert::try_convert(r_self).unwrap(); - let ruby = Ruby::get().unwrap(); + let promise: &Promise<(), RubyValue> = + TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise"); + let ruby = + Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); promise .clone() .then(RubyValue::new( if ruby.block_given() { - ruby.block_proc().expect("Failed to create Proc") + ruby.block_proc() + .expect("Failed to create Proc for Promise") } else { ruby.proc_new(|ruby, _, _| ruby.qnil().as_value()) } @@ -195,38 +198,42 @@ impl TryConvert for BevyVec3 { } } +impl From for ScriptingError { + fn from(value: magnus::Error) -> Self { + ScriptingError::RuntimeError(Box::new(io::Error::other(value.to_string()))) + } +} + impl Default for RubyRuntime { fn default() -> Self { let (lock, cvar) = &*Arc::clone(&RUBY_THREAD); - let mut ruby_thread = lock.lock().unwrap(); + let mut ruby_thread = lock.lock().expect("Failed to acquire lock on Ruby thread"); while ruby_thread.is_none() { - ruby_thread = cvar.wait(ruby_thread).unwrap(); + ruby_thread = cvar + .wait(ruby_thread) + .expect("Failed to acquire lock on Ruby thread after waiting"); } - let ruby_thread = ruby_thread.take().unwrap(); + let ruby_thread = ruby_thread.take().expect("Ruby thread is not available"); cvar.notify_all(); - ruby_thread.execute(Box::new(|ruby| { - // TODO: maybe put promise in a module , maybe do so for other runtimes too - let promise = ruby.define_class("Promise", ruby.class_object()).unwrap(); - promise - .define_method("and_then", magnus::method!(then, 0)) - .unwrap(); + ruby_thread + .execute(Box::new(|ruby| { + // TODO: maybe put promise in a module , maybe do so for other runtimes too + let promise = ruby.define_class("Promise", ruby.class_object())?; + promise.define_method("and_then", magnus::method!(then, 0))?; - let entity = ruby - .define_class("BevyEntity", ruby.class_object()) - .unwrap(); - entity - .define_method("index", method!(BevyEntity::index, 0)) - .unwrap(); + let entity = ruby.define_class("BevyEntity", ruby.class_object())?; + entity.define_method("index", method!(BevyEntity::index, 0))?; - let vec3 = ruby.define_class("Vec3", ruby.class_object()).unwrap(); - vec3.define_singleton_method("new", function!(BevyVec3::new, 3)) - .unwrap(); - vec3.define_method("x", method!(BevyVec3::x, 0)).unwrap(); - vec3.define_method("y", method!(BevyVec3::y, 0)).unwrap(); - vec3.define_method("z", method!(BevyVec3::z, 0)).unwrap(); - })); + let vec3 = ruby.define_class("Vec3", ruby.class_object())?; + vec3.define_singleton_method("new", function!(BevyVec3::new, 3))?; + vec3.define_method("x", method!(BevyVec3::x, 0))?; + vec3.define_method("y", method!(BevyVec3::y, 0))?; + vec3.define_method("z", method!(BevyVec3::z, 0))?; + Ok::<(), ScriptingError>(()) + })) + .unwrap(); Self { ruby_thread: Some(ruby_thread), } @@ -399,12 +406,8 @@ impl Runtime for RubyRuntime { .into_iter() .map(|a| ruby.get_inner(a.0)) .collect(); - let return_value: magnus::Value = ruby - .class_object() - .funcall(name, args.as_slice()) - .map_err(|e| { - ScriptingError::RuntimeError(Box::new(io::Error::other(e.to_string()))) - })?; + let return_value: magnus::Value = + ruby.class_object().funcall(name, args.as_slice())?; // SAFETY: this is guaranteed to be executed on a single thread // so should be safe according to Magnus documentation @@ -433,9 +436,7 @@ impl Runtime for RubyRuntime { .into_iter() .map(|x| ruby.get_inner(x.0).as_value()) .collect(); - let result: magnus::Value = f.funcall("call", args.as_slice()).map_err(|e| { - ScriptingError::RuntimeError(Box::new(io::Error::other(e.to_string()))) - })?; + let result: magnus::Value = f.funcall("call", args.as_slice())?; Ok(RubyValue::new(result)) })) } -- 2.45.2 From 900d1579ee511e9f2269bcd8bc370ac98cc7b4b6 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 22:33:44 +0200 Subject: [PATCH 062/165] Change ruby API --- assets/tests/ruby/entity_variable.rb | 2 +- assets/tests/ruby/entity_variable_eval.rb | 2 +- assets/tests/ruby/pass_entity_from_script.rb | 2 +- src/runtimes/ruby.rs | 57 +++++++++++--------- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/assets/tests/ruby/entity_variable.rb b/assets/tests/ruby/entity_variable.rb index 84fdc86..2c3925b 100644 --- a/assets/tests/ruby/entity_variable.rb +++ b/assets/tests/ruby/entity_variable.rb @@ -1,3 +1,3 @@ def test_func - rust_func($entity.index) + rust_func(Bevy::Entity.current.index) end diff --git a/assets/tests/ruby/entity_variable_eval.rb b/assets/tests/ruby/entity_variable_eval.rb index f2f0594..a07bd59 100644 --- a/assets/tests/ruby/entity_variable_eval.rb +++ b/assets/tests/ruby/entity_variable_eval.rb @@ -1,4 +1,4 @@ -$index = $entity.index +$index = Bevy::Entity.current.index def test_func rust_func($index) diff --git a/assets/tests/ruby/pass_entity_from_script.rb b/assets/tests/ruby/pass_entity_from_script.rb index d62632b..3f14b64 100644 --- a/assets/tests/ruby/pass_entity_from_script.rb +++ b/assets/tests/ruby/pass_entity_from_script.rb @@ -1,3 +1,3 @@ def test_func - rust_func($entity) + rust_func(Bevy::Entity.current) end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index a436d54..bf89bec 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,11 +1,3 @@ -// TODO: maybe make all runtime engines not send and spawn threads for them like Ruby, benchmark? -// TODO: make sure ruby is statically linked -// TODO: add tests for every runtime for return value -// TODO: maybe unify api and call non thread methods non_send -// TODO: add tests for entity variable and buitin types for every runtime -// TODO: line numbers for errors -// TODO: caan rhai have Vec3 constructor instead of vec3_new - use std::{ collections::HashMap, sync::{Arc, Condvar, LazyLock, Mutex}, @@ -21,7 +13,7 @@ use bevy::{ }; use magnus::{ block::Proc, data_type_builder, function, value::Lazy, DataType, DataTypeFunctions, IntoValue, - RClass, Ruby, TryConvert, TypedData, + Object, RClass, RModule, Ruby, TryConvert, TypedData, }; use magnus::{method, prelude::*}; use serde::Deserialize; @@ -115,8 +107,10 @@ unsafe impl TypedData for Promise<(), RubyValue> { fn class(ruby: &Ruby) -> magnus::RClass { static CLASS: Lazy = Lazy::new(|ruby| { let class = ruby + .define_module("Bevy") + .unwrap() .define_class("Promise", ruby.class_object()) - .expect("Failed to define Promise class in Ruby"); + .expect("Failed to define Bevy::Promise class in Ruby"); class.undef_default_alloc_func(); class }); @@ -124,7 +118,8 @@ unsafe impl TypedData for Promise<(), RubyValue> { } fn data_type() -> &'static magnus::DataType { - static DATA_TYPE: DataType = data_type_builder!(Promise<(), RubyValue>, "promise").build(); + static DATA_TYPE: DataType = + data_type_builder!(Promise<(), RubyValue>, "Bevy::Promise").build(); &DATA_TYPE } } @@ -155,7 +150,7 @@ fn then(r_self: magnus::Value) -> magnus::Value { } #[derive(Clone)] -#[magnus::wrap(class = "BevyEntity")] +#[magnus::wrap(class = "Bevy::Entity")] pub struct BevyEntity(pub Entity); impl BevyEntity { @@ -219,13 +214,21 @@ impl Default for RubyRuntime { ruby_thread .execute(Box::new(|ruby| { - // TODO: maybe put promise in a module , maybe do so for other runtimes too - let promise = ruby.define_class("Promise", ruby.class_object())?; - promise.define_method("and_then", magnus::method!(then, 0))?; + let module = ruby.define_module("Bevy")?; - let entity = ruby.define_class("BevyEntity", ruby.class_object())?; + let entity = module.define_class("Entity", ruby.class_object())?; + entity.class().define_method( + "current", + method!( + |r_self: RClass| { r_self.ivar_get::<_, BevyEntity>("_current") }, + 0 + ), + )?; entity.define_method("index", method!(BevyEntity::index, 0))?; + let promise = module.define_class("Promise", ruby.class_object())?; + promise.define_method("and_then", magnus::method!(then, 0))?; + let vec3 = ruby.define_class("Vec3", ruby.class_object())?; vec3.define_singleton_method("new", function!(BevyVec3::new, 3))?; vec3.define_method("x", method!(BevyVec3::x, 0))?; @@ -314,14 +317,14 @@ impl Runtime for RubyRuntime { .unwrap() .execute(Box::new(move |ruby| { let var = ruby - .define_variable(ENTITY_VAR_NAME, BevyEntity(entity)) + .class_object() + .const_get::<_, RModule>("Bevy") + .unwrap() + .const_get::<_, RClass>("Entity") .unwrap(); - + var.ivar_set("_current", BevyEntity(entity)).unwrap(); let value = ruby.eval::(&script).unwrap(); - - // SAFETY: this is guaranteed to be executed on a single thread - // so should be safe according to Magnus documentation - unsafe { *var = ruby.qnil().as_value() }; + var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); RubyValue::new(value) })); @@ -398,8 +401,12 @@ impl Runtime for RubyRuntime { .unwrap() .execute(Box::new(move |ruby| { let var = ruby - .define_variable(ENTITY_VAR_NAME, BevyEntity(entity)) + .class_object() + .const_get::<_, RModule>("Bevy") + .unwrap() + .const_get::<_, RClass>("Entity") .unwrap(); + var.ivar_set("_current", BevyEntity(entity)).unwrap(); let args: Vec<_> = args .parse(&ruby) @@ -409,9 +416,7 @@ impl Runtime for RubyRuntime { let return_value: magnus::Value = ruby.class_object().funcall(name, args.as_slice())?; - // SAFETY: this is guaranteed to be executed on a single thread - // so should be safe according to Magnus documentation - unsafe { *var = ruby.qnil().as_value() }; + var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); Ok(RubyValue::new(return_value)) })) -- 2.45.2 From 78eca45723b28704b8b8fb20428c1934b4910919 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 22:35:48 +0200 Subject: [PATCH 063/165] change rb api --- assets/tests/ruby/pass_vec3_from_script.rb | 4 +++- assets/tests/ruby/pass_vec3_to_script.rb | 2 +- src/runtimes/ruby.rs | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assets/tests/ruby/pass_vec3_from_script.rb b/assets/tests/ruby/pass_vec3_from_script.rb index 393422e..c837f59 100644 --- a/assets/tests/ruby/pass_vec3_from_script.rb +++ b/assets/tests/ruby/pass_vec3_from_script.rb @@ -1,3 +1,5 @@ +Vec3 = Bevy::Vec3 + def test_func - rust_func(Vec3.new(1.5, 2.5, -3.5)) + rust_func(Bevy::Vec3.new(1.5, 2.5, -3.5)) end diff --git a/assets/tests/ruby/pass_vec3_to_script.rb b/assets/tests/ruby/pass_vec3_to_script.rb index fae4ac3..4d27eac 100644 --- a/assets/tests/ruby/pass_vec3_to_script.rb +++ b/assets/tests/ruby/pass_vec3_to_script.rb @@ -1,5 +1,5 @@ def test_func(vec3) - raise unless vec3.is_a?(Vec3) # TODO: BevyScriptum::Vec3 and add example how to include it globally like Sinatra does + raise unless vec3.is_a?(Bevy::Vec3) # TODO: BevyScriptum::Vec3 and add example how to include it globally like Sinatra does raise unless vec3.x == 1.5 raise unless vec3.y == 2.5 raise unless vec3.z == -3.5 diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index bf89bec..2c6e535 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -166,7 +166,7 @@ impl TryConvert for BevyEntity { } #[derive(Clone)] -#[magnus::wrap(class = "Vec3")] +#[magnus::wrap(class = "Bevy::Vec3")] pub struct BevyVec3(pub Vec3); impl BevyVec3 { @@ -229,7 +229,7 @@ impl Default for RubyRuntime { let promise = module.define_class("Promise", ruby.class_object())?; promise.define_method("and_then", magnus::method!(then, 0))?; - let vec3 = ruby.define_class("Vec3", ruby.class_object())?; + let vec3 = module.define_class("Vec3", ruby.class_object())?; vec3.define_singleton_method("new", function!(BevyVec3::new, 3))?; vec3.define_method("x", method!(BevyVec3::x, 0))?; vec3.define_method("y", method!(BevyVec3::y, 0))?; -- 2.45.2 From d1f8297c5fe8c61ebcaf5cd41078aace62382a6f Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 22:36:04 +0200 Subject: [PATCH 064/165] cleanup --- assets/tests/ruby/pass_vec3_from_script.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/tests/ruby/pass_vec3_from_script.rb b/assets/tests/ruby/pass_vec3_from_script.rb index c837f59..e6f117b 100644 --- a/assets/tests/ruby/pass_vec3_from_script.rb +++ b/assets/tests/ruby/pass_vec3_from_script.rb @@ -1,5 +1,3 @@ -Vec3 = Bevy::Vec3 - def test_func rust_func(Bevy::Vec3.new(1.5, 2.5, -3.5)) end -- 2.45.2 From 8d583642e9616bc1baf5b71b650c14d36b78d7c6 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 18 May 2025 22:44:14 +0200 Subject: [PATCH 065/165] add ruby example --- Cargo.toml | 5 +++ .../examples/ruby/call_function_from_rust.rb | 13 ++++++++ examples/ruby/call_function_from_rust.rs | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 assets/examples/ruby/call_function_from_rust.rb create mode 100644 examples/ruby/call_function_from_rust.rs diff --git a/Cargo.toml b/Cargo.toml index 240f3f5..832862c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,6 +154,11 @@ name = "function_return_value_lua" path = "examples/lua/function_return_value.rs" required-features = ["lua"] +[[example]] +name = "call_function_from_rust_ruby" +path = "examples/ruby/call_function_from_rust.rs" +required-features = ["ruby"] + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } diff --git a/assets/examples/ruby/call_function_from_rust.rb b/assets/examples/ruby/call_function_from_rust.rb new file mode 100644 index 0000000..a5adb3b --- /dev/null +++ b/assets/examples/ruby/call_function_from_rust.rb @@ -0,0 +1,13 @@ +$my_state = { + iterations: 0, +} + +def on_update + $my_state[:iterations] += 1 + puts("on_update called #{$my_state[:iterations]} times") + + if $my_state[:iterations] >= 10 + print("calling quit"); + quit() + end +end diff --git a/examples/ruby/call_function_from_rust.rs b/examples/ruby/call_function_from_rust.rs new file mode 100644 index 0000000..039170e --- /dev/null +++ b/examples/ruby/call_function_from_rust.rs @@ -0,0 +1,33 @@ +use bevy::{app::AppExit, prelude::*}; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, startup) + .add_systems(Update, call_ruby_on_update_from_rust) + .add_scripting::(|runtime| { + runtime.add_function(String::from("quit"), |mut exit: EventWriter| { + exit.write(AppExit::Success); + }); + }) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/ruby/call_function_from_rust.rb"), + )); +} + +fn call_ruby_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut RubyScriptData)>, + scripting_runtime: ResMut, +) { + for (entity, mut script_data) in &mut scripted_entities { + scripting_runtime + .call_fn("on_update", &mut script_data, entity, ()) + .unwrap(); + } +} -- 2.45.2 From 9438fa62bce9c3d4f2d57f95b41c7d1178c44143 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 19 May 2025 07:00:00 +0200 Subject: [PATCH 066/165] add example --- assets/examples/ruby/current_entity.rb | 3 +++ examples/ruby/current_entity.rs | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 assets/examples/ruby/current_entity.rb create mode 100644 examples/ruby/current_entity.rs diff --git a/assets/examples/ruby/current_entity.rb b/assets/examples/ruby/current_entity.rb new file mode 100644 index 0000000..4c2c77b --- /dev/null +++ b/assets/examples/ruby/current_entity.rb @@ -0,0 +1,3 @@ +get_name(Bevy::Entity.current).and_then do |name| + print(name) +end diff --git a/examples/ruby/current_entity.rs b/examples/ruby/current_entity.rs new file mode 100644 index 0000000..6e1c777 --- /dev/null +++ b/examples/ruby/current_entity.rs @@ -0,0 +1,25 @@ +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("get_name"), + |In((BevyEntity(entity),)): In<(BevyEntity,)>, names: Query<&Name>| { + names.get(entity).unwrap().to_string() + }, + ); + }) + .add_systems(Startup, startup) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn(( + Name::from("MyEntityName"), + Script::::new(assets_server.load("examples/ruby/current_entity.rb")), + )); +} -- 2.45.2 From b2ebf08805787f3d631be3148209a472ec48650c Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 19 May 2025 07:00:00 +0200 Subject: [PATCH 067/165] add example --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 832862c..0ab04f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -159,6 +159,11 @@ name = "call_function_from_rust_ruby" path = "examples/ruby/call_function_from_rust.rs" required-features = ["ruby"] +[[example]] +name = "current_entity_ruby" +path = "examples/ruby/current_entity.rs" +required-features = ["ruby"] + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } -- 2.45.2 From d0746fa5dbf8a410dbecc57ec1bff135f8395cfb Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 19 May 2025 07:00:00 +0200 Subject: [PATCH 068/165] rb static link --- .cargo/config.toml | 7 +++++++ Cargo.toml | 3 ++- assets/tests/lua/pass_vec3_to_script.lua | 7 +++---- assets/tests/rhai/pass_vec3_to_script.rhai | 5 +++-- assets/tests/ruby/pass_vec3_to_script.rb | 2 +- src/runtimes/ruby.rs | 21 +++++++++++++++------ 6 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e44bca7 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +# Without this flag, when linking static libruby, the linker removes symbols +# (such as `_rb_ext_ractor_safe`) which it thinks are dead code... but they are +# not, and they need to be included for the `embed` feature to work with static +# Ruby. +# rustflags = ["-C", "link-args=-lz", "-C", "link-dead-code=on"] +rustflags = ["-C", "link-args=-lz"] diff --git a/Cargo.toml b/Cargo.toml index 0ab04f3..759fcf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["bevy", "rhai", "scripting", "game", "gamedev"] [features] lua = ["dep:mlua", "mlua/luajit"] rhai = ["dep:rhai"] -ruby = ["dep:magnus"] +ruby = ["dep:magnus", "dep:rb-sys"] [dependencies] bevy = { default-features = false, version = "0.16", features = ["bevy_asset", "bevy_log"] } @@ -32,6 +32,7 @@ mlua = { version = "0.9.8", features = [ "send", ], optional = true } magnus = { version = "0.7.1", optional = true, features = ["embed"] } +rb-sys = { version = "*", default-features = false, features = ["link-ruby", "ruby-static"], optional = true } crossbeam-channel = "0.5.15" [[example]] diff --git a/assets/tests/lua/pass_vec3_to_script.lua b/assets/tests/lua/pass_vec3_to_script.lua index 4a9d395..0b50c5c 100644 --- a/assets/tests/lua/pass_vec3_to_script.lua +++ b/assets/tests/lua/pass_vec3_to_script.lua @@ -1,7 +1,6 @@ function test_func(vec3) - -- raise unless vec3.is_a?(Vec3) # TODO: BevyScriptum::Vec3 and add example how to include it globally like Sinatra does - -- raise unless vec3.x == 1.5 - -- raise unless vec3.y == 2.5 - -- raise unless vec3.z == -3.5 + assert(vec3.x == 1.5) + assert(vec3.y == 2.5) + assert(vec3.z == -3.5) mark_success() end diff --git a/assets/tests/rhai/pass_vec3_to_script.rhai b/assets/tests/rhai/pass_vec3_to_script.rhai index e0550d0..4d04eb1 100644 --- a/assets/tests/rhai/pass_vec3_to_script.rhai +++ b/assets/tests/rhai/pass_vec3_to_script.rhai @@ -1,4 +1,5 @@ fn test_func(vec3) { - // TODO: asser vec3 correctness - mark_success() + if type_of(vec3) != "tests::rhai_tests::BevyVec3" { throw() } + // TODO: assert x,y,z + mark_success(); } diff --git a/assets/tests/ruby/pass_vec3_to_script.rb b/assets/tests/ruby/pass_vec3_to_script.rb index 4d27eac..477ba1a 100644 --- a/assets/tests/ruby/pass_vec3_to_script.rb +++ b/assets/tests/ruby/pass_vec3_to_script.rb @@ -1,5 +1,5 @@ def test_func(vec3) - raise unless vec3.is_a?(Bevy::Vec3) # TODO: BevyScriptum::Vec3 and add example how to include it globally like Sinatra does + raise unless vec3.is_a?(Bevy::Vec3) raise unless vec3.x == 1.5 raise unless vec3.y == 2.5 raise unless vec3.z == -3.5 diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 2c6e535..3d557a3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -12,10 +12,13 @@ use bevy::{ tasks::futures_lite::io, }; use magnus::{ - block::Proc, data_type_builder, function, value::Lazy, DataType, DataTypeFunctions, IntoValue, - Object, RClass, RModule, Ruby, TryConvert, TypedData, + block::Proc, + data_type_builder, function, + value::{Lazy, ReprValue}, + DataType, DataTypeFunctions, IntoValue, Object, RClass, RModule, Ruby, TryConvert, TypedData, }; use magnus::{method, prelude::*}; +use rb_sys::{ruby_finalize, ruby_init_stack, VALUE}; use serde::Deserialize; use crate::{ @@ -66,11 +69,18 @@ impl RubyThread { let (sender, receiver) = crossbeam_channel::unbounded::>(); let handle = thread::spawn(move || { - let _cleanup = unsafe { magnus::embed::init() }; + unsafe { + let mut variable_in_this_stack_frame: VALUE = 0; + ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); + rb_sys::ruby_init() + }; while let Ok(f) = receiver.recv() { let ruby = Ruby::get().expect("Failed to get a handle to Ruby API"); f(ruby); } + unsafe { + ruby_finalize(); + } }); RubyThread { @@ -364,9 +374,9 @@ impl Runtime for RubyRuntime { let ruby = magnus::Ruby::get().unwrap(); let method_name: magnus::value::StaticSymbol = ruby.class_object().funcall("__method__", ()).unwrap(); - let method_name = method_name.to_string(); + let method_name = method_name.name().unwrap(); let callbacks = RUBY_CALLBACKS.lock().unwrap(); - let f = callbacks.get(&method_name).unwrap(); + let f = callbacks.get(method_name).unwrap(); let result = f( (), args.iter() @@ -422,7 +432,6 @@ impl Runtime for RubyRuntime { })) } - // TODO: add test fn call_fn_from_value( &self, value: &Self::Value, -- 2.45.2 From 450937566652edf25eb132f0fc5563e32ae3170c Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 19 May 2025 07:00:00 +0200 Subject: [PATCH 069/165] fix --- src/runtimes/ruby.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 3d557a3..609c68b 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,5 +1,6 @@ use std::{ collections::HashMap, + ffi::CString, sync::{Arc, Condvar, LazyLock, Mutex}, thread::{self, JoinHandle}, }; @@ -18,7 +19,9 @@ use magnus::{ DataType, DataTypeFunctions, IntoValue, Object, RClass, RModule, Ruby, TryConvert, TypedData, }; use magnus::{method, prelude::*}; -use rb_sys::{ruby_finalize, ruby_init_stack, VALUE}; +use rb_sys::{ + ruby_exec_node, ruby_finalize, ruby_init_stack, ruby_options, ruby_process_options, VALUE, +}; use serde::Deserialize; use crate::{ @@ -72,7 +75,15 @@ impl RubyThread { unsafe { let mut variable_in_this_stack_frame: VALUE = 0; ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); - rb_sys::ruby_init() + rb_sys::ruby_init(); + let opts = ["-e", ""]; + let mut argv = vec![]; + argv.extend(opts.iter().map(|s| CString::new(*s).unwrap())); + let mut argv = argv + .iter() + .map(|cs| cs.as_ptr() as *mut _) + .collect::>(); + ruby_options(argv.len() as i32, argv.as_mut_ptr()); }; while let Ok(f) = receiver.recv() { let ruby = Ruby::get().expect("Failed to get a handle to Ruby API"); -- 2.45.2 From 6bce009e0442d62754bf67c8b11442eafda93c2d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 20 May 2025 07:00:00 +0200 Subject: [PATCH 070/165] fixes --- .cargo/config.toml | 7 ------- build.rs | 3 +++ src/runtimes/ruby.rs | 8 -------- 3 files changed, 3 insertions(+), 15 deletions(-) delete mode 100644 .cargo/config.toml create mode 100644 build.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index e44bca7..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,7 +0,0 @@ -[build] -# Without this flag, when linking static libruby, the linker removes symbols -# (such as `_rb_ext_ractor_safe`) which it thinks are dead code... but they are -# not, and they need to be included for the `embed` feature to work with static -# Ruby. -# rustflags = ["-C", "link-args=-lz", "-C", "link-dead-code=on"] -rustflags = ["-C", "link-args=-lz"] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..62f4b71 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-lib=z"); // TODO: if features Ruby +} diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 609c68b..8a74fc1 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -76,14 +76,6 @@ impl RubyThread { let mut variable_in_this_stack_frame: VALUE = 0; ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); rb_sys::ruby_init(); - let opts = ["-e", ""]; - let mut argv = vec![]; - argv.extend(opts.iter().map(|s| CString::new(*s).unwrap())); - let mut argv = argv - .iter() - .map(|cs| cs.as_ptr() as *mut _) - .collect::>(); - ruby_options(argv.len() as i32, argv.as_mut_ptr()); }; while let Ok(f) = receiver.recv() { let ruby = Ruby::get().expect("Failed to get a handle to Ruby API"); -- 2.45.2 From b6e251dccad45873f5a74f3e84fa7e0a4c8b533e Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 20 May 2025 07:00:00 +0200 Subject: [PATCH 071/165] add todo --- build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.rs b/build.rs index 62f4b71..b43b3de 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,6 @@ fn main() { + // export RUBY_CONFIGURE_OPTS="--disable-shared --disable-install-doc --disable-install-rdoc" + // ./configure --with-cc-opt=“-I/usr/local/ssl/include” –with-ld-opt=“-L/usr/local/ssl/lib -Wl,-Bstatic -lssl -lcrypto -Wl,-Bdynamic -ldl” + println!("cargo:rustc-link-lib=z"); // TODO: if features Ruby } -- 2.45.2 From 5d104a1d01b8382798355e46e3808a4374d07c52 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 20 May 2025 17:02:57 +0200 Subject: [PATCH 072/165] cleanup --- Cargo.toml | 3 ++- build.rs | 5 ++--- src/runtimes/ruby.rs | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 759fcf2..f4666e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,9 @@ mlua = { version = "0.9.8", features = [ "vendored", "send", ], optional = true } -magnus = { version = "0.7.1", optional = true, features = ["embed"] } +magnus = { version = "0.7.1", optional = true } rb-sys = { version = "*", default-features = false, features = ["link-ruby", "ruby-static"], optional = true } +openssl-sys = { version = "0.9", features = ["vendored"] } crossbeam-channel = "0.5.15" [[example]] diff --git a/build.rs b/build.rs index b43b3de..a90c73a 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,5 @@ -fn main() { - // export RUBY_CONFIGURE_OPTS="--disable-shared --disable-install-doc --disable-install-rdoc" - // ./configure --with-cc-opt=“-I/usr/local/ssl/include” –with-ld-opt=“-L/usr/local/ssl/lib -Wl,-Bstatic -lssl -lcrypto -Wl,-Bdynamic -ldl” +use std::{env, path::PathBuf, process::Command}; +fn main() { println!("cargo:rustc-link-lib=z"); // TODO: if features Ruby } diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 8a74fc1..fa535a6 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,3 +1,5 @@ +// TODO: install with CC=clang rbenv install 3.4.3 + use std::{ collections::HashMap, ffi::CString, -- 2.45.2 From 07331f8d48818565c439898f79d2d87eae61ed9d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 20 May 2025 22:20:05 +0200 Subject: [PATCH 073/165] fix init --- .cargo/config.toml | 6 ++++++ Cargo.toml | 1 - src/runtimes/ruby.rs | 14 ++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..f3e9974 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +# Without this flag, when linking static libruby, the linker removes symbols +# (such as `_rb_ext_ractor_safe`) which it thinks are dead code... but they are +# not, and they need to be included for the `embed` feature to work with static +# Ruby. +rustflags = ["-C", "link-dead-code=on", "-C", "link-arg=-lz"] diff --git a/Cargo.toml b/Cargo.toml index f4666e6..8c7712c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ mlua = { version = "0.9.8", features = [ ], optional = true } magnus = { version = "0.7.1", optional = true } rb-sys = { version = "*", default-features = false, features = ["link-ruby", "ruby-static"], optional = true } -openssl-sys = { version = "0.9", features = ["vendored"] } crossbeam-channel = "0.5.15" [[example]] diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index fa535a6..399d58e 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -21,16 +21,14 @@ use magnus::{ DataType, DataTypeFunctions, IntoValue, Object, RClass, RModule, Ruby, TryConvert, TypedData, }; use magnus::{method, prelude::*}; -use rb_sys::{ - ruby_exec_node, ruby_finalize, ruby_init_stack, ruby_options, ruby_process_options, VALUE, -}; +use rb_sys::{ruby_finalize, ruby_init_stack, VALUE}; use serde::Deserialize; use crate::{ assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, promise::Promise, - FuncArgs, Runtime, ScriptingError, ENTITY_VAR_NAME, + FuncArgs, Runtime, ScriptingError, }; #[derive(Resource)] @@ -74,10 +72,18 @@ impl RubyThread { let (sender, receiver) = crossbeam_channel::unbounded::>(); let handle = thread::spawn(move || { + let argc: i32 = 0; + let argv = vec![CString::new("ruby").unwrap(), CString::new("-e").unwrap()]; + let mut argv = argv + .iter() + .map(|cs| cs.as_ptr() as *mut _) + .collect::>(); + unsafe { let mut variable_in_this_stack_frame: VALUE = 0; ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); rb_sys::ruby_init(); + rb_sys::ruby_options(argc, argv.as_mut_ptr()); }; while let Ok(f) = receiver.recv() { let ruby = Ruby::get().expect("Failed to get a handle to Ruby API"); -- 2.45.2 From 553abeb0c3a81b6af9e6a7277f159374e83113a2 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 20 May 2025 23:36:38 +0200 Subject: [PATCH 074/165] fix --- build.rs | 1 + src/runtimes/ruby.rs | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/build.rs b/build.rs index a90c73a..c2bbcc5 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,6 @@ use std::{env, path::PathBuf, process::Command}; fn main() { + println!("cargo:rustc-link-arg=-rdynamic"); println!("cargo:rustc-link-lib=z"); // TODO: if features Ruby } diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 399d58e..e9ee8e7 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -72,17 +72,18 @@ impl RubyThread { let (sender, receiver) = crossbeam_channel::unbounded::>(); let handle = thread::spawn(move || { - let argc: i32 = 0; - let argv = vec![CString::new("ruby").unwrap(), CString::new("-e").unwrap()]; - let mut argv = argv - .iter() - .map(|cs| cs.as_ptr() as *mut _) - .collect::>(); + let argc: i32 = 3; + let mut argv = vec![ + CString::new("ruby").unwrap().into_raw(), + CString::new("-e").unwrap().into_raw(), + CString::new("").unwrap().into_raw(), + ]; unsafe { let mut variable_in_this_stack_frame: VALUE = 0; ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); rb_sys::ruby_init(); + rb_sys::ruby_init_loadpath(); rb_sys::ruby_options(argc, argv.as_mut_ptr()); }; while let Ok(f) = receiver.recv() { @@ -90,7 +91,7 @@ impl RubyThread { f(ruby); } unsafe { - ruby_finalize(); + rb_sys::ruby_finalize(); } }); -- 2.45.2 From 49a3ac716d7be24b42947346ec3b6dfbed285ec5 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 07:00:00 +0200 Subject: [PATCH 075/165] update --- .cargo/config.toml | 6 ------ Cargo.toml | 2 ++ build.rs | 10 ++++++---- tests/tests.rs | 12 ++++++++++++ 4 files changed, 20 insertions(+), 10 deletions(-) delete mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index f3e9974..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -[build] -# Without this flag, when linking static libruby, the linker removes symbols -# (such as `_rb_ext_ractor_safe`) which it thinks are dead code... but they are -# not, and they need to be included for the `embed` feature to work with static -# Ruby. -rustflags = ["-C", "link-dead-code=on", "-C", "link-arg=-lz"] diff --git a/Cargo.toml b/Cargo.toml index 8c7712c..56e7372 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ categories = ["game-development"] description = "Plugin for Bevy engine that allows you to write some of your game logic in a scripting language" repository = "https://github.com/jarkonik/bevy_scriptum" keywords = ["bevy", "rhai", "scripting", "game", "gamedev"] +links = "lz" # TODO: conditional, probably will need a crate for ruby [features] lua = ["dep:mlua", "mlua/luajit"] @@ -33,6 +34,7 @@ mlua = { version = "0.9.8", features = [ ], optional = true } magnus = { version = "0.7.1", optional = true } rb-sys = { version = "*", default-features = false, features = ["link-ruby", "ruby-static"], optional = true } +libz-sys = { version = "1.1", default-features = false, features = ["static"] } crossbeam-channel = "0.5.15" [[example]] diff --git a/build.rs b/build.rs index c2bbcc5..989fef5 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,8 @@ -use std::{env, path::PathBuf, process::Command}; - fn main() { - println!("cargo:rustc-link-arg=-rdynamic"); - println!("cargo:rustc-link-lib=z"); // TODO: if features Ruby + #[cfg(feature = "ruby")] + { + println!("cargo:rustc-link-arg=-rdynamic"); + println!("cargo:rustc-link-arg=-lz"); + println!("cargo:rustc-link-lib=z"); + } } diff --git a/tests/tests.rs b/tests/tests.rs index 53b1416..411c4a6 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -676,5 +676,17 @@ mod ruby_tests { } } + #[test] + fn test_symbol_inspection() { + let mut app = build_test_app(); + + app.add_scripting::(|_| {}); + let runtime = app.world().get_resource::().unwrap(); + runtime.with_engine_thread(|engine| { + let symbol_string: String = engine.eval(":test_symbol.inspect").unwrap(); + assert_eq!(symbol_string, ":test_symbol") + }); + } + scripting_tests!(RubyRuntime, "ruby", "rb", BevyEntity, BevyVec3); } -- 2.45.2 From 521407a6ac154f4c5cde87f72c0415851f339cf6 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 17:37:09 +0200 Subject: [PATCH 076/165] refactor --- src/runtimes/ruby.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index e9ee8e7..9f0cecf 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -72,7 +72,6 @@ impl RubyThread { let (sender, receiver) = crossbeam_channel::unbounded::>(); let handle = thread::spawn(move || { - let argc: i32 = 3; let mut argv = vec![ CString::new("ruby").unwrap().into_raw(), CString::new("-e").unwrap().into_raw(), @@ -84,7 +83,7 @@ impl RubyThread { ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); rb_sys::ruby_init(); rb_sys::ruby_init_loadpath(); - rb_sys::ruby_options(argc, argv.as_mut_ptr()); + rb_sys::ruby_options(argv.len() as i32, argv.as_mut_ptr()); }; while let Ok(f) = receiver.recv() { let ruby = Ruby::get().expect("Failed to get a handle to Ruby API"); -- 2.45.2 From 3028b6fe4ea4288bce806bbdd91be353fd55c627 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 21:31:10 +0200 Subject: [PATCH 077/165] clippy --- src/runtimes/ruby.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 9f0cecf..d7a3e97 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -21,7 +21,7 @@ use magnus::{ DataType, DataTypeFunctions, IntoValue, Object, RClass, RModule, Ruby, TryConvert, TypedData, }; use magnus::{method, prelude::*}; -use rb_sys::{ruby_finalize, ruby_init_stack, VALUE}; +use rb_sys::{ruby_init_stack, VALUE}; use serde::Deserialize; use crate::{ @@ -147,7 +147,8 @@ unsafe impl TypedData for Promise<(), RubyValue> { impl TryConvert for Promise<(), RubyValue> { fn try_convert(val: magnus::Value) -> Result { - TryConvert::try_convert(val).map(|p: &Self| p.clone()) + let result: Result<&Self, _> = TryConvert::try_convert(val); + result.cloned() } } @@ -182,7 +183,8 @@ impl BevyEntity { impl TryConvert for BevyEntity { fn try_convert(val: magnus::Value) -> Result { - TryConvert::try_convert(val).map(|p: &Self| p.clone()) + let result: Result<&Self, _> = TryConvert::try_convert(val); + result.cloned() } } @@ -210,7 +212,8 @@ impl BevyVec3 { impl TryConvert for BevyVec3 { fn try_convert(val: magnus::Value) -> Result { - TryConvert::try_convert(val).map(|p: &Self| p.clone()) + let result: Result<&Self, _> = TryConvert::try_convert(val); + result.cloned() } } -- 2.45.2 From 853879ef6d8571ba306fff70653ae8b1c8c44485 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 21:51:45 +0200 Subject: [PATCH 078/165] fix rhai tests --- assets/tests/rhai/pass_vec3_to_script.rhai | 2 +- src/runtimes/lua.rs | 18 ++++++++ src/runtimes/rhai.rs | 48 ++++++++++++++++++---- src/runtimes/ruby.rs | 2 +- tests/tests.rs | 20 ++------- 5 files changed, 63 insertions(+), 27 deletions(-) diff --git a/assets/tests/rhai/pass_vec3_to_script.rhai b/assets/tests/rhai/pass_vec3_to_script.rhai index 4d04eb1..2c8a176 100644 --- a/assets/tests/rhai/pass_vec3_to_script.rhai +++ b/assets/tests/rhai/pass_vec3_to_script.rhai @@ -1,5 +1,5 @@ fn test_func(vec3) { - if type_of(vec3) != "tests::rhai_tests::BevyVec3" { throw() } + if type_of(vec3) != "Vec3" { throw() } // TODO: assert x,y,z mark_success(); } diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 9d0962b..256c249 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -42,6 +42,12 @@ pub struct LuaRuntime { #[derive(Debug, Clone, Copy)] pub struct BevyEntity(pub Entity); +impl BevyEntity { + pub fn index(&self) -> u32 { + self.0.index() + } +} + impl UserData for BevyEntity {} impl FromLua<'_> for BevyEntity { @@ -63,6 +69,18 @@ impl BevyVec3 { pub fn new(x: f32, y: f32, z: f32) -> Self { BevyVec3(Vec3 { x, y, z }) } + + pub fn x(&self) -> f32 { + self.0.x + } + + pub fn y(&self) -> f32 { + self.0.y + } + + pub fn z(&self) -> f32 { + self.0.z + } } impl UserData for BevyVec3 {} diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 5228075..31faeda 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -51,6 +51,36 @@ pub struct RhaiScriptData { #[derive(Debug, Clone)] pub struct RhaiValue(pub rhai::Dynamic); +#[derive(Clone)] +pub struct BevyEntity(pub Entity); + +impl BevyEntity { + pub fn index(&self) -> u32 { + self.0.index() + } +} + +#[derive(Clone)] +pub struct BevyVec3(pub Vec3); + +impl BevyVec3 { + pub fn new(x: f32, y: f32, z: f32) -> Self { + Self(Vec3::new(x, y, z)) + } + + pub fn x(&self) -> f32 { + self.0.x + } + + pub fn y(&self) -> f32 { + self.0.y + } + + pub fn z(&self) -> f32 { + self.0.z + } +} + impl Runtime for RhaiRuntime { type Schedule = RhaiSchedule; type ScriptAsset = RhaiScript; @@ -66,7 +96,7 @@ impl Runtime for RhaiRuntime { entity: Entity, ) -> Result { let mut scope = Scope::new(); - scope.push(ENTITY_VAR_NAME, entity); + scope.push(ENTITY_VAR_NAME, BevyEntity(entity)); let engine = &self.engine; @@ -78,7 +108,7 @@ impl Runtime for RhaiRuntime { .run_ast_with_scope(&mut scope, &ast) .map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?; - scope.remove::(ENTITY_VAR_NAME).unwrap(); + scope.remove::(ENTITY_VAR_NAME).unwrap(); Ok(Self::ScriptData { ast, scope }) } @@ -114,7 +144,7 @@ impl Runtime for RhaiRuntime { ) -> Result { let ast = script_data.ast.clone(); let scope = &mut script_data.scope; - scope.push(ENTITY_VAR_NAME, entity); + scope.push(ENTITY_VAR_NAME, BevyEntity(entity)); let options = CallFnOptions::new().eval_ast(false); let args = args .parse(&self.engine) @@ -124,7 +154,7 @@ impl Runtime for RhaiRuntime { let result = self .engine .call_fn_with_options::(options, scope, &ast, name, args); - scope.remove::(ENTITY_VAR_NAME).unwrap(); + scope.remove::(ENTITY_VAR_NAME).unwrap(); match result { Ok(val) => Ok(RhaiValue(val)), Err(e) => Err(ScriptingError::RuntimeError(Box::new(e))), @@ -188,8 +218,8 @@ impl Default for RhaiRuntime { let mut engine = Engine::new(); engine - .register_type_with_name::("Entity") - .register_get("index", |entity: &mut Entity| entity.index()); + .register_type_with_name::("Entity") + .register_get("index", |entity: &mut BevyEntity| entity.index()); #[allow(deprecated)] engine .register_type_with_name::>("Promise") @@ -202,9 +232,9 @@ impl Default for RhaiRuntime { ); engine - .register_type_with_name::("Vec3") + .register_type_with_name::("Vec3") .register_fn("new_vec3", |x: f64, y: f64, z: f64| { - Vec3::new(x as f32, y as f32, z as f32) + BevyVec3(Vec3::new(x as f32, y as f32, z as f32)) }) .register_get("x", |vec: &mut Vec3| vec.x as f64) .register_get("y", |vec: &mut Vec3| vec.y as f64) @@ -242,7 +272,7 @@ impl FromRuntimeValueWithEngine<'_, RhaiRuntime> for T { } pub mod prelude { - pub use super::{RhaiRuntime, RhaiScript, RhaiScriptData}; + pub use super::{BevyEntity, BevyVec3, RhaiRuntime, RhaiScript, RhaiScriptData}; } macro_rules! impl_tuple { diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index d7a3e97..ccba49b 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -176,7 +176,7 @@ fn then(r_self: magnus::Value) -> magnus::Value { pub struct BevyEntity(pub Entity); impl BevyEntity { - fn index(&self) -> u32 { + pub fn index(&self) -> u32 { self.0.index() } } diff --git a/tests/tests.rs b/tests/tests.rs index 411c4a6..e39de94 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -458,7 +458,7 @@ macro_rules! scripting_tests { runtime.add_function( String::from("rust_func"), |In((entity,)): In<($entity_type,)>, mut res: ResMut| { - res.index = Some(entity.0.index()); + res.index = Some(entity.index()); }, ); }); @@ -490,9 +490,9 @@ macro_rules! scripting_tests { runtime.add_function( String::from("rust_func"), |In((v,)): In<($vec_type,)>, mut res: ResMut| { - assert_eq!(v.0.x, 1.5); - assert_eq!(v.0.y, 2.5); - assert_eq!(v.0.z, -3.5); + assert_eq!(v.x(), 1.5); + assert_eq!(v.y(), 2.5); + assert_eq!(v.z(), -3.5); res.success = true }, ); @@ -551,18 +551,6 @@ mod rhai_tests { use bevy::prelude::*; use bevy_scriptum::runtimes::rhai::prelude::*; - #[derive(Clone)] - struct BevyEntity(Entity); - - #[derive(Clone)] - struct BevyVec3(Vec3); - - impl BevyVec3 { - fn new(x: f32, y: f32, z: f32) -> Self { - Self(Vec3 { x, y, z }) - } - } - impl AssertStateKeyValue for RhaiRuntime { type ScriptData = RhaiScriptData; -- 2.45.2 From 992eb960cbed076f92489467a0e1690aca7066c8 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 21:52:31 +0200 Subject: [PATCH 079/165] add todo --- src/runtimes/ruby.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index ccba49b..da00a42 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,4 +1,5 @@ // TODO: install with CC=clang rbenv install 3.4.3 +// TODO: adjust rhai examples and docs or maybe more rhai changes to different PR use std::{ collections::HashMap, -- 2.45.2 From 992d19cba7af123c54a8a95eaaf09762e5b9d026 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 21:58:16 +0200 Subject: [PATCH 080/165] fix tests --- assets/tests/rhai/pass_vec3_to_script.rhai | 4 +++- src/runtimes/rhai.rs | 6 +++--- src/runtimes/ruby.rs | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/assets/tests/rhai/pass_vec3_to_script.rhai b/assets/tests/rhai/pass_vec3_to_script.rhai index 2c8a176..0afb1c2 100644 --- a/assets/tests/rhai/pass_vec3_to_script.rhai +++ b/assets/tests/rhai/pass_vec3_to_script.rhai @@ -1,5 +1,7 @@ fn test_func(vec3) { if type_of(vec3) != "Vec3" { throw() } - // TODO: assert x,y,z + if vec3.x != 1.5 { throw() } + if vec3.y != 2.5 { throw() } + if vec3.z != -3.5 { throw() } mark_success(); } diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 31faeda..09eb02e 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -236,9 +236,9 @@ impl Default for RhaiRuntime { .register_fn("new_vec3", |x: f64, y: f64, z: f64| { BevyVec3(Vec3::new(x as f32, y as f32, z as f32)) }) - .register_get("x", |vec: &mut Vec3| vec.x as f64) - .register_get("y", |vec: &mut Vec3| vec.y as f64) - .register_get("z", |vec: &mut Vec3| vec.z as f64); + .register_get("x", |vec: &mut BevyVec3| vec.x() as f64) + .register_get("y", |vec: &mut BevyVec3| vec.y() as f64) + .register_get("z", |vec: &mut BevyVec3| vec.z() as f64); #[allow(deprecated)] engine.on_def_var(|_, info, _| Ok(info.name != "entity")); diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index da00a42..a4fe595 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,5 +1,6 @@ // TODO: install with CC=clang rbenv install 3.4.3 -// TODO: adjust rhai examples and docs or maybe more rhai changes to different PR +// TODO: adjust rhai examples and docs or maybe more rhai changes to different PR or even better +// find a better way to test them(dont wrap, generic bevy etc) use std::{ collections::HashMap, -- 2.45.2 From c75f158dfb72f7930995ea56fd1b472fb115eecd Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 22:07:45 +0200 Subject: [PATCH 081/165] refactor --- src/callback.rs | 6 +-- src/lib.rs | 2 +- src/runtimes/lua.rs | 2 +- src/runtimes/rhai.rs | 2 +- src/runtimes/ruby.rs | 110 +++++++++++++++++++++++-------------------- 5 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index f3b544e..c8bedbd 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -78,7 +78,7 @@ where inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - if R::is_current_thread() { + if R::needs_own_thread() { runtime.with_engine_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) @@ -110,7 +110,7 @@ macro_rules! impl_tuple { inner_system.initialize(world); let system_fn = move |args: In>, world: &mut World| { let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - let args = if RN::is_current_thread() { + let args = if RN::needs_own_thread() { runtime.with_engine_mut(move |engine| { ( $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ @@ -127,7 +127,7 @@ macro_rules! impl_tuple { let result = inner_system.run(args, world); inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - if RN::is_current_thread() { + if RN::needs_own_thread() { runtime.with_engine_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) diff --git a/src/lib.rs b/src/lib.rs index ceb7496..fb5ced6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ pub trait Runtime: Resource + Default { type Value: Send + Clone; type RawEngine; - fn is_current_thread() -> bool; + fn needs_own_thread() -> bool; /// Provides mutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 256c249..4ee5bab 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -299,7 +299,7 @@ impl Runtime for LuaRuntime { self.with_engine(f) } - fn is_current_thread() -> bool { + fn needs_own_thread() -> bool { true } } diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 09eb02e..6807450 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -208,7 +208,7 @@ impl Runtime for RhaiRuntime { self.with_engine(f) } - fn is_current_thread() -> bool { + fn needs_own_thread() -> bool { true } } diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index a4fe595..e0f706f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -291,6 +291,28 @@ impl RubyValue { } } +impl RubyRuntime { + fn execute_in_thread( + &self, + f: impl FnOnce(&magnus::Ruby) -> T + Send + 'static, + ) -> T { + self.ruby_thread + .as_ref() + .unwrap() + .execute(Box::new(move |ruby| f(&ruby))) + } + + fn execute_in_thread_mut( + &self, + f: impl FnOnce(&mut magnus::Ruby) -> T + Send + 'static, + ) -> T { + self.ruby_thread + .as_ref() + .unwrap() + .execute(Box::new(move |mut ruby| f(&mut ruby))) + } +} + impl Runtime for RubyRuntime { type Schedule = RubySchedule; @@ -308,20 +330,14 @@ impl Runtime for RubyRuntime { &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { - self.ruby_thread - .as_ref() - .unwrap() - .execute(Box::new(move |mut ruby| f(&mut ruby))) + self.execute_in_thread_mut(f) } fn with_engine_thread( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { - self.ruby_thread - .as_ref() - .unwrap() - .execute(Box::new(move |ruby| f(&ruby))) + self.execute_in_thread(f) } fn with_engine_mut(&mut self, _f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { @@ -338,22 +354,19 @@ impl Runtime for RubyRuntime { entity: bevy::prelude::Entity, ) -> Result { let script = script.0.clone(); - self.ruby_thread - .as_ref() - .unwrap() - .execute(Box::new(move |ruby| { - let var = ruby - .class_object() - .const_get::<_, RModule>("Bevy") - .unwrap() - .const_get::<_, RClass>("Entity") - .unwrap(); - var.ivar_set("_current", BevyEntity(entity)).unwrap(); - let value = ruby.eval::(&script).unwrap(); - var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); + self.execute_in_thread(Box::new(move |ruby: &Ruby| { + let var = ruby + .class_object() + .const_get::<_, RModule>("Bevy") + .unwrap() + .const_get::<_, RClass>("Entity") + .unwrap(); + var.ivar_set("_current", BevyEntity(entity)).unwrap(); + let value = ruby.eval::(&script).unwrap(); + var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); - RubyValue::new(value) - })); + RubyValue::new(value) + })); Ok(RubyScriptData) } @@ -403,13 +416,10 @@ impl Runtime for RubyRuntime { result.into_value() } - self.ruby_thread - .as_ref() - .unwrap() - .execute(Box::new(move |ruby| { - ruby.define_global_function(&name, function!(callback, -1)); - RubyValue::nil(&ruby) - })); + self.execute_in_thread(Box::new(move |ruby: &Ruby| { + ruby.define_global_function(&name, function!(callback, -1)); + RubyValue::nil(&ruby) + })); Ok(()) } @@ -422,30 +432,26 @@ impl Runtime for RubyRuntime { args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self> + Send + 'static, ) -> Result { let name = name.to_string(); - self.ruby_thread - .as_ref() - .unwrap() - .execute(Box::new(move |ruby| { - let var = ruby - .class_object() - .const_get::<_, RModule>("Bevy") - .unwrap() - .const_get::<_, RClass>("Entity") - .unwrap(); - var.ivar_set("_current", BevyEntity(entity)).unwrap(); + self.execute_in_thread(Box::new(move |ruby: &Ruby| { + let var = ruby + .class_object() + .const_get::<_, RModule>("Bevy") + .unwrap() + .const_get::<_, RClass>("Entity") + .unwrap(); + var.ivar_set("_current", BevyEntity(entity)).unwrap(); - let args: Vec<_> = args - .parse(&ruby) - .into_iter() - .map(|a| ruby.get_inner(a.0)) - .collect(); - let return_value: magnus::Value = - ruby.class_object().funcall(name, args.as_slice())?; + let args: Vec<_> = args + .parse(&ruby) + .into_iter() + .map(|a| ruby.get_inner(a.0)) + .collect(); + let return_value: magnus::Value = ruby.class_object().funcall(name, args.as_slice())?; - var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); + var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); - Ok(RubyValue::new(return_value)) - })) + Ok(RubyValue::new(return_value)) + })) } fn call_fn_from_value( @@ -471,7 +477,7 @@ impl Runtime for RubyRuntime { })) } - fn is_current_thread() -> bool { + fn needs_own_thread() -> bool { false } } -- 2.45.2 From 86e75674386c0730696ecf39fa5aaeaab4ebd963 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 22:09:30 +0200 Subject: [PATCH 082/165] refactor --- src/callback.rs | 8 ++++---- src/runtimes/lua.rs | 2 +- src/runtimes/rhai.rs | 2 +- src/runtimes/ruby.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index c8bedbd..41f90ff 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -79,11 +79,11 @@ where let mut runtime = world.get_resource_mut::().expect("No runtime resource"); if R::needs_own_thread() { - runtime.with_engine_mut(move |engine| { + runtime.with_engine_thread_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) } else { - runtime.with_engine_thread_mut(move |engine| { + runtime.with_engine_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) } @@ -128,11 +128,11 @@ macro_rules! impl_tuple { inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); if RN::needs_own_thread() { - runtime.with_engine_mut(move |engine| { + runtime.with_engine_thread_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) } else { - runtime.with_engine_thread_mut(move |engine| { + runtime.with_engine_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) } diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 4ee5bab..347030a 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -300,7 +300,7 @@ impl Runtime for LuaRuntime { } fn needs_own_thread() -> bool { - true + false } } diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 6807450..00d6fe4 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -209,7 +209,7 @@ impl Runtime for RhaiRuntime { } fn needs_own_thread() -> bool { - true + false } } diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index e0f706f..96f5e6b 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -478,7 +478,7 @@ impl Runtime for RubyRuntime { } fn needs_own_thread() -> bool { - false + true } } -- 2.45.2 From 35efc45a20f83b067252c26a6efd13621e635e5d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 22:12:55 +0200 Subject: [PATCH 083/165] add todo --- src/callback.rs | 4 ++-- src/runtimes/ruby.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index 41f90ff..cc76bcb 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -111,13 +111,13 @@ macro_rules! impl_tuple { let system_fn = move |args: In>, world: &mut World| { let mut runtime = world.get_resource_mut::().expect("No runtime resource"); let args = if RN::needs_own_thread() { - runtime.with_engine_mut(move |engine| { + runtime.with_engine_thread_mut(move |engine| { ( $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ ) }) } else { - runtime.with_engine_thread_mut(move |engine| { + runtime.with_engine_mut(move |engine| { ( $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ ) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 96f5e6b..b9737a8 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,6 +1,7 @@ // TODO: install with CC=clang rbenv install 3.4.3 // TODO: adjust rhai examples and docs or maybe more rhai changes to different PR or even better // find a better way to test them(dont wrap, generic bevy etc) +// TODO: Move with_engine_thread and with_engine to 2 separate traits use std::{ collections::HashMap, -- 2.45.2 From e06fcecec9b15e130a9d2389c7d8225cd86bf477 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Wed, 21 May 2025 22:14:26 +0200 Subject: [PATCH 084/165] add todo --- src/runtimes/ruby.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index b9737a8..f92320f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -2,6 +2,7 @@ // TODO: adjust rhai examples and docs or maybe more rhai changes to different PR or even better // find a better way to test them(dont wrap, generic bevy etc) // TODO: Move with_engine_thread and with_engine to 2 separate traits +// TODO: add modules to lua and rhai for entity and vec3 use std::{ collections::HashMap, -- 2.45.2 From 2835d490f6fd899a8d6d1e40bee97aec116b635b Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Thu, 22 May 2025 07:00:00 +0200 Subject: [PATCH 085/165] cleanup --- src/runtimes/ruby.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index f92320f..cb076e6 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,9 +1,3 @@ -// TODO: install with CC=clang rbenv install 3.4.3 -// TODO: adjust rhai examples and docs or maybe more rhai changes to different PR or even better -// find a better way to test them(dont wrap, generic bevy etc) -// TODO: Move with_engine_thread and with_engine to 2 separate traits -// TODO: add modules to lua and rhai for entity and vec3 - use std::{ collections::HashMap, ffi::CString, -- 2.45.2 From ebb6a50285fb934ffa1a477129ec517aba99e940 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 18:54:21 +0200 Subject: [PATCH 086/165] cleanup --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 56e7372..8c7712c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ categories = ["game-development"] description = "Plugin for Bevy engine that allows you to write some of your game logic in a scripting language" repository = "https://github.com/jarkonik/bevy_scriptum" keywords = ["bevy", "rhai", "scripting", "game", "gamedev"] -links = "lz" # TODO: conditional, probably will need a crate for ruby [features] lua = ["dep:mlua", "mlua/luajit"] @@ -34,7 +33,6 @@ mlua = { version = "0.9.8", features = [ ], optional = true } magnus = { version = "0.7.1", optional = true } rb-sys = { version = "*", default-features = false, features = ["link-ruby", "ruby-static"], optional = true } -libz-sys = { version = "1.1", default-features = false, features = ["static"] } crossbeam-channel = "0.5.15" [[example]] -- 2.45.2 From e8b95f609935434c9d663a50a192ca510cef4c73 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 19:03:36 +0200 Subject: [PATCH 087/165] cleanup --- src/runtimes/ruby.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index cb076e6..6c65b7a 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -456,8 +456,8 @@ impl Runtime for RubyRuntime { _context: &Self::CallContext, args: Vec, ) -> Result { - let value = value.clone(); // TODO: maybe just clone/wrap where we added Send + static - // currently?> + let value = value.clone(); + self.ruby_thread .as_ref() .unwrap() -- 2.45.2 From 9bcf3ee589d8faebc8f8fe3ea86f826525960937 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 19:12:27 +0200 Subject: [PATCH 088/165] cleanup --- src/runtimes/ruby.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 6c65b7a..195a4c3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -386,9 +386,7 @@ impl Runtime for RubyRuntime { Vec, ) -> Result, crate::ScriptingError> - + Send - + Sync - + 'static, + + Send, >; static RUBY_CALLBACKS: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); -- 2.45.2 From 2a2b2343d73033339176e016d356283cf1ef6ca4 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 21:10:35 +0200 Subject: [PATCH 089/165] add example --- Cargo.toml | 6 +++ assets/examples/ruby/current_entity.rb | 2 +- assets/examples/ruby/custom_type.rb | 4 ++ examples/ruby/custom_type.rs | 54 ++++++++++++++++++++++++++ src/lib.rs | 2 +- src/runtimes/lua.rs | 2 - src/runtimes/rhai.rs | 2 - src/runtimes/ruby.rs | 6 +++ 8 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 assets/examples/ruby/custom_type.rb create mode 100644 examples/ruby/custom_type.rs diff --git a/Cargo.toml b/Cargo.toml index 8c7712c..d683189 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -165,6 +165,12 @@ name = "current_entity_ruby" path = "examples/ruby/current_entity.rs" required-features = ["ruby"] +[[example]] +name = "custom_type_ruby" +path = "examples/ruby/custom_type.rs" +required-features = ["ruby"] + + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } diff --git a/assets/examples/ruby/current_entity.rb b/assets/examples/ruby/current_entity.rb index 4c2c77b..172b3e9 100644 --- a/assets/examples/ruby/current_entity.rb +++ b/assets/examples/ruby/current_entity.rb @@ -1,3 +1,3 @@ get_name(Bevy::Entity.current).and_then do |name| - print(name) + puts(name) end diff --git a/assets/examples/ruby/custom_type.rb b/assets/examples/ruby/custom_type.rb new file mode 100644 index 0000000..0ab76a2 --- /dev/null +++ b/assets/examples/ruby/custom_type.rb @@ -0,0 +1,4 @@ +# Create a new instance of MyType +my_type = MyType.new() +# Call registered method +puts(my_type.my_method) diff --git a/examples/ruby/custom_type.rs b/examples/ruby/custom_type.rs new file mode 100644 index 0000000..e775a7f --- /dev/null +++ b/examples/ruby/custom_type.rs @@ -0,0 +1,54 @@ +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::magnus; +use bevy_scriptum::runtimes::ruby::magnus::Module as _; +use bevy_scriptum::runtimes::ruby::magnus::Object as _; +use bevy_scriptum::runtimes::ruby::prelude::*; +use bevy_scriptum::ScriptingError; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("hello_bevy"), || { + println!("hello bevy, called from script"); + }); + }) + .add_systems(Startup, startup) + .run(); +} + +#[magnus::wrap(class = "MyType")] +struct MyType { + my_field: u32, +} + +impl MyType { + fn new() -> Self { + Self { my_field: 42 } + } + + fn my_method(&self) -> u32 { + self.my_field + } +} + +fn startup( + mut commands: Commands, + scripting_runtime: ResMut, + assets_server: Res, +) { + scripting_runtime + .with_engine_thread(|ruby| { + let my_type = ruby.define_class("MyType", ruby.class_object())?; + my_type.define_singleton_method("new", magnus::function!(MyType::new, 0))?; + my_type.define_method("my_method", magnus::method!(MyType::my_method, 0))?; + + Ok::<(), ScriptingError>(()) + }) + .unwrap(); + + commands.spawn(Script::::new( + assets_server.load("examples/ruby/custom_type.rb"), + )); +} diff --git a/src/lib.rs b/src/lib.rs index fb5ced6..844940a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,7 +273,7 @@ use self::{ systems::{process_new_scripts, reload_scripts}, }; -#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] +#[cfg(any(feature = "rhai", feature = "lua"))] const ENTITY_VAR_NAME: &str = "entity"; /// An error that can occur when internal [ScriptingPlugin] systems are being executed diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 347030a..0926649 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -287,7 +287,6 @@ impl Runtime for LuaRuntime { &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { - log::warn!("runtime can be used on current thread, wil run on current thread"); self.with_engine_mut(f) } @@ -295,7 +294,6 @@ impl Runtime for LuaRuntime { &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { - log::warn!("runtime can be used on current thread, wil run on current thread"); self.with_engine(f) } diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 00d6fe4..ca2b950 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -196,7 +196,6 @@ impl Runtime for RhaiRuntime { &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { - log::warn!("runtime can be used on current thread, wil run on current thread"); self.with_engine_mut(f) } @@ -204,7 +203,6 @@ impl Runtime for RhaiRuntime { &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { - log::warn!("runtime can be used on current thread, wil run on current thread"); self.with_engine(f) } diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 195a4c3..0a9f18f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -322,6 +322,7 @@ impl Runtime for RubyRuntime { type RawEngine = magnus::Ruby; + // TODO: it should be somehow possible to remove 'static here fn with_engine_thread_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, @@ -329,6 +330,7 @@ impl Runtime for RubyRuntime { self.execute_in_thread_mut(f) } + // TODO: it should be somehow possible to remove 'static here fn with_engine_thread( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, @@ -476,6 +478,10 @@ impl Runtime for RubyRuntime { } } +pub mod magnus { + pub use magnus::*; +} + pub mod prelude { pub use super::{BevyEntity, BevyVec3, RubyRuntime, RubyScript, RubyScriptData}; } -- 2.45.2 From 2deec07a33aeb30358eccd830b1fe7b6d779efa2 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 21:14:03 +0200 Subject: [PATCH 090/165] add example --- Cargo.toml | 4 ++++ assets/examples/ruby/ecs.rb | 1 + examples/ruby/ecs.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 assets/examples/ruby/ecs.rb create mode 100644 examples/ruby/ecs.rs diff --git a/Cargo.toml b/Cargo.toml index d683189..58e4d1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -170,6 +170,10 @@ name = "custom_type_ruby" path = "examples/ruby/custom_type.rs" required-features = ["ruby"] +[[example]] +name = "ecs_ruby" +path = "examples/ruby/ecs.rs" +required-features = ["ruby"] [dev-dependencies] tracing-subscriber = "0.3.18" diff --git a/assets/examples/ruby/ecs.rb b/assets/examples/ruby/ecs.rb new file mode 100644 index 0000000..63e06a7 --- /dev/null +++ b/assets/examples/ruby/ecs.rb @@ -0,0 +1 @@ +print_player_names diff --git a/examples/ruby/ecs.rs b/examples/ruby/ecs.rs new file mode 100644 index 0000000..a48c4c6 --- /dev/null +++ b/examples/ruby/ecs.rs @@ -0,0 +1,33 @@ +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; + +#[derive(Component)] +struct Player; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("print_player_names"), + |players: Query<&Name, With>| { + for player in &players { + println!("player name: {}", player); + } + }, + ); + }) + .add_systems(Startup, startup) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn((Player, Name::new("John"))); + commands.spawn((Player, Name::new("Mary"))); + commands.spawn((Player, Name::new("Alice"))); + + commands.spawn(Script::::new( + assets_server.load("examples/ruby/ecs.rb"), + )); +} -- 2.45.2 From 8c055c5ccbb02df60ded1f75127c0aeccc5a3be8 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 21:19:22 +0200 Subject: [PATCH 091/165] add example --- Cargo.toml | 5 +++++ assets/examples/ruby/entity_variable.rb | 2 ++ examples/ruby/entity_variable.rs | 17 +++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 assets/examples/ruby/entity_variable.rb create mode 100644 examples/ruby/entity_variable.rs diff --git a/Cargo.toml b/Cargo.toml index 58e4d1c..f4bc6c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -175,6 +175,11 @@ name = "ecs_ruby" path = "examples/ruby/ecs.rs" required-features = ["ruby"] +[[example]] +name = "entity_variable_ruby" +path = "examples/ruby/entity_variable.rs" +required-features = ["ruby"] + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } diff --git a/assets/examples/ruby/entity_variable.rb b/assets/examples/ruby/entity_variable.rb new file mode 100644 index 0000000..d54a372 --- /dev/null +++ b/assets/examples/ruby/entity_variable.rb @@ -0,0 +1,2 @@ +# Bevy::Entity.current can be used to access the entity that is currently being processed +puts("Current entity index: #{Bevy::Entity.current.index}") diff --git a/examples/ruby/entity_variable.rs b/examples/ruby/entity_variable.rs new file mode 100644 index 0000000..e9e25ee --- /dev/null +++ b/examples/ruby/entity_variable.rs @@ -0,0 +1,17 @@ +use bevy::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; +use bevy_scriptum::{prelude::*, BuildScriptingRuntime}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|_| {}) + .add_systems(Startup, startup) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/ruby/entity_variable.rb"), + )); +} -- 2.45.2 From 222aa10f2b711b0128d3d7a320b9d336d71aefee Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 21:29:36 +0200 Subject: [PATCH 092/165] add example --- Cargo.toml | 5 +++ assets/examples/ruby/function_params.rb | 5 +++ examples/ruby/function_params.rs | 60 +++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 assets/examples/ruby/function_params.rb create mode 100644 examples/ruby/function_params.rs diff --git a/Cargo.toml b/Cargo.toml index f4bc6c2..dbea7df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,6 +180,11 @@ name = "entity_variable_ruby" path = "examples/ruby/entity_variable.rs" required-features = ["ruby"] +[[example]] +name = "function_params_ruby" +path = "examples/ruby/function_params.rs" +required-features = ["ruby"] + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } diff --git a/assets/examples/ruby/function_params.rb b/assets/examples/ruby/function_params.rb new file mode 100644 index 0000000..89433cd --- /dev/null +++ b/assets/examples/ruby/function_params.rb @@ -0,0 +1,5 @@ +fun_with_string_param("hello") +fun_with_i64_param(5) +fun_with_multiple_params(5, "hello") +# fun_with_i64_and_array_param(5, [1, 2, "third element"]) +# TODO: add test for heteregenous array diff --git a/examples/ruby/function_params.rs b/examples/ruby/function_params.rs new file mode 100644 index 0000000..0999a5a --- /dev/null +++ b/examples/ruby/function_params.rs @@ -0,0 +1,60 @@ +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::magnus; +use bevy_scriptum::runtimes::ruby::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime + .add_function(String::from("fun_without_params"), || { + println!("called without params"); + }) + .add_function( + String::from("fun_with_string_param"), + |In((x,)): In<(String,)>| { + println!("called with string: '{}'", x); + }, + ) + .add_function( + String::from("fun_with_i64_param"), + |In((x,)): In<(i64,)>| { + println!("called with i64: {}", x); + }, + ) + .add_function( + String::from("fun_with_multiple_params"), + |In((x, y)): In<(i64, String)>| { + println!("called with i64: {} and string: '{}'", x, y); + }, + ); + // .add_function( + // String::from("fun_with_i64_and_array_param"), + // |In((x, y)): In<(i64, magnus::value::RArray)>, runtime: Res| { + // runtime.with_engine(|engine| { + // println!( + // "called with i64: {} and dynamically typed array: [{:?}]", + // x, + // engine + // .registry_value::(&y) + // .unwrap() + // .pairs::() + // .map(|pair| pair.unwrap()) + // .map(|(_, v)| format!("{:?}", v)) + // .collect::>() + // .join(",") + // ); + // }); + // }, + // ); + }) + .add_systems(Startup, startup) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/ruby/function_params.rb"), + )); +} -- 2.45.2 From 1467fa3bba569f98ab26eef592002df7a2916d82 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 23 May 2025 21:30:44 +0200 Subject: [PATCH 093/165] add todo --- assets/examples/ruby/function_params.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/examples/ruby/function_params.rb b/assets/examples/ruby/function_params.rb index 89433cd..7e87c8f 100644 --- a/assets/examples/ruby/function_params.rb +++ b/assets/examples/ruby/function_params.rb @@ -3,3 +3,4 @@ fun_with_i64_param(5) fun_with_multiple_params(5, "hello") # fun_with_i64_and_array_param(5, [1, 2, "third element"]) # TODO: add test for heteregenous array +# TODO: for every runtime add example for wrapping with BevyEntity and BevyVec -- 2.45.2 From 21828eea8da1b53383db98d9477ddf96c900afb3 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 12:12:43 +0200 Subject: [PATCH 094/165] add example --- Cargo.toml | 6 +++ assets/examples/ruby/function_return_value.rb | 3 ++ examples/ruby/function_return_value.rs | 42 +++++++++++++++++++ src/runtimes/ruby.rs | 6 +-- 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 assets/examples/ruby/function_return_value.rb create mode 100644 examples/ruby/function_return_value.rs diff --git a/Cargo.toml b/Cargo.toml index dbea7df..18a91a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,6 +185,12 @@ name = "function_params_ruby" path = "examples/ruby/function_params.rs" required-features = ["ruby"] +[[example]] +name = "function_return_value_ruby" +path = "examples/ruby/function_return_value.rs" +required-features = ["ruby"] + + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } diff --git a/assets/examples/ruby/function_return_value.rb b/assets/examples/ruby/function_return_value.rb new file mode 100644 index 0000000..522e7f3 --- /dev/null +++ b/assets/examples/ruby/function_return_value.rb @@ -0,0 +1,3 @@ +def get_value + 42 +end diff --git a/examples/ruby/function_return_value.rs b/examples/ruby/function_return_value.rs new file mode 100644 index 0000000..2dbfc46 --- /dev/null +++ b/examples/ruby/function_return_value.rs @@ -0,0 +1,42 @@ +use bevy::{app::AppExit, prelude::*}; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; +use magnus::value::InnerValue; +use magnus::TryConvert; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, startup) + .add_systems(Update, call_lua_on_update_from_rust) + .add_scripting::(|runtime| { + runtime.add_function(String::from("quit"), |mut exit: EventWriter| { + exit.write(AppExit::Success); + }); + }) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/ruby/function_return_value.rb"), + )); +} + +fn call_lua_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut RubyScriptData)>, + scripting_runtime: ResMut, + mut exit: EventWriter, +) { + for (entity, mut script_data) in &mut scripted_entities { + let val = scripting_runtime + .call_fn("get_value", &mut script_data, entity, ()) + .unwrap() + .0; + scripting_runtime.with_engine_thread(move |ruby| { + let val: i32 = TryConvert::try_convert(val.get_inner_with(&ruby)).unwrap(); + println!("script returned: {}", val); + }); + exit.write(AppExit::Success); + } +} diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 0a9f18f..9abab09 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -275,7 +275,7 @@ impl Drop for RubyRuntime { } #[derive(Clone)] -pub struct RubyValue(magnus::value::Opaque); +pub struct RubyValue(pub magnus::value::Opaque); impl RubyValue { fn nil(ruby: &Ruby) -> Self { @@ -339,11 +339,11 @@ impl Runtime for RubyRuntime { } fn with_engine_mut(&mut self, _f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { - unimplemented!(); + unimplemented!("Ruby requires single threaded execution, use `with_engine_thread`"); } fn with_engine(&self, _f: impl FnOnce(&Self::RawEngine) -> T) -> T { - unimplemented!(); + unimplemented!("Ruby requires single threaded execution, use `with_engine_thread`"); } fn eval( -- 2.45.2 From 9bec12deecc30b83833cc282c550c4af858965bb Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 12:18:52 +0200 Subject: [PATCH 095/165] add example --- Cargo.toml | 5 +++++ assets/examples/ruby/hello_world.rb | 1 + examples/ruby/hello_world.rs | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 assets/examples/ruby/hello_world.rb create mode 100644 examples/ruby/hello_world.rs diff --git a/Cargo.toml b/Cargo.toml index 18a91a9..bdb8424 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -190,6 +190,11 @@ name = "function_return_value_ruby" path = "examples/ruby/function_return_value.rs" required-features = ["ruby"] +[[example]] +name = "hello_world_ruby" +path = "examples/ruby/hello_world.rs" +required-features = ["ruby"] + [dev-dependencies] tracing-subscriber = "0.3.18" diff --git a/assets/examples/ruby/hello_world.rb b/assets/examples/ruby/hello_world.rb new file mode 100644 index 0000000..7f4cbfd --- /dev/null +++ b/assets/examples/ruby/hello_world.rb @@ -0,0 +1 @@ +hello_bevy() diff --git a/examples/ruby/hello_world.rs b/examples/ruby/hello_world.rs new file mode 100644 index 0000000..218067c --- /dev/null +++ b/examples/ruby/hello_world.rs @@ -0,0 +1,21 @@ +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("hello_bevy"), || { + println!("hello bevy, called from script"); + }); + }) + .add_systems(Startup, startup) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/ruby/hello_world.rb"), + )); +} -- 2.45.2 From ca82a8f3a3812c3e3c6dcdb9f0cfe0b55f5a1185 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 12:27:20 +0200 Subject: [PATCH 096/165] add example --- Cargo.toml | 4 ++ .../ruby/multiple_plugins_plugin_a.rb | 1 + .../ruby/multiple_plugins_plugin_b.rb | 1 + examples/ruby/multiple_plugins.rs | 67 +++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 assets/examples/ruby/multiple_plugins_plugin_a.rb create mode 100644 assets/examples/ruby/multiple_plugins_plugin_b.rb create mode 100644 examples/ruby/multiple_plugins.rs diff --git a/Cargo.toml b/Cargo.toml index bdb8424..4651841 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -195,6 +195,10 @@ name = "hello_world_ruby" path = "examples/ruby/hello_world.rs" required-features = ["ruby"] +[[example]] +name = "multiple_plugins_ruby" +path = "examples/ruby/multiple_plugins.rs" +required-features = ["ruby"] [dev-dependencies] tracing-subscriber = "0.3.18" diff --git a/assets/examples/ruby/multiple_plugins_plugin_a.rb b/assets/examples/ruby/multiple_plugins_plugin_a.rb new file mode 100644 index 0000000..1ae9ec9 --- /dev/null +++ b/assets/examples/ruby/multiple_plugins_plugin_a.rb @@ -0,0 +1 @@ +hello_from_plugin_a diff --git a/assets/examples/ruby/multiple_plugins_plugin_b.rb b/assets/examples/ruby/multiple_plugins_plugin_b.rb new file mode 100644 index 0000000..ded6dc2 --- /dev/null +++ b/assets/examples/ruby/multiple_plugins_plugin_b.rb @@ -0,0 +1 @@ +hello_from_plugin_b_with_parameters("hello", 42) diff --git a/examples/ruby/multiple_plugins.rs b/examples/ruby/multiple_plugins.rs new file mode 100644 index 0000000..886fee1 --- /dev/null +++ b/examples/ruby/multiple_plugins.rs @@ -0,0 +1,67 @@ +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; + +// Plugin A +struct PluginA; +impl Plugin for PluginA { + fn build(&self, app: &mut App) { + app.add_scripting_api::(|runtime| { + runtime.add_function(String::from("hello_from_plugin_a"), || { + info!("Hello from Plugin A"); + }); + }) + .add_systems(Startup, plugin_a_startup); + } +} + +fn plugin_a_startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/lua/multiple_plugins_plugin_a.lua"), + )); +} + +// Plugin B +struct PluginB; +impl Plugin for PluginB { + fn build(&self, app: &mut App) { + app.add_scripting_api::(|runtime| { + runtime.add_function( + String::from("hello_from_plugin_b_with_parameters"), + hello_from_b, + ); + }) + .add_systems(Startup, plugin_b_startup); + } +} + +fn plugin_b_startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/lua/multiple_plugins_plugin_b.lua"), + )); +} + +fn hello_from_b(In((text, x)): In<(String, i32)>) { + info!("{} from Plugin B: {}", text, x); +} + +// Main +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("hello_bevy"), || { + info!("hello bevy, called from script"); + }); + }) + .add_systems(Startup, main_startup) + .add_plugins(PluginA) + .add_plugins(PluginB) + .run(); +} + +fn main_startup(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("examples/ruby/hello_world.rb"), + )); +} -- 2.45.2 From 7f7943fe842cd0ef0eacb5ecefd65623156b87e2 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 12:33:13 +0200 Subject: [PATCH 097/165] add example --- Cargo.toml | 5 +++++ assets/examples/ruby/promises.rb | 3 +++ examples/ruby/multiple_plugins.rs | 2 +- examples/ruby/promises.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 assets/examples/ruby/promises.rb create mode 100644 examples/ruby/promises.rs diff --git a/Cargo.toml b/Cargo.toml index 4651841..e750347 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -200,6 +200,11 @@ name = "multiple_plugins_ruby" path = "examples/ruby/multiple_plugins.rs" required-features = ["ruby"] +[[example]] +name = "promises_ruby" +path = "examples/ruby/promises.rs" +required-features = ["ruby"] + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } diff --git a/assets/examples/ruby/promises.rb b/assets/examples/ruby/promises.rb new file mode 100644 index 0000000..dd0ecbe --- /dev/null +++ b/assets/examples/ruby/promises.rb @@ -0,0 +1,3 @@ +get_player_name.and_then do |name| + puts name +end diff --git a/examples/ruby/multiple_plugins.rs b/examples/ruby/multiple_plugins.rs index 886fee1..59438fd 100644 --- a/examples/ruby/multiple_plugins.rs +++ b/examples/ruby/multiple_plugins.rs @@ -17,7 +17,7 @@ impl Plugin for PluginA { fn plugin_a_startup(mut commands: Commands, assets_server: Res) { commands.spawn(Script::::new( - assets_server.load("examples/lua/multiple_plugins_plugin_a.lua"), + assets_server.load("examples/ruby/multiple_plugins_plugin_a.rb"), )); } diff --git a/examples/ruby/promises.rs b/examples/ruby/promises.rs new file mode 100644 index 0000000..1b3ed62 --- /dev/null +++ b/examples/ruby/promises.rs @@ -0,0 +1,31 @@ +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; + +#[derive(Component)] +struct Player; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|builder| { + builder.add_function( + String::from("get_player_name"), + |player_names: Query<&Name, With>| { + player_names + .single() + .expect("Missing player_names") + .to_string() + }, + ); + }) + .add_systems(Startup, startup) + .run(); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn((Player, Name::new("John"))); + commands.spawn(Script::::new( + assets_server.load("examples/ruby/promises.rb"), + )); +} -- 2.45.2 From 20bf7121ffbf15cbfa815337f0de440efb44c2e6 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 12:35:36 +0200 Subject: [PATCH 098/165] add example --- Cargo.toml | 5 ++++ assets/examples/ruby/side_effects.rb | 1 + examples/ruby/side_effects.rs | 41 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 assets/examples/ruby/side_effects.rb create mode 100644 examples/ruby/side_effects.rs diff --git a/Cargo.toml b/Cargo.toml index e750347..6305975 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -205,6 +205,11 @@ name = "promises_ruby" path = "examples/ruby/promises.rs" required-features = ["ruby"] +[[example]] +name = "side_effects_ruby" +path = "examples/ruby/side_effects.rs" +required-features = ["ruby"] + [dev-dependencies] tracing-subscriber = "0.3.18" mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] } diff --git a/assets/examples/ruby/side_effects.rb b/assets/examples/ruby/side_effects.rb new file mode 100644 index 0000000..63642de --- /dev/null +++ b/assets/examples/ruby/side_effects.rb @@ -0,0 +1 @@ +spawn_entity() diff --git a/examples/ruby/side_effects.rs b/examples/ruby/side_effects.rs new file mode 100644 index 0000000..031bdea --- /dev/null +++ b/examples/ruby/side_effects.rs @@ -0,0 +1,41 @@ +use bevy::{app::AppExit, prelude::*}; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; + +fn main() { + App::new() + // This is just needed for headless console app, not needed for a regular bevy game + // that uses a winit window + .set_runner(move |mut app: App| loop { + app.update(); + if let Some(exit) = app.should_exit() { + return exit; + } + }) + .add_plugins(DefaultPlugins) + .add_systems(Startup, startup) + .add_systems(Update, print_entity_names_and_quit) + .add_scripting::(|runtime| { + runtime.add_function(String::from("spawn_entity"), spawn_entity); + }) + .run(); +} + +fn spawn_entity(mut commands: Commands) { + commands.spawn(Name::new("SpawnedEntity")); +} + +fn startup(mut commands: Commands, assets_server: Res) { + commands.spawn((Script::::new( + assets_server.load("examples/ruby/side_effects.rb"), + ),)); +} + +fn print_entity_names_and_quit(query: Query<&Name>, mut exit: EventWriter) { + if !query.is_empty() { + for e in &query { + println!("{}", e); + } + exit.write(AppExit::Success); + } +} -- 2.45.2 From e5eb7cc4f56175067eb7b7c54247eb96e8218a0e Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 22:47:12 +0200 Subject: [PATCH 099/165] add example --- assets/examples/ruby/function_params.rb | 2 +- examples/ruby/function_params.rs | 34 ++++++++++--------------- src/runtimes/ruby.rs | 11 ++++++++ 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/assets/examples/ruby/function_params.rb b/assets/examples/ruby/function_params.rb index 7e87c8f..ea73e66 100644 --- a/assets/examples/ruby/function_params.rb +++ b/assets/examples/ruby/function_params.rb @@ -1,6 +1,6 @@ fun_with_string_param("hello") fun_with_i64_param(5) fun_with_multiple_params(5, "hello") -# fun_with_i64_and_array_param(5, [1, 2, "third element"]) +fun_with_i64_and_array_param(5, [1, 2, "third element"]) # TODO: add test for heteregenous array # TODO: for every runtime add example for wrapping with BevyEntity and BevyVec diff --git a/examples/ruby/function_params.rs b/examples/ruby/function_params.rs index 0999a5a..fab2d46 100644 --- a/examples/ruby/function_params.rs +++ b/examples/ruby/function_params.rs @@ -1,7 +1,6 @@ use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::ruby::magnus; -use bevy_scriptum::runtimes::ruby::prelude::*; +use bevy_scriptum::runtimes::ruby::{prelude::*, RArray}; fn main() { App::new() @@ -28,26 +27,19 @@ fn main() { |In((x, y)): In<(i64, String)>| { println!("called with i64: {} and string: '{}'", x, y); }, + ) + .add_function( + String::from("fun_with_i64_and_array_param"), + |In((x, y)): In<(i64, RArray)>, runtime: Res| { + runtime.with_engine_thread(move |ruby| { + println!( + "called with i64: {} and dynamically typed array: [{:?}]", + x, + ruby.get_inner(y.0) + ); + }); + }, ); - // .add_function( - // String::from("fun_with_i64_and_array_param"), - // |In((x, y)): In<(i64, magnus::value::RArray)>, runtime: Res| { - // runtime.with_engine(|engine| { - // println!( - // "called with i64: {} and dynamically typed array: [{:?}]", - // x, - // engine - // .registry_value::(&y) - // .unwrap() - // .pairs::() - // .map(|pair| pair.unwrap()) - // .map(|(_, v)| format!("{:?}", v)) - // .collect::>() - // .join(",") - // ); - // }); - // }, - // ); }) .add_systems(Startup, startup) .run(); diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 9abab09..7c4e7a8 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -5,6 +5,7 @@ use std::{ thread::{self, JoinHandle}, }; +use ::magnus::value::Opaque; use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, @@ -513,6 +514,16 @@ impl FuncArgs<'_, RubyValue, RubyRuntime> for Vec { } } +pub struct RArray(pub Opaque); + +impl FromRuntimeValueWithEngine<'_, RubyRuntime> for RArray { + fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self { + let inner = engine.get_inner(value.0); + let array = magnus::RArray::try_convert(inner).unwrap(); + RArray(Opaque::from(array)) + } +} + macro_rules! impl_tuple { ($($idx:tt $t:tt),+) => { impl<'a, $($t: IntoValue,)+> FuncArgs<'a, RubyValue, RubyRuntime> -- 2.45.2 From fc76c927ca18ddf0869f2dbeb1b62423d56c074d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 22:49:03 +0200 Subject: [PATCH 100/165] fix example --- examples/ruby/function_params.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ruby/function_params.rs b/examples/ruby/function_params.rs index fab2d46..ed3699b 100644 --- a/examples/ruby/function_params.rs +++ b/examples/ruby/function_params.rs @@ -33,7 +33,7 @@ fn main() { |In((x, y)): In<(i64, RArray)>, runtime: Res| { runtime.with_engine_thread(move |ruby| { println!( - "called with i64: {} and dynamically typed array: [{:?}]", + "called with i64: {} and dynamically typed array: {:?}", x, ruby.get_inner(y.0) ); -- 2.45.2 From 9e3dce14a26a02ef0131849d054378c6e86b591b Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sat, 24 May 2025 22:51:07 +0200 Subject: [PATCH 101/165] bump rust edition --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6305975..6b9f8e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "bevy_scriptum" authors = ["Jaroslaw Konik "] version = "0.8.1" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" categories = ["game-development"] @@ -46,7 +46,7 @@ path = "examples/rhai/current_entity.rs" required-features = ["rhai"] [[example]] -name = "custom_type_rhai" +name = "custom5type_rhai" path = "examples/rhai/custom_type.rs" required-features = ["rhai"] -- 2.45.2 From 04d2b6b93bae30a3ed69955460bb2efc1741176a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 07:56:35 +0200 Subject: [PATCH 102/165] rename --- examples/ruby/custom_type.rs | 4 ++-- examples/ruby/function_params.rs | 4 ++-- examples/ruby/function_return_value.rs | 4 ++-- src/callback.rs | 4 ++-- src/lib.rs | 16 +++++++-------- src/runtimes/lua.rs | 22 ++++++++++---------- src/runtimes/rhai.rs | 18 ++++++++--------- src/runtimes/ruby.rs | 28 ++++++++++++-------------- tests/tests.rs | 12 +++++------ 9 files changed, 55 insertions(+), 57 deletions(-) diff --git a/examples/ruby/custom_type.rs b/examples/ruby/custom_type.rs index e775a7f..2877b8d 100644 --- a/examples/ruby/custom_type.rs +++ b/examples/ruby/custom_type.rs @@ -1,10 +1,10 @@ use bevy::prelude::*; +use bevy_scriptum::ScriptingError; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::ruby::magnus; use bevy_scriptum::runtimes::ruby::magnus::Module as _; use bevy_scriptum::runtimes::ruby::magnus::Object as _; use bevy_scriptum::runtimes::ruby::prelude::*; -use bevy_scriptum::ScriptingError; fn main() { App::new() @@ -39,7 +39,7 @@ fn startup( assets_server: Res, ) { scripting_runtime - .with_engine_thread(|ruby| { + .with_engine_send(|ruby| { let my_type = ruby.define_class("MyType", ruby.class_object())?; my_type.define_singleton_method("new", magnus::function!(MyType::new, 0))?; my_type.define_method("my_method", magnus::method!(MyType::my_method, 0))?; diff --git a/examples/ruby/function_params.rs b/examples/ruby/function_params.rs index ed3699b..65fb3b6 100644 --- a/examples/ruby/function_params.rs +++ b/examples/ruby/function_params.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::ruby::{prelude::*, RArray}; +use bevy_scriptum::runtimes::ruby::{RArray, prelude::*}; fn main() { App::new() @@ -31,7 +31,7 @@ fn main() { .add_function( String::from("fun_with_i64_and_array_param"), |In((x, y)): In<(i64, RArray)>, runtime: Res| { - runtime.with_engine_thread(move |ruby| { + runtime.with_engine_send(move |ruby| { println!( "called with i64: {} and dynamically typed array: {:?}", x, diff --git a/examples/ruby/function_return_value.rs b/examples/ruby/function_return_value.rs index 2dbfc46..02875e4 100644 --- a/examples/ruby/function_return_value.rs +++ b/examples/ruby/function_return_value.rs @@ -1,8 +1,8 @@ use bevy::{app::AppExit, prelude::*}; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::ruby::prelude::*; -use magnus::value::InnerValue; use magnus::TryConvert; +use magnus::value::InnerValue; fn main() { App::new() @@ -33,7 +33,7 @@ fn call_lua_on_update_from_rust( .call_fn("get_value", &mut script_data, entity, ()) .unwrap() .0; - scripting_runtime.with_engine_thread(move |ruby| { + scripting_runtime.with_engine(|ruby| { let val: i32 = TryConvert::try_convert(val.get_inner_with(&ruby)).unwrap(); println!("script returned: {}", val); }); diff --git a/src/callback.rs b/src/callback.rs index cc76bcb..f6a9a3c 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -2,7 +2,7 @@ use bevy::prelude::*; use core::any::TypeId; use std::sync::{Arc, Mutex}; -use crate::{promise::Promise, Runtime}; +use crate::{Runtime, promise::Promise}; /// A system that can be used to call a script function. pub struct CallbackSystem { @@ -79,7 +79,7 @@ where let mut runtime = world.get_resource_mut::().expect("No runtime resource"); if R::needs_own_thread() { - runtime.with_engine_thread_mut(move |engine| { + runtime.with_engine_send_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) } else { diff --git a/src/lib.rs b/src/lib.rs index 844940a..569b09b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -305,7 +305,7 @@ pub trait Runtime: Resource + Default { /// Provides mutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. - fn with_engine_thread_mut( + fn with_engine_send_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T; @@ -313,7 +313,7 @@ pub trait Runtime: Resource + Default { /// Provides immutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. - fn with_engine_thread( + fn with_engine_send( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T; @@ -342,12 +342,12 @@ pub trait Runtime: Resource + Default { name: String, arg_types: Vec, f: impl Fn( - Self::CallContext, - Vec, - ) -> Result, ScriptingError> - + Send - + Sync - + 'static, + Self::CallContext, + Vec, + ) -> Result, ScriptingError> + + Send + + Sync + + 'static, ) -> Result<(), ScriptingError>; /// Calls a function by name defined within the runtime in the context of the diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 0926649..32d34b0 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -13,10 +13,10 @@ use serde::Deserialize; use std::sync::{Arc, Mutex}; use crate::{ + ENTITY_VAR_NAME, FuncArgs, Runtime, ScriptingError, assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, promise::Promise, - FuncArgs, Runtime, ScriptingError, ENTITY_VAR_NAME, }; type LuaEngine = Arc>; @@ -197,14 +197,14 @@ impl Runtime for LuaRuntime { name: String, _arg_types: Vec, f: impl Fn( - Self::CallContext, - Vec, - ) -> Result< - crate::promise::Promise, - crate::ScriptingError, - > + Send - + Sync - + 'static, + Self::CallContext, + Vec, + ) -> Result< + crate::promise::Promise, + crate::ScriptingError, + > + Send + + Sync + + 'static, ) -> Result<(), crate::ScriptingError> { self.with_engine(|engine| { let func = engine @@ -283,14 +283,14 @@ impl Runtime for LuaRuntime { f(&engine) } - fn with_engine_thread_mut( + fn with_engine_send_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { self.with_engine_mut(f) } - fn with_engine_thread( + fn with_engine_send( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index ca2b950..492ef6e 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -11,10 +11,10 @@ use rhai::{CallFnOptions, Dynamic, Engine, FnPtr, Scope, Variant}; use serde::Deserialize; use crate::{ + ENTITY_VAR_NAME, FuncArgs, Runtime, ScriptingError, assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, promise::Promise, - FuncArgs, Runtime, ScriptingError, ENTITY_VAR_NAME, }; #[derive(Asset, Debug, Deserialize, TypePath)] @@ -118,12 +118,12 @@ impl Runtime for RhaiRuntime { name: String, arg_types: Vec, f: impl Fn( - Self::CallContext, - Vec, - ) -> Result, ScriptingError> - + Send - + Sync - + 'static, + Self::CallContext, + Vec, + ) -> Result, ScriptingError> + + Send + + Sync + + 'static, ) -> Result<(), ScriptingError> { self.engine .register_raw_fn(name, arg_types, move |context, args| { @@ -192,14 +192,14 @@ impl Runtime for RhaiRuntime { f(&self.engine) } - fn with_engine_thread_mut( + fn with_engine_send_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { self.with_engine_mut(f) } - fn with_engine_thread( + fn with_engine_send( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 7c4e7a8..31a0358 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -14,20 +14,20 @@ use bevy::{ tasks::futures_lite::io, }; use magnus::{ + DataType, DataTypeFunctions, IntoValue, Object, RClass, RModule, Ruby, TryConvert, TypedData, block::Proc, data_type_builder, function, value::{Lazy, ReprValue}, - DataType, DataTypeFunctions, IntoValue, Object, RClass, RModule, Ruby, TryConvert, TypedData, }; use magnus::{method, prelude::*}; -use rb_sys::{ruby_init_stack, VALUE}; +use rb_sys::{VALUE, ruby_init_stack}; use serde::Deserialize; use crate::{ + FuncArgs, Runtime, ScriptingError, assets::GetExtensions, callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine}, promise::Promise, - FuncArgs, Runtime, ScriptingError, }; #[derive(Resource)] @@ -323,16 +323,14 @@ impl Runtime for RubyRuntime { type RawEngine = magnus::Ruby; - // TODO: it should be somehow possible to remove 'static here - fn with_engine_thread_mut( + fn with_engine_send_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, ) -> T { self.execute_in_thread_mut(f) } - // TODO: it should be somehow possible to remove 'static here - fn with_engine_thread( + fn with_engine_send( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, ) -> T { @@ -374,14 +372,14 @@ impl Runtime for RubyRuntime { name: String, _arg_types: Vec, f: impl Fn( - Self::CallContext, - Vec, - ) -> Result< - crate::promise::Promise, - crate::ScriptingError, - > + Send - + Sync - + 'static, + Self::CallContext, + Vec, + ) -> Result< + crate::promise::Promise, + crate::ScriptingError, + > + Send + + Sync + + 'static, ) -> Result<(), crate::ScriptingError> { type CallbackClosure = Box< dyn Fn( diff --git a/tests/tests.rs b/tests/tests.rs index e39de94..904c469 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -6,7 +6,7 @@ use bevy::ecs::system::RunSystemOnce as _; #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] use bevy::prelude::*; #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] -use bevy_scriptum::{prelude::*, FuncArgs, Runtime}; +use bevy_scriptum::{FuncArgs, Runtime, prelude::*}; #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] static TRACING_SUBSCRIBER: OnceLock<()> = OnceLock::new(); @@ -621,7 +621,7 @@ mod lua_tests { #[cfg(feature = "ruby")] mod ruby_tests { use bevy::prelude::*; - use bevy_scriptum::runtimes::ruby::{prelude::*, RubyScriptData}; + use bevy_scriptum::runtimes::ruby::{RubyScriptData, prelude::*}; use magnus::value::ReprValue; impl AssertStateKeyValue for RubyRuntime { @@ -630,7 +630,7 @@ mod ruby_tests { fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { let runtime = world.get_resource::().unwrap(); let key = key.to_string(); - runtime.with_engine_thread(move |engine| { + runtime.with_engine_send(move |engine| { let state: magnus::value::Value = engine.eval("$state").unwrap(); let res: i64 = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value) @@ -640,7 +640,7 @@ mod ruby_tests { fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { let runtime = world.get_resource::().unwrap(); let key = key.to_string(); - runtime.with_engine_thread(move |engine| { + runtime.with_engine_send(move |engine| { let state: magnus::value::Value = engine.eval("$state").unwrap(); let res: i32 = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value) @@ -656,7 +656,7 @@ mod ruby_tests { let runtime = world.get_resource::().unwrap(); let key = key.to_string(); let value = value.to_string(); - runtime.with_engine_thread(move |engine| { + runtime.with_engine_send(move |engine| { let state: magnus::value::Value = engine.eval("$state").unwrap(); let res: String = state.funcall_public("[]", (key,)).unwrap(); assert_eq!(res, value); @@ -670,7 +670,7 @@ mod ruby_tests { app.add_scripting::(|_| {}); let runtime = app.world().get_resource::().unwrap(); - runtime.with_engine_thread(|engine| { + runtime.with_engine_send(|engine| { let symbol_string: String = engine.eval(":test_symbol.inspect").unwrap(); assert_eq!(symbol_string, ":test_symbol") }); -- 2.45.2 From 06a7f518052c190fe78feda5e03cc0bb3b285d63 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 07:59:13 +0200 Subject: [PATCH 103/165] fix --- src/callback.rs | 4 ++-- src/runtimes/ruby.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index f6a9a3c..ca93abe 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -111,7 +111,7 @@ macro_rules! impl_tuple { let system_fn = move |args: In>, world: &mut World| { let mut runtime = world.get_resource_mut::().expect("No runtime resource"); let args = if RN::needs_own_thread() { - runtime.with_engine_thread_mut(move |engine| { + runtime.with_engine_send_mut(move |engine| { ( $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ ) @@ -128,7 +128,7 @@ macro_rules! impl_tuple { inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); if RN::needs_own_thread() { - runtime.with_engine_thread_mut(move |engine| { + runtime.with_engine_send_mut(move |engine| { Out::into_runtime_value_with_engine(result, engine) }) } else { diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 31a0358..eaba3bf 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -338,11 +338,15 @@ impl Runtime for RubyRuntime { } fn with_engine_mut(&mut self, _f: impl FnOnce(&mut Self::RawEngine) -> T) -> T { - unimplemented!("Ruby requires single threaded execution, use `with_engine_thread`"); + unimplemented!( + "Ruby runtime requires sending execution to another thread, use `with_engine_mut_send`" + ); } fn with_engine(&self, _f: impl FnOnce(&Self::RawEngine) -> T) -> T { - unimplemented!("Ruby requires single threaded execution, use `with_engine_thread`"); + unimplemented!( + "Ruby runtime requires sending execution to another thread, use `with_engine_send`" + ); } fn eval( -- 2.45.2 From b483dcf5033d877c60bfe66660a00c3df14c3f3e Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 08:06:38 +0200 Subject: [PATCH 104/165] add documentation --- src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 569b09b..1f49a07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -305,6 +305,10 @@ pub trait Runtime: Resource + Default { /// Provides mutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. + /// Using this function make the closure be executed on another thread for + /// some runtimes. If you need to operate on non-`'static` borrows and/or + /// `!Send` data, you can use `with_engine_mut` - it may not be implemented + /// for some of the runtimes though. fn with_engine_send_mut( &mut self, f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static, @@ -313,6 +317,10 @@ pub trait Runtime: Resource + Default { /// Provides immutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. + /// Using this function make the closure be executed on another thread for + /// some runtimes. If you need to operate on non-`'static` borrows and/or + /// `!Send` data, you can use `with_engine` - it may not be implemented + /// for some of the runtimes though. fn with_engine_send( &self, f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static, @@ -321,11 +329,15 @@ pub trait Runtime: Resource + Default { /// Provides mutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. + /// May not be implemented for runtimes which require the closure to pass + /// thread boundary. fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T; /// Provides immutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. + /// May not be implemented for runtimes which require the closure to pass + /// thread boundary. fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T; fn eval( -- 2.45.2 From 6f265c3ec59f5e3c8f2c2325f9028f50cd6dd37a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 08:07:30 +0200 Subject: [PATCH 105/165] add docs --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f49a07..c2375f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -330,14 +330,14 @@ pub trait Runtime: Resource + Default { /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. /// May not be implemented for runtimes which require the closure to pass - /// thread boundary. + /// thread boundary - use `with_engine_send_mut` then. fn with_engine_mut(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T; /// Provides immutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. /// May not be implemented for runtimes which require the closure to pass - /// thread boundary. + /// thread boundary - use `with_engine_send` then. fn with_engine(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T; fn eval( -- 2.45.2 From a888888c9df0bb995fa9fd3379742a24922f847c Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 12:11:12 +0200 Subject: [PATCH 106/165] cleanup --- assets/examples/ruby/function_params.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/examples/ruby/function_params.rb b/assets/examples/ruby/function_params.rb index ea73e66..10cf48d 100644 --- a/assets/examples/ruby/function_params.rb +++ b/assets/examples/ruby/function_params.rb @@ -2,5 +2,3 @@ fun_with_string_param("hello") fun_with_i64_param(5) fun_with_multiple_params(5, "hello") fun_with_i64_and_array_param(5, [1, 2, "third element"]) -# TODO: add test for heteregenous array -# TODO: for every runtime add example for wrapping with BevyEntity and BevyVec -- 2.45.2 From 822cf12d59472e70416c6296a02d596ff0e9d356 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 12:13:07 +0200 Subject: [PATCH 107/165] cleanup --- src/runtimes/ruby.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index eaba3bf..df918c5 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -81,7 +81,6 @@ impl RubyThread { let mut variable_in_this_stack_frame: VALUE = 0; ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); rb_sys::ruby_init(); - rb_sys::ruby_init_loadpath(); rb_sys::ruby_options(argv.len() as i32, argv.as_mut_ptr()); }; while let Ok(f) = receiver.recv() { -- 2.45.2 From 0da69454c7fd3d5dcfa0fd3142cf3a3ddfbc8712 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 13:38:43 +0200 Subject: [PATCH 108/165] cleanup --- src/runtimes/lua.rs | 1 - src/runtimes/rhai.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 32d34b0..42fb9bf 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -1,7 +1,6 @@ use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, - log, math::Vec3, reflect::TypePath, }; diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 492ef6e..43c1fa4 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -3,7 +3,6 @@ use std::fmt::Debug; use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, - log, math::Vec3, reflect::TypePath, }; -- 2.45.2 From 8bf37d0d1ab064dbb692c2cb05f9b7c708cffce1 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 13:48:20 +0200 Subject: [PATCH 109/165] remove unwraps --- src/runtimes/ruby.rs | 48 ++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index df918c5..351b04c 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -67,20 +67,26 @@ static RUBY_THREAD: LazyLock>, Condvar)>> = LazyLock::new(|| Arc::new((Mutex::new(Some(RubyThread::spawn())), Condvar::new()))); impl RubyThread { + fn build_ruby_process_argv() -> anyhow::Result> { + Ok(vec![ + CString::new("ruby")?.into_raw(), + CString::new("-e")?.into_raw(), + CString::new("")?.into_raw(), + ]) + } + fn spawn() -> Self { let (sender, receiver) = crossbeam_channel::unbounded::>(); let handle = thread::spawn(move || { - let mut argv = vec![ - CString::new("ruby").unwrap().into_raw(), - CString::new("-e").unwrap().into_raw(), - CString::new("").unwrap().into_raw(), - ]; - unsafe { let mut variable_in_this_stack_frame: VALUE = 0; ruby_init_stack(&mut variable_in_this_stack_frame as *mut VALUE as *mut _); + rb_sys::ruby_init(); + + let mut argv = + Self::build_ruby_process_argv().expect("Failed to build ruby process args"); rb_sys::ruby_options(argv.len() as i32, argv.as_mut_ptr()); }; while let Ok(f) = receiver.recv() { @@ -127,7 +133,7 @@ unsafe impl TypedData for Promise<(), RubyValue> { static CLASS: Lazy = Lazy::new(|ruby| { let class = ruby .define_module("Bevy") - .unwrap() + .expect("Failed to define Bevy module") .define_class("Promise", ruby.class_object()) .expect("Failed to define Bevy::Promise class in Ruby"); class.undef_default_alloc_func(); @@ -258,7 +264,7 @@ impl Default for RubyRuntime { vec3.define_method("z", method!(BevyVec3::z, 0))?; Ok::<(), ScriptingError>(()) })) - .unwrap(); + .expect("Failed to define builtin types"); Self { ruby_thread: Some(ruby_thread), } @@ -268,7 +274,9 @@ impl Default for RubyRuntime { impl Drop for RubyRuntime { fn drop(&mut self) { let (lock, cvar) = &*Arc::clone(&RUBY_THREAD); - let mut ruby_thread = lock.lock().unwrap(); + let mut ruby_thread = lock + .lock() + .expect("Failed to lock ruby thread while dropping the runtime"); *ruby_thread = self.ruby_thread.take(); cvar.notify_all(); } @@ -294,7 +302,7 @@ impl RubyRuntime { ) -> T { self.ruby_thread .as_ref() - .unwrap() + .expect("No Ruby thread") .execute(Box::new(move |ruby| f(&ruby))) } @@ -304,7 +312,7 @@ impl RubyRuntime { ) -> T { self.ruby_thread .as_ref() - .unwrap() + .expect("No Ruby thread") .execute(Box::new(move |mut ruby| f(&mut ruby))) } } @@ -358,12 +366,15 @@ impl Runtime for RubyRuntime { let var = ruby .class_object() .const_get::<_, RModule>("Bevy") - .unwrap() + .expect("Failed to get Bevy module") .const_get::<_, RClass>("Entity") - .unwrap(); - var.ivar_set("_current", BevyEntity(entity)).unwrap(); + .expect("Failed to get Entity class"); + + var.ivar_set("_current", BevyEntity(entity)) + .expect("Failed to set current entity handle"); let value = ruby.eval::(&script).unwrap(); - var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); + var.ivar_set("_current", ruby.qnil().as_value()) + .expect("Failed to unset current entity handle"); RubyValue::new(value) })); @@ -394,11 +405,14 @@ impl Runtime for RubyRuntime { >; static RUBY_CALLBACKS: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); - let mut callbacks = RUBY_CALLBACKS.lock().unwrap(); + let mut callbacks = RUBY_CALLBACKS + .lock() + .expect("Failed to lock callbacks static when registering a callback"); callbacks.insert(name.clone(), Box::new(f)); fn callback(args: &[magnus::Value]) -> magnus::Value { - let ruby = magnus::Ruby::get().unwrap(); + let ruby = magnus::Ruby::get() + .expect("Failed to get a handle to Ruby API while processing callback"); let method_name: magnus::value::StaticSymbol = ruby.class_object().funcall("__method__", ()).unwrap(); let method_name = method_name.name().unwrap(); -- 2.45.2 From d00b98edb9bca07d7cefb9725d19cb25647e44d5 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 13:50:25 +0200 Subject: [PATCH 110/165] add todos --- src/runtimes/ruby.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 351b04c..bd24c42 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -363,6 +363,7 @@ impl Runtime for RubyRuntime { ) -> Result { let script = script.0.clone(); self.execute_in_thread(Box::new(move |ruby: &Ruby| { + // TODO: refactor let var = ruby .class_object() .const_get::<_, RModule>("Bevy") @@ -445,6 +446,7 @@ impl Runtime for RubyRuntime { ) -> Result { let name = name.to_string(); self.execute_in_thread(Box::new(move |ruby: &Ruby| { + // TOOD: refactor let var = ruby .class_object() .const_get::<_, RModule>("Bevy") -- 2.45.2 From 469e9aca15dcd2f3bf78dfaceb57761ee4a56751 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Sun, 25 May 2025 22:04:34 +0200 Subject: [PATCH 111/165] clippy --- examples/ruby/function_return_value.rs | 2 +- src/runtimes/ruby.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/ruby/function_return_value.rs b/examples/ruby/function_return_value.rs index 02875e4..088779a 100644 --- a/examples/ruby/function_return_value.rs +++ b/examples/ruby/function_return_value.rs @@ -34,7 +34,7 @@ fn call_lua_on_update_from_rust( .unwrap() .0; scripting_runtime.with_engine(|ruby| { - let val: i32 = TryConvert::try_convert(val.get_inner_with(&ruby)).unwrap(); + let val: i32 = TryConvert::try_convert(val.get_inner_with(ruby)).unwrap(); println!("script returned: {}", val); }); exit.write(AppExit::Success); diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index bd24c42..93cb8a3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -431,7 +431,7 @@ impl Runtime for RubyRuntime { self.execute_in_thread(Box::new(move |ruby: &Ruby| { ruby.define_global_function(&name, function!(callback, -1)); - RubyValue::nil(&ruby) + RubyValue::nil(ruby) })); Ok(()) @@ -456,7 +456,7 @@ impl Runtime for RubyRuntime { var.ivar_set("_current", BevyEntity(entity)).unwrap(); let args: Vec<_> = args - .parse(&ruby) + .parse(ruby) .into_iter() .map(|a| ruby.get_inner(a.0)) .collect(); -- 2.45.2 From db573426da88433235dddae2818f431bf92d75c3 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 112/165] conditional doctests --- README.md | 2 +- .../ruby/eval_that_causes_runtime_error.rb | 2 + src/lib.rs | 15 +++++ tests/tests.rs | 58 +++++++++++++++---- 4 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 assets/tests/ruby/eval_that_causes_runtime_error.rb diff --git a/README.md b/README.md index 6e55837..85f507a 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Add the following to your `Cargo.toml`: ```toml [dependencies] -bevy_scriptum = { version = "0.7", features = ["lua"] } +bevy_scriptum = { version = "0.8", features = ["lua"] } ``` or execute `cargo add bevy_scriptum --features lua` from your project directory. diff --git a/assets/tests/ruby/eval_that_causes_runtime_error.rb b/assets/tests/ruby/eval_that_causes_runtime_error.rb new file mode 100644 index 0000000..a8a033e --- /dev/null +++ b/assets/tests/ruby/eval_that_causes_runtime_error.rb @@ -0,0 +1,2 @@ +mark_called +# puts("foo" + 1) diff --git a/src/lib.rs b/src/lib.rs index c2375f6..3ae0518 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,10 @@ //! ```no_run //! use bevy::prelude::*; //! use bevy_scriptum::prelude::*; +//! # #[cfg(feature = "lua")] //! use bevy_scriptum::runtimes::lua::prelude::*; //! +//! # #[cfg(feature = "lua")] //! App::new() //! .add_plugins(DefaultPlugins) //! .add_scripting::(|runtime| { @@ -42,11 +44,13 @@ //! ```no_run //! use bevy::prelude::*; //! use bevy_scriptum::prelude::*; +//! # #[cfg(feature = "lua")] //! use bevy_scriptum::runtimes::lua::prelude::*; //! //! #[derive(Component)] //! struct Player; //! +//! # #[cfg(feature = "lua")] //! App::new() //! .add_plugins(DefaultPlugins) //! .add_scripting::(|runtime| { @@ -66,8 +70,10 @@ //! ```no_run //! use bevy::prelude::*; //! use bevy_scriptum::prelude::*; +//! # #[cfg(feature = "lua")] //! use bevy_scriptum::runtimes::lua::prelude::*; //! +//! # #[cfg(feature = "lua")] //! App::new() //! .add_plugins(DefaultPlugins) //! .add_scripting::(|runtime| { @@ -89,9 +95,11 @@ //! ```no_run //! use bevy::prelude::*; //! use bevy_scriptum::prelude::*; +//! # #[cfg(feature = "lua")] //! use bevy_scriptum::runtimes::lua::prelude::*; //! //! struct MyPlugin; +//! # #[cfg(feature = "lua")] //! impl Plugin for MyPlugin { //! fn build(&self, app: &mut App) { //! app.add_scripting_api::(|runtime| { @@ -102,6 +110,7 @@ //! } //! } //! +//! # #[cfg(feature = "lua")] //! App::new() //! .add_plugins(DefaultPlugins) //! .add_scripting::(|_| { @@ -128,8 +137,10 @@ //! ```no_run //! use bevy::prelude::*; //! use bevy_scriptum::prelude::*; +//! # #[cfg(feature = "lua")] //! use bevy_scriptum::runtimes::lua::prelude::*; //! +//! # #[cfg(feature = "lua")] //! App::new() //! .add_plugins(DefaultPlugins) //! .add_scripting::(|runtime| { @@ -154,8 +165,10 @@ //! ```no_run //! use bevy::prelude::*; //! use bevy_scriptum::prelude::*; +//! # #[cfg(feature = "lua")] //! use bevy_scriptum::runtimes::lua::prelude::*; //! +//! # #[cfg(feature = "lua")] //! App::new() //! .add_plugins(DefaultPlugins) //! .add_scripting::(|runtime| { @@ -209,8 +222,10 @@ //! ``` //! use bevy::prelude::*; //! use bevy_scriptum::prelude::*; +//! # #[cfg(feature = "lua")] //! use bevy_scriptum::runtimes::lua::prelude::*; //! +//! # #[cfg(feature = "lua")] //! App::new() //! .add_plugins(DefaultPlugins) //! .add_scripting::(|runtime| { diff --git a/tests/tests.rs b/tests/tests.rs index 904c469..fde6066 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -11,6 +11,24 @@ use bevy_scriptum::{FuncArgs, Runtime, prelude::*}; #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] static TRACING_SUBSCRIBER: OnceLock<()> = OnceLock::new(); +#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] +#[derive(Default, Resource)] +struct TimesCalled { + times_called: u8, +} + +macro_rules! assert_n_times_called { + ($app: expr, $count: expr) => { + assert_eq!( + $app.world() + .get_resource::() + .unwrap() + .times_called, + $count + ); + }; +} + #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] fn build_test_app() -> App { let mut app = App::new(); @@ -224,6 +242,33 @@ macro_rules! scripting_tests { ); } + #[test] + fn eval_that_casues_runtime_error_doesnt_panic() { + let mut app = build_test_app(); + + app.add_scripting::<$runtime>(|r| { + r.add_function( + String::from("mark_called"), + |mut times_called: ResMut| { + times_called.times_called += 1; + }, + ); + }) + .init_resource::(); + + run_script::<$runtime, _, _>( + &mut app, + format!( + "tests/{}/eval_that_causes_runtime_error.{}", + $script, $extension + ) + .to_string(), + || {}, + ); + + assert_n_times_called!(app, 1); + } + #[test] fn call_script_function_that_casues_runtime_error() { let mut app = build_test_app(); @@ -347,11 +392,6 @@ macro_rules! scripting_tests { fn rust_function_gets_called_from_script() { let mut app = build_test_app(); - #[derive(Default, Resource)] - struct TimesCalled { - times_called: u8, - } - app.world_mut().init_resource::(); app.add_scripting::<$runtime>(|runtime| { @@ -370,13 +410,7 @@ macro_rules! scripting_tests { call_script_on_update_from_rust::<$runtime>, ); - assert_eq!( - app.world() - .get_resource::() - .unwrap() - .times_called, - 1 - ); + assert_n_times_called!(app, 1); } #[test] -- 2.45.2 From b339283901acf09623f3cb2df918c3fdcc134605 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 113/165] error handling --- ...ll_script_function_that_causes_runtime_error.rb | 2 +- .../tests/ruby/eval_that_causes_runtime_error.rb | 2 +- assets/tests/ruby/promise_runtime_error.rb | 2 +- src/runtimes/ruby.rs | 14 ++++++++------ src/systems.rs | 4 ++-- tests/tests.rs | 2 +- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb b/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb index ae0afac..e6200c0 100644 --- a/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb +++ b/assets/tests/ruby/call_script_function_that_causes_runtime_error.rb @@ -1,3 +1,3 @@ def test_func - print('abc' + 5) + raise end diff --git a/assets/tests/ruby/eval_that_causes_runtime_error.rb b/assets/tests/ruby/eval_that_causes_runtime_error.rb index a8a033e..7b460ab 100644 --- a/assets/tests/ruby/eval_that_causes_runtime_error.rb +++ b/assets/tests/ruby/eval_that_causes_runtime_error.rb @@ -1,2 +1,2 @@ mark_called -# puts("foo" + 1) +raise diff --git a/assets/tests/ruby/promise_runtime_error.rb b/assets/tests/ruby/promise_runtime_error.rb index 52715ce..8c3e31a 100644 --- a/assets/tests/ruby/promise_runtime_error.rb +++ b/assets/tests/ruby/promise_runtime_error.rb @@ -1,5 +1,5 @@ def test_func rust_func.and_then do |x| - print('abc' + 5) + raise end end diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 93cb8a3..0aa1be1 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -6,12 +6,12 @@ use std::{ }; use ::magnus::value::Opaque; +use anyhow::anyhow; use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, math::Vec3, reflect::TypePath, - tasks::futures_lite::io, }; use magnus::{ DataType, DataTypeFunctions, IntoValue, Object, RClass, RModule, Ruby, TryConvert, TypedData, @@ -223,7 +223,7 @@ impl TryConvert for BevyVec3 { impl From for ScriptingError { fn from(value: magnus::Error) -> Self { - ScriptingError::RuntimeError(Box::new(io::Error::other(value.to_string()))) + ScriptingError::RuntimeError(anyhow!(value.to_string()).into()) } } @@ -373,13 +373,15 @@ impl Runtime for RubyRuntime { var.ivar_set("_current", BevyEntity(entity)) .expect("Failed to set current entity handle"); - let value = ruby.eval::(&script).unwrap(); + + ruby.eval::(&script) + .map_err(|e| ScriptingError::RuntimeError(anyhow!(e.to_string()).into()))?; + var.ivar_set("_current", ruby.qnil().as_value()) .expect("Failed to unset current entity handle"); - RubyValue::new(value) - })); - Ok(RubyScriptData) + Ok::(RubyScriptData) + })) } fn register_fn( diff --git a/src/systems.rs b/src/systems.rs index a259fe9..a891720 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -1,13 +1,13 @@ -use bevy::{prelude::*, log::tracing}; +use bevy::{log::tracing, prelude::*}; use std::{ fmt::Display, sync::{Arc, Mutex}, }; use crate::{ + Callback, Callbacks, Runtime, ScriptingError, callback::FunctionCallEvent, promise::{Promise, PromiseInner}, - Callback, Callbacks, Runtime, ScriptingError, }; use super::components::Script; diff --git a/tests/tests.rs b/tests/tests.rs index fde6066..cad660b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -266,7 +266,7 @@ macro_rules! scripting_tests { || {}, ); - assert_n_times_called!(app, 1); + assert_n_times_called!(app, 2); } #[test] -- 2.45.2 From c763dd06d1ee53c942d921743d024ca9e046083e Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 114/165] add backtrace --- src/lib.rs | 4 ++-- src/runtimes/ruby.rs | 35 ++++++++++++++++++++++++++++++----- src/systems.rs | 2 +- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3ae0518..2e86af5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,8 +294,8 @@ const ENTITY_VAR_NAME: &str = "entity"; /// An error that can occur when internal [ScriptingPlugin] systems are being executed #[derive(Error, Debug)] pub enum ScriptingError { - #[error("script runtime error: {0}")] - RuntimeError(Box), + #[error("script runtime error: {0}\nscript backtrace:\n{1}")] + RuntimeError(String, String), #[error("script compilation error: {0}")] CompileError(Box), #[error("no runtime resource present")] diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 0aa1be1..168d66a 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,3 +1,4 @@ +// TODO: use funcall_public? use std::{ collections::HashMap, ffi::CString, @@ -5,7 +6,7 @@ use std::{ thread::{self, JoinHandle}, }; -use ::magnus::value::Opaque; +use ::magnus::{error::IntoError, typed_data::Inspect, value::Opaque}; use anyhow::anyhow; use bevy::{ asset::Asset, @@ -20,7 +21,7 @@ use magnus::{ value::{Lazy, ReprValue}, }; use magnus::{method, prelude::*}; -use rb_sys::{VALUE, ruby_init_stack}; +use rb_sys::{VALUE, rb_backtrace, rb_make_backtrace, ruby_init_stack}; use serde::Deserialize; use crate::{ @@ -223,7 +224,19 @@ impl TryConvert for BevyVec3 { impl From for ScriptingError { fn from(value: magnus::Error) -> Self { - ScriptingError::RuntimeError(anyhow!(value.to_string()).into()) + // TODO: DRY + ScriptingError::RuntimeError( + value.inspect(), + value + .value() + .unwrap() + .funcall::<_, _, magnus::RArray>("backtrace", ()) // TODO: is there an API for this + // somehwere + .unwrap() + .to_vec::() + .unwrap() + .join("\n"), + ) } } @@ -374,8 +387,20 @@ impl Runtime for RubyRuntime { var.ivar_set("_current", BevyEntity(entity)) .expect("Failed to set current entity handle"); - ruby.eval::(&script) - .map_err(|e| ScriptingError::RuntimeError(anyhow!(e.to_string()).into()))?; + unsafe { + ruby.eval::(&script).map_err(|e| { + ScriptingError::RuntimeError( + e.inspect(), + e.value() + .unwrap() + .funcall::<_, _, magnus::RArray>("backtrace", ()) + .unwrap() + .to_vec::() + .unwrap() + .join("\n"), + ) + })?; + } var.ivar_set("_current", ruby.qnil().as_value()) .expect("Failed to unset current entity handle"); diff --git a/src/systems.rs b/src/systems.rs index a891720..b48931b 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -52,7 +52,7 @@ pub(crate) fn process_new_scripts( let path = asset_server .get_path(&script_component.script) .unwrap_or_default(); - tracing::error!("error running script {} {:?}", path, e); + tracing::error!("error running script {} {}", path, e); } } } -- 2.45.2 From f211769dc4e9d6a4312df259f9b73c72a19bbc05 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 115/165] remove unsafe --- src/runtimes/ruby.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 168d66a..b01882f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -387,20 +387,18 @@ impl Runtime for RubyRuntime { var.ivar_set("_current", BevyEntity(entity)) .expect("Failed to set current entity handle"); - unsafe { - ruby.eval::(&script).map_err(|e| { - ScriptingError::RuntimeError( - e.inspect(), - e.value() - .unwrap() - .funcall::<_, _, magnus::RArray>("backtrace", ()) - .unwrap() - .to_vec::() - .unwrap() - .join("\n"), - ) - })?; - } + ruby.eval::(&script).map_err(|e| { + ScriptingError::RuntimeError( + e.inspect(), + e.value() + .unwrap() + .funcall::<_, _, magnus::RArray>("backtrace", ()) + .unwrap() + .to_vec::() + .unwrap() + .join("\n"), + ) + })?; var.ivar_set("_current", ruby.qnil().as_value()) .expect("Failed to unset current entity handle"); -- 2.45.2 From 1ea7e30c632f4529c6587066ab421dcb9f204f73 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 116/165] better errors --- src/runtimes/lua.rs | 10 +++++----- src/runtimes/rhai.rs | 8 ++++---- src/systems.rs | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 42fb9bf..d5ff891 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -187,7 +187,7 @@ impl Runtime for LuaRuntime { .expect("Error clearing entity global variable"); result }) - .map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; Ok(LuaScriptData) } @@ -236,14 +236,14 @@ impl Runtime for LuaRuntime { let func = engine .globals() .get::<_, Function>(name) - .map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; let args = args .parse(engine) .into_iter() .map(|a| engine.registry_value::(&a.0).unwrap()); let result = func .call::<_, mlua::Value>(Variadic::from_iter(args)) - .map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; engine .globals() .set(ENTITY_VAR_NAME, mlua::Value::Nil) @@ -261,13 +261,13 @@ impl Runtime for LuaRuntime { self.with_engine(|engine| { let val = engine .registry_value::(&value.0) - .map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; let args = args .into_iter() .map(|a| engine.registry_value::(&a.0).unwrap()); let result = val .call::<_, mlua::Value>(Variadic::from_iter(args)) - .map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; Ok(LuaValue::new(engine, result)) }) } diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 43c1fa4..c927cbb 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -105,7 +105,7 @@ impl Runtime for RhaiRuntime { engine .run_ast_with_scope(&mut scope, &ast) - .map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; scope.remove::(ENTITY_VAR_NAME).unwrap(); @@ -156,7 +156,7 @@ impl Runtime for RhaiRuntime { scope.remove::(ENTITY_VAR_NAME).unwrap(); match result { Ok(val) => Ok(RhaiValue(val)), - Err(e) => Err(ScriptingError::RuntimeError(Box::new(e))), + Err(e) => Err(ScriptingError::RuntimeError(e.to_string(), "".to_string())), } } @@ -173,11 +173,11 @@ impl Runtime for RhaiRuntime { let result = if args.len() == 1 && args.first().unwrap().0.is_unit() { f.call_raw(ctx, None, []) - .map_err(|e| ScriptingError::RuntimeError(e))? + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))? } else { let args = args.into_iter().map(|a| a.0).collect::>(); f.call_raw(ctx, None, args) - .map_err(|e| ScriptingError::RuntimeError(e))? + .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))? }; Ok(RhaiValue(result)) diff --git a/src/systems.rs b/src/systems.rs index b48931b..7cbf81b 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -107,7 +107,7 @@ pub(crate) fn init_callbacks(world: &mut World) -> Result<(), Script }, ); if let Err(e) = result { - tracing::error!("error registering function: {:?}", e); + tracing::error!("error registering function: {}", e); } } } @@ -158,7 +158,7 @@ pub(crate) fn process_calls(world: &mut World) -> Result<(), Scripti match result { Ok(_) => {} Err(e) => { - tracing::error!("error resolving call: {} {:?}", callback.name, e); + tracing::error!("error resolving call: {} {}", callback.name, e); } } } -- 2.45.2 From 9e29b803b63a5563b027670bb9bc62aaef08cb6c Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 117/165] cleanupp --- src/runtimes/ruby.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index b01882f..26c94d5 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,4 +1,3 @@ -// TODO: use funcall_public? use std::{ collections::HashMap, ffi::CString, -- 2.45.2 From 80e753812a39027705772e16cb47ec9fc7e22b0b Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 118/165] clippy --- src/runtimes/ruby.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 26c94d5..2db6f76 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -5,8 +5,7 @@ use std::{ thread::{self, JoinHandle}, }; -use ::magnus::{error::IntoError, typed_data::Inspect, value::Opaque}; -use anyhow::anyhow; +use ::magnus::{typed_data::Inspect, value::Opaque}; use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, @@ -20,7 +19,7 @@ use magnus::{ value::{Lazy, ReprValue}, }; use magnus::{method, prelude::*}; -use rb_sys::{VALUE, rb_backtrace, rb_make_backtrace, ruby_init_stack}; +use rb_sys::{VALUE, ruby_init_stack}; use serde::Deserialize; use crate::{ -- 2.45.2 From da759a7e0b2f7b16d79569739682ddb009f44752 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 119/165] refactor --- src/runtimes/ruby.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 2db6f76..e8a8b3d 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -222,17 +222,15 @@ impl TryConvert for BevyVec3 { impl From for ScriptingError { fn from(value: magnus::Error) -> Self { - // TODO: DRY ScriptingError::RuntimeError( value.inspect(), value .value() - .unwrap() - .funcall::<_, _, magnus::RArray>("backtrace", ()) // TODO: is there an API for this - // somehwere - .unwrap() + .expect("No error value") + .funcall::<_, _, magnus::RArray>("backtrace", ()) + .expect("Failed to get backtrace") .to_vec::() - .unwrap() + .expect("Failed to convert backtrace to vec of strings") .join("\n"), ) } @@ -385,18 +383,8 @@ impl Runtime for RubyRuntime { var.ivar_set("_current", BevyEntity(entity)) .expect("Failed to set current entity handle"); - ruby.eval::(&script).map_err(|e| { - ScriptingError::RuntimeError( - e.inspect(), - e.value() - .unwrap() - .funcall::<_, _, magnus::RArray>("backtrace", ()) - .unwrap() - .to_vec::() - .unwrap() - .join("\n"), - ) - })?; + ruby.eval::(&script) + .map_err(|e| >::into(e))?; var.ivar_set("_current", ruby.qnil().as_value()) .expect("Failed to unset current entity handle"); -- 2.45.2 From 10985ca77768f6b844af2d671cc27e4cf7633533 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 120/165] refactor --- src/runtimes/ruby.rs | 64 +++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index e8a8b3d..e22dce3 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -324,6 +324,25 @@ impl RubyRuntime { .expect("No Ruby thread") .execute(Box::new(move |mut ruby| f(&mut ruby))) } + + fn with_current_entity(ruby: &Ruby, entity: Entity, f: impl FnOnce() -> T) -> T { + let var = ruby + .class_object() + .const_get::<_, RModule>("Bevy") + .expect("Failed to get Bevy module") + .const_get::<_, RClass>("Entity") + .expect("Failed to get Entity class"); + + var.ivar_set("_current", BevyEntity(entity)) + .expect("Failed to set current entity handle"); + + let result = f(); + + var.ivar_set("_current", ruby.qnil().as_value()) + .expect("Failed to unset current entity handle"); + + result + } } impl Runtime for RubyRuntime { @@ -372,23 +391,10 @@ impl Runtime for RubyRuntime { ) -> Result { let script = script.0.clone(); self.execute_in_thread(Box::new(move |ruby: &Ruby| { - // TODO: refactor - let var = ruby - .class_object() - .const_get::<_, RModule>("Bevy") - .expect("Failed to get Bevy module") - .const_get::<_, RClass>("Entity") - .expect("Failed to get Entity class"); - - var.ivar_set("_current", BevyEntity(entity)) - .expect("Failed to set current entity handle"); - - ruby.eval::(&script) - .map_err(|e| >::into(e))?; - - var.ivar_set("_current", ruby.qnil().as_value()) - .expect("Failed to unset current entity handle"); - + Self::with_current_entity(ruby, entity, || { + ruby.eval::(&script) + .map_err(|e| >::into(e)) + })?; Ok::(RubyScriptData) })) } @@ -457,23 +463,15 @@ impl Runtime for RubyRuntime { ) -> Result { let name = name.to_string(); self.execute_in_thread(Box::new(move |ruby: &Ruby| { - // TOOD: refactor - let var = ruby - .class_object() - .const_get::<_, RModule>("Bevy") - .unwrap() - .const_get::<_, RClass>("Entity") - .unwrap(); - var.ivar_set("_current", BevyEntity(entity)).unwrap(); + let return_value = Self::with_current_entity(ruby, entity, || { + let args: Vec<_> = args + .parse(ruby) + .into_iter() + .map(|a| ruby.get_inner(a.0)) + .collect(); - let args: Vec<_> = args - .parse(ruby) - .into_iter() - .map(|a| ruby.get_inner(a.0)) - .collect(); - let return_value: magnus::Value = ruby.class_object().funcall(name, args.as_slice())?; - - var.ivar_set("_current", ruby.qnil().as_value()).unwrap(); + ruby.class_object().funcall(name, args.as_slice()) + })?; Ok(RubyValue::new(return_value)) })) -- 2.45.2 From 783183975a68676b2cb8b44f65023081a836924b Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 121/165] remove unwraps --- src/runtimes/ruby.rs | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index e22dce3..4299a0c 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -431,11 +431,19 @@ impl Runtime for RubyRuntime { fn callback(args: &[magnus::Value]) -> magnus::Value { let ruby = magnus::Ruby::get() .expect("Failed to get a handle to Ruby API while processing callback"); - let method_name: magnus::value::StaticSymbol = - ruby.class_object().funcall("__method__", ()).unwrap(); - let method_name = method_name.name().unwrap(); - let callbacks = RUBY_CALLBACKS.lock().unwrap(); - let f = callbacks.get(method_name).unwrap(); + let method_name: magnus::value::StaticSymbol = ruby + .class_object() + .funcall("__method__", ()) + .expect("Failed to get currently called method name symbol from Ruby"); + let method_name = method_name + .name() + .expect("Failed to convert method symbol to string"); + let callbacks = RUBY_CALLBACKS + .lock() + .expect("Failed to lock callbacks when executing a script callback"); + let f = callbacks + .get(method_name) + .expect("No callback found to execute with specified name"); let result = f( (), args.iter() @@ -485,19 +493,16 @@ impl Runtime for RubyRuntime { ) -> Result { let value = value.clone(); - self.ruby_thread - .as_ref() - .unwrap() - .execute(Box::new(move |ruby| { - let f: Proc = TryConvert::try_convert(ruby.get_inner(value.0)).unwrap(); + self.execute_in_thread(move |ruby| { + let f: Proc = TryConvert::try_convert(ruby.get_inner(value.0))?; - let args: Vec<_> = args - .into_iter() - .map(|x| ruby.get_inner(x.0).as_value()) - .collect(); - let result: magnus::Value = f.funcall("call", args.as_slice())?; - Ok(RubyValue::new(result)) - })) + let args: Vec<_> = args + .into_iter() + .map(|x| ruby.get_inner(x.0).as_value()) + .collect(); + let result: magnus::Value = f.funcall("call", args.as_slice())?; + Ok(RubyValue::new(result)) + }) } fn needs_own_thread() -> bool { @@ -516,7 +521,7 @@ pub mod prelude { impl FromRuntimeValueWithEngine<'_, RubyRuntime> for T { fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self { let inner = engine.get_inner(value.0); - T::try_convert(inner).unwrap() + T::try_convert(inner).expect("Failed to convert from Ruby value to native type") } } @@ -545,7 +550,8 @@ pub struct RArray(pub Opaque); impl FromRuntimeValueWithEngine<'_, RubyRuntime> for RArray { fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self { let inner = engine.get_inner(value.0); - let array = magnus::RArray::try_convert(inner).unwrap(); + let array = + magnus::RArray::try_convert(inner).expect("Failed to convert Ruby value to RArray"); RArray(Opaque::from(array)) } } -- 2.45.2 From 20f2ad8fb19a3ee3617036354885ad7f67c429e2 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 122/165] improve errors --- src/lib.rs | 4 ++-- src/runtimes/lua.rs | 10 +++++----- src/runtimes/rhai.rs | 8 ++++---- src/runtimes/ruby.rs | 5 +++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2e86af5..2c8a116 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,8 +294,8 @@ const ENTITY_VAR_NAME: &str = "entity"; /// An error that can occur when internal [ScriptingPlugin] systems are being executed #[derive(Error, Debug)] pub enum ScriptingError { - #[error("script runtime error: {0}\nscript backtrace:\n{1}")] - RuntimeError(String, String), + #[error("script runtime error: {0}")] + RuntimeError(String), #[error("script compilation error: {0}")] CompileError(Box), #[error("no runtime resource present")] diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index d5ff891..59c1217 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -187,7 +187,7 @@ impl Runtime for LuaRuntime { .expect("Error clearing entity global variable"); result }) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))?; Ok(LuaScriptData) } @@ -236,14 +236,14 @@ impl Runtime for LuaRuntime { let func = engine .globals() .get::<_, Function>(name) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))?; let args = args .parse(engine) .into_iter() .map(|a| engine.registry_value::(&a.0).unwrap()); let result = func .call::<_, mlua::Value>(Variadic::from_iter(args)) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))?; engine .globals() .set(ENTITY_VAR_NAME, mlua::Value::Nil) @@ -261,13 +261,13 @@ impl Runtime for LuaRuntime { self.with_engine(|engine| { let val = engine .registry_value::(&value.0) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))?; let args = args .into_iter() .map(|a| engine.registry_value::(&a.0).unwrap()); let result = val .call::<_, mlua::Value>(Variadic::from_iter(args)) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))?; Ok(LuaValue::new(engine, result)) }) } diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index c927cbb..6718f95 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -105,7 +105,7 @@ impl Runtime for RhaiRuntime { engine .run_ast_with_scope(&mut scope, &ast) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))?; + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))?; scope.remove::(ENTITY_VAR_NAME).unwrap(); @@ -156,7 +156,7 @@ impl Runtime for RhaiRuntime { scope.remove::(ENTITY_VAR_NAME).unwrap(); match result { Ok(val) => Ok(RhaiValue(val)), - Err(e) => Err(ScriptingError::RuntimeError(e.to_string(), "".to_string())), + Err(e) => Err(ScriptingError::RuntimeError(e.to_string())), } } @@ -173,11 +173,11 @@ impl Runtime for RhaiRuntime { let result = if args.len() == 1 && args.first().unwrap().0.is_unit() { f.call_raw(ctx, None, []) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))? + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))? } else { let args = args.into_iter().map(|a| a.0).collect::>(); f.call_raw(ctx, None, args) - .map_err(|e| ScriptingError::RuntimeError(e.to_string(), "".to_string()))? + .map_err(|e| ScriptingError::RuntimeError(e.to_string()))? }; Ok(RhaiValue(result)) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 4299a0c..f00103d 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -222,7 +222,8 @@ impl TryConvert for BevyVec3 { impl From for ScriptingError { fn from(value: magnus::Error) -> Self { - ScriptingError::RuntimeError( + ScriptingError::RuntimeError(format!( + "error: {}\nbacktrace:\n{}\n", value.inspect(), value .value() @@ -232,7 +233,7 @@ impl From for ScriptingError { .to_vec::() .expect("Failed to convert backtrace to vec of strings") .join("\n"), - ) + )) } } -- 2.45.2 From fc490f5d13428ab89e830e25b232070badf7e17f Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 123/165] improve err --- src/runtimes/ruby.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index f00103d..379e302 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -223,7 +223,7 @@ impl TryConvert for BevyVec3 { impl From for ScriptingError { fn from(value: magnus::Error) -> Self { ScriptingError::RuntimeError(format!( - "error: {}\nbacktrace:\n{}\n", + "{}\nbacktrace:\n{}\n", value.inspect(), value .value() -- 2.45.2 From c49188847a431f85257a1a926474bd762f6875a0 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 124/165] improve err --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2c8a116..c6943e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,7 +294,7 @@ const ENTITY_VAR_NAME: &str = "entity"; /// An error that can occur when internal [ScriptingPlugin] systems are being executed #[derive(Error, Debug)] pub enum ScriptingError { - #[error("script runtime error: {0}")] + #[error("script runtime error:\n {0}")] RuntimeError(String), #[error("script compilation error: {0}")] CompileError(Box), -- 2.45.2 From 16ad6630ddd151f4d188e838d7671c22afed0103 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 125/165] fix errs --- assets/tests/lua/eval_that_causes_runtime_error.lua | 2 ++ assets/tests/rhai/eval_that_causes_runtime_error.rhai | 2 ++ src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 assets/tests/lua/eval_that_causes_runtime_error.lua create mode 100644 assets/tests/rhai/eval_that_causes_runtime_error.rhai diff --git a/assets/tests/lua/eval_that_causes_runtime_error.lua b/assets/tests/lua/eval_that_causes_runtime_error.lua new file mode 100644 index 0000000..f4713a8 --- /dev/null +++ b/assets/tests/lua/eval_that_causes_runtime_error.lua @@ -0,0 +1,2 @@ +mark_called() +error() diff --git a/assets/tests/rhai/eval_that_causes_runtime_error.rhai b/assets/tests/rhai/eval_that_causes_runtime_error.rhai new file mode 100644 index 0000000..68c56f0 --- /dev/null +++ b/assets/tests/rhai/eval_that_causes_runtime_error.rhai @@ -0,0 +1,2 @@ +mark_called(); +throw(); diff --git a/src/lib.rs b/src/lib.rs index c6943e9..2060054 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,9 +294,9 @@ const ENTITY_VAR_NAME: &str = "entity"; /// An error that can occur when internal [ScriptingPlugin] systems are being executed #[derive(Error, Debug)] pub enum ScriptingError { - #[error("script runtime error:\n {0}")] + #[error("script runtime error:\n{0}")] RuntimeError(String), - #[error("script compilation error: {0}")] + #[error("script compilation error:\n{0}")] CompileError(Box), #[error("no runtime resource present")] NoRuntimeResource, -- 2.45.2 From 2bf20831bec4b6bba5b44893707e48bb77fbed36 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 15:52:07 +0200 Subject: [PATCH 126/165] fix typo --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6b9f8e5..6b43c45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ path = "examples/rhai/current_entity.rs" required-features = ["rhai"] [[example]] -name = "custom5type_rhai" +name = "custom_type_rhai" path = "examples/rhai/custom_type.rs" required-features = ["rhai"] -- 2.45.2 From 679cfd814038d61c93a5c671334179bffe45b385 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 07:00:00 +0200 Subject: [PATCH 127/165] cleanup --- src/callback.rs | 37 ++++++++++--------------------------- src/lib.rs | 2 -- src/runtimes/lua.rs | 4 ---- src/runtimes/rhai.rs | 4 ---- src/runtimes/ruby.rs | 4 ---- 5 files changed, 10 insertions(+), 41 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index ca93abe..f19a898 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -78,15 +78,9 @@ where inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - if R::needs_own_thread() { - runtime.with_engine_send_mut(move |engine| { - Out::into_runtime_value_with_engine(result, engine) - }) - } else { - runtime.with_engine_mut(move |engine| { - Out::into_runtime_value_with_engine(result, engine) - }) - } + runtime.with_engine_send_mut(move |engine| { + Out::into_runtime_value_with_engine(result, engine) + }) }; let system = IntoSystem::into_system(system_fn); CallbackSystem { @@ -110,32 +104,21 @@ macro_rules! impl_tuple { inner_system.initialize(world); let system_fn = move |args: In>, world: &mut World| { let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - let args = if RN::needs_own_thread() { + + let args = runtime.with_engine_send_mut(move |engine| { ( $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ ) - }) - } else { - runtime.with_engine_mut(move |engine| { - ( - $($t::from_runtime_value_with_engine(args.get($idx).expect(&format!("Failed to get function argument for index {}", $idx)).clone(), engine), )+ - ) - }) - }; + }); let result = inner_system.run(args, world); inner_system.apply_deferred(world); let mut runtime = world.get_resource_mut::().expect("No runtime resource"); - if RN::needs_own_thread() { - runtime.with_engine_send_mut(move |engine| { - Out::into_runtime_value_with_engine(result, engine) - }) - } else { - runtime.with_engine_mut(move |engine| { - Out::into_runtime_value_with_engine(result, engine) - }) - } + + runtime.with_engine_send_mut(move |engine| { + Out::into_runtime_value_with_engine(result, engine) + }) }; let system = IntoSystem::into_system(system_fn); CallbackSystem { diff --git a/src/lib.rs b/src/lib.rs index 2060054..173dda0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -315,8 +315,6 @@ pub trait Runtime: Resource + Default { type Value: Send + Clone; type RawEngine; - fn needs_own_thread() -> bool; - /// Provides mutable reference to raw scripting engine instance. /// Can be used to directly interact with an interpreter to use interfaces /// that bevy_scriptum does not provided adapters for. diff --git a/src/runtimes/lua.rs b/src/runtimes/lua.rs index 59c1217..a049fbb 100644 --- a/src/runtimes/lua.rs +++ b/src/runtimes/lua.rs @@ -295,10 +295,6 @@ impl Runtime for LuaRuntime { ) -> T { self.with_engine(f) } - - fn needs_own_thread() -> bool { - false - } } impl<'a, T: IntoLuaMulti<'a>> IntoRuntimeValueWithEngine<'a, T, LuaRuntime> for T { diff --git a/src/runtimes/rhai.rs b/src/runtimes/rhai.rs index 6718f95..57dd83a 100644 --- a/src/runtimes/rhai.rs +++ b/src/runtimes/rhai.rs @@ -204,10 +204,6 @@ impl Runtime for RhaiRuntime { ) -> T { self.with_engine(f) } - - fn needs_own_thread() -> bool { - false - } } impl Default for RhaiRuntime { diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 379e302..437ac23 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -505,10 +505,6 @@ impl Runtime for RubyRuntime { Ok(RubyValue::new(result)) }) } - - fn needs_own_thread() -> bool { - true - } } pub mod magnus { -- 2.45.2 From 35dc89322e001cbe934a22370fdf7e5a2f701673 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 16:59:56 +0200 Subject: [PATCH 128/165] copy lua book chapters as starting point for ruby book --- book/src/ruby/builtin_types.md | 87 ++++++++++++++++ book/src/ruby/builtin_variables.md | 13 +++ book/src/ruby/calling_rust_from_script.md | 121 ++++++++++++++++++++++ book/src/ruby/calling_script_from_rust.md | 67 ++++++++++++ book/src/ruby/hello_world.md | 66 ++++++++++++ book/src/ruby/installation.md | 12 +++ book/src/ruby/interacting_with_bevy.md | 75 ++++++++++++++ book/src/ruby/lua.md | 3 + book/src/ruby/spawning_scripts.md | 40 +++++++ 9 files changed, 484 insertions(+) create mode 100644 book/src/ruby/builtin_types.md create mode 100644 book/src/ruby/builtin_variables.md create mode 100644 book/src/ruby/calling_rust_from_script.md create mode 100644 book/src/ruby/calling_script_from_rust.md create mode 100644 book/src/ruby/hello_world.md create mode 100644 book/src/ruby/installation.md create mode 100644 book/src/ruby/interacting_with_bevy.md create mode 100644 book/src/ruby/lua.md create mode 100644 book/src/ruby/spawning_scripts.md diff --git a/book/src/ruby/builtin_types.md b/book/src/ruby/builtin_types.md new file mode 100644 index 0000000..e376a33 --- /dev/null +++ b/book/src/ruby/builtin_types.md @@ -0,0 +1,87 @@ +# Builtin types + +bevy_scriptum provides following types that can be used in Lua: + +- ```Vec3``` +- ```BevyEntity``` + +## Vec3 + +### Constructor + +`Vec3(x: number, y: number, z: number)` + +### Properties + +- `x: number` +- `y: number` +- `z: number` + + +### Example Lua usage + +``` +my_vec = Vec3(1, 2, 3) +set_translation(entity, my_vec) +``` + +### Example Rust usage + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("set_translation"), set_translation); + }) + .run(); +} + +fn set_translation( + In((entity, translation)): In<(BevyEntity, BevyVec3)>, + mut entities: Query<&mut Transform>, +) { + let mut transform = entities.get_mut(entity.0).unwrap(); + transform.translation = translation.0; +} +``` + +## BevyEntity + +### Constructor + +None - instances can only be acquired by using built-in `entity` global variable. + +### Properties + +- `index: integer` + +### Example Lua usage + +```lua +print(entity.index) +pass_to_rust(entity) +``` + +### Example Rust usage + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("pass_to_rust"), |In((entity,)): In<(BevyEntity,)>| { + println!("pass_to_rust called with entity: {:?}", entity); + }); + }) + .run(); +} +``` diff --git a/book/src/ruby/builtin_variables.md b/book/src/ruby/builtin_variables.md new file mode 100644 index 0000000..558aa39 --- /dev/null +++ b/book/src/ruby/builtin_variables.md @@ -0,0 +1,13 @@ +# Builtin variables + +## entity + +A variable called `entity` is automatically available to all scripts - it represents bevy entity that the `Script` component is attached to. +It exposes `index` property that returns bevy entity index. +It is useful for accessing entity's components from scripts. +It can be used in the following way: +```lua +print("Current entity index: " .. entity.index) +``` + +`entity` variable is currently not available within promise callbacks. diff --git a/book/src/ruby/calling_rust_from_script.md b/book/src/ruby/calling_rust_from_script.md new file mode 100644 index 0000000..3b7321e --- /dev/null +++ b/book/src/ruby/calling_rust_from_script.md @@ -0,0 +1,121 @@ +# Calling Rust from Lua + +To call a rust function from Lua first you need to register a function +within Rust using builder pattern. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + // `runtime` is a builder that you can use to register functions + }) + .run(); +} +``` + +For example to register a function called `my_rust_func` you can do the following: + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("my_rust_func"), || { + println!("my_rust_func has been called"); + }); + }) + .run(); +} +``` + +After you do that the function will be available to Lua code in your spawned scripts. + +```lua +my_rust_func() +``` + +Registered functions can also take parameters. A parameter can be any type +that implements `FromLua`. + +Since a registered callback function is a Bevy system, the parameters are passed +to it as `In` struct with tuple, which has to be the first parameter of the closure. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("func_with_params"), |args: In<(String, i64)>| { + println!("my_rust_func has been called with string {} and i64 {}", args.0.0, args.0.1); + }); + }) + .run(); +} +``` + +To make it look nicer you can destructure the `In` struct. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("func_with_params"), |In((a, b)): In<(String, i64)>| { + println!("my_rust_func has been called with string {} and i64 {}", a, b); + }); + }) + .run(); +} +``` + +The above function can be called from Lua + +```lua +func_with_params("abc", 123) +``` + +## Return value via promise + +Any registered rust function that returns a value will retrurn a promise when +called within a script. By calling `:and_then` on the promise you can register +a callback that will receive the value returned from Rust function. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function(String::from("returns_value"), || { + 123 + }); + }) + .run(); +} +``` + +```lua +returns_value():and_then(function (value) + print(value) -- 123 +end) +``` diff --git a/book/src/ruby/calling_script_from_rust.md b/book/src/ruby/calling_script_from_rust.md new file mode 100644 index 0000000..b543c2c --- /dev/null +++ b/book/src/ruby/calling_script_from_rust.md @@ -0,0 +1,67 @@ +# Calling Lua from Rust + +To call a function defined in Lua + +```lua +function on_update() +end +``` + +We need to acquire `LuaRuntime` resource within a bevy system. +Then we will be able to call `call_fn` on it, providing the name +of the function to call, `LuaScriptData` that has been automatically +attached to entity after an entity with script attached has been spawned +and its script evaluated, the entity and optionally some arguments. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn call_lua_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, + scripting_runtime: ResMut, +) { + for (entity, mut script_data) in &mut scripted_entities { + // calling function named `on_update` defined in lua script + scripting_runtime + .call_fn("on_update", &mut script_data, entity, ()) + .unwrap(); + } +} + +fn main() {} +``` + +We can also pass some arguments by providing a tuple or `Vec` as the last +`call_fn` argument. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn call_lua_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, + scripting_runtime: ResMut, +) { + for (entity, mut script_data) in &mut scripted_entities { + scripting_runtime + .call_fn("on_update", &mut script_data, entity, (123, String::from("hello"))) + .unwrap(); + } +} + +fn main() {} +``` + +They will be passed to `on_update` Lua function +```lua +function on_update(a, b) + print(a) -- 123 + print(b) -- hello +end +``` + +Any type that implements `IntoLua` can be passed as an argument withing the +tuple in `call_fn`. diff --git a/book/src/ruby/hello_world.md b/book/src/ruby/hello_world.md new file mode 100644 index 0000000..469f551 --- /dev/null +++ b/book/src/ruby/hello_world.md @@ -0,0 +1,66 @@ +# Hello World + +After you are done installing the required crates, you can start developing +your first game or application using bevy_scriptum. + +To start using the library you need to first import some structs and traits +with Rust `use` statements. + +For convenience there is a main "prelude" module provided called +`bevy_scriptum::prelude` and a prelude for each runtime you have enabled as +a create feature. + +You can now start exposing functions to the scripting language. For example, you can expose a function that prints a message to the console: + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("my_print"), + |In((x,)): In<(String,)>| { + println!("my_print: '{}'", x); + }, + ); + }) + .run(); +} +``` + +Then you can create a script file in `assets` directory called `script.lua` that calls this function: + +```lua +my_print("Hello world!") +``` + +And spawn an entity with attached `Script` component with a handle to a script source file: + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("my_print"), + |In((x,)): In<(String,)>| { + println!("my_print: '{}'", x); + }, + ); + }) + .add_systems(Startup,|mut commands: Commands, asset_server: Res| { + commands.spawn(Script::::new(asset_server.load("script.lua"))); + }) + .run(); +} +``` + +You should then see `my_print: 'Hello world!'` printed in your console. diff --git a/book/src/ruby/installation.md b/book/src/ruby/installation.md new file mode 100644 index 0000000..4caaac9 --- /dev/null +++ b/book/src/ruby/installation.md @@ -0,0 +1,12 @@ +# Installation + +Add the following to your `Cargo.toml`: + +```toml +[dependencies] +bevy = "0.16" +bevy_scriptum = { version = "0.8", features = ["lua"] } +``` + +If you need a different version of bevy you need to use a matching bevy_scriptum +version according to the [bevy support matrix](../bevy_support_matrix.md) diff --git a/book/src/ruby/interacting_with_bevy.md b/book/src/ruby/interacting_with_bevy.md new file mode 100644 index 0000000..6e7637d --- /dev/null +++ b/book/src/ruby/interacting_with_bevy.md @@ -0,0 +1,75 @@ +# Interacting with bevy in callbacks + +Every registered function is also just a regular Bevy system. + +That allows you to do anything you would do in a Bevy system. + +You could for example create a callback system function that prints names +of all entities with `Player` component. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +#[derive(Component)] +struct Player; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("print_player_names"), + |players: Query<&Name, With>| { + for player in &players { + println!("player name: {}", player); + } + }, + ); + }) + .run(); +} +``` + +In script: + +```lua +print_player_names() +``` + +You can use functions that interact with Bevy entities and resources and +take arguments at the same time. It could be used for example to mutate a +component. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +#[derive(Component)] +struct Player { + health: i32 +} + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|runtime| { + runtime.add_function( + String::from("hurt_player"), + |In((hit_value,)): In<(i32,)>, mut players: Query<&mut Player>| { + let mut player = players.single_mut(); + player.health -= hit_value; + }, + ); + }) + .run(); +} +``` + +And it could be called in script like: + +```lua +hurt_player(5) +``` diff --git a/book/src/ruby/lua.md b/book/src/ruby/lua.md new file mode 100644 index 0000000..891ffd4 --- /dev/null +++ b/book/src/ruby/lua.md @@ -0,0 +1,3 @@ +# Lua + +This chapter demonstrates how to work with bevy_scriptum when using Lua language runtime. diff --git a/book/src/ruby/spawning_scripts.md b/book/src/ruby/spawning_scripts.md new file mode 100644 index 0000000..41a49b4 --- /dev/null +++ b/book/src/ruby/spawning_scripts.md @@ -0,0 +1,40 @@ +# Spawning scripts + +To spawn a Lua script you will need to get a handle to a script asset using +bevy's `AssetServer`. + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn my_spawner(mut commands: Commands, assets_server: Res) { + commands.spawn(Script::::new( + assets_server.load("my_script.lua"), + )); +} + +fn main() {} +``` + +After they scripts have been evaled by bevy_scriptum, the entities that they've +been attached to will get the `Script::` component stripped and instead +```LuaScriptData``` component will be attached. + +So to query scipted entities you could do something like: + +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +fn my_system( + mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, +) { + for (entity, mut script_data) in &mut scripted_entities { + // do something with scripted entities + } +} + +fn main() {} +``` -- 2.45.2 From 2d2ba975bb3e5b26eb8792c6a039765bd800a9f7 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:04:52 +0200 Subject: [PATCH 129/165] mention app dev in docs --- Cargo.toml | 4 ++-- README.md | 2 +- book/src/introduction.md | 2 +- book/src/workflow/live_reload.md | 16 ++++++++-------- examples/lua/side_effects.rs | 2 +- examples/rhai/side_effects.rs | 12 +++++++----- examples/ruby/side_effects.rs | 12 +++++++----- src/lib.rs | 4 ++-- 8 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6b43c45..10f089d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,9 @@ edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" categories = ["game-development"] -description = "Plugin for Bevy engine that allows you to write some of your game logic in a scripting language" +description = "Plugin for Bevy engine that allows you to write some of your game or application logic in a scripting language" repository = "https://github.com/jarkonik/bevy_scriptum" -keywords = ["bevy", "rhai", "scripting", "game", "gamedev"] +keywords = ["bevy", "lua", "scripting", "game", "script"] [features] lua = ["dep:mlua", "mlua/luajit"] diff --git a/README.md b/README.md index 85f507a..a78498f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![demo](demo.gif) -bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game logic in a scripting language. +bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language. Currently [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are supported, but more languages may be added in the future. Everything you need to know to get started with using this library is contained in the diff --git a/book/src/introduction.md b/book/src/introduction.md index 63a6a32..823fa41 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -1,6 +1,6 @@ # bevy_scriptum 📜 -bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game logic in a scripting language. +bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language. Currently [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are supported, but more languages may be added in the future. API docs are available in [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) diff --git a/book/src/workflow/live_reload.md b/book/src/workflow/live_reload.md index 447e84c..72e21bd 100644 --- a/book/src/workflow/live_reload.md +++ b/book/src/workflow/live_reload.md @@ -9,18 +9,18 @@ within bevy dependency in `Cargo.toml` bevy = { version = "0.16", features = ["file_watcher"] } ``` -## Init-teardown pattern for game development +## Init-teardown pattern -It is useful to structure your game in a way that would allow making changes to -the scripting code without restarting the game. +It is useful to structure your application in a way that would allow making changes to +the scripting code without restarting the application. A useful pattern is to hava three functions "init", "update" and "teardown". -- "init" function will take care of starting the game(spawning the player, the level etc) +- "init" function will take care of starting the application(spawning the player, the level etc) -- "update" function will run the main game logic +- "update" function will run the main application logic -- "teardown" function will despawn all the entities so game starts at fresh state. +- "teardown" function will despawn all the entities so application starts at fresh state. This pattern is very easy to implement in bevy_scriptum. All you need is to define all needed functions in script: @@ -35,7 +35,7 @@ local function init() player.entity = spawn_player() end --- game logic here, should be called in a bevy system using call_fn +-- application logic here, should be called in a bevy system using call_fn local function update() (...) end @@ -45,7 +45,7 @@ local function teardown() despawn(player.entity) end --- call init to start the game, this will be called on each file-watcher script +-- call init to start the application, this will be called on each file-watcher script -- reload init() ``` diff --git a/examples/lua/side_effects.rs b/examples/lua/side_effects.rs index e3567ff..2bf4a86 100644 --- a/examples/lua/side_effects.rs +++ b/examples/lua/side_effects.rs @@ -4,7 +4,7 @@ use bevy_scriptum::runtimes::lua::prelude::*; fn main() { App::new() - // This is just needed for headless console app, not needed for a regular bevy game + // This is just needed for headless console app, not needed for a regular bevy application // that uses a winit window .set_runner(move |mut app: App| { loop { diff --git a/examples/rhai/side_effects.rs b/examples/rhai/side_effects.rs index 28a6770..f91ca3b 100644 --- a/examples/rhai/side_effects.rs +++ b/examples/rhai/side_effects.rs @@ -4,12 +4,14 @@ use bevy_scriptum::runtimes::rhai::prelude::*; fn main() { App::new() - // This is just needed for headless console app, not needed for a regular bevy game + // This is just needed for headless console app, not needed for a regular bevy application // that uses a winit window - .set_runner(move |mut app: App| loop { - app.update(); - if let Some(exit) = app.should_exit() { - return exit; + .set_runner(move |mut app: App| { + loop { + app.update(); + if let Some(exit) = app.should_exit() { + return exit; + } } }) .add_plugins(DefaultPlugins) diff --git a/examples/ruby/side_effects.rs b/examples/ruby/side_effects.rs index 031bdea..85c8564 100644 --- a/examples/ruby/side_effects.rs +++ b/examples/ruby/side_effects.rs @@ -4,12 +4,14 @@ use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() - // This is just needed for headless console app, not needed for a regular bevy game + // This is just needed for headless console app, not needed for a regular bevy application // that uses a winit window - .set_runner(move |mut app: App| loop { - app.update(); - if let Some(exit) = app.should_exit() { - return exit; + .set_runner(move |mut app: App| { + loop { + app.update(); + if let Some(exit) = app.should_exit() { + return exit; + } } }) .add_plugins(DefaultPlugins) diff --git a/src/lib.rs b/src/lib.rs index 173dda0..5e57a21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! ![demo](demo.gif) //! -//! bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game logic in a scripting language. +//! bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language. //! Currently [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are supported, but more languages may be added in the future. //! //! Everything you need to know to get started with using this library is contained in the @@ -15,7 +15,7 @@ //! - flexibility //! - hot-reloading //! -//! Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game logic without having to recompile your game. +//! Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile your game. //! //! All you need to do is register callbacks on your Bevy app like this: //! ```no_run -- 2.45.2 From d232ca45b10ed45d9472b9583053327d41d19f33 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:17:46 +0200 Subject: [PATCH 130/165] docs emoji --- README.md | 14 +++++++++----- book/src/introduction.md | 12 ++++++++++-- src/lib.rs | 13 +++++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a78498f..f095853 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,16 @@ ![demo](demo.gif) bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language. -Currently [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are supported, but more languages may be added in the future. +### Supported scripting languages/runtimes -Everything you need to know to get started with using this library is contained in the -[bevy_scriptum book](https://jarkonik.github.io/bevy_scriptum/) +| language/runtime | cargo feature | documentation chapter | +| ----------------- | ------------- | --------------------------------------------------------------- | +| 🌙 LuaJIT | lua | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | +| 🌾 Rhai | rhai | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | +| 💎 Ruby | ruby | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | -API docs are available in [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) +Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 +Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 bevy_scriptum's main advantages include: - low-boilerplate @@ -17,7 +21,7 @@ bevy_scriptum's main advantages include: - flexibility - hot-reloading -Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game logic without having to recompile your game. +Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile your game. All you need to do is register callbacks on your Bevy app like this: ```rust diff --git a/book/src/introduction.md b/book/src/introduction.md index 823fa41..080e247 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -1,9 +1,17 @@ # bevy_scriptum 📜 bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language. -Currently [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are supported, but more languages may be added in the future. -API docs are available in [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) + ## Supported scripting languages/runtimes + + | language/runtime | cargo feature | documentation chapter | + | ----------------- | ------------- | --------------------------------------------------------------- | + | 🌙 LuaJIT | lua | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | + | 🌾 Rhai | rhai | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | + | 💎 Ruby | ruby | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | + + Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 + Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 bevy_scriptum's main advantages include: - low-boilerplate diff --git a/src/lib.rs b/src/lib.rs index 5e57a21..f46f8aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,17 @@ //! ![demo](demo.gif) //! //! bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language. -//! Currently [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are supported, but more languages may be added in the future. + +//! ## Supported scripting languages/runtimes //! -//! Everything you need to know to get started with using this library is contained in the -//! [bevy_scriptum book](https://jarkonik.github.io/bevy_scriptum/) +//! | language/runtime | cargo feature | documentation chapter | +//! | ----------------- | ------------- | --------------------------------------------------------------- | +//! | 🌙 LuaJIT | lua | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | +//! | 🌾 Rhai | rhai | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | +//! | 💎 Ruby | ruby | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | //! -//! API docs are available in [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) +//! Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 +//! Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 //! //! bevy_scriptum's main advantages include: //! - low-boilerplate -- 2.45.2 From 836c31dbf18e96083aee798b39e3a0621c815358 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:20:53 +0200 Subject: [PATCH 131/165] docs --- README.md | 33 +++------------------------------ book/src/introduction.md | 6 +++--- src/lib.rs | 36 +++--------------------------------- 3 files changed, 9 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index f095853..bb057b3 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you | language/runtime | cargo feature | documentation chapter | | ----------------- | ------------- | --------------------------------------------------------------- | -| 🌙 LuaJIT | lua | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | -| 🌾 Rhai | rhai | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | -| 💎 Ruby | ruby | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | +| 🌙 LuaJIT | `lua` | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | +| 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | +| 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 @@ -90,33 +90,6 @@ which you can then call in your script like this: ```lua fun_with_string_param("Hello world!") ``` -It is also possible to split the definition of your callback functions up over multiple plugins. This enables you to split up your code by subject and keep the main initialization light and clean. -This can be accomplished by using `add_scripting_api`. Be careful though, `add_scripting` has to be called before adding plugins. -```rust -use bevy::prelude::*; -use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; - -struct MyPlugin; -impl Plugin for MyPlugin { - fn build(&self, app: &mut App) { - app.add_scripting_api::(|runtime| { - runtime.add_function(String::from("hello_from_my_plugin"), || { - info!("Hello from MyPlugin"); - }); - }); - } -} - -App::new() - .add_plugins(DefaultPlugins) - .add_scripting::(|_| { - // nice and clean - }) - .add_plugins(MyPlugin) - .run(); -``` - ### Usage diff --git a/book/src/introduction.md b/book/src/introduction.md index 080e247..59dd69d 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -6,9 +6,9 @@ bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you | language/runtime | cargo feature | documentation chapter | | ----------------- | ------------- | --------------------------------------------------------------- | - | 🌙 LuaJIT | lua | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | - | 🌾 Rhai | rhai | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | - | 💎 Ruby | ruby | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | + | 🌙 LuaJIT | `lua` | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | + | 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | + | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 diff --git a/src/lib.rs b/src/lib.rs index f46f8aa..4f6eee7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,9 +6,9 @@ //! //! | language/runtime | cargo feature | documentation chapter | //! | ----------------- | ------------- | --------------------------------------------------------------- | -//! | 🌙 LuaJIT | lua | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | -//! | 🌾 Rhai | rhai | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | -//! | 💎 Ruby | ruby | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | +//! | 🌙 LuaJIT | `lua` | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) | +//! | 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | +//! | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | //! //! Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 //! Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 @@ -95,36 +95,6 @@ //! ```lua //! fun_with_string_param("Hello world!") //! ``` -//! It is also possible to split the definition of your callback functions up over multiple plugins. This enables you to split up your code by subject and keep the main initialization light and clean. -//! This can be accomplished by using `add_scripting_api`. Be careful though, `add_scripting` has to be called before adding plugins. -//! ```no_run -//! use bevy::prelude::*; -//! use bevy_scriptum::prelude::*; -//! # #[cfg(feature = "lua")] -//! use bevy_scriptum::runtimes::lua::prelude::*; -//! -//! struct MyPlugin; -//! # #[cfg(feature = "lua")] -//! impl Plugin for MyPlugin { -//! fn build(&self, app: &mut App) { -//! app.add_scripting_api::(|runtime| { -//! runtime.add_function(String::from("hello_from_my_plugin"), || { -//! info!("Hello from MyPlugin"); -//! }); -//! }); -//! } -//! } -//! -//! # #[cfg(feature = "lua")] -//! App::new() -//! .add_plugins(DefaultPlugins) -//! .add_scripting::(|_| { -//! // nice and clean -//! }) -//! .add_plugins(MyPlugin) -//! .run(); -//! ``` -//! //! //! ## Usage //! -- 2.45.2 From 663d54e72937ef75269133e947165e6b799a5b11 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:22:52 +0200 Subject: [PATCH 132/165] docs --- README.md | 2 +- book/src/introduction.md | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bb057b3..c2d8c5a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ bevy_scriptum's main advantages include: - flexibility - hot-reloading -Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile your game. +Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile it. All you need to do is register callbacks on your Bevy app like this: ```rust diff --git a/book/src/introduction.md b/book/src/introduction.md index 59dd69d..ca1382e 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -20,7 +20,7 @@ bevy_scriptum's main advantages include: - flexibility - hot-reloading -Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game logic without having to recompile your game. +Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game logic without having to recompile it. All you need to do is register callbacks on your Bevy app like this: ```rust diff --git a/src/lib.rs b/src/lib.rs index 4f6eee7..7d87b12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ //! - flexibility //! - hot-reloading //! -//! Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile your game. +//! Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile it. //! //! All you need to do is register callbacks on your Bevy app like this: //! ```no_run -- 2.45.2 From cdaceadd1b84a4e06ddc25f11d8ac143588f2562 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:25:58 +0200 Subject: [PATCH 133/165] formatting --- README.md | 1 + book/src/introduction.md | 1 + src/lib.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/README.md b/README.md index c2d8c5a..49ef5b9 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 + Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 bevy_scriptum's main advantages include: diff --git a/book/src/introduction.md b/book/src/introduction.md index ca1382e..506e459 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -11,6 +11,7 @@ bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 + Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 bevy_scriptum's main advantages include: diff --git a/src/lib.rs b/src/lib.rs index 7d87b12..2f19df5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ //! | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | //! //! Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 +//! //! Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 //! //! bevy_scriptum's main advantages include: -- 2.45.2 From d3ea89a417167f2d0c76f0da84b0f0dc091d34a2 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:27:02 +0200 Subject: [PATCH 134/165] wording --- README.md | 2 +- book/src/introduction.md | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 49ef5b9..5dbb0d0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you | 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | -Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 +Documentation book is available [here](https://jarkonik.github.io/bevy_scriptum/) 📖 Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 diff --git a/book/src/introduction.md b/book/src/introduction.md index 506e459..b4a1dcb 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -10,7 +10,7 @@ bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you | 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | - Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 + Documentation book is available [here](https://jarkonik.github.io/bevy_scriptum/) 📖 Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 diff --git a/src/lib.rs b/src/lib.rs index 2f19df5..0e83be6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ //! | 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) | //! | 💎 Ruby | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) | //! -//! Documentation book is available at [documentation book](https://jarkonik.github.io/bevy_scriptum/) 📖 +//! Documentation book is available [here](https://jarkonik.github.io/bevy_scriptum/) 📖 //! //! Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻 //! -- 2.45.2 From 60a22ca808f81c852d5002c3969637e3af4f6e2b Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:29:00 +0200 Subject: [PATCH 135/165] create chapter --- book/src/SUMMARY.md | 1 + book/src/introduction.md | 30 ------------------------------ book/src/multiple_plugins.md | 31 +++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 book/src/multiple_plugins.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index cfaddce..16b173a 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -14,6 +14,7 @@ - [Rhai](./rhai/rhai.md) - [Installation](./rhai/installation.md) - [Hello World(TBD)]() +- [Multiple plugins](./multiple_plugins.md) - [Multiple runtimes(TBD)]() - [Implementing custom runtimes(TBD)]() - [Workflow](./workflow/workflow.md) diff --git a/book/src/introduction.md b/book/src/introduction.md index b4a1dcb..c47b40a 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -97,36 +97,6 @@ which you can then call in your script like this: fun_with_string_param("Hello world!") ``` -It is also possible to split the definition of your callback functions up over multiple plugins. This enables you to split up your code by subject and keep the main initialization light and clean. -This can be accomplished by using `add_scripting_api`. Be careful though, `add_scripting` has to be called before adding plugins. -```rust -use bevy::prelude::*; -use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; - -struct MyPlugin; -impl Plugin for MyPlugin { - fn build(&self, app: &mut App) { - app.add_scripting_api::(|runtime| { - runtime.add_function(String::from("hello_from_my_plugin"), || { - info!("Hello from MyPlugin"); - }); - }); - } -} - -// Main -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .add_scripting::(|_| { - // nice and clean - }) - .add_plugins(MyPlugin) - .run(); -} -``` - ### Usage Add the following to your `Cargo.toml`: diff --git a/book/src/multiple_plugins.md b/book/src/multiple_plugins.md new file mode 100644 index 0000000..c840490 --- /dev/null +++ b/book/src/multiple_plugins.md @@ -0,0 +1,31 @@ +# Multiple plugins + +It is possible to split the definition of your callback functions up over multiple plugins. This enables you to split up your code by subject and keep the main initialization light and clean. +This can be accomplished by using `add_scripting_api`. Be careful though, `add_scripting` has to be called before adding plugins. +```rust +use bevy::prelude::*; +use bevy_scriptum::prelude::*; +use bevy_scriptum::runtimes::lua::prelude::*; + +struct MyPlugin; +impl Plugin for MyPlugin { + fn build(&self, app: &mut App) { + app.add_scripting_api::(|runtime| { + runtime.add_function(String::from("hello_from_my_plugin"), || { + info!("Hello from MyPlugin"); + }); + }); + } +} + +// Main +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_scripting::(|_| { + // nice and clean + }) + .add_plugins(MyPlugin) + .run(); +} +``` -- 2.45.2 From 410d246cd2e5157207e1bb692d7bb1d9241d4aa7 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 17:30:41 +0200 Subject: [PATCH 136/165] add ruby chapters --- book/src/SUMMARY.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 16b173a..0f600b4 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -11,6 +11,15 @@ - [Interacting with bevy in callbacks](./lua/interacting_with_bevy.md) - [Builtin types](./lua/builtin_types.md) - [Builtin variables](./lua/builtin_variables.md) + - [Ruby](./ruby/ruby.md) + - [Installation](./ruby/installation.md) + - [Hello World](./ruby/hello_world.md) + - [Spawning scripts](./ruby/spawning_scripts.md) + - [Calling Rust from Ruby](./ruby/calling_rust_from_script.md) + - [Calling Ruby from Rust](./ruby/calling_script_from_rust.md) + - [Interacting with bevy in callbacks](./ruby/interacting_with_bevy.md) + - [Builtin types](./ruby/builtin_types.md) + - [Builtin variables](./ruby/builtin_variables.md) - [Rhai](./rhai/rhai.md) - [Installation](./rhai/installation.md) - [Hello World(TBD)]() -- 2.45.2 From 08e177dacd0ca4775df1c30d5a0c6c9f006f5963 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 21:53:45 +0200 Subject: [PATCH 137/165] clippy --- src/runtimes/ruby.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 437ac23..2f3f7f5 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -394,7 +394,7 @@ impl Runtime for RubyRuntime { self.execute_in_thread(Box::new(move |ruby: &Ruby| { Self::with_current_entity(ruby, entity, || { ruby.eval::(&script) - .map_err(|e| >::into(e)) + .map_err(>::into) })?; Ok::(RubyScriptData) })) -- 2.45.2 From d505fb33aa0ef4bd18e704dc84471719f62eac73 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 22:45:50 +0200 Subject: [PATCH 138/165] book testing --- book/.gitignore | 1 + book/Cargo.lock | 2498 ++++++++++++++++++++++++ book/Cargo.toml | 6 + book/book.toml | 6 - book/src/lib.rs | 0 book/src/multiple_plugins.md | 5 +- book/src/ruby/builtin_types.md | 12 +- book/src/ruby/interacting_with_bevy.md | 14 +- book/src/ruby/ruby.md | 1 + book/src/workflow/live_reload.md | 13 +- book/test.sh | 3 + 11 files changed, 2544 insertions(+), 15 deletions(-) create mode 100644 book/Cargo.lock create mode 100644 book/Cargo.toml create mode 100644 book/src/lib.rs create mode 100644 book/src/ruby/ruby.md create mode 100755 book/test.sh diff --git a/book/.gitignore b/book/.gitignore index 517ed82..32b4ea1 100644 --- a/book/.gitignore +++ b/book/.gitignore @@ -1,2 +1,3 @@ book doctest_cache +target diff --git a/book/Cargo.lock b/book/Cargo.lock new file mode 100644 index 0000000..13a5e66 --- /dev/null +++ b/book/Cargo.lock @@ -0,0 +1,2498 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "const-random", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "assert_type_match" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "atomicow" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52e8890bb9844440d0c412fa74b67fd2f14e85248b6e00708059b6da9e5f8bf" +dependencies = [ + "portable-atomic", + "portable-atomic-util", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bevy" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5cd3b24a5adb7c7378da7b3eea47639877643d11b6b087fc8a8094f2528615" +dependencies = [ + "bevy_internal", +] + +[[package]] +name = "bevy_app" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b6267ac23a9947d5b2725ff047a1e1add70076d85fa9fb73d044ab9bea1f3c" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "cfg-if", + "console_error_panic_hook", + "ctrlc", + "downcast-rs", + "log", + "thiserror 2.0.12", + "variadics_please", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "bevy_asset" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0698040d63199391ea77fd02e039630748e3e335c3070c6d932fd96cbf80f5d6" +dependencies = [ + "async-broadcast", + "async-fs", + "async-lock", + "atomicow", + "bevy_app", + "bevy_asset_macros", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bevy_window", + "bitflags", + "blake3", + "crossbeam-channel", + "derive_more", + "disqualified", + "downcast-rs", + "either", + "futures-io", + "futures-lite", + "js-sys", + "parking_lot", + "ron", + "serde", + "stackfuture", + "thiserror 2.0.12", + "tracing", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "bevy_asset_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf8c00b5d532f8e5ac7b49af10602f9f7774a2d522cf0638323b5dfeee7b31c" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_derive" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f626531b9c05c25a758ede228727bd11c2c2c8498ecbed9925044386d525a2a3" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn", +] + +[[package]] +name = "bevy_diagnostic" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048a1ff3944a534b8472516866284181eef0a75b6dd4d39b6e5925715e350766" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_tasks", + "bevy_time", + "bevy_utils", + "const-fnv1a-hash", + "log", +] + +[[package]] +name = "bevy_ecs" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e807b5d9aab3bb8dfe47e7a44c9ff088bad2ceefe299b80ac77609a87fe9d4" +dependencies = [ + "arrayvec", + "bevy_ecs_macros", + "bevy_platform", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bitflags", + "bumpalo", + "concurrent-queue", + "derive_more", + "disqualified", + "fixedbitset", + "indexmap", + "log", + "nonmax", + "serde", + "smallvec", + "thiserror 2.0.12", + "variadics_please", +] + +[[package]] +name = "bevy_ecs_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d7bb98aeb8dd30f36e6a773000c12a891d4f1bee2adc3841ec89cc8eaf54e" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_input" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763410715714f3d4d2dcdf077af276e2e4ea93fd8081b183d446d060ea95baaa" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_utils", + "derive_more", + "log", + "thiserror 2.0.12", +] + +[[package]] +name = "bevy_internal" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "526ffd64c58004cb97308826e896c07d0e23dc056c243b97492e31cdf72e2830" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", + "bevy_input", + "bevy_log", + "bevy_math", + "bevy_platform", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_time", + "bevy_transform", + "bevy_utils", +] + +[[package]] +name = "bevy_log" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7156df8d2f11135cf71c03eb4c11132b65201fd4f51648571e59e39c9c9ee2f6" +dependencies = [ + "android_log-sys", + "bevy_app", + "bevy_ecs", + "bevy_utils", + "tracing", + "tracing-log", + "tracing-oslog", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "bevy_macro_utils" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2473db70d8785b5c75d6dd951a2e51e9be2c2311122db9692c79c9d887517b" +dependencies = [ + "parking_lot", + "proc-macro2", + "quote", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_math" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a3a926d02dc501c6156a047510bdb538dcb1fa744eeba13c824b73ba88de55" +dependencies = [ + "approx", + "bevy_reflect", + "derive_more", + "glam", + "itertools 0.14.0", + "libm", + "rand", + "rand_distr", + "serde", + "smallvec", + "thiserror 2.0.12", + "variadics_please", +] + +[[package]] +name = "bevy_platform" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704db2c11b7bc31093df4fbbdd3769f9606a6a5287149f4b51f2680f25834ebc" +dependencies = [ + "cfg-if", + "critical-section", + "foldhash", + "getrandom 0.2.16", + "hashbrown", + "portable-atomic", + "portable-atomic-util", + "serde", + "spin 0.9.8", + "web-time", +] + +[[package]] +name = "bevy_ptr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f1275dfb4cfef4ffc90c3fa75408964864facf833acc932413d52aa5364ba4" + +[[package]] +name = "bevy_reflect" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "607ebacc31029cf2f39ac330eabf1d4bc411b159528ec08dbe6b0593eaccfd41" +dependencies = [ + "assert_type_match", + "bevy_platform", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "derive_more", + "disqualified", + "downcast-rs", + "erased-serde", + "foldhash", + "glam", + "serde", + "smallvec", + "smol_str", + "thiserror 2.0.12", + "uuid", + "variadics_please", + "wgpu-types", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf35e45e4eb239018369f63f2adc2107a54c329f9276d020e01eee1625b0238b" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_scriptum" +version = "0.8.1" +dependencies = [ + "anyhow", + "bevy", + "crossbeam-channel", + "magnus", + "mlua", + "rb-sys", + "rhai", + "serde", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "bevy_scriptum_book" +version = "0.0.0" +dependencies = [ + "bevy_scriptum", +] + +[[package]] +name = "bevy_tasks" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444c450b65e108855f42ecb6db0c041a56ea7d7f10cc6222f0ca95e9536a7d19" +dependencies = [ + "async-executor", + "async-task", + "atomic-waker", + "bevy_platform", + "cfg-if", + "crossbeam-queue", + "derive_more", + "futures-channel", + "futures-lite", + "heapless", + "pin-project", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_time" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456369ca10f8e039aaf273332744674844827854833ee29e28f9e161702f2f55" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "log", +] + +[[package]] +name = "bevy_transform" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8479cdd5461246943956a7c8347e4e5d6ff857e57add889fb50eee0b5c26ab48" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_tasks", + "derive_more", + "serde", + "thiserror 2.0.12", +] + +[[package]] +name = "bevy_utils" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2da3b3c1f94dadefcbe837aaa4aa119fcea37f7bdc5307eb05b4ede1921e24" +dependencies = [ + "bevy_platform", + "thread_local", +] + +[[package]] +name = "bevy_window" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d83327cc5584da463d12b7a88ddb97f9e006828832287e1564531171fffdeb4" +dependencies = [ + "android-activity", + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_math", + "bevy_platform", + "bevy_reflect", + "bevy_utils", + "log", + "raw-window-handle", + "serde", + "smol_str", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", +] + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] + +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", + "portable-atomic", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-fnv1a-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "ctrlc" +version = "3.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +dependencies = [ + "nix", + "windows-sys 0.59.0", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "disqualified" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd" + +[[package]] +name = "downcast-rs" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" +dependencies = [ + "bytemuck", + "libm", + "serde", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "equivalent", + "serde", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "portable-atomic", + "stable_deref_trait", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libloading" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "lua-src" +version = "547.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edaf29e3517b49b8b746701e5648ccb5785cde1c119062cbabbc5d5cd115e42" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.5.12+a4f56a4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a8e7962a5368d5f264d045a5a255e90f9aa3fc1941ae15a8d2940d42cac671" +dependencies = [ + "cc", + "which", +] + +[[package]] +name = "magnus" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d87ae53030f3a22e83879e666cb94e58a7bdf31706878a0ba48752994146dab" +dependencies = [ + "magnus-macros", + "rb-sys", + "rb-sys-env", + "seq-macro", +] + +[[package]] +name = "magnus-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5968c820e2960565f647819f5928a42d6e874551cab9d88d75e3e0660d7f71e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mlua" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7" +dependencies = [ + "bstr", + "mlua-sys", + "num-traits", + "once_cell", + "rustc-hash 2.1.1", +] + +[[package]] +name = "mlua-sys" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380c1f7e2099cafcf40e51d3a9f20a346977587aa4d012eae1f043149a728a93" +dependencies = [ + "cc", + "cfg-if", + "lua-src", + "luajit-src", + "pkg-config", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rb-sys" +version = "0.9.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ca6726be0eca74687047fed7dcbc2d509571f3962e190c343ac1eb40e482b3" +dependencies = [ + "rb-sys-build", +] + +[[package]] +name = "rb-sys-build" +version = "0.9.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2390cfc87b7513656656faad6567291e581542d3ec41dd0a2bf381896e0880" +dependencies = [ + "bindgen 0.69.5", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "shell-words", + "syn", +] + +[[package]] +name = "rb-sys-env" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35802679f07360454b418a5d1735c89716bde01d35b1560fc953c1415a0b3bb" + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rhai" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce4d759a4729a655ddfdbb3ff6e77fb9eadd902dae12319455557796e435d2a6" +dependencies = [ + "ahash", + "bitflags", + "instant", + "no-std-compat", + "num-traits", + "once_cell", + "rhai_codegen", + "smallvec", + "smartstring", + "thin-vec", +] + +[[package]] +name = "rhai_codegen" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags", + "serde", + "serde_derive", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stackfuture" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eae92052b72ef70dafa16eddbabffc77e5ca3574be2f7bc1127b36f0a7ad7f2" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thin-vec" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-oslog" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528bdd1f0e27b5dd9a4ededf154e824b0532731e4af73bb531de46276e0aab1e" +dependencies = [ + "bindgen 0.70.1", + "cc", + "cfg-if", + "once_cell", + "parking_lot", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "variadics_please" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wgpu-types" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +dependencies = [ + "bitflags", + "js-sys", + "log", + "serde", + "web-sys", +] + +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/book/Cargo.toml b/book/Cargo.toml new file mode 100644 index 0000000..3f4ec50 --- /dev/null +++ b/book/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bevy_scriptum_book" +publish = false + +[dependencies] +bevy_scriptum = { path = "../", features = ["ruby", "lua", "rhai"] } diff --git a/book/book.toml b/book/book.toml index 35d684f..8903447 100644 --- a/book/book.toml +++ b/book/book.toml @@ -4,9 +4,3 @@ language = "en" multilingual = false src = "src" title = "bevy_scriptum" - -[preprocessor.keeper] -command = "mdbook-keeper" -manifest_dir = "../" -externs = ["bevy", "bevy_scriptum"] -build_features = ["lua", "rhai"] diff --git a/book/src/lib.rs b/book/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/book/src/multiple_plugins.md b/book/src/multiple_plugins.md index c840490..db15636 100644 --- a/book/src/multiple_plugins.md +++ b/book/src/multiple_plugins.md @@ -2,7 +2,10 @@ It is possible to split the definition of your callback functions up over multiple plugins. This enables you to split up your code by subject and keep the main initialization light and clean. This can be accomplished by using `add_scripting_api`. Be careful though, `add_scripting` has to be called before adding plugins. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/ruby/builtin_types.md b/book/src/ruby/builtin_types.md index e376a33..1a136ae 100644 --- a/book/src/ruby/builtin_types.md +++ b/book/src/ruby/builtin_types.md @@ -20,14 +20,17 @@ bevy_scriptum provides following types that can be used in Lua: ### Example Lua usage -``` +```lua my_vec = Vec3(1, 2, 3) set_translation(entity, my_vec) ``` ### Example Rust usage -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -69,7 +72,10 @@ pass_to_rust(entity) ### Example Rust usage -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/ruby/interacting_with_bevy.md b/book/src/ruby/interacting_with_bevy.md index 6e7637d..96aad07 100644 --- a/book/src/ruby/interacting_with_bevy.md +++ b/book/src/ruby/interacting_with_bevy.md @@ -7,7 +7,11 @@ That allows you to do anything you would do in a Bevy system. You could for example create a callback system function that prints names of all entities with `Player` component. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_ecs; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -42,7 +46,11 @@ You can use functions that interact with Bevy entities and resources and take arguments at the same time. It could be used for example to mutate a component. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_ecs; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -59,7 +67,7 @@ fn main() { runtime.add_function( String::from("hurt_player"), |In((hit_value,)): In<(i32,)>, mut players: Query<&mut Player>| { - let mut player = players.single_mut(); + let mut player = players.single_mut().unwrap(); player.health -= hit_value; }, ); diff --git a/book/src/ruby/ruby.md b/book/src/ruby/ruby.md new file mode 100644 index 0000000..99ea45d --- /dev/null +++ b/book/src/ruby/ruby.md @@ -0,0 +1 @@ +# Ruby diff --git a/book/src/workflow/live_reload.md b/book/src/workflow/live_reload.md index 72e21bd..4c2a283 100644 --- a/book/src/workflow/live_reload.md +++ b/book/src/workflow/live_reload.md @@ -5,7 +5,7 @@ To enable live reload it should be enough to enable `file-watcher` feature within bevy dependency in `Cargo.toml` -``` +```toml bevy = { version = "0.16", features = ["file_watcher"] } ``` @@ -53,6 +53,9 @@ init() The function calls can be implemented on Rust side the following way: ```rust +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -98,7 +101,10 @@ fn main() {} And to tie this all together we do the following: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -126,6 +132,9 @@ fn spawn_player() {} // Implemented elsewhere `despawn` can be implemented as: ```rust +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/test.sh b/book/test.sh new file mode 100755 index 0000000..5814256 --- /dev/null +++ b/book/test.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +CARGO_MANIFEST_DIR=$(pwd) cargo clean && cargo build && mdbook test -L target/debug/deps/ -- 2.45.2 From ba799ee1101f14362be8e7b15bc2fa47f3559fb1 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Mon, 26 May 2025 22:47:00 +0200 Subject: [PATCH 139/165] set edition --- book/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/book/Cargo.toml b/book/Cargo.toml index 3f4ec50..d2afdf9 100644 --- a/book/Cargo.toml +++ b/book/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "bevy_scriptum_book" publish = false +edition = "2024" [dependencies] bevy_scriptum = { path = "../", features = ["ruby", "lua", "rhai"] } -- 2.45.2 From 0d03a47eedd0d75e1affe10f7171b13bbeda87f6 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 07:00:00 +0200 Subject: [PATCH 140/165] add book tasks --- book/justfile | 15 +++++++++++++++ book/test.sh | 3 --- 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 book/justfile delete mode 100755 book/test.sh diff --git a/book/justfile b/book/justfile new file mode 100644 index 0000000..53ffc65 --- /dev/null +++ b/book/justfile @@ -0,0 +1,15 @@ +export CARGO_MANIFEST_DIR := `pwd` + +build-deps: + cargo clean && cargo build + +_test: + mdbook test -L target/debug/deps/ + +test: build-deps _test + +test-watch: build-deps + watchexec --exts md just _test + +serve: + mdbook serve diff --git a/book/test.sh b/book/test.sh deleted file mode 100755 index 5814256..0000000 --- a/book/test.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -CARGO_MANIFEST_DIR=$(pwd) cargo clean && cargo build && mdbook test -L target/debug/deps/ -- 2.45.2 From 4b25bd0f1823d08cc68b5767aea65f1bf4c04580 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 07:00:00 +0200 Subject: [PATCH 141/165] fix ruby docs --- book/justfile | 2 +- book/src/ruby/calling_rust_from_script.md | 25 ++++++++++++++++++----- book/src/ruby/calling_script_from_rust.md | 10 +++++++-- book/src/ruby/hello_world.md | 10 +++++++-- book/src/ruby/spawning_scripts.md | 6 ++++++ 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/book/justfile b/book/justfile index 53ffc65..a221a90 100644 --- a/book/justfile +++ b/book/justfile @@ -9,7 +9,7 @@ _test: test: build-deps _test test-watch: build-deps - watchexec --exts md just _test + watchexec --exts md -r just _test serve: mdbook serve diff --git a/book/src/ruby/calling_rust_from_script.md b/book/src/ruby/calling_rust_from_script.md index 3b7321e..de90227 100644 --- a/book/src/ruby/calling_rust_from_script.md +++ b/book/src/ruby/calling_rust_from_script.md @@ -3,7 +3,10 @@ To call a rust function from Lua first you need to register a function within Rust using builder pattern. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -20,7 +23,10 @@ fn main() { For example to register a function called `my_rust_func` you can do the following: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -49,7 +55,10 @@ that implements `FromLua`. Since a registered callback function is a Bevy system, the parameters are passed to it as `In` struct with tuple, which has to be the first parameter of the closure. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -68,7 +77,10 @@ fn main() { To make it look nicer you can destructure the `In` struct. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -97,7 +109,10 @@ Any registered rust function that returns a value will retrurn a promise when called within a script. By calling `:and_then` on the promise you can register a callback that will receive the value returned from Rust function. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/ruby/calling_script_from_rust.md b/book/src/ruby/calling_script_from_rust.md index b543c2c..f572431 100644 --- a/book/src/ruby/calling_script_from_rust.md +++ b/book/src/ruby/calling_script_from_rust.md @@ -13,7 +13,10 @@ of the function to call, `LuaScriptData` that has been automatically attached to entity after an entity with script attached has been spawned and its script evaluated, the entity and optionally some arguments. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -36,7 +39,10 @@ fn main() {} We can also pass some arguments by providing a tuple or `Vec` as the last `call_fn` argument. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/ruby/hello_world.md b/book/src/ruby/hello_world.md index 469f551..a271b2e 100644 --- a/book/src/ruby/hello_world.md +++ b/book/src/ruby/hello_world.md @@ -12,7 +12,10 @@ a create feature. You can now start exposing functions to the scripting language. For example, you can expose a function that prints a message to the console: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -40,7 +43,10 @@ my_print("Hello world!") And spawn an entity with attached `Script` component with a handle to a script source file: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/ruby/spawning_scripts.md b/book/src/ruby/spawning_scripts.md index 41a49b4..9cabc3b 100644 --- a/book/src/ruby/spawning_scripts.md +++ b/book/src/ruby/spawning_scripts.md @@ -4,6 +4,9 @@ To spawn a Lua script you will need to get a handle to a script asset using bevy's `AssetServer`. ```rust +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -24,6 +27,9 @@ been attached to will get the `Script::` component stripped and inste So to query scipted entities you could do something like: ```rust +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; -- 2.45.2 From 97077338f91f4acaa3c5449fb8346ae6300a68a1 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 07:00:00 +0200 Subject: [PATCH 142/165] fix lua docs --- book/src/introduction.md | 31 +++++++++++++++++++----- book/src/lua/builtin_types.md | 12 ++++++--- book/src/lua/calling_rust_from_script.md | 25 +++++++++++++++---- book/src/lua/calling_script_from_rust.md | 10 ++++++-- book/src/lua/hello_world.md | 10 ++++++-- book/src/lua/interacting_with_bevy.md | 14 ++++++++--- book/src/lua/spawning_scripts.md | 6 +++++ 7 files changed, 87 insertions(+), 21 deletions(-) diff --git a/book/src/introduction.md b/book/src/introduction.md index c47b40a..54975f7 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -24,7 +24,10 @@ bevy_scriptum's main advantages include: Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game logic without having to recompile it. All you need to do is register callbacks on your Bevy app like this: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -47,7 +50,11 @@ hello_bevy() Every callback function that you expose to the scripting language is also a Bevy system, so you can easily query and mutate ECS components and resources just like you would in a regular Bevy system: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_ecs; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -73,7 +80,10 @@ fn main() { ``` You can also pass arguments to your callback functions, just like you would in a regular Bevy system - using `In` structs with tuples: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -110,7 +120,10 @@ or execute `cargo add bevy_scriptum --features lua` from your project directory. You can now start exposing functions to the scripting language. For example, you can expose a function that prints a message to the console: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -138,7 +151,10 @@ my_print("Hello world!") And spawn an entity with attached `Script` component with a handle to a script source file: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -183,7 +199,10 @@ end) ``` which will print out `John` when used with following exposed function: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/lua/builtin_types.md b/book/src/lua/builtin_types.md index e376a33..1a136ae 100644 --- a/book/src/lua/builtin_types.md +++ b/book/src/lua/builtin_types.md @@ -20,14 +20,17 @@ bevy_scriptum provides following types that can be used in Lua: ### Example Lua usage -``` +```lua my_vec = Vec3(1, 2, 3) set_translation(entity, my_vec) ``` ### Example Rust usage -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -69,7 +72,10 @@ pass_to_rust(entity) ### Example Rust usage -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/lua/calling_rust_from_script.md b/book/src/lua/calling_rust_from_script.md index 3b7321e..de90227 100644 --- a/book/src/lua/calling_rust_from_script.md +++ b/book/src/lua/calling_rust_from_script.md @@ -3,7 +3,10 @@ To call a rust function from Lua first you need to register a function within Rust using builder pattern. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -20,7 +23,10 @@ fn main() { For example to register a function called `my_rust_func` you can do the following: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -49,7 +55,10 @@ that implements `FromLua`. Since a registered callback function is a Bevy system, the parameters are passed to it as `In` struct with tuple, which has to be the first parameter of the closure. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -68,7 +77,10 @@ fn main() { To make it look nicer you can destructure the `In` struct. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -97,7 +109,10 @@ Any registered rust function that returns a value will retrurn a promise when called within a script. By calling `:and_then` on the promise you can register a callback that will receive the value returned from Rust function. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/lua/calling_script_from_rust.md b/book/src/lua/calling_script_from_rust.md index b543c2c..f572431 100644 --- a/book/src/lua/calling_script_from_rust.md +++ b/book/src/lua/calling_script_from_rust.md @@ -13,7 +13,10 @@ of the function to call, `LuaScriptData` that has been automatically attached to entity after an entity with script attached has been spawned and its script evaluated, the entity and optionally some arguments. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -36,7 +39,10 @@ fn main() {} We can also pass some arguments by providing a tuple or `Vec` as the last `call_fn` argument. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/lua/hello_world.md b/book/src/lua/hello_world.md index 469f551..a271b2e 100644 --- a/book/src/lua/hello_world.md +++ b/book/src/lua/hello_world.md @@ -12,7 +12,10 @@ a create feature. You can now start exposing functions to the scripting language. For example, you can expose a function that prints a message to the console: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -40,7 +43,10 @@ my_print("Hello world!") And spawn an entity with attached `Script` component with a handle to a script source file: -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; diff --git a/book/src/lua/interacting_with_bevy.md b/book/src/lua/interacting_with_bevy.md index 6e7637d..96aad07 100644 --- a/book/src/lua/interacting_with_bevy.md +++ b/book/src/lua/interacting_with_bevy.md @@ -7,7 +7,11 @@ That allows you to do anything you would do in a Bevy system. You could for example create a callback system function that prints names of all entities with `Player` component. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_ecs; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -42,7 +46,11 @@ You can use functions that interact with Bevy entities and resources and take arguments at the same time. It could be used for example to mutate a component. -```rust +```rust,no_run +# extern crate bevy; +# extern crate bevy_ecs; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -59,7 +67,7 @@ fn main() { runtime.add_function( String::from("hurt_player"), |In((hit_value,)): In<(i32,)>, mut players: Query<&mut Player>| { - let mut player = players.single_mut(); + let mut player = players.single_mut().unwrap(); player.health -= hit_value; }, ); diff --git a/book/src/lua/spawning_scripts.md b/book/src/lua/spawning_scripts.md index 41a49b4..9cabc3b 100644 --- a/book/src/lua/spawning_scripts.md +++ b/book/src/lua/spawning_scripts.md @@ -4,6 +4,9 @@ To spawn a Lua script you will need to get a handle to a script asset using bevy's `AssetServer`. ```rust +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; @@ -24,6 +27,9 @@ been attached to will get the `Script::` component stripped and inste So to query scipted entities you could do something like: ```rust +# extern crate bevy; +# extern crate bevy_scriptum; + use bevy::prelude::*; use bevy_scriptum::prelude::*; use bevy_scriptum::runtimes::lua::prelude::*; -- 2.45.2 From f9a7a9eb77134b13b7251f037965c7e466a127d5 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 07:00:00 +0200 Subject: [PATCH 143/165] cleanup docs --- book/src/lua/calling_script_from_rust.md | 4 ---- book/src/lua/spawning_scripts.md | 4 ---- book/src/ruby/calling_script_from_rust.md | 4 ---- book/src/ruby/spawning_scripts.md | 4 ---- book/src/workflow/live_reload.md | 14 +++++--------- 5 files changed, 5 insertions(+), 25 deletions(-) diff --git a/book/src/lua/calling_script_from_rust.md b/book/src/lua/calling_script_from_rust.md index f572431..c42fb08 100644 --- a/book/src/lua/calling_script_from_rust.md +++ b/book/src/lua/calling_script_from_rust.md @@ -32,8 +32,6 @@ fn call_lua_on_update_from_rust( .unwrap(); } } - -fn main() {} ``` We can also pass some arguments by providing a tuple or `Vec` as the last @@ -57,8 +55,6 @@ fn call_lua_on_update_from_rust( .unwrap(); } } - -fn main() {} ``` They will be passed to `on_update` Lua function diff --git a/book/src/lua/spawning_scripts.md b/book/src/lua/spawning_scripts.md index 9cabc3b..8acd82d 100644 --- a/book/src/lua/spawning_scripts.md +++ b/book/src/lua/spawning_scripts.md @@ -16,8 +16,6 @@ fn my_spawner(mut commands: Commands, assets_server: Res) { assets_server.load("my_script.lua"), )); } - -fn main() {} ``` After they scripts have been evaled by bevy_scriptum, the entities that they've @@ -41,6 +39,4 @@ fn my_system( // do something with scripted entities } } - -fn main() {} ``` diff --git a/book/src/ruby/calling_script_from_rust.md b/book/src/ruby/calling_script_from_rust.md index f572431..c42fb08 100644 --- a/book/src/ruby/calling_script_from_rust.md +++ b/book/src/ruby/calling_script_from_rust.md @@ -32,8 +32,6 @@ fn call_lua_on_update_from_rust( .unwrap(); } } - -fn main() {} ``` We can also pass some arguments by providing a tuple or `Vec` as the last @@ -57,8 +55,6 @@ fn call_lua_on_update_from_rust( .unwrap(); } } - -fn main() {} ``` They will be passed to `on_update` Lua function diff --git a/book/src/ruby/spawning_scripts.md b/book/src/ruby/spawning_scripts.md index 9cabc3b..8acd82d 100644 --- a/book/src/ruby/spawning_scripts.md +++ b/book/src/ruby/spawning_scripts.md @@ -16,8 +16,6 @@ fn my_spawner(mut commands: Commands, assets_server: Res) { assets_server.load("my_script.lua"), )); } - -fn main() {} ``` After they scripts have been evaled by bevy_scriptum, the entities that they've @@ -41,6 +39,4 @@ fn my_system( // do something with scripted entities } } - -fn main() {} ``` diff --git a/book/src/workflow/live_reload.md b/book/src/workflow/live_reload.md index 4c2a283..5bd90bf 100644 --- a/book/src/workflow/live_reload.md +++ b/book/src/workflow/live_reload.md @@ -95,8 +95,6 @@ fn teardown( } } } - -fn main() {} ``` And to tie this all together we do the following: @@ -122,11 +120,11 @@ fn main() { .run(); } -fn init() {} // Implemented elsewhere -fn update() {} // Implemented elsewhere -fn despawn() {} // Implemented elsewhere -fn teardown() {} // Implemented elsewhere -fn spawn_player() {} // Implemented elsewhere +# fn init() {} +# fn update() {} +# fn despawn() {} +# fn teardown() {} +# fn spawn_player() {} ``` `despawn` can be implemented as: @@ -141,8 +139,6 @@ use bevy_scriptum::runtimes::lua::prelude::*; fn despawn(In((entity,)): In<(BevyEntity,)>, mut commands: Commands) { commands.entity(entity.0).despawn(); } - -fn main() {} // Implemented elsewhere ``` Implementation of `spawn_player` has been left out as an exercise for the reader. -- 2.45.2 From 35b4317040924a139213e61f77b51c6c03c091b1 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 07:00:00 +0200 Subject: [PATCH 144/165] ruby docs --- book/src/ruby/builtin_types.md | 42 ++++++++++----------- book/src/ruby/builtin_variables.md | 10 ++--- book/src/ruby/calling_rust_from_script.md | 45 +++++++++++------------ book/src/ruby/calling_script_from_rust.md | 43 ++++++++++------------ book/src/ruby/hello_world.md | 14 +++---- book/src/ruby/installation.md | 2 +- book/src/ruby/interacting_with_bevy.md | 14 +++---- book/src/ruby/lua.md | 3 -- book/src/ruby/ruby.md | 2 + book/src/ruby/spawning_scripts.md | 14 +++---- src/runtimes/ruby.rs | 4 +- 11 files changed, 93 insertions(+), 100 deletions(-) delete mode 100644 book/src/ruby/lua.md diff --git a/book/src/ruby/builtin_types.md b/book/src/ruby/builtin_types.md index 1a136ae..0bb57e1 100644 --- a/book/src/ruby/builtin_types.md +++ b/book/src/ruby/builtin_types.md @@ -1,27 +1,27 @@ # Builtin types -bevy_scriptum provides following types that can be used in Lua: +bevy_scriptum provides following types that can be used in Ruby: -- ```Vec3``` -- ```BevyEntity``` +- ```Bevy::Vec3``` +- ```Bevy::Entity``` ## Vec3 -### Constructor +### Class Methods -`Vec3(x: number, y: number, z: number)` +- `new(x, y, z)` +- `current` -### Properties +### Instance Methods -- `x: number` -- `y: number` -- `z: number` +- `x` +- `y` +- `z` +### Example Ruby usage -### Example Lua usage - -```lua -my_vec = Vec3(1, 2, 3) +```ruby +my_vec = Bevy::Vec3.new(1, 2, 3) set_translation(entity, my_vec) ``` @@ -33,12 +33,12 @@ set_translation(entity, my_vec) use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function(String::from("set_translation"), set_translation); }) .run(); @@ -63,11 +63,11 @@ None - instances can only be acquired by using built-in `entity` global variable - `index: integer` -### Example Lua usage +### Example Ruby usage -```lua -print(entity.index) -pass_to_rust(entity) +```ruby +puts(Bevy::Entity.current.index) +pass_to_rust(Bevy::Entity.current) ``` ### Example Rust usage @@ -78,12 +78,12 @@ pass_to_rust(entity) use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function(String::from("pass_to_rust"), |In((entity,)): In<(BevyEntity,)>| { println!("pass_to_rust called with entity: {:?}", entity); }); diff --git a/book/src/ruby/builtin_variables.md b/book/src/ruby/builtin_variables.md index 558aa39..2896ecf 100644 --- a/book/src/ruby/builtin_variables.md +++ b/book/src/ruby/builtin_variables.md @@ -2,12 +2,12 @@ ## entity -A variable called `entity` is automatically available to all scripts - it represents bevy entity that the `Script` component is attached to. -It exposes `index` property that returns bevy entity index. +Current entity that the script is atteched to can be retrieved by calling `Bevy::Entity.current`. +It exposes `index` method that returns bevy entity index. It is useful for accessing entity's components from scripts. It can be used in the following way: -```lua -print("Current entity index: " .. entity.index) +```ruby +puts("Current entity index: #{Bevy::Entity.current.index}") ``` -`entity` variable is currently not available within promise callbacks. +`Bevy::Entity.current` variable is currently not available within promise callbacks. diff --git a/book/src/ruby/calling_rust_from_script.md b/book/src/ruby/calling_rust_from_script.md index de90227..f4520fb 100644 --- a/book/src/ruby/calling_rust_from_script.md +++ b/book/src/ruby/calling_rust_from_script.md @@ -1,6 +1,6 @@ -# Calling Rust from Lua +# Calling Rust from Ruby -To call a rust function from Lua first you need to register a function +To call a rust function from Ruby first you need to register a function within Rust using builder pattern. ```rust,no_run @@ -9,12 +9,12 @@ within Rust using builder pattern. use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { // `runtime` is a builder that you can use to register functions }) .run(); @@ -29,12 +29,12 @@ For example to register a function called `my_rust_func` you can do the followin use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function(String::from("my_rust_func"), || { println!("my_rust_func has been called"); }); @@ -43,15 +43,12 @@ fn main() { } ``` -After you do that the function will be available to Lua code in your spawned scripts. +After you do that the function will be available to Ruby code in your spawned scripts. -```lua -my_rust_func() +```ruby +my_rust_func ``` -Registered functions can also take parameters. A parameter can be any type -that implements `FromLua`. - Since a registered callback function is a Bevy system, the parameters are passed to it as `In` struct with tuple, which has to be the first parameter of the closure. @@ -61,12 +58,12 @@ to it as `In` struct with tuple, which has to be the first parameter of the clos use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function(String::from("func_with_params"), |args: In<(String, i64)>| { println!("my_rust_func has been called with string {} and i64 {}", args.0.0, args.0.1); }); @@ -83,12 +80,12 @@ To make it look nicer you can destructure the `In` struct. use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function(String::from("func_with_params"), |In((a, b)): In<(String, i64)>| { println!("my_rust_func has been called with string {} and i64 {}", a, b); }); @@ -97,9 +94,9 @@ fn main() { } ``` -The above function can be called from Lua +The above function can be called from Ruby -```lua +```ruby func_with_params("abc", 123) ``` @@ -115,12 +112,12 @@ a callback that will receive the value returned from Rust function. use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function(String::from("returns_value"), || { 123 }); @@ -129,8 +126,8 @@ fn main() { } ``` -```lua -returns_value():and_then(function (value) - print(value) -- 123 -end) +```ruby +returns_value.and_then do |value| + puts(value) # 123 +end ``` diff --git a/book/src/ruby/calling_script_from_rust.md b/book/src/ruby/calling_script_from_rust.md index c42fb08..6fcd068 100644 --- a/book/src/ruby/calling_script_from_rust.md +++ b/book/src/ruby/calling_script_from_rust.md @@ -1,15 +1,15 @@ -# Calling Lua from Rust +# Calling Ruby from Rust -To call a function defined in Lua +To call a function defined in Ruby -```lua -function on_update() +```ruby +def on_update end ``` -We need to acquire `LuaRuntime` resource within a bevy system. +We need to acquire `RubyRuntime` resource within a bevy system. Then we will be able to call `call_fn` on it, providing the name -of the function to call, `LuaScriptData` that has been automatically +of the function to call, `RubyScriptData` that has been automatically attached to entity after an entity with script attached has been spawned and its script evaluated, the entity and optionally some arguments. @@ -19,14 +19,14 @@ and its script evaluated, the entity and optionally some arguments. use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; -fn call_lua_on_update_from_rust( - mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, - scripting_runtime: ResMut, +fn call_ruby_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut RubyScriptData)>, + scripting_runtime: ResMut, ) { for (entity, mut script_data) in &mut scripted_entities { - // calling function named `on_update` defined in lua script + // calling function named `on_update` defined in Ruby script scripting_runtime .call_fn("on_update", &mut script_data, entity, ()) .unwrap(); @@ -43,11 +43,11 @@ We can also pass some arguments by providing a tuple or `Vec` as the last use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; -fn call_lua_on_update_from_rust( - mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, - scripting_runtime: ResMut, +fn call_ruby_on_update_from_rust( + mut scripted_entities: Query<(Entity, &mut RubyScriptData)>, + scripting_runtime: ResMut, ) { for (entity, mut script_data) in &mut scripted_entities { scripting_runtime @@ -57,13 +57,10 @@ fn call_lua_on_update_from_rust( } ``` -They will be passed to `on_update` Lua function -```lua -function on_update(a, b) - print(a) -- 123 - print(b) -- hello +They will be passed to `on_update` Ruby function +```ruby +def on_update(a, b) + puts(a) # 123 + puts(b) # hello end ``` - -Any type that implements `IntoLua` can be passed as an argument withing the -tuple in `call_fn`. diff --git a/book/src/ruby/hello_world.md b/book/src/ruby/hello_world.md index a271b2e..b16f780 100644 --- a/book/src/ruby/hello_world.md +++ b/book/src/ruby/hello_world.md @@ -18,12 +18,12 @@ You can now start exposing functions to the scripting language. For example, you use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function( String::from("my_print"), |In((x,)): In<(String,)>| { @@ -35,9 +35,9 @@ fn main() { } ``` -Then you can create a script file in `assets` directory called `script.lua` that calls this function: +Then you can create a script file in `assets` directory called `script.rb` that calls this function: -```lua +```ruby my_print("Hello world!") ``` @@ -49,12 +49,12 @@ And spawn an entity with attached `Script` component with a handle to a script s use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function( String::from("my_print"), |In((x,)): In<(String,)>| { @@ -63,7 +63,7 @@ fn main() { ); }) .add_systems(Startup,|mut commands: Commands, asset_server: Res| { - commands.spawn(Script::::new(asset_server.load("script.lua"))); + commands.spawn(Script::::new(asset_server.load("script.rb"))); }) .run(); } diff --git a/book/src/ruby/installation.md b/book/src/ruby/installation.md index 4caaac9..a23325c 100644 --- a/book/src/ruby/installation.md +++ b/book/src/ruby/installation.md @@ -5,7 +5,7 @@ Add the following to your `Cargo.toml`: ```toml [dependencies] bevy = "0.16" -bevy_scriptum = { version = "0.8", features = ["lua"] } +bevy_scriptum = { version = "0.8", features = ["ruby"] } ``` If you need a different version of bevy you need to use a matching bevy_scriptum diff --git a/book/src/ruby/interacting_with_bevy.md b/book/src/ruby/interacting_with_bevy.md index 96aad07..0fe5e9a 100644 --- a/book/src/ruby/interacting_with_bevy.md +++ b/book/src/ruby/interacting_with_bevy.md @@ -14,7 +14,7 @@ of all entities with `Player` component. use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; #[derive(Component)] struct Player; @@ -22,7 +22,7 @@ struct Player; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function( String::from("print_player_names"), |players: Query<&Name, With>| { @@ -38,8 +38,8 @@ fn main() { In script: -```lua -print_player_names() +```ruby +print_player_names ``` You can use functions that interact with Bevy entities and resources and @@ -53,7 +53,7 @@ component. use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; #[derive(Component)] struct Player { @@ -63,7 +63,7 @@ struct Player { fn main() { App::new() .add_plugins(DefaultPlugins) - .add_scripting::(|runtime| { + .add_scripting::(|runtime| { runtime.add_function( String::from("hurt_player"), |In((hit_value,)): In<(i32,)>, mut players: Query<&mut Player>| { @@ -78,6 +78,6 @@ fn main() { And it could be called in script like: -```lua +```ruby hurt_player(5) ``` diff --git a/book/src/ruby/lua.md b/book/src/ruby/lua.md deleted file mode 100644 index 891ffd4..0000000 --- a/book/src/ruby/lua.md +++ /dev/null @@ -1,3 +0,0 @@ -# Lua - -This chapter demonstrates how to work with bevy_scriptum when using Lua language runtime. diff --git a/book/src/ruby/ruby.md b/book/src/ruby/ruby.md index 99ea45d..1eb429f 100644 --- a/book/src/ruby/ruby.md +++ b/book/src/ruby/ruby.md @@ -1 +1,3 @@ # Ruby + +This chapter demonstrates how to work with bevy_scriptum when using Ruby language runtime. diff --git a/book/src/ruby/spawning_scripts.md b/book/src/ruby/spawning_scripts.md index 8acd82d..9466733 100644 --- a/book/src/ruby/spawning_scripts.md +++ b/book/src/ruby/spawning_scripts.md @@ -9,18 +9,18 @@ bevy's `AssetServer`. use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn my_spawner(mut commands: Commands, assets_server: Res) { - commands.spawn(Script::::new( - assets_server.load("my_script.lua"), + commands.spawn(Script::::new( + assets_server.load("my_script.rb"), )); } ``` After they scripts have been evaled by bevy_scriptum, the entities that they've -been attached to will get the `Script::` component stripped and instead -```LuaScriptData``` component will be attached. +been attached to will get the `Script::` component stripped and instead +```RubyScriptData``` component will be attached. So to query scipted entities you could do something like: @@ -30,10 +30,10 @@ So to query scipted entities you could do something like: use bevy::prelude::*; use bevy_scriptum::prelude::*; -use bevy_scriptum::runtimes::lua::prelude::*; +use bevy_scriptum::runtimes::ruby::prelude::*; fn my_system( - mut scripted_entities: Query<(Entity, &mut LuaScriptData)>, + mut scripted_entities: Query<(Entity, &mut RubyScriptData)>, ) { for (entity, mut script_data) in &mut scripted_entities { // do something with scripted entities diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 2f3f7f5..b41262e 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -174,7 +174,7 @@ fn then(r_self: magnus::Value) -> magnus::Value { .into_value() } -#[derive(Clone)] +#[derive(Clone, Debug)] #[magnus::wrap(class = "Bevy::Entity")] pub struct BevyEntity(pub Entity); @@ -191,7 +191,7 @@ impl TryConvert for BevyEntity { } } -#[derive(Clone)] +#[derive(Clone, Debug)] #[magnus::wrap(class = "Bevy::Vec3")] pub struct BevyVec3(pub Vec3); -- 2.45.2 From 281f7851b75ccea36caa8fb06782b96b4ed1f886 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 07:00:00 +0200 Subject: [PATCH 145/165] add test book job --- .github/workflows/test_book.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/test_book.yml diff --git a/.github/workflows/test_book.yml b/.github/workflows/test_book.yml new file mode 100644 index 0000000..b58efea --- /dev/null +++ b/.github/workflows/test_book.yml @@ -0,0 +1,32 @@ +name: Test book + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: write # To push a branch + pages: write # To push to a GitHub Pages site + id-token: write # To update the deployment status + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install latest mdbook + run: | + tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') + url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" + mkdir mdbook + curl -sSL $url | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + - name: Test Book + run: | + cd book + export CARGO_MANIFEST_DIR = $(pwd) + cargo build + mdbook test -L target/debug/deps/ -- 2.45.2 From 3a9a048e3425ff4a768fb3564d1a20cd814c7f24 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 10:18:25 +0200 Subject: [PATCH 146/165] fix job --- .github/workflows/{test_book.yml => book.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{test_book.yml => book.yml} (94%) diff --git a/.github/workflows/test_book.yml b/.github/workflows/book.yml similarity index 94% rename from .github/workflows/test_book.yml rename to .github/workflows/book.yml index b58efea..a4af51f 100644 --- a/.github/workflows/test_book.yml +++ b/.github/workflows/book.yml @@ -7,7 +7,7 @@ on: branches: [ "main" ] jobs: - deploy: + test: runs-on: ubuntu-latest permissions: contents: write # To push a branch @@ -27,6 +27,6 @@ jobs: - name: Test Book run: | cd book - export CARGO_MANIFEST_DIR = $(pwd) + export CARGO_MANIFEST_DIR=$(pwd) cargo build mdbook test -L target/debug/deps/ -- 2.45.2 From 1dd1e07d160d0aa1e88622e3c94d2a4662934232 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 10:19:28 +0200 Subject: [PATCH 147/165] rename --- .github/workflows/book.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index a4af51f..850336f 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -1,4 +1,4 @@ -name: Test book +name: Book on: push: -- 2.45.2 From 81fed90c5ccfa3da1c2d9b7aebc63120a80640fd Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 11:38:31 +0200 Subject: [PATCH 148/165] install ruby in ci --- .github/workflows/book.yml | 3 +++ .github/workflows/rust.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 850336f..33910c6 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -17,6 +17,9 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' - name: Install latest mdbook run: | tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b7bd998..b8a3deb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,6 +16,9 @@ jobs: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v3 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' - name: Clippy run: cargo clippy --all-features --verbose -- -D warnings - name: Build -- 2.45.2 From 472d03bc38092807f4bf435c281a0d7990c620b9 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:05:59 +0200 Subject: [PATCH 149/165] build ruby --- .github/workflows/rust.yml | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b8a3deb..9249ba4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] env: CARGO_TERM_COLOR: always @@ -15,13 +15,23 @@ jobs: env: RUSTFLAGS: -D warnings steps: - - uses: actions/checkout@v3 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.4' - - name: Clippy - run: cargo clippy --all-features --verbose -- -D warnings - - name: Build - run: cargo build --all-features --verbose - - name: Run tests - run: cargo test --all-features --verbose + - uses: actions/checkout@v3 + - name: Install Ruby + env: + CC: clang + run: | + url="https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.4.tar.gz" + mkdir ruby_src + curl -sSL $url | tar -xz --directory=./ruby_src + cd ruby_src + mkdir build && cd build + mkdir /rubies + ../configure --without-shared --prefix="/rubies/ruby-3.4" + make install + echo /rubies/ruby-3.4/ruby/bin >> $GITHUB_PATH + - name: Clippy + run: cargo clippy --all-features --verbose -- -D warnings + - name: Build + run: cargo build --all-features --verbose + - name: Run tests + run: cargo test --all-features --verbose -- 2.45.2 From d0936c55492075c892fdfb66b2b46461bd66ed9a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:08:07 +0200 Subject: [PATCH 150/165] fix build --- .github/workflows/rust.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9249ba4..e5cb270 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,10 +25,10 @@ jobs: curl -sSL $url | tar -xz --directory=./ruby_src cd ruby_src mkdir build && cd build - mkdir /rubies - ../configure --without-shared --prefix="/rubies/ruby-3.4" + mkdir rubies + ../configure --without-shared --prefix="${pwd}/rubies/ruby-3.4" make install - echo /rubies/ruby-3.4/ruby/bin >> $GITHUB_PATH + echo `pwd`/rubies/ruby-3.4/ruby/bin >> $GITHUB_PATH - name: Clippy run: cargo clippy --all-features --verbose -- -D warnings - name: Build -- 2.45.2 From acebb4b8c1a50b4cb6906bb8377cb55f34be4d90 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:11:22 +0200 Subject: [PATCH 151/165] fix build --- .github/workflows/rust.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e5cb270..63e1b26 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,12 +21,14 @@ jobs: CC: clang run: | url="https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.4.tar.gz" + prefix=`pwd`/rubies/ruby-3.4 + mkdir rubies mkdir ruby_src curl -sSL $url | tar -xz --directory=./ruby_src cd ruby_src - mkdir build && cd build - mkdir rubies - ../configure --without-shared --prefix="${pwd}/rubies/ruby-3.4" + mkdir build + cd build + ../configure --without-shared --prefix=`prefix` make install echo `pwd`/rubies/ruby-3.4/ruby/bin >> $GITHUB_PATH - name: Clippy -- 2.45.2 From 0b68d54d536c9622cdb3aa9c9b406fa9218383a7 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:14:10 +0200 Subject: [PATCH 152/165] fix build --- .github/workflows/rust.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 63e1b26..ed98f4a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,11 +24,11 @@ jobs: prefix=`pwd`/rubies/ruby-3.4 mkdir rubies mkdir ruby_src - curl -sSL $url | tar -xz --directory=./ruby_src - cd ruby_src + curl -sSL $url | tar -xz + cd ruby-3.4.4 mkdir build cd build - ../configure --without-shared --prefix=`prefix` + ../configure --without-shared --prefix=$prefix make install echo `pwd`/rubies/ruby-3.4/ruby/bin >> $GITHUB_PATH - name: Clippy -- 2.45.2 From ad24866ba4311e6bc283af1fbc86c5ccd7a4c070 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:16:03 +0200 Subject: [PATCH 153/165] fix build --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ed98f4a..01ca3e2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,7 @@ jobs: cd build ../configure --without-shared --prefix=$prefix make install - echo `pwd`/rubies/ruby-3.4/ruby/bin >> $GITHUB_PATH + echo $prefix/bin >> $GITHUB_PATH - name: Clippy run: cargo clippy --all-features --verbose -- -D warnings - name: Build -- 2.45.2 From 97b448d6f4e25c0c06c515641b4b0bebd2a08841 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:29:05 +0200 Subject: [PATCH 154/165] fix build --- .github/workflows/book.yml | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 33910c6..d87f462 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -2,24 +2,36 @@ name: Book on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] jobs: test: runs-on: ubuntu-latest permissions: - contents: write # To push a branch - pages: write # To push to a GitHub Pages site + contents: write # To push a branch + pages: write # To push to a GitHub Pages site id-token: write # To update the deployment status steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.4' + - name: Install Ruby + env: + CC: clang + run: | + url="https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.4.tar.gz" + prefix=`pwd`/rubies/ruby-3.4 + mkdir rubies + mkdir ruby_src + curl -sSL $url | tar -xz + cd ruby-3.4.4 + mkdir build + cd build + ../configure --without-shared --prefix=$prefix + make install + echo $prefix/bin >> $GITHUB_PATH - name: Install latest mdbook run: | tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') -- 2.45.2 From 3e6e14fa71684e2558e9b6772458a951ea855132 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:51:14 +0200 Subject: [PATCH 155/165] installation docs --- book/src/ruby/installation.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/book/src/ruby/installation.md b/book/src/ruby/installation.md index a23325c..427ac4e 100644 --- a/book/src/ruby/installation.md +++ b/book/src/ruby/installation.md @@ -1,5 +1,33 @@ # Installation +## Ruby + +To build `bevy_scriptum` with Ruby support a Ruby installation is needed to be +present on your development machine. + +The easiest way to produce a compatible Ruby installation is to use [rbenv](https://rbenv.org/). + +After installing `rbenv` along with its `ruby-build` plugin you can build and +install a Ruby installation that will work with `bevy_scriptum` by executing: + +```sh +CC=clang rbenv install 3.4.4 +``` + +Above assumes that you also have `clang` installed on your system. +For `clang` installation instruction consult your +Linux distribution or [clang official webiste](https://clang.llvm.org). + +If you rather not use `rbenv` you are free to supply your own installation of +Ruby provided the following is true about it: + +- it is compiled with `clang` +- it is compiled as a static library +- it is accessible as `ruby` within `PATH` or `RUBY` environment variable is set + to path of desired `ruby` binary. + +## Main Library + Add the following to your `Cargo.toml`: ```toml -- 2.45.2 From c6f52bdc8d59961ffee3f6ba0697fab299c28f6a Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:53:19 +0200 Subject: [PATCH 156/165] fix docs --- book/src/ruby/builtin_types.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/book/src/ruby/builtin_types.md b/book/src/ruby/builtin_types.md index 0bb57e1..a661888 100644 --- a/book/src/ruby/builtin_types.md +++ b/book/src/ruby/builtin_types.md @@ -2,10 +2,10 @@ bevy_scriptum provides following types that can be used in Ruby: -- ```Bevy::Vec3``` -- ```Bevy::Entity``` +- `Bevy::Vec3` +- `Bevy::Entity` -## Vec3 +## Bevy::Vec3 ### Class Methods @@ -53,15 +53,15 @@ fn set_translation( } ``` -## BevyEntity +## Bevy::Entity ### Constructor -None - instances can only be acquired by using built-in `entity` global variable. +None - instances can only be acquired by using `Bevy::Entity.current` -### Properties +### Class method -- `index: integer` +- `index` ### Example Ruby usage -- 2.45.2 From bf9c94b7f094ef3a59371d981a99e026e5492a7f Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 17:55:16 +0200 Subject: [PATCH 157/165] fix docs --- book/src/SUMMARY.md | 43 +++++++++++++++--------------- book/src/ruby/builtin_types.md | 2 ++ book/src/ruby/builtin_variables.md | 12 --------- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 0f600b4..348dea7 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -2,30 +2,29 @@ - [Introduction](./introduction.md) - [Runtimes](./runtimes.md) - - [Lua](./lua/lua.md) - - [Installation](./lua/installation.md) - - [Hello World](./lua/hello_world.md) - - [Spawning scripts](./lua/spawning_scripts.md) - - [Calling Rust from Lua](./lua/calling_rust_from_script.md) - - [Calling Lua from Rust](./lua/calling_script_from_rust.md) - - [Interacting with bevy in callbacks](./lua/interacting_with_bevy.md) - - [Builtin types](./lua/builtin_types.md) - - [Builtin variables](./lua/builtin_variables.md) - - [Ruby](./ruby/ruby.md) - - [Installation](./ruby/installation.md) - - [Hello World](./ruby/hello_world.md) - - [Spawning scripts](./ruby/spawning_scripts.md) - - [Calling Rust from Ruby](./ruby/calling_rust_from_script.md) - - [Calling Ruby from Rust](./ruby/calling_script_from_rust.md) - - [Interacting with bevy in callbacks](./ruby/interacting_with_bevy.md) - - [Builtin types](./ruby/builtin_types.md) - - [Builtin variables](./ruby/builtin_variables.md) - - [Rhai](./rhai/rhai.md) - - [Installation](./rhai/installation.md) - - [Hello World(TBD)]() + - [Lua](./lua/lua.md) + - [Installation](./lua/installation.md) + - [Hello World](./lua/hello_world.md) + - [Spawning scripts](./lua/spawning_scripts.md) + - [Calling Rust from Lua](./lua/calling_rust_from_script.md) + - [Calling Lua from Rust](./lua/calling_script_from_rust.md) + - [Interacting with bevy in callbacks](./lua/interacting_with_bevy.md) + - [Builtin types](./lua/builtin_types.md) + - [Builtin variables](./lua/builtin_variables.md) + - [Ruby](./ruby/ruby.md) + - [Installation](./ruby/installation.md) + - [Hello World](./ruby/hello_world.md) + - [Spawning scripts](./ruby/spawning_scripts.md) + - [Calling Rust from Ruby](./ruby/calling_rust_from_script.md) + - [Calling Ruby from Rust](./ruby/calling_script_from_rust.md) + - [Interacting with bevy in callbacks](./ruby/interacting_with_bevy.md) + - [Builtin types](./ruby/builtin_types.md) + - [Rhai](./rhai/rhai.md) + - [Installation](./rhai/installation.md) + - [Hello World(TBD)]() - [Multiple plugins](./multiple_plugins.md) - [Multiple runtimes(TBD)]() - [Implementing custom runtimes(TBD)]() - [Workflow](./workflow/workflow.md) - - [Live-reload](./workflow/live_reload.md) + - [Live-reload](./workflow/live_reload.md) - [Bevy support matrix](./bevy_support_matrix.md) diff --git a/book/src/ruby/builtin_types.md b/book/src/ruby/builtin_types.md index a661888..dd48962 100644 --- a/book/src/ruby/builtin_types.md +++ b/book/src/ruby/builtin_types.md @@ -55,6 +55,8 @@ fn set_translation( ## Bevy::Entity +`Bevy::Entity.current` is currently not available within promise callbacks. + ### Constructor None - instances can only be acquired by using `Bevy::Entity.current` diff --git a/book/src/ruby/builtin_variables.md b/book/src/ruby/builtin_variables.md index 2896ecf..a857bfe 100644 --- a/book/src/ruby/builtin_variables.md +++ b/book/src/ruby/builtin_variables.md @@ -1,13 +1 @@ # Builtin variables - -## entity - -Current entity that the script is atteched to can be retrieved by calling `Bevy::Entity.current`. -It exposes `index` method that returns bevy entity index. -It is useful for accessing entity's components from scripts. -It can be used in the following way: -```ruby -puts("Current entity index: #{Bevy::Entity.current.index}") -``` - -`Bevy::Entity.current` variable is currently not available within promise callbacks. -- 2.45.2 From 2621e1ef532e26d1e12a4bc25d215718e2b87e84 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 18:01:02 +0200 Subject: [PATCH 158/165] wording --- book/src/ruby/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/ruby/installation.md b/book/src/ruby/installation.md index 427ac4e..40f291e 100644 --- a/book/src/ruby/installation.md +++ b/book/src/ruby/installation.md @@ -16,7 +16,7 @@ CC=clang rbenv install 3.4.4 Above assumes that you also have `clang` installed on your system. For `clang` installation instruction consult your -Linux distribution or [clang official webiste](https://clang.llvm.org). +OS vendor provided documentation or [clang official webiste](https://clang.llvm.org). If you rather not use `rbenv` you are free to supply your own installation of Ruby provided the following is true about it: -- 2.45.2 From f404ee91f220508421276b78ed7d9fa9236547eb Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 18:09:59 +0200 Subject: [PATCH 159/165] cache ruby build --- .github/workflows/book.yml | 11 +++++++++++ .github/workflows/rust.yml | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index d87f462..77598d0 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -17,7 +17,14 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Cache Ruby + id: cache-ruby + uses: actions/cache@v4 + with: + path: rubies + key: ${{ runner.os }}-ruby - name: Install Ruby + if: steps.cache-ruby.outputs.cache-hit != 'true' env: CC: clang run: | @@ -32,6 +39,10 @@ jobs: ../configure --without-shared --prefix=$prefix make install echo $prefix/bin >> $GITHUB_PATH + - name: Add Ruby to PATH + run: | + prefix=`pwd`/rubies/ruby-3.4 + echo $prefix/bin >> $GITHUB_PATH - name: Install latest mdbook run: | tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 01ca3e2..85c2931 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,7 +16,14 @@ jobs: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v3 + - name: Cache Ruby + id: cache-ruby + uses: actions/cache@v4 + with: + path: rubies + key: ${{ runner.os }}-ruby - name: Install Ruby + if: steps.cache-ruby.outputs.cache-hit != 'true' env: CC: clang run: | @@ -30,6 +37,9 @@ jobs: cd build ../configure --without-shared --prefix=$prefix make install + - name: Add Ruby to PATH + run: | + prefix=`pwd`/rubies/ruby-3.4 echo $prefix/bin >> $GITHUB_PATH - name: Clippy run: cargo clippy --all-features --verbose -- -D warnings -- 2.45.2 From e22236f201613d3e4864b4ca8af386321ec9cc7d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 18:58:02 +0200 Subject: [PATCH 160/165] dynamic resolutions docs --- Cargo.toml | 1 + book/src/ruby/installation.md | 10 ++++++++++ src/lib.rs | 30 ++++++++++++++++++++++++++++++ src/runtimes/ruby.rs | 4 ++++ 4 files changed, 45 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 10f089d..ad3fc0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ mlua = { version = "0.9.8", features = [ magnus = { version = "0.7.1", optional = true } rb-sys = { version = "*", default-features = false, features = ["link-ruby", "ruby-static"], optional = true } crossbeam-channel = "0.5.15" +libc = "0.2.172" [[example]] name = "call_function_from_rust_rhai" diff --git a/book/src/ruby/installation.md b/book/src/ruby/installation.md index 40f291e..1dc0a82 100644 --- a/book/src/ruby/installation.md +++ b/book/src/ruby/installation.md @@ -38,3 +38,13 @@ bevy_scriptum = { version = "0.8", features = ["ruby"] } If you need a different version of bevy you need to use a matching bevy_scriptum version according to the [bevy support matrix](../bevy_support_matrix.md) + +Ruby also needs dynamic symbol resolution and since `bevy_scriptum` links Ruby +statically the following `build.rs` file is needed to be present in project +root directory. + +```rust +fn main() { + println!("cargo:rustc-link-arg=-rdynamic"); +} +``` diff --git a/src/lib.rs b/src/lib.rs index 0e83be6..94bad40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -371,6 +371,10 @@ pub trait Runtime: Resource + Default { context: &Self::CallContext, args: Vec, ) -> Result; + + fn needs_rdynamic_linking() -> bool { + false + } } pub trait FuncArgs<'a, V, R: Runtime> { @@ -433,6 +437,19 @@ impl BuildScriptingRuntime for App { /// Adds a scripting runtime. Registers required bevy systems that take /// care of processing and running the scripts. fn add_scripting(&mut self, f: impl Fn(ScriptingRuntimeBuilder)) -> &mut Self { + #[cfg(debug_assertions)] + if R::needs_rdynamic_linking() { + if !is_rdynamic_linking() { + panic!( + "Missing `-rdynamic`: symbol resolution failed.\n\ + It is needed by {:?}.\n\ + Please add `println!(\"cargo:rustc-link-arg=-rdynamic\");` to your build.rs\n\ + or set `RUSTFLAGS=\"-C link-arg=-rdynamic\"`.", + std::any::type_name::() + ); + } + } + self.world_mut() .resource_mut::() .insert_after(Update, R::Schedule::default()); @@ -495,6 +512,19 @@ impl Default for Callbacks { } } +pub extern "C" fn is_rdynamic_linking() -> bool { + unsafe { + // Get a function pointer to itself + let addr = is_rdynamic_linking as *const libc::c_void; + let mut info: libc::Dl_info = std::mem::zeroed(); + + // Try to resolve symbol info + let result = libc::dladdr(addr, &mut info); + + result != 0 && !info.dli_sname.is_null() + } +} + pub mod prelude { pub use crate::{BuildScriptingRuntime as _, Runtime as _, Script}; } diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index b41262e..1ccccdd 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -505,6 +505,10 @@ impl Runtime for RubyRuntime { Ok(RubyValue::new(result)) }) } + + fn needs_rdynamic_linking() -> bool { + true + } } pub mod magnus { -- 2.45.2 From 5a4a214f679e77e6d32c5b1e2dd631b82a9321c8 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 19:00:55 +0200 Subject: [PATCH 161/165] clippy --- src/lib.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 94bad40..fe0010a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -438,16 +438,14 @@ impl BuildScriptingRuntime for App { /// care of processing and running the scripts. fn add_scripting(&mut self, f: impl Fn(ScriptingRuntimeBuilder)) -> &mut Self { #[cfg(debug_assertions)] - if R::needs_rdynamic_linking() { - if !is_rdynamic_linking() { - panic!( - "Missing `-rdynamic`: symbol resolution failed.\n\ - It is needed by {:?}.\n\ - Please add `println!(\"cargo:rustc-link-arg=-rdynamic\");` to your build.rs\n\ - or set `RUSTFLAGS=\"-C link-arg=-rdynamic\"`.", - std::any::type_name::() - ); - } + if R::needs_rdynamic_linking() && !is_rdynamic_linking() { + panic!( + "Missing `-rdynamic`: symbol resolution failed.\n\ + It is needed by {:?}.\n\ + Please add `println!(\"cargo:rustc-link-arg=-rdynamic\");` to your build.rs\n\ + or set `RUSTFLAGS=\"-C link-arg=-rdynamic\"`.", + std::any::type_name::() + ); } self.world_mut() -- 2.45.2 From b93482d1254a65ecaee951a1fbfee571b2defbf4 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 19:02:14 +0200 Subject: [PATCH 162/165] remove unused permissions --- .github/workflows/book.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 77598d0..0ea529d 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -9,10 +9,6 @@ on: jobs: test: runs-on: ubuntu-latest - permissions: - contents: write # To push a branch - pages: write # To push to a GitHub Pages site - id-token: write # To update the deployment status steps: - uses: actions/checkout@v4 with: -- 2.45.2 From fd03186d7393fc8d3c1072d6814d54b73d1237f0 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 19:08:08 +0200 Subject: [PATCH 163/165] cfg --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index fe0010a..85f41ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -510,6 +510,7 @@ impl Default for Callbacks { } } +#[cfg(debug_assertions)] pub extern "C" fn is_rdynamic_linking() -> bool { unsafe { // Get a function pointer to itself -- 2.45.2 From 61a8d991404e1a21cc43907bc1ae60e584679557 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 19:11:25 +0200 Subject: [PATCH 164/165] cleanup --- book/src/ruby/builtin_variables.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 book/src/ruby/builtin_variables.md diff --git a/book/src/ruby/builtin_variables.md b/book/src/ruby/builtin_variables.md deleted file mode 100644 index a857bfe..0000000 --- a/book/src/ruby/builtin_variables.md +++ /dev/null @@ -1 +0,0 @@ -# Builtin variables -- 2.45.2 From 5269c57e7bdee5b267d79a6f732767a7cfda1971 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Tue, 27 May 2025 19:15:08 +0200 Subject: [PATCH 165/165] doc fix --- book/src/ruby/spawning_scripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/ruby/spawning_scripts.md b/book/src/ruby/spawning_scripts.md index 9466733..3191f6f 100644 --- a/book/src/ruby/spawning_scripts.md +++ b/book/src/ruby/spawning_scripts.md @@ -1,6 +1,6 @@ # Spawning scripts -To spawn a Lua script you will need to get a handle to a script asset using +To spawn a Ruby script you will need to get a handle to a script asset using bevy's `AssetServer`. ```rust -- 2.45.2