bones_scripting/lua/
bindings.rs1use bones_lib::prelude::*;
2use gc_arena_derive::Collect;
3use piccolo::{
4 self as lua, meta_ops, meta_ops::MetaResult, BoxSequence, Callback, CallbackReturn, Context,
5 Error, Sequence, SequencePoll, Stack,
6};
7
8use super::*;
9
10pub mod assets;
11pub mod components;
12pub mod entities;
13pub mod resources;
14pub mod schema;
15pub mod world;
16
17pub mod ecsref;
18pub use ecsref::*;
19
20pub fn register_lua_typedata() {
22 Entities::schema()
23 .type_data
24 .insert(SchemaLuaEcsRefMetatable(entities::entities_metatable))
25 .unwrap();
26}
27
28pub fn no_newindex(ctx: Context) -> Callback {
29 Callback::from_fn(&ctx, |_ctx, _fuel, _stack| {
30 Err(anyhow::format_err!("Creating fields not allowed on this type").into())
31 })
32}
33
34pub fn env(ctx: Context) -> Table {
36 let env = Table::new(&ctx);
37
38 env.set(ctx, "math", ctx.globals().get(ctx, "math"))
39 .unwrap();
40
41 let schema_fn = ctx.singletons().get(ctx, schema::schema_fn);
42 env.set(ctx, "schema", schema_fn).unwrap();
43 env.set(ctx, "s", schema_fn).unwrap(); let schema_of_fn = ctx.singletons().get(ctx, schema::schema_of_fn);
45 env.set(ctx, "schema_of", schema_of_fn).unwrap();
46
47 WorldRef::default().add_to_env(ctx, env);
48
49 let core_stage_table = Table::new(&ctx);
51 for (name, stage) in [
52 ("First", CoreStage::First),
53 ("PreUpdate", CoreStage::PreUpdate),
54 ("Update", CoreStage::Update),
55 ("PostUpdate", CoreStage::PostUpdate),
56 ("Last", CoreStage::Last),
57 ] {
58 core_stage_table
59 .set(ctx, name, UserData::new_static(&ctx, stage))
60 .unwrap();
61 }
62 env.set(ctx, "CoreStage", core_stage_table).unwrap();
63
64 macro_rules! add_log_fn {
65 ($level:ident) => {
66 let $level = Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
67 #[derive(Debug, Copy, Clone, Eq, PartialEq, Collect)]
68 #[collect(require_static)]
69 enum Mode {
70 Init,
71 First,
72 }
73
74 #[derive(Collect)]
75 #[collect(no_drop)]
76 struct PrintSeq<'gc> {
77 mode: Mode,
78 values: Vec<Value<'gc>>,
79 }
80
81 impl<'gc> Sequence<'gc> for PrintSeq<'gc> {
82 fn poll<'a>(
83 &mut self,
84 ctx: Context<'gc>,
85 _ex: piccolo::Execution<'gc, '_>,
86 mut stack: Stack<'gc, 'a>,
87 ) -> Result<SequencePoll<'gc>, Error<'gc>> {
88 if self.mode == Mode::Init {
89 self.mode = Mode::First;
90 } else {
91 self.values.push(stack.get(0));
92 }
93 stack.clear();
94
95 while let Some(value) = self.values.pop() {
96 match meta_ops::tostring(ctx, value)? {
97 MetaResult::Value(v) => tracing::$level!("{v}"),
98 MetaResult::Call(call) => {
99 stack.extend(call.args);
100 return Ok(SequencePoll::Call {
101 function: call.function,
102 is_tail: false,
103 });
104 }
105 }
106 }
107
108 Ok(SequencePoll::Return)
109 }
110 }
111
112 Ok(CallbackReturn::Sequence(BoxSequence::new(
113 &ctx,
114 PrintSeq {
115 mode: Mode::Init,
116 values: stack.drain(..).rev().collect(),
117 },
118 )))
119 });
120 env.set(ctx, stringify!($level), $level).unwrap();
121 };
122 }
123
124 add_log_fn!(trace);
126 add_log_fn!(debug);
127 add_log_fn!(info);
128 add_log_fn!(warn);
129 add_log_fn!(error);
130 env.set(ctx, "print", info).unwrap();
131
132 let metatable = Table::new(&ctx);
135 metatable
136 .set(
137 ctx,
138 "__newindex",
139 Callback::from_fn(&ctx, |_ctx, _fuel, _stack| {
140 Err(anyhow::format_err!(
141 "Cannot set global variables, you must use `world` \
142 to persist any state across frames."
143 )
144 .into())
145 }),
146 )
147 .unwrap();
148 env.set_metatable(&ctx, Some(metatable));
149
150 env
151}