Skip to content

fix: Set get hashmap #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions assets/tests/set/set_primitives_works.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ 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))
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
Expand All @@ -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)
32 changes: 21 additions & 11 deletions crates/bevy_mod_scripting_core/src/bindings/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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`].
Expand All @@ -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<dyn PartialReflect>,
allocator: &mut ReflectAllocator,
) -> Result<ReflectReference, InteropError> {
Expand Down Expand Up @@ -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<String>);

Expand Down
32 changes: 27 additions & 5 deletions crates/bevy_mod_scripting_core/src/reflection_extensions.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -66,6 +68,12 @@ pub trait PartialReflectExt {
value: Box<dyn PartialReflect>,
) -> Result<(), InteropError>;

/// Gets value of a map using index key, if possible
fn try_get_boxed(
&self,
index: Box<dyn PartialReflect>,
) -> Result<Option<Box<dyn PartialReflect>>, 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.
Expand Down Expand Up @@ -263,6 +271,20 @@ impl<T: PartialReflect + ?Sized> PartialReflectExt for T {
}
}

fn try_get_boxed(
&self,
key: Box<dyn PartialReflect>,
) -> Result<Option<Box<dyn PartialReflect>>, 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<dyn PartialReflect>) -> Result<(), InteropError> {
match self.reflect_mut() {
bevy::reflect::ReflectMut::List(l) => {
Expand Down
39 changes: 31 additions & 8 deletions crates/bevy_mod_scripting_functions/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,36 @@ impl ReflectReference {
key: ScriptValue,
) -> Result<ScriptValue, InteropError> {
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 = <Box<dyn PartialReflect>>::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(
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down
18 changes: 13 additions & 5 deletions crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
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,
Expand All @@ -8,7 +9,8 @@ use bevy_mod_scripting_core::{
reflection_extensions::TypeIdExtensions,
};
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.
Expand Down Expand Up @@ -79,13 +81,19 @@ 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 = {
if is_map {
"insert"
} else {
"set"
}
};
let func = world
.lookup_function([type_id, TypeId::of::<ReflectReference>()], "set")
.lookup_function([type_id, TypeId::of::<ReflectReference>()], func_name)
.map_err(|f| {
InteropError::missing_function(TypeId::of::<ReflectReference>(), f)
})?;

let out = func.call(
vec![ScriptValue::Reference(self_), key, value],
LUA_CALLER_CONTEXT,
Expand Down
Loading