Skip to content

Commit

Permalink
feat: add lua scripting support. (#252)
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Nov 22, 2023
1 parent 642a1fb commit b865f8f
Show file tree
Hide file tree
Showing 65 changed files with 4,970 additions and 1,277 deletions.
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

0 comments on commit b865f8f

Please sign in to comment.