Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add lua scripting support. #252

Merged
merged 23 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fdf4dd8
feat: add lua scripting support.
zicklag Oct 30, 2023
962acf2
feat: add scripting demo.
zicklag Oct 31, 2023
dc147ad
feat: add schemaref access helper and debug implementation.
zicklag Oct 31, 2023
791293d
feat: unify handling of all schema types into a LuaRef type.
zicklag Nov 1, 2023
635325e
refactor: refactor schema refs to allow safe, dynamic access.
zicklag Nov 5, 2023
d811463
feat: implement asset lua bindings.
zicklag Nov 7, 2023
5bc4085
feat: add entities bindings to lua.
zicklag Nov 8, 2023
fc6a001
fix: cleanup component data for killed entities.
zicklag Nov 8, 2023
eeaf348
feat: allow killing entities in lua.
zicklag Nov 8, 2023
6085270
refactor: use a cleaner design for storing lua singletons.
zicklag Nov 8, 2023
d63d8fe
refactor: re-organize helper methods around schema access slightly.
zicklag Nov 8, 2023
495f357
refactor: use stack.consume() to take in lua arguments.
zicklag Nov 9, 2023
7484c34
fix: fix bug when doing indexed assignments to ecsref values.
zicklag Nov 9, 2023
04639fc
refactor: organize things into smaller Rust modules.
zicklag Nov 9, 2023
16de1ca
feat: implement lua bindings to world components.
zicklag Nov 10, 2023
b789418
feat: add required features for scripts to spawn and update sprites.
zicklag Nov 11, 2023
e918d4c
feat: implement entities:iter_with() lua bindings.
zicklag Nov 14, 2023
d0975bc
refactor: abstract repetitive code into a helper method on `EcsRef`.
zicklag Nov 15, 2023
92b7037
refactor: create lua plugin concept to allow adding multiple systems …
zicklag Nov 16, 2023
bcb7934
feat: allow referencing components and resources in scripts without t…
zicklag Nov 20, 2023
c43f544
feat: support custom schemas loaded from asset packs.
zicklag Nov 22, 2023
ec8b881
fix: don't require drop on schema loader struct fields.
zicklag Nov 22, 2023
c2d2bc7
fix: fix build errors and update crates.
zicklag Nov 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
377 changes: 283 additions & 94 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions demos/features/assets/game.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
menu_script: ./menu.lua
menu_image: ./menu-image.png
sprite_demo: ./sprite/fractal.png
atlas_demo: ./atlas/atlas-demo.yaml
Expand Down
9 changes: 9 additions & 0 deletions demos/features/assets/menu.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
local menuData = world.resources:get(schema("MenuData"))

-- Increment the frame counter
menuData.frame = menuData.frame + 1

if menuData.frame % 30 == 0 then
info(menuData)
end

