Compare commits

...

5 commits

Author SHA1 Message Date
783183975a remove unwraps 2025-05-26 07:00:00 +02:00
10985ca777 refactor 2025-05-26 07:00:00 +02:00
da759a7e0b refactor 2025-05-26 07:00:00 +02:00
80e753812a clippy 2025-05-26 07:00:00 +02:00
9e29b803b6 cleanupp 2025-05-26 07:00:00 +02:00

View file

@ -1,4 +1,3 @@
// TODO: use funcall_public?
use std::{ use std::{
collections::HashMap, collections::HashMap,
ffi::CString, ffi::CString,
@ -6,8 +5,7 @@ use std::{
thread::{self, JoinHandle}, thread::{self, JoinHandle},
}; };
use ::magnus::{error::IntoError, typed_data::Inspect, value::Opaque}; use ::magnus::{typed_data::Inspect, value::Opaque};
use anyhow::anyhow;
use bevy::{ use bevy::{
asset::Asset, asset::Asset,
ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel},
@ -21,7 +19,7 @@ use magnus::{
value::{Lazy, ReprValue}, value::{Lazy, ReprValue},
}; };
use magnus::{method, prelude::*}; 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 serde::Deserialize;
use crate::{ use crate::{
@ -224,17 +222,15 @@ impl TryConvert for BevyVec3 {
impl From<magnus::Error> for ScriptingError { impl From<magnus::Error> for ScriptingError {
fn from(value: magnus::Error) -> Self { fn from(value: magnus::Error) -> Self {
// TODO: DRY
ScriptingError::RuntimeError( ScriptingError::RuntimeError(
value.inspect(), value.inspect(),
value value
.value() .value()
.unwrap() .expect("No error value")
.funcall::<_, _, magnus::RArray>("backtrace", ()) // TODO: is there an API for this .funcall::<_, _, magnus::RArray>("backtrace", ())
// somehwere .expect("Failed to get backtrace")
.unwrap()
.to_vec::<String>() .to_vec::<String>()
.unwrap() .expect("Failed to convert backtrace to vec of strings")
.join("\n"), .join("\n"),
) )
} }
@ -328,6 +324,25 @@ impl RubyRuntime {
.expect("No Ruby thread") .expect("No Ruby thread")
.execute(Box::new(move |mut ruby| f(&mut ruby))) .execute(Box::new(move |mut ruby| f(&mut ruby)))
} }
fn with_current_entity<T>(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 { impl Runtime for RubyRuntime {
@ -376,33 +391,10 @@ impl Runtime for RubyRuntime {
) -> Result<Self::ScriptData, crate::ScriptingError> { ) -> Result<Self::ScriptData, crate::ScriptingError> {
let script = script.0.clone(); let script = script.0.clone();
self.execute_in_thread(Box::new(move |ruby: &Ruby| { self.execute_in_thread(Box::new(move |ruby: &Ruby| {
// TODO: refactor Self::with_current_entity(ruby, entity, || {
let var = ruby ruby.eval::<magnus::value::Value>(&script)
.class_object() .map_err(|e| <magnus::Error as Into<ScriptingError>>::into(e))
.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::<magnus::value::Value>(&script).map_err(|e| {
ScriptingError::RuntimeError(
e.inspect(),
e.value()
.unwrap()
.funcall::<_, _, magnus::RArray>("backtrace", ())
.unwrap()
.to_vec::<String>()
.unwrap()
.join("\n"),
)
})?; })?;
var.ivar_set("_current", ruby.qnil().as_value())
.expect("Failed to unset current entity handle");
Ok::<Self::ScriptData, ScriptingError>(RubyScriptData) Ok::<Self::ScriptData, ScriptingError>(RubyScriptData)
})) }))
} }
@ -439,11 +431,19 @@ impl Runtime for RubyRuntime {
fn callback(args: &[magnus::Value]) -> magnus::Value { fn callback(args: &[magnus::Value]) -> magnus::Value {
let ruby = magnus::Ruby::get() let ruby = magnus::Ruby::get()
.expect("Failed to get a handle to Ruby API while processing callback"); .expect("Failed to get a handle to Ruby API while processing callback");
let method_name: magnus::value::StaticSymbol = let method_name: magnus::value::StaticSymbol = ruby
ruby.class_object().funcall("__method__", ()).unwrap(); .class_object()
let method_name = method_name.name().unwrap(); .funcall("__method__", ())
let callbacks = RUBY_CALLBACKS.lock().unwrap(); .expect("Failed to get currently called method name symbol from Ruby");
let f = callbacks.get(method_name).unwrap(); 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( let result = f(
(), (),
args.iter() args.iter()
@ -471,23 +471,15 @@ impl Runtime for RubyRuntime {
) -> Result<Self::Value, crate::ScriptingError> { ) -> Result<Self::Value, crate::ScriptingError> {
let name = name.to_string(); let name = name.to_string();
self.execute_in_thread(Box::new(move |ruby: &Ruby| { self.execute_in_thread(Box::new(move |ruby: &Ruby| {
// TOOD: refactor let return_value = Self::with_current_entity(ruby, entity, || {
let var = ruby let args: Vec<_> = args
.class_object() .parse(ruby)
.const_get::<_, RModule>("Bevy") .into_iter()
.unwrap() .map(|a| ruby.get_inner(a.0))
.const_get::<_, RClass>("Entity") .collect();
.unwrap();
var.ivar_set("_current", BevyEntity(entity)).unwrap();
let args: Vec<_> = args ruby.class_object().funcall(name, args.as_slice())
.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();
Ok(RubyValue::new(return_value)) Ok(RubyValue::new(return_value))
})) }))
@ -501,19 +493,16 @@ impl Runtime for RubyRuntime {
) -> Result<Self::Value, crate::ScriptingError> { ) -> Result<Self::Value, crate::ScriptingError> {
let value = value.clone(); let value = value.clone();
self.ruby_thread self.execute_in_thread(move |ruby| {
.as_ref() let f: Proc = TryConvert::try_convert(ruby.get_inner(value.0))?;
.unwrap()
.execute(Box::new(move |ruby| {
let f: Proc = TryConvert::try_convert(ruby.get_inner(value.0)).unwrap();
let args: Vec<_> = args let args: Vec<_> = args
.into_iter() .into_iter()
.map(|x| ruby.get_inner(x.0).as_value()) .map(|x| ruby.get_inner(x.0).as_value())
.collect(); .collect();
let result: magnus::Value = f.funcall("call", args.as_slice())?; let result: magnus::Value = f.funcall("call", args.as_slice())?;
Ok(RubyValue::new(result)) Ok(RubyValue::new(result))
})) })
} }
fn needs_own_thread() -> bool { fn needs_own_thread() -> bool {
@ -532,7 +521,7 @@ pub mod prelude {
impl<T: TryConvert> FromRuntimeValueWithEngine<'_, RubyRuntime> for T { impl<T: TryConvert> 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 {
let inner = engine.get_inner(value.0); 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")
} }
} }
@ -561,7 +550,8 @@ pub struct RArray(pub Opaque<magnus::RArray>);
impl FromRuntimeValueWithEngine<'_, RubyRuntime> for RArray { impl FromRuntimeValueWithEngine<'_, RubyRuntime> for RArray {
fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self { fn from_runtime_value_with_engine(value: RubyValue, engine: &magnus::Ruby) -> Self {
let inner = engine.get_inner(value.0); 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)) RArray(Opaque::from(array))
} }
} }