Skip to content

Commit d35d9b9

Browse files
committed
Add commands + get/mut methods on entity_ref/mut
1 parent 156a097 commit d35d9b9

File tree

3 files changed

+222
-20
lines changed

3 files changed

+222
-20
lines changed

crates/bevy_ecs/src/system/commands.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,24 @@ impl<'a, 'b> EntityCommands<'a, 'b> {
281281
pub fn commands(&mut self) -> &mut Commands<'a> {
282282
self.commands
283283
}
284+
285+
pub fn insert_relation<T: Component>(&mut self, relation: T, target: Entity) -> &mut Self {
286+
self.commands.add(InsertRelation {
287+
this: self.entity,
288+
relation,
289+
target,
290+
});
291+
self
292+
}
293+
294+
pub fn remove_relation<T: Component>(&mut self, target: Entity) -> &mut Self {
295+
self.commands.add(RemoveRelation::<T> {
296+
this: self.entity,
297+
target,
298+
p: PhantomData,
299+
});
300+
self
301+
}
284302
}
285303

286304
#[derive(Debug)]
@@ -413,6 +431,34 @@ impl<T: Component> Command for RemoveResource<T> {
413431
}
414432
}
415433

434+
pub struct InsertRelation<T: Component> {
435+
this: Entity,
436+
relation: T,
437+
target: Entity,
438+
}
439+
440+
impl<T: Component> Command for InsertRelation<T> {
441+
fn write(self: Box<Self>, world: &mut World) {
442+
world
443+
.entity_mut(self.this)
444+
.insert_relation(self.relation, self.target);
445+
}
446+
}
447+
448+
pub struct RemoveRelation<T: Component> {
449+
this: Entity,
450+
target: Entity,
451+
p: PhantomData<T>,
452+
}
453+
454+
impl<T: Component> Command for RemoveRelation<T> {
455+
fn write(self: Box<Self>, world: &mut World) {
456+
world
457+
.entity_mut(self.this)
458+
.remove_relation::<T>(self.target);
459+
}
460+
}
461+
416462
#[cfg(test)]
417463
#[allow(clippy::float_cmp, clippy::approx_constant)]
418464
mod tests {
@@ -510,4 +556,63 @@ mod tests {
510556
assert!(!world.contains_resource::<i32>());
511557
assert!(world.contains_resource::<f64>());
512558
}
559+
560+
#[test]
561+
fn relations() {
562+
struct MyRelation(bool);
563+
struct MyRelationTwo(u32);
564+
565+
let mut world = World::default();
566+
let mut queue = CommandQueue::default();
567+
568+
let t1 = world.spawn().id();
569+
let t2 = world.spawn().id();
570+
let t3 = world.spawn().id();
571+
572+
let mut commands = Commands::new(&mut queue, &world);
573+
let e1 = commands
574+
.spawn()
575+
.insert_relation(MyRelation(true), t1)
576+
.insert_relation(MyRelation(false), t2)
577+
.insert_relation(MyRelationTwo(18), t3)
578+
.id();
579+
let e2 = commands
580+
.spawn()
581+
.insert_relation(MyRelation(true), t3)
582+
.insert_relation(MyRelationTwo(10), t1)
583+
.insert_relation(MyRelation(false), t1)
584+
.insert_relation(MyRelation(false), t3)
585+
.id();
586+
drop(commands);
587+
queue.apply(&mut world);
588+
589+
let e1 = world.entity(e1);
590+
assert!(e1.get_relation::<MyRelation>(Some(t1)).unwrap().0 == true);
591+
assert!(e1.get_relation::<MyRelation>(Some(t2)).unwrap().0 == false);
592+
assert!(e1.get_relation::<MyRelationTwo>(Some(t3)).unwrap().0 == 18);
593+
let e1 = e1.id();
594+
595+
let e2 = world.entity(e2);
596+
assert!(e2.get_relation::<MyRelation>(Some(t3)).unwrap().0 == false);
597+
assert!(e2.get_relation::<MyRelationTwo>(Some(t1)).unwrap().0 == 10);
598+
assert!(e2.get_relation::<MyRelation>(Some(t1)).unwrap().0 == false);
599+
let e2 = e2.id();
600+
601+
let mut commands = Commands::new(&mut queue, &world);
602+
603+
commands.entity(e1).remove_relation::<MyRelation>(t2);
604+
commands.entity(e2).remove_relation::<MyRelation>(t3);
605+
606+
drop(commands);
607+
queue.apply(&mut world);
608+
609+
assert!(world
610+
.entity(e1)
611+
.get_relation::<MyRelation>(Some(t2))
612+
.is_none());
613+
assert!(world
614+
.entity(e2)
615+
.get_relation::<MyRelation>(Some(t3))
616+
.is_none());
617+
}
513618
}

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,21 @@ impl<'w> EntityRef<'w> {
7575

7676
#[inline]
7777
pub fn get<T: Component>(&self) -> Option<&'w T> {
78+
self.get_relation::<T>(None)
79+
}
80+
81+
#[inline]
82+
pub fn get_relation<T: Component>(&self, target: Option<Entity>) -> Option<&'w T> {
7883
// SAFE: entity location is valid and returned component is of type T
7984
unsafe {
80-
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
81-
.map(|value| &*value.cast::<T>())
85+
get_component_with_type(
86+
self.world,
87+
TypeId::of::<T>(),
88+
target,
89+
self.entity,
90+
self.location,
91+
)
92+
.map(|value| &*value.cast::<T>())
8293
}
8394
}
8495

