bones_scripting/lua/bindings/
ecsref.rs1use super::*;
2
3#[derive(HasSchema)]
6#[schema(no_clone, no_default)]
7pub struct SchemaLuaEcsRefMetatable(pub fn(piccolo::Context) -> piccolo::Table);
8
9#[derive(Clone)]
11pub struct EcsRef {
12 pub data: EcsRefData,
14 pub path: Ustr,
16}
17impl Default for EcsRef {
18 fn default() -> Self {
19 #[derive(HasSchema, Clone, Default)]
20 struct Void;
21 Self {
22 data: EcsRefData::Free(Rc::new(AtomicCell::new(SchemaBox::new(Void)))),
23 path: default(),
24 }
25 }
26}
27impl<'gc> FromValue<'gc> for &'gc EcsRef {
28 fn from_value(_ctx: Context<'gc>, value: Value<'gc>) -> Result<Self, piccolo::TypeError> {
29 value.as_static_user_data::<EcsRef>()
30 }
31}
32
33impl EcsRef {
34 pub fn borrow(&self) -> EcsRefBorrow<'_> {
36 EcsRefBorrow {
37 borrow: self.data.borrow(),
38 path: self.path,
39 }
40 }
41
42 pub fn borrow_mut(&self) -> EcsRefBorrowMut<'_> {
44 EcsRefBorrowMut {
45 borrow: self.data.borrow_mut(),
46 path: self.path,
47 }
48 }
49
50 pub fn into_value(self, ctx: Context) -> Value {
52 let metatable = ctx.singletons().get(ctx, self.metatable_fn());
53 let ecsref = UserData::new_static(&ctx, self);
54 ecsref.set_metatable(&ctx, Some(metatable));
55 ecsref.into()
56 }
57}
58
59pub struct EcsRefBorrow<'a> {
61 borrow: EcsRefBorrowKind<'a>,
62 path: Ustr,
63}
64
65impl EcsRefBorrow<'_> {
66 pub fn schema_ref(&self) -> Result<SchemaRef<'_>, EcsRefBorrowError> {
68 let b = self.borrow.schema_ref()?;
69 let b = b
70 .field_path(FieldPath(self.path))
71 .ok_or(EcsRefBorrowError::FieldNotFound(self.path))?;
72 Ok(b)
73 }
74}
75
76pub struct EcsRefBorrowMut<'a> {
78 borrow: EcsRefBorrowMutKind<'a>,
79 path: Ustr,
80}
81
82impl EcsRefBorrowMut<'_> {
83 pub fn schema_ref_mut(&mut self) -> Result<SchemaRefMut<'_>, EcsRefBorrowError> {
85 let b = self.borrow.schema_ref_mut()?;
86 let b = b
87 .into_field_path(FieldPath(self.path))
88 .ok_or(EcsRefBorrowError::FieldNotFound(self.path))?;
89 Ok(b)
90 }
91}
92
93#[derive(Clone)]
95pub enum EcsRefData {
96 Resource(AtomicUntypedResource),
98 Component(ComponentRef),
100 Asset(AssetRef),
102 Free(Rc<AtomicCell<SchemaBox>>),
105}
106
107pub enum EcsRefBorrowKind<'a> {
109 Resource(Ref<'a, Option<SchemaBox>>),
110 Component(ComponentBorrow<'a>),
111 Free(Ref<'a, SchemaBox>),
112 Asset(Option<MappedRef<'a, Cid, LoadedAsset, SchemaBox>>),
113}
114
115#[derive(Debug)]
117pub enum EcsRefBorrowError {
118 MissingResource,
119 MissingComponent {
120 entity: Entity,
121 component_name: &'static str,
122 },
123 AssetNotLoaded,
124 FieldNotFound(Ustr),
125}
126impl std::error::Error for EcsRefBorrowError {}
127impl std::fmt::Display for EcsRefBorrowError {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 match self {
130 EcsRefBorrowError::MissingComponent {
131 entity,
132 component_name,
133 } => write!(
134 f,
135 "Cannot access variable because entity {entity:?} no longer has the \
136 component `{component_name}`. This may happen if you remove the cmponent \
137 while you still have a variable referencing the component data, and you \
138 then try to access the component data through the variable."
139 ),
140 EcsRefBorrowError::AssetNotLoaded => write!(f, "Asset not loaded"),
141 EcsRefBorrowError::FieldNotFound(field) => write!(f, "Field not found: {field}"),
142 EcsRefBorrowError::MissingResource => {
143 write!(f, "Resource not in world.")
144 }
145 }
146 }
147}
148
149impl EcsRefBorrowKind<'_> {
150 pub fn schema_ref(&self) -> Result<SchemaRef<'_>, EcsRefBorrowError> {
155 match self {
156 EcsRefBorrowKind::Resource(r) => Ok(r
157 .as_ref()
158 .ok_or(EcsRefBorrowError::MissingResource)?
159 .as_ref()),
160 EcsRefBorrowKind::Component(c) => {
161 c.borrow
162 .get_ref(c.entity)
163 .ok_or(EcsRefBorrowError::MissingComponent {
164 entity: c.entity,
165 component_name: &c.borrow.schema().full_name,
166 })
167 }
168 EcsRefBorrowKind::Free(f) => Ok(f.as_ref()),
169 EcsRefBorrowKind::Asset(a) => a
170 .as_ref()
171 .map(|x| x.as_ref())
172 .ok_or(EcsRefBorrowError::AssetNotLoaded),
173 }
174 }
175}
176
177pub struct ComponentBorrow<'a> {
179 pub borrow: Ref<'a, UntypedComponentStore>,
180 pub entity: Entity,
181}
182
183pub struct ComponentBorrowMut<'a> {
185 pub borrow: RefMut<'a, UntypedComponentStore>,
186 pub entity: Entity,
187}
188
189pub enum EcsRefBorrowMutKind<'a> {
191 Resource(RefMut<'a, Option<SchemaBox>>),
192 Component(ComponentBorrowMut<'a>),
193 Free(RefMut<'a, SchemaBox>),
194 Asset(Option<MappedRefMut<'a, Cid, LoadedAsset, SchemaBox>>),
195}
196
197impl EcsRefBorrowMutKind<'_> {
198 pub fn schema_ref_mut(&mut self) -> Result<SchemaRefMut<'_>, EcsRefBorrowError> {
203 match self {
204 EcsRefBorrowMutKind::Resource(r) => Ok(r
205 .as_mut()
206 .ok_or(EcsRefBorrowError::MissingResource)?
207 .as_mut()),
208 EcsRefBorrowMutKind::Component(c) => {
209 c.borrow
210 .get_ref_mut(c.entity)
211 .ok_or(EcsRefBorrowError::MissingComponent {
212 entity: c.entity,
213 component_name: &c.borrow.schema().full_name,
214 })
215 }
216 EcsRefBorrowMutKind::Free(f) => Ok(f.as_mut()),
217 EcsRefBorrowMutKind::Asset(a) => a
218 .as_mut()
219 .map(|x| x.as_mut())
220 .ok_or(EcsRefBorrowError::AssetNotLoaded),
221 }
222 }
223}
224
225impl EcsRefData {
226 pub fn borrow(&self) -> EcsRefBorrowKind<'_> {
228 match self {
229 EcsRefData::Resource(resource) => {
230 let b = resource.as_ref().borrow();
231 EcsRefBorrowKind::Resource(b)
232 }
233 EcsRefData::Component(componentref) => {
234 let b = componentref.store.as_ref();
235 EcsRefBorrowKind::Component(ComponentBorrow {
236 borrow: b.borrow(),
237 entity: componentref.entity,
238 })
239 }
240 EcsRefData::Asset(assetref) => {
241 let b = assetref.server.try_get_untyped(assetref.handle);
242 EcsRefBorrowKind::Asset(b)
243 }
244 EcsRefData::Free(rc) => {
245 let b = rc.as_ref();
246 EcsRefBorrowKind::Free(b.borrow())
247 }
248 }
249 }
250
251 pub fn borrow_mut(&self) -> EcsRefBorrowMutKind<'_> {
253 match self {
254 EcsRefData::Resource(resource) => {
255 let b = resource.borrow_mut();
256 EcsRefBorrowMutKind::Resource(b)
257 }
258 EcsRefData::Component(componentref) => {
259 let b = componentref.store.borrow_mut();
260 EcsRefBorrowMutKind::Component(ComponentBorrowMut {
261 borrow: b,
262 entity: componentref.entity,
263 })
264 }
265 EcsRefData::Asset(assetref) => {
266 let b = assetref.server.try_get_untyped_mut(assetref.handle);
267 EcsRefBorrowMutKind::Asset(b)
268 }
269 EcsRefData::Free(rc) => {
270 let b = rc.borrow_mut();
271 EcsRefBorrowMutKind::Free(b)
272 }
273 }
274 }
275}
276
277#[derive(Clone)]
279pub struct ComponentRef {
280 pub store: UntypedAtomicComponentStore,
282 pub entity: Entity,
284}
285
286#[derive(Clone)]
288pub struct AssetRef {
289 pub server: AssetServer,
291 pub handle: UntypedHandle,
293}
294
295pub fn metatable(ctx: Context) -> Table {
296 let metatable = Table::new(&ctx);
297
298 metatable
299 .set(
300 ctx,
301 "__tostring",
302 Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
303 let this: &EcsRef = stack.consume(ctx)?;
304
305 let b = this.borrow();
306 if let Ok(value) = b.schema_ref() {
307 let access = value.access();
308 stack.push_front(Value::String(piccolo::String::from_slice(
309 &ctx,
310 format!("{access:?}"),
311 )));
312 }
313 Ok(CallbackReturn::Return)
314 }),
315 )
316 .unwrap();
317 metatable
318 .set(
319 ctx,
320 "__index",
321 Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
322 let (this, key): (&EcsRef, lua::Value) = stack.consume(ctx)?;
323
324 let mut newref = this.clone();
325 newref.path = ustr(&format!("{}.{key}", this.path));
326 let b = newref.borrow();
327
328 match b.schema_ref()?.access() {
329 SchemaRefAccess::Primitive(p) if !matches!(p, PrimitiveRef::Opaque { .. }) => {
330 match p {
331 PrimitiveRef::Bool(b) => stack.push_front(Value::Boolean(*b)),
332 PrimitiveRef::U8(n) => stack.push_front(Value::Integer(*n as i64)),
333 PrimitiveRef::U16(n) => stack.push_front(Value::Integer(*n as i64)),
334 PrimitiveRef::U32(n) => stack.push_front(Value::Integer(*n as i64)),
335 PrimitiveRef::U64(n) => stack.push_front(Value::Integer(*n as i64)),
336 PrimitiveRef::U128(n) => stack.push_front(Value::Integer(*n as i64)),
337 PrimitiveRef::I8(n) => stack.push_front(Value::Integer(*n as i64)),
338 PrimitiveRef::I16(n) => stack.push_front(Value::Integer(*n as i64)),
339 PrimitiveRef::I32(n) => stack.push_front(Value::Integer(*n as i64)),
340 PrimitiveRef::I64(n) => stack.push_front(Value::Integer(*n)),
341 PrimitiveRef::I128(n) => stack.push_front(Value::Integer(*n as i64)),
342 PrimitiveRef::F32(n) => stack.push_front(Value::Number(*n as f64)),
343 PrimitiveRef::F64(n) => stack.push_front(Value::Number(*n)),
344 PrimitiveRef::String(s) => stack
345 .push_front(Value::String(piccolo::String::from_slice(&ctx, s))),
346 PrimitiveRef::Opaque { .. } => unreachable!(),
347 }
348 }
349 _ => {
350 stack.push_front(newref.clone().into_value(ctx));
351 }
352 }
353
354 Ok(CallbackReturn::Return)
355 }),
356 )
357 .unwrap();
358 metatable
359 .set(
360 ctx,
361 "__newindex",
362 Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
363 let (this, key, newvalue): (&EcsRef, lua::Value, lua::Value) =
364 stack.consume(ctx)?;
365
366 let mut this = this.clone();
367 this.path = ustr(&format!("{}.{key}", this.path));
368 let mut b = this.borrow_mut();
369 let mut this_ref = b.schema_ref_mut()?;
370
371 match this_ref.access_mut() {
372 SchemaRefMutAccess::Struct(_)
373 | SchemaRefMutAccess::Vec(_)
374 | SchemaRefMutAccess::Enum(_)
375 | SchemaRefMutAccess::Map(_) => {
376 let newvalue = newvalue.as_static_user_data::<EcsRef>()?;
377 let newvalue_b = newvalue.borrow();
378 let newvalue_ref = newvalue_b.schema_ref()?;
379
380 if this_ref
382 .schema()
383 .type_data
384 .get::<SchemaAssetHandle>()
385 .is_some()
386 && newvalue_ref
387 .schema()
388 .type_data
389 .get::<SchemaAssetHandle>()
390 .is_some()
391 {
392 unsafe {
397 *this_ref.cast_mut_unchecked::<UntypedHandle>() =
398 *newvalue_ref.cast_unchecked::<UntypedHandle>()
399 }
400 } else {
401 this_ref.write(newvalue_ref)?;
404 }
405 }
406 SchemaRefMutAccess::Primitive(p) => match (p, newvalue) {
407 (PrimitiveRefMut::Bool(b), Value::Boolean(newb)) => *b = newb,
408 (PrimitiveRefMut::U8(n), Value::Integer(newi)) => {
409 *n = newi.try_into().unwrap()
410 }
411 (PrimitiveRefMut::U16(n), Value::Integer(newi)) => {
412 *n = newi.try_into().unwrap()
413 }
414 (PrimitiveRefMut::U32(n), Value::Integer(newi)) => {
415 *n = newi.try_into().unwrap()
416 }
417 (PrimitiveRefMut::U64(n), Value::Integer(newi)) => {
418 *n = newi.try_into().unwrap()
419 }
420 (PrimitiveRefMut::U128(n), Value::Integer(newi)) => {
421 *n = newi.try_into().unwrap()
422 }
423 (PrimitiveRefMut::I8(n), Value::Integer(newi)) => {
424 *n = newi.try_into().unwrap()
425 }
426 (PrimitiveRefMut::I16(n), Value::Integer(newi)) => {
427 *n = newi.try_into().unwrap()
428 }
429 (PrimitiveRefMut::I32(n), Value::Integer(newi)) => {
430 *n = newi.try_into().unwrap()
431 }
432 (PrimitiveRefMut::I64(n), Value::Integer(newi)) => *n = newi,
433 (PrimitiveRefMut::I128(n), Value::Integer(newi)) => *n = newi.into(),
434 (PrimitiveRefMut::F32(n), Value::Number(newf)) => *n = newf as f32,
435 (PrimitiveRefMut::F64(n), Value::Number(newf)) => *n = newf,
436 (PrimitiveRefMut::F32(n), Value::Integer(newi)) => *n = newi as f32,
437 (PrimitiveRefMut::F64(n), Value::Integer(newi)) => *n = newi as f64,
438 (PrimitiveRefMut::String(s), Value::String(news)) => {
439 if let Ok(news) = news.to_str() {
440 s.clear();
441 s.push_str(news);
442 } else {
443 return Err(
444 anyhow::format_err!("Non UTF-8 string assignment.").into()
445 );
446 }
447 }
448 (PrimitiveRefMut::Opaque { mut schema_ref, .. }, value) => {
449 if let Ok(ustr) = schema_ref.reborrow().try_cast_mut::<Ustr>() {
451 if let Value::String(s) = value {
452 *ustr = s.to_str()?.into()
453 } else if let Value::UserData(data) = value {
454 let ecsref = data.downcast_static::<EcsRef>()?;
455 let b = ecsref.borrow();
456 let value_ref = b.schema_ref()?;
457
458 if let Ok(value) = value_ref.try_cast::<Ustr>() {
459 *ustr = *value;
460 } else if let Ok(value) = value_ref.try_cast::<String>() {
461 *ustr = value.as_str().into();
462 }
463 }
464 } else {
465 todo!("Opaque type assignment")
466 }
467 }
468 _ => return Err(anyhow::format_err!("Invalid type").into()),
469 },
470 }
471
472 Ok(CallbackReturn::Return)
473 }),
474 )
475 .unwrap();
476
477 metatable
478}