bones_scripting/lua/bindings/
entities.rs1use lua::Variadic;
2
3use crate::prelude::bindings::schema::WithoutSchema;
4
5use super::*;
6
7pub fn entities_metatable(ctx: Context) -> Table {
8 let metatable = Table::new(&ctx);
9 metatable
10 .set(
11 ctx,
12 "__tostring",
13 Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
14 stack.push_front(
15 piccolo::String::from_static(&ctx, "Entities { create, kill, iter_with }")
16 .into(),
17 );
18 Ok(CallbackReturn::Return)
19 }),
20 )
21 .unwrap();
22 metatable
23 .set(ctx, "__newindex", ctx.singletons().get(ctx, no_newindex))
24 .unwrap();
25
26 let create_callback = ctx.registry().stash(
27 &ctx,
28 Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
29 let this: &EcsRef = stack.consume(ctx)?;
30
31 let mut b = this.borrow_mut();
32 let entities = b.schema_ref_mut()?.cast_into_mut::<Entities>();
33
34 let entity = entities.create();
35 let newecsref = EcsRef {
36 data: EcsRefData::Free(Rc::new(AtomicCell::new(SchemaBox::new(entity)))),
37 path: default(),
38 }
39 .into_value(ctx);
40 stack.push_front(newecsref);
41
42 Ok(CallbackReturn::Return)
43 }),
44 );
45 let kill_callback = ctx.registry().stash(
46 &ctx,
47 Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
48 let (this, entity_ecsref): (&EcsRef, &EcsRef) = stack.consume(ctx)?;
49 let mut b = this.borrow_mut();
50 let entities = b.schema_ref_mut()?.cast_into_mut::<Entities>();
51
52 let b = entity_ecsref.borrow();
53 let entity = b.schema_ref()?.cast::<Entity>();
54 entities.kill(*entity);
55
56 Ok(CallbackReturn::Return)
57 }),
58 );
59 let iter_with_callback = ctx.registry().stash(
60 &ctx,
61 Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
62 let (this, schema_args): (&EcsRef, Variadic<Vec<UserData>>) = stack.consume(ctx)?;
63 let mut b = this.borrow_mut();
64 let entities = b.schema_ref_mut()?.cast_into_mut::<Entities>();
65 let world = ctx
66 .globals()
67 .get(ctx, "world")
68 .as_static_user_data::<WorldRef>()?;
69 let mut bitset = entities.bitset().clone();
70
71 let mut schemas = Vec::with_capacity(schema_args.len());
72 world.with(|world| {
73 for schema_arg in &schema_args {
74 if let Ok(schema) = schema_arg.downcast_static::<&Schema>() {
75 let components = world.components.get_by_schema(schema);
76 let components = components.borrow();
77 bitset.bit_and(components.bitset());
78 schemas.push(*schema);
79 } else if let Ok(without_schema) = schema_arg.downcast_static::<WithoutSchema>()
80 {
81 let components = world.components.get_by_schema(without_schema.0);
82 let components = components.borrow();
83 bitset.bit_and(components.bitset().clone().bit_not());
84 } else {
85 return Err(anyhow::format_err!(
86 "Invalid type for argument to `entities:iter_with()`: {schema_arg:?}"
87 ));
88 }
89 }
90 Ok::<_, anyhow::Error>(())
91 })?;
92 let entities = entities
93 .iter_with_bitset(&bitset)
94 .collect::<Vec<_>>()
95 .into_iter();
96
97 struct IteratorState {
98 pub entities: std::vec::IntoIter<Entity>,
99 schemas: Vec<&'static Schema>,
100 }
101
102 let iter_fn = Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
103 let state: UserData = stack.consume(ctx)?;
104 let state = state.downcast_static::<AtomicCell<IteratorState>>()?;
105 let mut state = state.borrow_mut();
106 let next_ent = state.entities.next();
107
108 if let Some(entity) = next_ent {
109 let world = ctx
110 .globals()
111 .get(ctx, "world")
112 .as_static_user_data::<WorldRef>()?;
113
114 let ecsref = EcsRef {
115 data: EcsRefData::Free(Rc::new(AtomicCell::new(SchemaBox::new(entity)))),
116 path: default(),
117 }
118 .into_value(ctx);
119 stack.push_back(ecsref);
120
121 world.with(|world| {
122 for schema in &state.schemas {
123 let store = world.components.get_cell_by_schema(schema);
124 let ecsref = EcsRef {
125 data: EcsRefData::Component(ComponentRef { store, entity }),
126 path: default(),
127 }
128 .into_value(ctx);
129 stack.push_back(ecsref);
130 }
131
132 Ok::<_, anyhow::Error>(())
133 })?;
134 }
135
136 Ok(CallbackReturn::Return)
137 });
138
139 let iterator_state =
140 UserData::new_static(&ctx, AtomicCell::new(IteratorState { entities, schemas }));
141
142 stack.replace(ctx, (iter_fn, iterator_state));
143
144 Ok(CallbackReturn::Return)
145 }),
146 );
147
148 metatable
149 .set(
150 ctx,
151 "__index",
152 Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
153 let (_this, key): (lua::Value, lua::String) = stack.consume(ctx)?;
154
155 #[allow(clippy::single_match)]
156 match key.as_bytes() {
157 b"create" => {
158 stack.push_front(ctx.registry().fetch(&create_callback).into());
159 }
160 b"kill" => {
161 stack.push_front(ctx.registry().fetch(&kill_callback).into());
162 }
163 b"iter_with" => {
164 stack.push_front(ctx.registry().fetch(&iter_with_callback).into());
165 }
166 _ => (),
167 }
168
169 Ok(CallbackReturn::Return)
170 }),
171 )
172 .unwrap();
173
174 metatable
175}