@@ -91,13 +102,33 @@ impl<'w> EntityRef<'w> {
91102
last_change_tick: u32,
92103
change_tick: u32,
93104
) -> Option<Mut<'w, T>> {
94-
get_component_and_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
95-
.map(|(value, ticks)| Mut {
96-
value: &mut *value.cast::<T>(),
97-
component_ticks: &mut *ticks,
98-
last_change_tick,
99-
change_tick,
100-
})
105+
// SAFETY: Caller
106+
self.get_relation_unchecked_mut(None, last_change_tick, change_tick)
107+
}
108+
109+
/// # Safety
110+
/// This allows aliased mutability. You must make sure this call does not result in multiple
111+
/// mutable references to the same component
112+
#[inline]
113+
pub unsafe fn get_relation_unchecked_mut<T: Component>(
114+
&self,
115+
target: Option<Entity>,
116+
last_change_tick: u32,
117+
change_tick: u32,
118+
) -> Option<Mut<'w, T>> {
119+
get_component_and_ticks_with_type(
120+
self.world,
121+
TypeId::of::<T>(),
122+
target,
123+
self.entity,
124+
self.location,
125+
)
126+
.map(|(value, ticks)| Mut {
127+
value: &mut *value.cast::<T>(),
128+
component_ticks: &mut *ticks,
129+
last_change_tick,
130+
change_tick,
131+
})
101132
}
102133
}
103134

@@ -165,21 +196,38 @@ impl<'w> EntityMut<'w> {
165196

166197
#[inline]
167198
pub fn get<T: Component>(&self) -> Option<&'w T> {
199+
self.get_relation::<T>(None)
200+
}
201+
202+
#[inline]
203+
pub fn get_relation<T: Component>(&self, target: Option<Entity>) -> Option<&'w T> {
168204
// SAFE: entity location is valid and returned component is of type T
169205
unsafe {
170-
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
171-
.map(|value| &*value.cast::<T>())
206+
get_component_with_type(
207+
self.world,
208+
TypeId::of::<T>(),
209+
target,
210+
self.entity,
211+
self.location,
212+
)
213+
.map(|value| &*value.cast::<T>())
172214
}
173215
}
174216

