bevy_yoleck/
entity_ref.rs1use std::any::TypeId;
2
3use bevy::ecs::component::Mutable;
4use bevy::prelude::*;
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8use crate::YoleckManaged;
9use crate::entity_uuid::YoleckUuidRegistry;
10use crate::errors::YoleckEntityRefCannotBeResolved;
11
12#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default, Debug)]
58pub struct YoleckEntityRef {
59 #[serde(default, skip_serializing_if = "Option::is_none")]
60 uuid: Option<Uuid>,
61 #[serde(skip)]
62 resolved: Option<Entity>,
63}
64
65impl YoleckEntityRef {
78 pub fn new() -> Self {
79 Self {
80 uuid: None,
81 resolved: None,
82 }
83 }
84
85 pub fn from_uuid(uuid: Uuid) -> Self {
86 Self {
87 uuid: Some(uuid),
88 resolved: None,
89 }
90 }
91
92 pub fn is_some(&self) -> bool {
93 self.uuid.is_some()
94 }
95
96 pub fn is_none(&self) -> bool {
97 self.uuid.is_none()
98 }
99
100 pub fn entity(&self) -> Option<Entity> {
101 self.resolved
102 }
103
104 pub fn uuid(&self) -> Option<Uuid> {
105 self.uuid
106 }
107
108 pub fn clear(&mut self) {
109 self.uuid = None;
110 self.resolved = None;
111 }
112
113 pub fn set(&mut self, uuid: Uuid) {
114 self.uuid = Some(uuid);
115 self.resolved = None;
116 }
117
118 pub fn resolve(
119 &mut self,
120 registry: &YoleckUuidRegistry,
121 ) -> Result<(), YoleckEntityRefCannotBeResolved> {
122 if let Some(uuid) = self.uuid {
123 self.resolved = registry.get(uuid);
124 if self.resolved.is_none() {
125 return Err(YoleckEntityRefCannotBeResolved { uuid });
126 }
127 } else {
128 self.resolved = None;
129 }
130 Ok(())
131 }
132}
133
134pub trait YoleckEntityRefAccessor: Sized + Send + Sync + 'static {
135 fn entity_ref_fields() -> &'static [(&'static str, Option<&'static str>)];
136 fn get_entity_ref_mut(&mut self, field_name: &str) -> &mut YoleckEntityRef;
137 fn resolve_entity_refs(&mut self, registry: &YoleckUuidRegistry);
139}
140
141pub(crate) fn validate_entity_ref_requirements_for<T: YoleckEntityRefAccessor>(
142 construction_specs: &crate::YoleckEntityConstructionSpecs,
143) {
144 for (field_name, filter) in T::entity_ref_fields() {
145 if let Some(required_entity_type) = filter
146 && let Some(entity_type_info) =
147 construction_specs.get_entity_type_info(required_entity_type)
148 && !entity_type_info.has_uuid
149 {
150 error!(
151 "Component '{}' field '{}' requires entity type '{}' to have UUID.",
152 std::any::type_name::<T>(),
153 field_name,
154 required_entity_type
155 );
156 }
157 }
158}
159
160pub fn resolve_entity_refs<
161 T: 'static + Component<Mutability = Mutable> + YoleckEntityRefAccessor,
162>(
163 mut query: Query<(&mut T, &mut YoleckManaged)>,
164 registry: Res<YoleckUuidRegistry>,
165) {
166 for (mut component, mut managed) in query.iter_mut() {
167 component.resolve_entity_refs(registry.as_ref());
168 if let Some(data) = managed.components_data.get_mut(&TypeId::of::<T>())
169 && let Some(data) = data.downcast_mut::<T>()
170 {
171 data.resolve_entity_refs(registry.as_ref());
172 }
173 }
174}