34 changes: 31 additions & 3 deletions demos/features/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use bones_framework::prelude::*;
// Allow asset to be loaded from "game.yaml" assets.
#[type_data(metadata_asset("game"))]
struct GameMeta {
/// A lua script that will be run every frame on the menu.
menu_script: Handle<LuaScript>,
/// The image displayed on the menu.
menu_image: Handle<Image>,
/// The image for the sprite demo
Expand Down Expand Up @@ -79,6 +81,7 @@ struct TileMeta {
idx: u32,
}

/// Struct containing data that will be persisted with the storage API.
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
struct PersistedTextData(String);
Expand All @@ -88,8 +91,16 @@ fn main() {
PersistedTextData::register_schema();

// Create a bones bevy renderer from our bones game
BonesBevyRenderer::new(create_game())
// Get a bevy app for running our game
let mut renderer = BonesBevyRenderer::new(create_game());
// Set the app namespace which will be used by the renderer to decide where to put
// persistent storage files.
renderer.app_namespace = (
"org".into(),
"fishfolk".into(),
"bones.demo_features".into(),
);
// Get a bevy app for running our game
renderer
.app()
// We can add our own bevy plugins now
.add_plugins((FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin::default()))
Expand Down Expand Up @@ -119,13 +130,26 @@ pub fn create_game() -> Game {
game
}

/// Resource containing data that we will access from our menu lua script.
#[derive(HasSchema, Default, Clone)]
#[repr(C)]
struct MenuData {
/// The index of the frame that we are on.
pub frame: u32,
}

/// Menu plugin
pub fn menu_plugin(session: &mut Session) {
// Register our menu system
session
// Install the bones_framework default plugins for this session
.install_plugin(DefaultSessionPlugin)
// And add our systems.
.world
// Initialize our menu data resource
.init_resource::<MenuData>();

// And add our systems.
session
.add_system_to_stage(Update, menu_system)
.add_startup_system(menu_startup);
}
Expand All @@ -150,7 +174,11 @@ fn menu_system(
// Get the localization field from our `GameMeta`
localization: Localization<GameMeta>,
world: &World,
lua_engine: Res<LuaEngine>,
) {
// Run our menu script.
lua_engine.run_script_system(world, meta.menu_script);

// Render the menu.
egui::CentralPanel::default()
.frame(egui::Frame::none())
Expand Down
10 changes: 10 additions & 0 deletions demos/scripting/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "demo_scripting"
edition.workspace = true
version.workspace = true
license.workspace = true
publish = false

[dependencies]
bones_framework = { path = "../../framework_crates/bones_framework" }
bones_bevy_renderer = { path = "../../framework_crates/bones_bevy_renderer" }
5 changes: 5 additions & 0 deletions demos/scripting/assets/game.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
plugins:
- plugin1.plugin.lua
version: 1
info: ./info.yaml
sprite: ./image.png
Binary file added demos/scripting/assets/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions demos/scripting/assets/info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name: Scripting Demo Game
gravity: -9.8
3 changes: 3 additions & 0 deletions demos/scripting/assets/pack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
root: game.yaml
schemas:
- schemas/DemoSprite.yaml
31 changes: 31 additions & 0 deletions demos/scripting/assets/plugin1.plugin.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
local Vec3 = s"Vec3"
local Transform = s"Transform"
local Sprite = s"Sprite"
local Time = s"Time"
local Entities = s"Entities"
local DemoSprite = s"DemoSprite"

local function startup()
local meta = assets.root
local entities = resources:get(Entities)

local ent = entities:create()
components:insert(ent, Transform:create())
local sprite = Sprite:create()
sprite.image = meta.sprite
components:insert(ent, sprite)
components:insert(ent, DemoSprite:create())
end

local function update()
local entities = resources:get(Entities)
local time = resources:get(Time)

for ent, t, s in entities:iter_with(Transform, Sprite, DemoSprite) do
t.translation.x = math.sin(time.elapsed_seconds * 2) * 100
t.translation.y = math.sin(time.elapsed_seconds * 1.8) * 100
end
end

session:add_startup_system(startup)
session:add_system_to_stage(CoreStage.Update, update)
4 changes: 4 additions & 0 deletions demos/scripting/assets/schemas/DemoSprite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# A marker type for our demo sprite.
name: DemoSprite
full_name: demo_scripting::DemoSprite
kind: !Struct
97 changes: 97 additions & 0 deletions demos/scripting/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use bones_bevy_renderer::BonesBevyRenderer;
use bones_framework::prelude::*;

#[derive(HasSchema, Default, Clone)]
#[type_data(metadata_asset("game"))]
#[repr(C)]
struct GameMeta {
plugins: SVec<Handle<LuaPlugin>>,
version: u32,
sprite: Handle<Image>,
info: Handle<GameInfoMeta>,
}

#[derive(HasSchema, Default, Clone)]
#[repr(C)]
#[type_data(metadata_asset("info"))]
struct GameInfoMeta {
name: String,
gravity: f32,
}

#[derive(HasSchema, Default, Clone)]
#[repr(C)]
struct DemoData {
name: String,
age: f32,
favorite_things: SVec<String>,
attributes: SMap<String, f32>,
best_friend: Maybe<String>,
state: DemoState,
}

#[derive(HasSchema, Default, Clone)]
#[repr(C, u8)]
pub enum DemoState {
#[default]
Ready,
Thinking(f32),
Finished {
score: u32,
},
}

fn main() {
let mut game = Game::new();
game.install_plugin(DefaultGamePlugin);
GameMeta::register_schema();
DemoData::register_schema();

game.sessions
.create("launch")
.add_startup_system(launch_game_session);

let mut renderer = BonesBevyRenderer::new(game);
renderer.app_namespace = (
"org".into(),
"fishfolk".into(),
"bones.demo_scripting".into(),
);
renderer.app().run();
}

fn launch_game_session(
meta: Root<GameMeta>,
mut sessions: ResMut<Sessions>,
mut session_ops: ResMut<SessionOptions>,
) {
session_ops.delete = true;
let game_session = sessions.create("game");
game_session
.install_plugin(DefaultSessionPlugin)
// Install the plugin that will load our lua plugins and run them in the game session
.install_plugin(LuaPluginLoaderSessionPlugin(
// Tell it to install the lua plugins specified in our game meta
meta.plugins.iter().copied().collect(),
))
.add_startup_system(game_startup);

game_session.world.insert_resource(DemoData {
name: "default name".into(),
age: 10.0,
favorite_things: ["candy".into(), "rain".into()].into_iter().collect(),
attributes: [("coolness".into(), 50.0), ("friendliness".into(), 10.57)]
.into_iter()
.collect(),
best_friend: Some("Jane".into()).into(),
state: DemoState::Thinking(20.),
});
}

fn game_startup(
mut entities: ResMut<Entities>,
mut transforms: CompMut<Transform>,
mut cameras: CompMut<Camera>,
) {
spawn_default_camera(&mut entities, &mut transforms, &mut cameras);
}
8 changes: 6 additions & 2 deletions framework_crates/bones_asset/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ default = []

[dependencies]
bones_utils = { version = "0.3", path = "../bones_utils", features = ["serde"] }
bones_schema = { version = "0.3", path = "../bones_schema" }
bones_schema = { version = "0.3", path = "../bones_schema", features = ["serde"] }

serde = { version = "1.0", features = ["derive"] }
sha2 = "0.10"
Expand All @@ -35,11 +35,15 @@ tracing = "0.1"
bevy_tasks = "0.11"
dashmap = "5.5"
event-listener = "3.0"
append-only-vec = "0.1"
elsa = "1.9"
append-only-vec = "0.1.3"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
notify = "6.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3", features = ["console"] }

[dev-dependencies]
bones_schema = { version = "0.3", path = "../bones_schema", features = ["glam"] }
glam = "0.24"
Expand Down
8 changes: 3 additions & 5 deletions framework_crates/bones_asset/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ pub struct AssetPack {
pub game_version: VersionReq,

/// Schemas provided in the asset pack.
pub schemas: HashMap<String, Schema>,
/// Specify schemas to import from other asset packs.
pub import_schemas: HashMap<String, SchemaPath>,
pub schemas: Vec<&'static Schema>,
/// The root asset for the asset pack.
pub root: UntypedHandle,
}
Expand Down Expand Up @@ -328,7 +326,7 @@ pub trait AssetLoader: Sync + Send + 'static {
pub struct SchemaMetaAssetLoader(
pub fn(
ctx: &mut MetaAssetLoadCtx,
ptr: SchemaRefMut<'_, '_>,
ptr: SchemaRefMut<'_>,
deserialzer: &mut dyn erased_serde::Deserializer,
) -> anyhow::Result<()>,
);
Expand All @@ -338,7 +336,7 @@ impl SchemaMetaAssetLoader {
pub fn load<'a, 'de, D>(
&self,
ctx: &mut MetaAssetLoadCtx,
ptr: SchemaRefMut<'a, 'a>,
ptr: SchemaRefMut<'a>,
deserializer: D,
) -> Result<(), erased_serde::Error>
where
Expand Down
16 changes: 8 additions & 8 deletions framework_crates/bones_asset/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ unsafe impl<T: HasSchema> HasSchema for Handle<T> {
schema: u128::schema(),
}],
}),
clone_fn: Some(<Self as RawClone>::raw_clone),
clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
drop_fn: None,
default_fn: Some(<Self as RawDefault>::raw_default),
eq_fn: Some(<Self as RawEq>::raw_eq),
hash_fn: Some(<Self as RawHash>::raw_hash),
default_fn: Some(<Self as RawDefault>::raw_default_cb()),
eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
type_data: {
let td = bones_schema::alloc::TypeDatas::default();
td.insert(SchemaAssetHandle {
Expand Down Expand Up @@ -179,11 +179,11 @@ unsafe impl HasSchema for UntypedHandle {
schema: u128::schema(),
}],
}),
clone_fn: Some(<Self as RawClone>::raw_clone),
clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
drop_fn: None,
default_fn: Some(<Self as RawDefault>::raw_default),
eq_fn: Some(<Self as RawEq>::raw_eq),
hash_fn: Some(<Self as RawHash>::raw_hash),
default_fn: Some(<Self as RawDefault>::raw_default_cb()),
eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
type_data: {
let td = bones_schema::alloc::TypeDatas::default();
td.insert(SchemaAssetHandle { schema: None }).unwrap();
Expand Down
6 changes: 3 additions & 3 deletions framework_crates/bones_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl<T> From<Option<T>> for Maybe<T> {

fn maybe_loader(
ctx: &mut MetaAssetLoadCtx,
ptr: SchemaRefMut<'_, '_>,
ptr: SchemaRefMut<'_>,
deserialzer: &mut dyn erased_serde::Deserializer,
) -> anyhow::Result<()> {
deserialzer.deserialize_option(MaybeVisitor { ctx, ptr })?;
Expand All @@ -82,7 +82,7 @@ fn maybe_loader(

struct MaybeVisitor<'a, 'srv> {
ctx: &'a mut MetaAssetLoadCtx<'srv>,
ptr: SchemaRefMut<'a, 'a>,
ptr: SchemaRefMut<'a>,
}

impl<'a, 'srv, 'de> serde::de::Visitor<'de> for MaybeVisitor<'a, 'srv> {
Expand Down Expand Up @@ -112,7 +112,7 @@ impl<'a, 'srv, 'de> serde::de::Visitor<'de> for MaybeVisitor<'a, 'srv> {
// Write the enum discriminant for the `Set` variant
// SOUND: we know the discriminant due to the `#[repr(C, u8)]` annotation.
unsafe {
self.ptr.as_ptr().write(1);
self.ptr.as_ptr().cast::<u8>().write(1);
}

// Get the pointer to the enum value
Expand Down
Loading
Loading