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>(|_| {});