bones_scripting/lua/bindings/
entities.rs

1use 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}