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 ("Startup", CoreStage::Startup),
53 ("First", CoreStage::First),
54 ("PreUpdate", CoreStage::PreUpdate),
55 ("Update", CoreStage::Update),
56 ("PostUpdate", CoreStage::PostUpdate),
57 ("Last", CoreStage::Last),
58 ] {
59 core_stage_table
60 .set(ctx, name, UserData::new_static(&ctx, stage))
61 .unwrap();
62 }
63 env.set(ctx, "CoreStage", core_stage_table).unwrap();
64
65 macro_rules! add_log_fn {
66 ($level:ident) => {
67 let $level = Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
68 #[derive(Debug, Copy, Clone, Eq, PartialEq, Collect)]
69 #[collect(require_static)]
70 enum Mode {
71 Init,
72 First,
73 }
74
75 #[derive(Collect)]
76 #[collect(no_drop)]
77 struct PrintSeq<'gc> {
78 mode: Mode,
79 values: Vec<Value<'gc>>,
80 }
81
82 impl<'gc> Sequence<'gc> for PrintSeq<'gc> {
83 fn poll<'a>(
84 &mut self,
85 ctx: Context<'gc>,
86 _ex: piccolo::Execution<'gc, '_>,
87 mut stack: Stack<'gc, 'a>,
88 ) -> Result<SequencePoll<'gc>, Error<'gc>> {
89 if self.mode == Mode::Init {
90 self.mode = Mode::First;
91 } else {
92 self.values.push(stack.get(0));
93 }
94 stack.clear();
95
96 while let Some(value) = self.values.pop() {
97 match meta_ops::tostring(ctx, value)? {
98 MetaResult::Value(v) => tracing::$level!("{v}"),
99 MetaResult::Call(call) => {
100 stack.extend(call.args);
101 return Ok(SequencePoll::Call {
102 function: call.function,
103 is_tail: false,
104 });
105 }
106 }
107 }
108
109 Ok(SequencePoll::Return)
110 }
111 }
112
113 Ok(CallbackReturn::Sequence(BoxSequence::new(
114 &ctx,
115 PrintSeq {
116 mode: Mode::Init,
117 values: stack.drain(..).rev().collect(),
118 },
119 )))
120 });
121 env.set(ctx, stringify!($level), $level).unwrap();
122 };
123 }
124
125 add_log_fn!(trace);
127 add_log_fn!(debug);
128 add_log_fn!(info);
129 add_log_fn!(warn);
130 add_log_fn!(error);
131 env.set(ctx, "print", info).unwrap();
132
133 let metatable = Table::new(&ctx);
136 metatable
137 .set(
138 ctx,
139 "__newindex",
140 Callback::from_fn(&ctx, |_ctx, _fuel, _stack| {
141 Err(anyhow::format_err!(
142 "Cannot set global variables, you must use `world` \
143 to persist any state across frames."
144 )
145 .into())
146 }),
147 )
148 .unwrap();
149 env.set_metatable(&ctx, Some(metatable));
150
151 env
152}