Skip to content

Commit 21e3333

Browse files
committed
Complete test suite for world methods
1 parent b9ca2b0 commit 21e3333

22 files changed

+258
-23
lines changed

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ impl WorldCallbackAccess {
185185
world.has_resource(resource_id)
186186
}
187187

188+
pub fn has_entity(&self, entity: Entity) -> bool {
189+
let world = self.read().unwrap_or_else(|| panic!("{STALE_WORLD_MSG}"));
190+
world.has_entity(entity)
191+
}
192+
188193
pub fn get_children(&self, entity: Entity) -> ScriptResult<Vec<Entity>> {
189194
let world = self.read().unwrap_or_else(|| panic!("{STALE_WORLD_MSG}"));
190195
world.get_children(entity)
@@ -588,6 +593,11 @@ impl<'w> WorldAccessGuard<'w> {
588593
))
589594
}
590595
}
596+
597+
/// checks if a given entity exists and is valid
598+
pub fn is_valid_entity(&self, entity: Entity) -> bool {
599+
self.cell.get_entity(entity).is_some()
600+
}
591601
}
592602

593603
/// Impl block for higher level world methods
@@ -773,7 +783,18 @@ impl<'w> WorldAccessGuard<'w> {
773783
res_ptr.is_some()
774784
}
775785

