From 4c3953f4c583faeebf79962d9fc05f203c4e796b Mon Sep 17 00:00:00 2001 From: Okko Hakola Date: Mon, 3 Mar 2025 11:31:40 +0200 Subject: [PATCH 1/2] Set hashmap through insert --- .../src/bindings/reference.rs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs index 2711619cd2..b6d3bc83c9 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs @@ -1,14 +1,16 @@ -use super::script_value::{LuaScriptValue, LUA_CALLER_CONTEXT}; +use std::any::TypeId; + use bevy_mod_scripting_core::{ bindings::{ pretty_print::DisplayWithWorld, script_value::ScriptValue, ReflectReference, ThreadWorldContainer, WorldContainer, }, error::InteropError, - reflection_extensions::TypeIdExtensions, + reflection_extensions::{TypeIdExtensions, TypeInfoExtensions}, }; use mlua::{MetaMethod, UserData, UserDataMethods}; -use std::any::TypeId; + +use super::script_value::{LuaScriptValue, LUA_CALLER_CONTEXT}; /// Lua UserData wrapper for [`bevy_mod_scripting_core::bindings::ReflectReference`]. /// Acts as a lua reflection interface. Any value which is registered in the type registry can be interacted with using this type. @@ -79,13 +81,24 @@ impl UserData for LuaReflectReference { let key: ScriptValue = key.into(); let value: ScriptValue = value.into(); let type_id = self_.tail_type_id(world.clone())?.or_fake_id(); - + + let func_name = { + let type_registry = world.type_registry(); + let type_registry = type_registry.read(); + let type_info = type_registry.get_type_info(type_id).ok_or_else(|| { + InteropError::missing_type_data( + type_id, + "Type was not registered, could not determine conversion strategy." + .to_owned(), + ) + })?; + if type_info.is_map() { "insert" } else { "set" } + }; let func = world - .lookup_function([type_id, TypeId::of::()], "set") + .lookup_function([type_id, TypeId::of::()], func_name) .map_err(|f| { InteropError::missing_function(TypeId::of::(), f) })?; - let out = func.call( vec![ScriptValue::Reference(self_), key, value], LUA_CALLER_CONTEXT, From bbac943f323e6b0d6ad7597fbf94fd02252a4953 Mon Sep 17 00:00:00 2001 From: Okko Hakola Date: Mon, 3 Mar 2025 12:48:56 +0200 Subject: [PATCH 2/2] Set & get for map --- assets/tests/set/set_primitives_works.lua | 14 +++++-- .../src/bindings/reference.rs | 32 +++++++++------ .../src/reflection_extensions.rs | 32 ++++++++++++--- .../bevy_mod_scripting_functions/src/core.rs | 39 +++++++++++++++---- .../src/bindings/reference.rs | 19 ++++----- 5 files changed, 96 insertions(+), 40 deletions(-) diff --git a/assets/tests/set/set_primitives_works.lua b/assets/tests/set/set_primitives_works.lua index cc35c7764e..95047b0d49 100644 --- a/assets/tests/set/set_primitives_works.lua +++ b/assets/tests/set/set_primitives_works.lua @@ -7,6 +7,7 @@ resource.int = 42 resource.float = 3.0 resource.vec_usize = { 1, 2 } resource.string_map = { foo = "hello", zoo = "world" } +local map_string = resource.string_map.foo assert(resource.string == "Hello, World!", "Expected 'Hello, World!', got " .. resource.string) assert(resource.bool == true, "Expected true, got " .. tostring(resource.bool)) @@ -14,8 +15,8 @@ assert(resource.int == 42, "Expected 42, got " .. resource.int) assert(resource.float == 3.0, "Expected 3.14, got " .. resource.float) assert(resource.vec_usize[1] == 1, "Expected 1, got " .. resource.vec_usize[1]) assert(resource.string_map:len() == 2, "Expected 2, got " .. resource.string_map:len()) --- assert(resource.string_map["foo"] == "hello", "Expected 'hello', got " .. resource.string_map["foo"]) --- assert(resource.string_map["zoo"] == "world", "Expected 'world', got " .. resource.string_map["zoo"]) +assert(resource.string_map.foo == "hello", "Expected 'hello', got " .. resource.string_map.foo) +assert(resource.string_map.zoo == "world", "Expected 'world', got " .. resource.string_map.zoo) resource.string = "Goodbye, World!" resource.bool = false @@ -29,6 +30,11 @@ assert(resource.bool == false, "Expected false, got " .. tostring(resource.bool) assert(resource.int == 24, "Expected 24, got " .. resource.int) assert(resource.float == 1.0, "Expected 1.41, got " .. resource.float) assert(resource.string_map:len() == 2, "Expected 2, got " .. resource.string_map:len()) --- assert(resource.string_map["foo"] == "goodbye", "Expected 'goodbye', got " .. resource.string_map["foo"]) --- assert(resource.string_map["zoo"] == "world", "Expected 'world', got " .. resource.string_map["zoo"]) +assert(resource.string_map.foo == "goodbye", "Expected 'goodbye', got " .. resource.string_map.foo) +assert(resource.string_map.zoo == "world", "Expected 'world', got " .. resource.string_map.zoo) +resource.string_map.foo = "hello" +resource.string_map.zoo = "once more" + +assert(resource.string_map.foo == "hello", "Expected 'hello', got " .. resource.string_map.foo) +assert(resource.string_map.zoo == "once more", "Expected 'once more', got " .. resource.string_map.zoo) \ No newline at end of file diff --git a/crates/bevy_mod_scripting_core/src/bindings/reference.rs b/crates/bevy_mod_scripting_core/src/bindings/reference.rs index 5b9e334eee..a15e8d410c 100644 --- a/crates/bevy_mod_scripting_core/src/bindings/reference.rs +++ b/crates/bevy_mod_scripting_core/src/bindings/reference.rs @@ -4,13 +4,8 @@ //! reflection gives us access to `dyn PartialReflect` objects via their type name, //! Scripting languages only really support `Clone` objects so if we want to support references, //! we need wrapper types which have owned and ref variants. -use super::{access_map::ReflectAccessId, WorldGuard}; -use crate::{ - bindings::ReflectAllocationId, - error::InteropError, - reflection_extensions::{PartialReflectExt, TypeIdExtensions}, - with_access_read, with_access_write, ReflectAllocator, -}; +use std::{any::TypeId, fmt::Debug}; + use bevy::{ ecs::{ change_detection::MutUntyped, component::ComponentId, entity::Entity, @@ -20,7 +15,14 @@ use bevy::{ ptr::Ptr, reflect::{ParsedPath, PartialReflect, Reflect, ReflectFromPtr, ReflectPath}, }; -use std::{any::TypeId, fmt::Debug}; + +use super::{access_map::ReflectAccessId, WorldGuard}; +use crate::{ + bindings::ReflectAllocationId, + error::InteropError, + reflection_extensions::{PartialReflectExt, TypeIdExtensions}, + with_access_read, with_access_write, ReflectAllocator, +}; /// An accessor to a `dyn PartialReflect` struct, stores a base ID of the type and a reflection path /// safe to build but to reflect on the value inside you need to ensure aliasing rules are upheld @@ -91,6 +93,15 @@ impl ReflectReference { }) } + /// If this is a reference to a map + pub fn is_map(&self, world: WorldGuard) -> bool { + self.with_reflect(world, |r| match r.reflect_ref() { + bevy::reflect::ReflectRef::Map(_) => true, + _ => false, + }) + .unwrap_or_default() + } + /// Create a new reference to a value by allocating it. /// /// You can retrieve the allocator from the world using [`WorldGuard::allocator`]. @@ -115,7 +126,7 @@ impl ReflectReference { /// /// Prefer using [`Self::new_allocated`] if you have a value that implements [`Reflect`]. /// Will fail if the value does not have represented type info with a specific type id. - pub fn new_allocated_boxed_parial_reflect( + pub fn new_allocated_boxed_partial_reflect( value: Box, allocator: &mut ReflectAllocator, ) -> Result { @@ -594,12 +605,11 @@ impl Iterator for ReflectRefIter { mod test { use bevy::prelude::{AppTypeRegistry, World}; + use super::*; use crate::bindings::{ function::script_function::AppScriptFunctionRegistry, AppReflectAllocator, }; - use super::*; - #[derive(Reflect, Component, Debug, Clone, PartialEq)] struct Component(Vec); diff --git a/crates/bevy_mod_scripting_core/src/reflection_extensions.rs b/crates/bevy_mod_scripting_core/src/reflection_extensions.rs index 9e7f5e57b6..eb8a030fbc 100644 --- a/crates/bevy_mod_scripting_core/src/reflection_extensions.rs +++ b/crates/bevy_mod_scripting_core/src/reflection_extensions.rs @@ -1,14 +1,16 @@ //! Various utility functions for working with reflection types. -use crate::{ - bindings::{ReflectReference, WorldGuard}, - error::InteropError, -}; -use bevy::reflect::{PartialReflect, Reflect, ReflectFromReflect, ReflectMut, TypeInfo}; use std::{ any::{Any, TypeId}, cmp::max, }; + +use bevy::reflect::{PartialReflect, Reflect, ReflectFromReflect, ReflectMut, TypeInfo}; + +use crate::{ + bindings::{ReflectReference, WorldGuard}, + error::InteropError, +}; /// Extension trait for [`PartialReflect`] providing additional functionality for working with specific types. pub trait PartialReflectExt { /// Try to remove the value at the given key, if the type supports removing with the given key. @@ -66,6 +68,12 @@ pub trait PartialReflectExt { value: Box, ) -> Result<(), InteropError>; + /// Gets value of a map using index key, if possible + fn try_get_boxed( + &self, + index: Box, + ) -> Result>, InteropError>; + /// Tries to insert the given value into the type, if the type is a container type. /// The insertion will happen at the natural `end` of the container. /// For lists, this is the length of the list. @@ -263,6 +271,20 @@ impl PartialReflectExt for T { } } + fn try_get_boxed( + &self, + key: Box, + ) -> Result>, InteropError> { + match self.reflect_ref() { + bevy::reflect::ReflectRef::Map(m) => Ok(m.get(key.as_ref()).map(|v| v.clone_value())), + _ => Err(InteropError::unsupported_operation( + self.get_represented_type_info().map(|ti| ti.type_id()), + Some(key), + "get".to_owned(), + )), + } + } + fn try_push_boxed(&mut self, value: Box) -> Result<(), InteropError> { match self.reflect_mut() { bevy::reflect::ReflectMut::List(l) => { diff --git a/crates/bevy_mod_scripting_functions/src/core.rs b/crates/bevy_mod_scripting_functions/src/core.rs index 95e29711c6..58f7f5bb8d 100644 --- a/crates/bevy_mod_scripting_functions/src/core.rs +++ b/crates/bevy_mod_scripting_functions/src/core.rs @@ -351,13 +351,36 @@ impl ReflectReference { key: ScriptValue, ) -> Result { profiling::function_scope!("get"); - let mut path: ParsedPath = key.try_into()?; - if ctxt.convert_to_0_indexed() { - path.convert_to_0_indexed(); - } - self_.index_path(path); let world = ctxt.world()?; - ReflectReference::into_script_ref(self_, world) + let is_map = self_.is_map(world.clone()); + if is_map { + let key_type_id = self_.key_type_id(world.clone())?.ok_or_else(|| { + InteropError::unsupported_operation( + self_.tail_type_id(world.clone()).unwrap_or_default(), + Some(Box::new(key.clone())), + "Could not get key type id. Are you trying to get element from a type that's not a map?".to_owned(), + ) + })?; + let key = >::from_script_ref(key_type_id, key, world.clone())?; + let result = self_.with_reflect(world.clone(), |s| s.try_get_boxed(key).ok()?)?; + if let Some(value) = result { + let value_ref = { + let allocator = world.allocator(); + let mut allocator = allocator.write(); + ReflectReference::new_allocated_boxed_partial_reflect(value, &mut allocator)? + }; + ReflectReference::into_script_ref(value_ref, world) + } else { + Ok(ScriptValue::Unit) + } + } else { + let mut path: ParsedPath = key.try_into()?; + if ctxt.convert_to_0_indexed() { + path.convert_to_0_indexed(); + } + self_.index_path(path); + ReflectReference::into_script_ref(self_, world) + } } fn set( @@ -420,7 +443,7 @@ impl ReflectReference { let reference = { let allocator = world.allocator(); let mut allocator = allocator.write(); - ReflectReference::new_allocated_boxed_parial_reflect(o, &mut allocator)? + ReflectReference::new_allocated_boxed_partial_reflect(o, &mut allocator)? }; ReflectReference::into_script_ref(reference, world) @@ -500,7 +523,7 @@ impl ReflectReference { let reference = { let allocator = world.allocator(); let mut allocator = allocator.write(); - ReflectReference::new_allocated_boxed_parial_reflect(removed, &mut allocator)? + ReflectReference::new_allocated_boxed_partial_reflect(removed, &mut allocator)? }; ReflectReference::into_script_ref(reference, world) } diff --git a/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs b/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs index b6d3bc83c9..d7e8c89b1a 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs @@ -6,7 +6,7 @@ use bevy_mod_scripting_core::{ ThreadWorldContainer, WorldContainer, }, error::InteropError, - reflection_extensions::{TypeIdExtensions, TypeInfoExtensions}, + reflection_extensions::TypeIdExtensions, }; use mlua::{MetaMethod, UserData, UserDataMethods}; @@ -81,18 +81,13 @@ impl UserData for LuaReflectReference { let key: ScriptValue = key.into(); let value: ScriptValue = value.into(); let type_id = self_.tail_type_id(world.clone())?.or_fake_id(); - + let is_map = self_.is_map(world.clone()); let func_name = { - let type_registry = world.type_registry(); - let type_registry = type_registry.read(); - let type_info = type_registry.get_type_info(type_id).ok_or_else(|| { - InteropError::missing_type_data( - type_id, - "Type was not registered, could not determine conversion strategy." - .to_owned(), - ) - })?; - if type_info.is_map() { "insert" } else { "set" } + if is_map { + "insert" + } else { + "set" + } }; let func = world .lookup_function([type_id, TypeId::of::()], func_name)