Change ruby API

This commit is contained in:
Jaroslaw Konik 2025-05-18 22:33:44 +02:00
parent fb627ffdda
commit 900d1579ee
4 changed files with 34 additions and 29 deletions

View file

@ -1,3 +1,3 @@
def test_func def test_func
rust_func($entity.index) rust_func(Bevy::Entity.current.index)
end end

View file

@ -1,4 +1,4 @@
$index = $entity.index $index = Bevy::Entity.current.index
def test_func def test_func
rust_func($index) rust_func($index)

View file

@ -1,3 +1,3 @@
def test_func def test_func
rust_func($entity) rust_func(Bevy::Entity.current)
end end

View file

@ -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::{ use std::{
collections::HashMap, collections::HashMap,
sync::{Arc, Condvar, LazyLock, Mutex}, sync::{Arc, Condvar, LazyLock, Mutex},
@ -21,7 +13,7 @@ use bevy::{
}; };
use magnus::{ use magnus::{
block::Proc, data_type_builder, function, value::Lazy, DataType, DataTypeFunctions, IntoValue, 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 magnus::{method, prelude::*};
use serde::Deserialize; use serde::Deserialize;
@ -115,8 +107,10 @@ unsafe impl TypedData for Promise<(), RubyValue> {
fn class(ruby: &Ruby) -> magnus::RClass { fn class(ruby: &Ruby) -> magnus::RClass {
static CLASS: Lazy<RClass> = Lazy::new(|ruby| { static CLASS: Lazy<RClass> = Lazy::new(|ruby| {
let class = ruby let class = ruby
.define_module("Bevy")
.unwrap()
.define_class("Promise", ruby.class_object()) .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.undef_default_alloc_func();
class class
}); });
@ -124,7 +118,8 @@ unsafe impl TypedData for Promise<(), RubyValue> {
} }
fn data_type() -> &'static magnus::DataType { 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 &DATA_TYPE
} }
} }
@ -155,7 +150,7 @@ fn then(r_self: magnus::Value) -> magnus::Value {
} }
#[derive(Clone)] #[derive(Clone)]
#[magnus::wrap(class = "BevyEntity")] #[magnus::wrap(class = "Bevy::Entity")]
pub struct BevyEntity(pub Entity); pub struct BevyEntity(pub Entity);
impl BevyEntity { impl BevyEntity {
@ -219,13 +214,21 @@ impl Default for RubyRuntime {
ruby_thread ruby_thread
.execute(Box::new(|ruby| { .execute(Box::new(|ruby| {
// TODO: maybe put promise in a module , maybe do so for other runtimes too let module = ruby.define_module("Bevy")?;
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())?; 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))?; 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())?; let vec3 = ruby.define_class("Vec3", ruby.class_object())?;
vec3.define_singleton_method("new", function!(BevyVec3::new, 3))?; vec3.define_singleton_method("new", function!(BevyVec3::new, 3))?;
vec3.define_method("x", method!(BevyVec3::x, 0))?; vec3.define_method("x", method!(BevyVec3::x, 0))?;
@ -314,14 +317,14 @@ impl Runtime for RubyRuntime {
.unwrap() .unwrap()
.execute(Box::new(move |ruby| { .execute(Box::new(move |ruby| {
let var = ruby let var = ruby
.define_variable(ENTITY_VAR_NAME, BevyEntity(entity)) .class_object()
.const_get::<_, RModule>("Bevy")
.unwrap()
.const_get::<_, RClass>("Entity")
.unwrap(); .unwrap();
var.ivar_set("_current", BevyEntity(entity)).unwrap();
let value = ruby.eval::<magnus::value::Value>(&script).unwrap(); let value = ruby.eval::<magnus::value::Value>(&script).unwrap();
var.ivar_set("_current", ruby.qnil().as_value()).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) RubyValue::new(value)
})); }));
@ -398,8 +401,12 @@ impl Runtime for RubyRuntime {
.unwrap() .unwrap()
.execute(Box::new(move |ruby| { .execute(Box::new(move |ruby| {
let var = ruby let var = ruby
.define_variable(ENTITY_VAR_NAME, BevyEntity(entity)) .class_object()
.const_get::<_, RModule>("Bevy")
.unwrap()
.const_get::<_, RClass>("Entity")
.unwrap(); .unwrap();
var.ivar_set("_current", BevyEntity(entity)).unwrap();
let args: Vec<_> = args let args: Vec<_> = args
.parse(&ruby) .parse(&ruby)
@ -409,9 +416,7 @@ impl Runtime for RubyRuntime {
let return_value: magnus::Value = let return_value: magnus::Value =
ruby.class_object().funcall(name, args.as_slice())?; ruby.class_object().funcall(name, args.as_slice())?;
// SAFETY: this is guaranteed to be executed on a single thread var.ivar_set("_current", ruby.qnil().as_value()).unwrap();
// so should be safe according to Magnus documentation
unsafe { *var = ruby.qnil().as_value() };
Ok(RubyValue::new(return_value)) Ok(RubyValue::new(return_value))
})) }))