gizmo_scripting/lib.rs
1//! Gizmo Scripting — a Lua-based game-logic scripting layer for the Gizmo engine.
2//!
3//! Scripts run inside a sandboxed [`mlua`] Lua 5.4 VM. Because Lua callbacks
4//! cannot borrow and mutate the ECS `World` directly, they enqueue changes as
5//! [`ScriptCommand`]s into a [`CommandQueue`]; the [`ScriptEngine`] later drains
6//! and applies those commands at a controlled point in the frame.
7//!
8//! ## Usage
9//! ```
10//! use gizmo_core::input::Input;
11//! use gizmo_core::World;
12//! use gizmo_math::Vec3;
13//! use gizmo_physics_core::Transform;
14//! use gizmo_scripting::ScriptEngine;
15//!
16//! let mut world = World::new();
17//! let player = world.spawn();
18//! world.add_component(player, Transform::new(Vec3::ZERO));
19//! # // Stand-in for `scripts/player.lua`, written where the doc test can read it:
20//! # // function on_update(ctx) entity.set_position(<player>, 1, 2, 3) end
21//! # let script = std::env::temp_dir().join(format!("gizmo_doc_player_{}.lua", std::process::id()));
22//! # std::fs::write(
23//! # &script,
24//! # format!("function on_update(ctx)\n entity.set_position({}, 1.0, 2.0, 3.0)\nend\n", player.id()),
25//! # )
26//! # .unwrap();
27//! # let script_path = script.to_string_lossy().into_owned();
28//!
29//! let mut script_engine = ScriptEngine::new().unwrap();
30//! script_engine.load_script(&script_path).unwrap(); // e.g. "scripts/player.lua"
31//!
32//! // Each frame:
33//! let (input, dt) = (Input::default(), 1.0 / 60.0);
34//! script_engine.update(&world, &input, dt).unwrap(); // runs `on_update`; commands are queued
35//! script_engine.flush_commands(&mut world, dt); // the queue is applied to the World here
36//! # std::fs::remove_file(&script).ok();
37//!
38//! // Lua never touched the World itself — the command it enqueued did, at flush time.
39//! let pos = world.borrow::<Transform>().get(player.id()).unwrap().position;
40//! assert_eq!(pos, Vec3::new(1.0, 2.0, 3.0));
41//! ```
42//!
43//! ## Lua API surface
44//! - `entity` — read/write position, rotation, scale, velocity; spawn/destroy
45//! - `input` — query key and mouse state
46//! - `physics` — apply forces and impulses
47//! - `scene` — save/load scenes, look up entities
48//! - `audio` — play 2D/3D sounds
49//! - `time` — delta time, elapsed time, FPS
50
51pub mod api_ai;
52pub mod api_table;
53pub mod api_audio;
54pub mod api_entity;
55pub mod api_fighter;
56pub mod api_input;
57pub mod api_physics;
58pub mod api_scene;
59pub mod api_time;
60pub mod api_vehicle;
61pub mod commands;
62
63#[cfg(target_arch = "wasm32")]
64pub mod dummy_engine;
65pub mod engine;
66
67pub use commands::{CommandQueue, ScriptCommand};
68
69pub use engine::{Script, ScriptContext, ScriptEngine, ScriptResult, ScriptValue};
70
71/// Registers the scripting layer's serializable scene components (currently
72/// [`Script`]) into a scene `ComponentRegistry`.
73///
74/// Call this from the layer that wires both scenes and scripting together (the
75/// app / editor / facade) so that `gizmo-scene` itself stays free of any
76/// dependency on `gizmo-scripting`. Without this call a scene round-trips fine,
77/// it simply won't (de)serialize `Script` components.
78#[cfg(not(target_arch = "wasm32"))]
79pub fn register_script_components(reg: &mut gizmo_core::registry::ComponentRegistry) {
80 reg.register_serializable::<Script>("Script")
81 .expect("built-in component 'Script' registration must not conflict");
82}
83
84/// No-op on `wasm32`, where the Lua-backed scripting engine is unavailable.
85#[cfg(target_arch = "wasm32")]
86pub fn register_script_components(_reg: &mut gizmo_core::registry::ComponentRegistry) {}
87
88#[cfg(target_arch = "wasm32")]
89pub use dummy_engine::{
90 Script as DummyScript, ScriptContext as DummyContext, ScriptEngine as DummyEngine,
91 ScriptResult as DummyResult,
92};