786+
pub fn has_entity(&self, entity: Entity) -> bool {
787+
self.is_valid_entity(entity)
788+
}
789+
776790
pub fn get_children(&self, entity: Entity) -> ScriptResult<Vec<Entity>> {
791+
if !self.is_valid_entity(entity) {
792+
return Err(ScriptError::new_runtime_error(format!(
793+
"Entity does not exist or is not valid: {:?}",
794+
entity
795+
)));
796+
}
797+
777798
let access = self
778799
.get_component_access_typed::<Children>()
779800
.unwrap_or_else(|| panic!("{CONCURRENT_ACCESS_MSG}"));
@@ -785,6 +806,13 @@ impl<'w> WorldAccessGuard<'w> {
785806
}
786807

787808
pub fn get_parent(&self, entity: Entity) -> ScriptResult<Option<Entity>> {
809+
if !self.is_valid_entity(entity) {
810+
return Err(ScriptError::new_runtime_error(format!(
811+
"Entity does not exist or is not valid: {:?}",
812+
entity
813+
)));
814+
}
815+
788816
let access = self
789817
.get_component_access_typed::<Parent>()
790818
.unwrap_or_else(|| panic!("{CONCURRENT_ACCESS_MSG}"));
@@ -795,6 +823,22 @@ impl<'w> WorldAccessGuard<'w> {
795823
}
796824

797825
pub fn push_children(&self, parent: Entity, children: &[Entity]) -> ScriptResult<()> {
826+
// verify entities exist
827+
if !self.is_valid_entity(parent) {
828+
return Err(ScriptError::new_runtime_error(format!(
829+
"The parent Entity does not exist or is not valid: {:?}",
830+
parent
831+
)));
832+
}
833+
for c in children {
834+
if !self.is_valid_entity(*c) {
835+
return Err(ScriptError::new_runtime_error(format!(
836+
"the Entity does not exist or is not valid: {:?}",
837+
c
838+
)));
839+
}
840+
}
841+
798842
if let Some(world) = self.get_whole_world_access() {
799843
let mut queue = CommandQueue::default();
800844
let mut commands = Commands::new(&mut queue, world);
@@ -808,6 +852,22 @@ impl<'w> WorldAccessGuard<'w> {
808852
}
809853

810854
pub fn remove_children(&self, parent: Entity, children: &[Entity]) -> ScriptResult<()> {
855+
if !self.is_valid_entity(parent) {
856+
return Err(ScriptError::new_runtime_error(format!(
857+
"The parent Entity does not exist or is not valid: {:?}",
858+
parent
859+
)));
860+
}
861+
862+
for c in children {
863+
if !self.is_valid_entity(*c) {
864+
return Err(ScriptError::new_runtime_error(format!(
865+
"the Entity does not exist or is not valid: {:?}",
866+
c
867+
)));
868+
}
869+
}
870+
811871
if let Some(world) = self.get_whole_world_access() {
812872
let mut queue = CommandQueue::default();
813873
let mut commands = Commands::new(&mut queue, world);
@@ -826,6 +886,22 @@ impl<'w> WorldAccessGuard<'w> {
826886
index: usize,
827887
children: &[Entity],
828888
) -> ScriptResult<()> {
889+
if !self.is_valid_entity(parent) {
890+
return Err(ScriptError::new_runtime_error(format!(
891+
"parent Entity does not exist or is not valid: {:?}",
892+
parent
893+
)));
894+
}
895+
896+
for c in children {
897+
if !self.is_valid_entity(*c) {
898+
return Err(ScriptError::new_runtime_error(format!(
899+
"the Entity does not exist or is not valid: {:?}",
900+
c
901+
)));
902+
}
903+
}
904+
829905
if let Some(world) = self.get_whole_world_access() {
830906
let mut queue = CommandQueue::default();
831907
let mut commands = Commands::new(&mut queue, world);
@@ -838,11 +914,18 @@ impl<'w> WorldAccessGuard<'w> {
838914
Ok(())
839915
}
840916

841-
pub fn despawn_recursive(&self, entity: Entity) -> ScriptResult<()> {
917+
pub fn despawn_recursive(&self, parent: Entity) -> ScriptResult<()> {
918+
if !self.is_valid_entity(parent) {
919+
return Err(ScriptError::new_runtime_error(format!(
920+
"parent Entity does not exist or is not valid: {:?}",
921+
parent
922+
)));
923+
}
924+
842925
if let Some(world) = self.get_whole_world_access() {
843926
let mut queue = CommandQueue::default();
844927
let mut commands = Commands::new(&mut queue, world);
845-
commands.entity(entity).despawn_recursive();
928+
commands.entity(parent).despawn_recursive();
846929
queue.apply(world);
847930
} else {
848931
panic!("{CONCURRENT_WORLD_ACCESS_MSG}")
@@ -852,6 +935,13 @@ impl<'w> WorldAccessGuard<'w> {
852935
}
853936

854937
pub fn despawn(&self, entity: Entity) -> ScriptResult<()> {
938+
if !self.is_valid_entity(entity) {
939+
return Err(ScriptError::new_runtime_error(format!(
940+
"Entity does not exist or is not valid: {:?}",
941+
entity
942+
)));
943+
}
944+
855945
if let Some(world) = self.get_whole_world_access() {
856946
let mut queue = CommandQueue::default();
857947
let mut commands = Commands::new(&mut queue, world);
@@ -864,11 +954,18 @@ impl<'w> WorldAccessGuard<'w> {
864954
Ok(())
865955
}
866956

867-
pub fn despawn_descendants(&self, entity: Entity) -> ScriptResult<()> {
957+
pub fn despawn_descendants(&self, parent: Entity) -> ScriptResult<()> {
958+
if !self.is_valid_entity(parent) {
959+
return Err(ScriptError::new_runtime_error(format!(
960+
"parent Entity does not exist or is not valid: {:?}",
961+
parent
962+
)));
963+
}
964+
868965
if let Some(world) = self.get_whole_world_access() {
869966
let mut queue = CommandQueue::default();
870967
let mut commands = Commands::new(&mut queue, world);
871-
commands.entity(entity).despawn_descendants();
968+
commands.entity(parent).despawn_descendants();
872969
queue.apply(world);
873970
} else {
874971
panic!("{CONCURRENT_WORLD_ACCESS_MSG}")

crates/languages/bevy_mod_scripting_lua/src/bindings/world.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,20 @@ impl TealData for LuaWorld {
236236
},
237237
);
238238

239+
methods.add_method(
240+
"has_entity",
241+
|_, this, entity: LuaReflectValProxy<Entity>| {
242+
let world = this.0.read().ok_or_else(|| {
243+
mlua::Error::external(ScriptError::new_reflection_error("Stale world access"))
244+
})?;
245+
let out: bool = world
246+
.proxy_call(entity, |entity| world.has_entity(entity))
247+
.map_err(mlua::Error::external)?;
248+
249+
Ok(out)
250+
},
251+
);
252+
239253
methods.add_method(
240254
"get_children",
241255
|_, this, entity: LuaReflectValProxy<Entity>| {
@@ -315,7 +329,7 @@ impl TealData for LuaWorld {
315329
})?;
316330
let out: Result<(), ErrorProxy<ScriptError>> = world
317331
.proxy_call(args, |(parent, index, children)| {
318-
world.insert_children(parent, index, &children)
332+
world.insert_children(parent, index - 1, &children)
319333
})
320334
.map_err(mlua::Error::external)?;
321335

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
local entity = world:spawn()
2+
local child = world:spawn()
3+
world:push_children(entity, {child})
4+
world:despawn(entity)
5+
6+
assert(world:has_entity(entity) == false, "Parent should be despawned")
7+
assert(world:has_entity(child) == true, "Child should not be despawned")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
assert_throws(function()
2+
world:despawn_recursive(Entity.from_raw(9999))
3+
end, "parent Entity does not exist")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
local entity = world:spawn()
2+
local child = world:spawn()
3+
world:push_children(entity, {child})
4+
world:despawn_descendants(entity)
5+
6+
assert(world:has_entity(entity) == true, "Parent should not be despawned")
7+
assert(world:has_entity(child) == false, "Child should be despawned")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
assert_throws(function()
2+
world:despawn_recursive(Entity.from_raw(9999))
3+
end, "parent Entity does not exist")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
local entity = world:spawn()
2+
local child = world:spawn()
3+
world:push_children(entity, {child})
4+
world:despawn_recursive(entity)
5+
6+
assert(world:has_entity(entity) == false, "Parent should be despawned")
7+
assert(world:has_entity(child) == false, "Child should be despawned")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
assert_throws(function()
2+
world:despawn_recursive(Entity.from_raw(9999))
3+
end, "parent Entity does not exist")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
local entity = world:spawn()
2+
local child = world:spawn(entity)
3+
4+
world:push_children(entity, {child})
5+
6+
local children = world:get_children(entity)
7+
8+
assert(#children == 1, "Expected 1 child")
9+
assert(children[1]:index() == child:index(), "Child is the wrong entity")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
assert_throws(function()
3+
world:get_children(Entity.from_raw(9999))
4+
end, "Entity does not exist")

0 commit comments

Comments
 (0)