175217
#[inline]
176218
pub fn get_mut<T: Component>(&mut self) -> Option<Mut<'w, T>> {
219+
self.get_relation_mut::<T>(None)
220+
}
221+
222+
#[inline]
223+
pub fn get_relation_mut<T: Component>(&mut self, target: Option<Entity>) -> Option<Mut<'w, T>> {
177224
// SAFE: world access is unique, entity location is valid, and returned component is of type
178225
// T
179226
unsafe {
180227
get_component_and_ticks_with_type(
181228
self.world,
182229
TypeId::of::<T>(),
230+
target,
183231
self.entity,
184232
self.location,
185233
)
@@ -197,13 +245,30 @@ impl<'w> EntityMut<'w> {
197245
/// mutable references to the same component
198246
#[inline]
199247
pub unsafe fn get_unchecked_mut<T: Component>(&self) -> Option<Mut<'w, T>> {
200-
get_component_and_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
201-
.map(|(value, ticks)| Mut {
202-
value: &mut *value.cast::<T>(),
203-
component_ticks: &mut *ticks,
204-
last_change_tick: self.world.last_change_tick(),
205-
change_tick: self.world.read_change_tick(),
206-
})
248+
self.get_relation_unchecked_mut::<T>(None)
249+
}
250+
251+
/// # Safety
252+
/// This allows aliased mutability. You must make sure this call does not result in multiple
253+
/// mutable references to the same component
254+
#[inline]
255+
pub unsafe fn get_relation_unchecked_mut<T: Component>(
256+
&self,
257+
target: Option<Entity>,
258+
) -> Option<Mut<'w, T>> {
259+
get_component_and_ticks_with_type(
260+
self.world,
261+
TypeId::of::<T>(),
262+
target,
263+
self.entity,
264+
self.location,
265+
)
266+
.map(|(value, ticks)| Mut {
267+
value: &mut *value.cast::<T>(),
268+
component_ticks: &mut *ticks,
269+
last_change_tick: self.world.last_change_tick(),
270+
change_tick: self.world.read_change_tick(),
271+
})
207272
}
208273

209274
// TODO: factor out non-generic part to cut down on monomorphization (just check perf)
@@ -796,28 +861,32 @@ unsafe fn remove_component(
796861
}
797862
}
798863

864+
/// Set `target` to None if you just want normal components
799865
/// # Safety
800866
/// `entity_location` must be within bounds of an archetype that exists.
801867
unsafe fn get_component_with_type(
802868
world: &World,
803869
type_id: TypeId,
870+
target: Option<Entity>,
804871
entity: Entity,
805872
location: EntityLocation,
806873
) -> Option<*mut u8> {
807874
let kind = world.components.get_component_kind(type_id)?;
808-
get_component(world, kind.id(), None, entity, location)
875+
get_component(world, kind.id(), target, entity, location)
809876
}
810877

878+
/// Set `target` to None if you just want normal components
811879
/// # Safety
812880
/// `entity_location` must be within bounds of an archetype that exists.
813881
pub(crate) unsafe fn get_component_and_ticks_with_type(
814882
world: &World,
815883
type_id: TypeId,
884+
target: Option<Entity>,
816885
entity: Entity,
817886
location: EntityLocation,
818887
) -> Option<(*mut u8, *mut ComponentTicks)> {
819888
let kind_info = world.components.get_component_kind(type_id)?;
820-
get_component_and_ticks(world, kind_info.id(), None, entity, location)
889+
get_component_and_ticks(world, kind_info.id(), target, entity, location)
821890
}
822891

823892
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {

crates/bevy_ecs/src/world/tests.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,34 @@ fn relation_query_mut_raw(storage_type: StorageType) {
351351
assert!(was_targeter1 && was_targeter2 && was_targeter3);
352352
}
353353

354+
#[test]
355+
fn some_example_code() {
356+
#[derive(PartialEq, Eq, Debug)]
357+
struct MyRelation;
358+
359+
let mut world = World::new();
360+
361+
let target1 = world.spawn().id();
362+
let target2 = world.spawn().id();
363+
let my_entity = world
364+
.spawn()
365+
.insert_relation(MyRelation, target1)
366+
.insert_relation(MyRelation, target2)
367+
.id();
368+
369+
let mut iterated_entities = Vec::new();
370+
let mut query = world.query::<(Entity, &Relation<MyRelation>)>();
371+
for (entity, relations) in query.iter_mut(&mut world) {
372+
iterated_entities.push(entity);
373+
assert_eq!(
374+
&relations.collect::<Vec<_>>(),
375+
&[(Some(target1), &MyRelation), (Some(target2), &MyRelation)],
376+
);
377+
}
378+
379+
assert_eq!(&iterated_entities, &[my_entity]);
380+
}
381+
354382
macro_rules! query_conflict_tests {
355383
($($name:ident => <$param:ty>)*) => {
356384
$(

0 commit comments

Comments
 (0)