Create an early scripting engine #2

Merged
SeanOMik merged 42 commits from feature/early-scripting into main 2024-03-03 03:28:57 +00:00
15 changed files with 570 additions and 57 deletions
Showing only changes of commit 5521d4a659 - Show all commits

40
Cargo.lock generated
View File

@ -45,6 +45,15 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "allocator-api2" name = "allocator-api2"
version = "0.2.16" version = "0.2.16"
@ -1558,11 +1567,13 @@ dependencies = [
"anyhow", "anyhow",
"elua", "elua",
"itertools 0.12.0", "itertools 0.12.0",
"lazy_static",
"lyra-ecs", "lyra-ecs",
"lyra-game", "lyra-game",
"lyra-reflect", "lyra-reflect",
"lyra-resource", "lyra-resource",
"lyra-scripting-derive", "lyra-scripting-derive",
"regex",
"thiserror", "thiserror",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -2204,6 +2215,35 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "regex"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]] [[package]]
name = "renderdoc-sys" name = "renderdoc-sys"
version = "0.7.1" version = "0.7.1"

View File

@ -6,6 +6,48 @@ function on_init()
local e = world:spawn(pos, cube) local e = world:spawn(pos, cube)
print("spawned entity " .. tostring(e)) print("spawned entity " .. tostring(e))
local action_handler_tbl = {
layouts = { 0 },
actions = {
MoveForwardBackward = "Axis",
MoveLeftRight = "Axis",
MoveUpDown = "Axis",
LookLeftRight = "Axis",
LookUpDown = "Axis",
LookRoll = "Axis",
},
mappings = {
{
layout = 0,
binds = {
MoveForwardBackward = {
"key:w=1.0", "key:s=-1.0"
},
MoveLeftRight = {
"key:a=-1.0", "key:d=1.0"
},
MoveUpDown = {
"key:c=1.0", "key:z=-1.0"
},
LookLeftRight = {
"key:left=-1.0", "key:right=1.0",
"mouse:axis:x"
},
LookUpDown = {
"key:up=-1.0", "key:down=1.0",
"mouse:axis:y",
},
LookRoll = {
"key:e=-1.0", "key:q=1.0",
}
}
}
}
}
local handler = ActionHandler.new(action_handler_tbl)
world:add_resource(handler)
end end
--[[ function on_first() --[[ function on_first()
@ -25,6 +67,9 @@ function on_update()
return t return t
end, Transform) end, Transform)
--local input = world:resource(Input)
--input.
end end
--[[ function on_post_update() --[[ function on_post_update()

View File

@ -240,7 +240,7 @@ async fn main() {
}; };
let action_handler_plugin = |game: &mut Game| { let action_handler_plugin = |game: &mut Game| {
let action_handler = ActionHandler::new() /* let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0)) .add_layout(LayoutId::from(0))
.add_action(CommonActionLabel::MoveForwardBackward, Action::new(ActionKind::Axis)) .add_action(CommonActionLabel::MoveForwardBackward, Action::new(ActionKind::Axis))
@ -250,7 +250,7 @@ async fn main() {
.add_action(CommonActionLabel::LookUpDown, Action::new(ActionKind::Axis)) .add_action(CommonActionLabel::LookUpDown, Action::new(ActionKind::Axis))
.add_action(CommonActionLabel::LookRoll, Action::new(ActionKind::Axis)) .add_action(CommonActionLabel::LookRoll, Action::new(ActionKind::Axis))
.add_mapping(ActionMapping::new(LayoutId::from(0), ActionMappingId::from(0)) .add_mapping(ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
.bind(CommonActionLabel::MoveForwardBackward, &[ .bind(CommonActionLabel::MoveForwardBackward, &[
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0) ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0)
@ -282,22 +282,9 @@ async fn main() {
.finish() .finish()
); );
/* #[allow(unused_variables)]
let test_system = |world: &mut World| -> anyhow::Result<()> {
let handler = world.get_resource::<ActionHandler>();
if let Some(alpha) = handler.get_axis_modifier("look_rotate") {
debug!("'look_rotate': {alpha}");
}
Ok(())
}; */
let world = game.world_mut(); let world = game.world_mut();
world.add_resource(action_handler); world.add_resource(action_handler);
world.spawn((Vec3::new(0.5, 0.1, 3.0),)); game.with_plugin(InputActionPlugin); */
game.with_plugin(InputActionPlugin);
//game.with_system("input_test", test_system, &[]);
}; };
let script_test_plugin = |game: &mut Game| { let script_test_plugin = |game: &mut Game| {
@ -322,6 +309,6 @@ async fn main() {
.with_plugin(script_test_plugin) .with_plugin(script_test_plugin)
//.with_plugin(fps_plugin) //.with_plugin(fps_plugin)
.with_plugin(jiggle_plugin) .with_plugin(jiggle_plugin)
.with_plugin(FreeFlyCameraPlugin) //.with_plugin(FreeFlyCameraPlugin)
.run().await; .run().await;
} }

View File

@ -255,7 +255,8 @@ impl World {
/// Will panic if the resource is not in the world. See [`try_get_resource`] for /// Will panic if the resource is not in the world. See [`try_get_resource`] for
/// a function that returns an option. /// a function that returns an option.
pub fn get_resource<T: 'static>(&self) -> Ref<T> { pub fn get_resource<T: 'static>(&self) -> Ref<T> {
self.resources.get(&TypeId::of::<T>()).unwrap() self.resources.get(&TypeId::of::<T>())
.expect(&format!("World is missing resource of type '{}'", std::any::type_name::<T>()))
.get() .get()
} }
@ -277,7 +278,8 @@ impl World {
/// Will panic if the resource is not in the world. See [`try_get_resource_mut`] for /// Will panic if the resource is not in the world. See [`try_get_resource_mut`] for
/// a function that returns an option. /// a function that returns an option.
pub fn get_resource_mut<T: 'static>(&self) -> RefMut<T> { pub fn get_resource_mut<T: 'static>(&self) -> RefMut<T> {
self.resources.get(&TypeId::of::<T>()).unwrap() self.resources.get(&TypeId::of::<T>())
.expect(&format!("World is missing resource of type '{}'", std::any::type_name::<T>()))
.get_mut() .get_mut()
} }

View File

@ -2,6 +2,7 @@ use std::{collections::HashMap, ops::Deref, hash::{Hash, DefaultHasher, Hasher},
use glam::Vec2; use glam::Vec2;
use lyra_ecs::world::World; use lyra_ecs::world::World;
use lyra_reflect::Reflect;
use crate::{plugin::Plugin, game::GameStages, EventQueue}; use crate::{plugin::Plugin, game::GameStages, EventQueue};
@ -213,7 +214,7 @@ impl Action {
} }
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct LayoutId(u32); pub struct LayoutId(pub u32);
impl From<u32> for LayoutId { impl From<u32> for LayoutId {
fn from(value: u32) -> Self { fn from(value: u32) -> Self {
@ -240,7 +241,7 @@ impl Layout {
} }
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ActionMappingId(u32); pub struct ActionMappingId(pub u32);
impl From<u32> for ActionMappingId { impl From<u32> for ActionMappingId {
fn from(value: u32) -> Self { fn from(value: u32) -> Self {
@ -264,6 +265,10 @@ impl ActionMapping {
} }
} }
pub fn builder(layout: LayoutId, id: ActionMappingId) -> ActionMappingBuilder {
ActionMappingBuilder::new(ActionMapping::new(layout, id))
}
/// Creates a binding for the action. /// Creates a binding for the action.
/// ///
/// If the action is not in this layout, this will panic! /// If the action is not in this layout, this will panic!
@ -271,7 +276,7 @@ impl ActionMapping {
/// Parameters: /// Parameters:
/// * `action` - The label corresponding to the action in this Layout. /// * `action` - The label corresponding to the action in this Layout.
/// * `bind` - The Binding to add to the Action. /// * `bind` - The Binding to add to the Action.
pub fn bind<L>(mut self, action: L, bindings: &[Binding]) -> Self pub fn bind<L>(&mut self, action: L, bindings: &[Binding]) -> &mut Self
where where
L: ActionLabel L: ActionLabel
{ {
@ -283,32 +288,48 @@ impl ActionMapping {
self self
} }
/// Creates multiple binding for the action.
///
/// If the action is not in this layout, this will panic!
///
/// Parameters:
/// * `action_label` - The label corresponding to the action in this Layout.
/// * `bindings` - The list of Bindings to add to the Action.
/* pub fn add_bindings(&mut self, action_label: String, bindings: &[Binding]) -> &mut Self {
let mut bindings = bindings.to_vec();
let action_binds = self.action_binds.entry(action_label)
.or_insert_with(Vec::new);
action_binds.append(&mut bindings);
self
} */
pub fn finish(self) -> Self { pub fn finish(self) -> Self {
self self
} }
} }
#[derive(Clone, Default)] pub struct ActionMappingBuilder {
mapping: ActionMapping,
}
impl ActionMappingBuilder {
fn new(mapping: ActionMapping) -> Self {
Self {
mapping,
}
}
pub fn bind<L>(mut self, action: L, bindings: &[Binding]) -> Self
where
L: ActionLabel
{
let mut bindings = bindings.to_vec();
let action_binds = self.mapping.action_binds.entry(action.label_hash()).or_default();
action_binds.append(&mut bindings);
self
}
pub fn finish(self) -> ActionMapping {
self.mapping
}
}
#[derive(Clone, Default, Reflect)]
pub struct ActionHandler { pub struct ActionHandler {
#[reflect(skip)] // TODO: dont just skip all these
pub actions: HashMap<u64, Action>, pub actions: HashMap<u64, Action>,
#[reflect(skip)]
pub layouts: HashMap<LayoutId, Layout>, pub layouts: HashMap<LayoutId, Layout>,
#[reflect(skip)]
pub current_layout: LayoutId, pub current_layout: LayoutId,
#[reflect(skip)]
pub current_mapping: ActionMappingId, pub current_mapping: ActionMappingId,
} }
@ -317,26 +338,31 @@ impl ActionHandler {
Self::default() Self::default()
} }
pub fn add_layout(mut self, id: LayoutId) -> Self { pub fn builder() -> ActionHandlerBuilder {
self.layouts.insert(id, Layout::new()); ActionHandlerBuilder::default()
self
} }
pub fn add_action<L>(mut self, label: L, action: Action) -> Self pub fn add_layout(&mut self, id: LayoutId) {
self.layouts.insert(id, Layout::new());
}
pub fn action<L>(&self, label: L) -> Option<&Action>
where
L: ActionLabel
{
self.actions.get(&label.label_hash())
}
pub fn add_action<L>(&mut self, label: L, action: Action)
where where
L: ActionLabel L: ActionLabel
{ {
self.actions.insert(label.label_hash(), action); self.actions.insert(label.label_hash(), action);
self
} }
pub fn add_mapping(mut self, mapping: ActionMapping) -> Self { pub fn add_mapping(&mut self, mapping: ActionMapping) {
let layout = self.layouts.get_mut(&mapping.layout).unwrap(); let layout = self.layouts.get_mut(&mapping.layout).unwrap();
layout.add_mapping(mapping); layout.add_mapping(mapping);
self
} }
/// Returns true if the action is pressed (or was just pressed). /// Returns true if the action is pressed (or was just pressed).
@ -443,6 +469,43 @@ impl ActionHandler {
} }
} }
#[derive(Default)]
pub struct ActionHandlerBuilder {
handler: ActionHandler,
}
impl ActionHandlerBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn add_layout(mut self, id: LayoutId) -> Self {
self.handler.layouts.insert(id, Layout::new());
self
}
pub fn add_action<L>(mut self, label: L, action: Action) -> Self
where
L: ActionLabel
{
self.handler.actions.insert(label.label_hash(), action);
self
}
pub fn add_mapping(mut self, mapping: ActionMapping) -> Self {
let layout = self.handler.layouts.get_mut(&mapping.layout).unwrap();
layout.add_mapping(mapping);
self
}
pub fn finish(self) -> ActionHandler {
self.handler
}
}
fn actions_system(world: &mut World) -> anyhow::Result<()> { fn actions_system(world: &mut World) -> anyhow::Result<()> {
let keys = world.try_get_resource::<InputButtons<KeyCode>>() let keys = world.try_get_resource::<InputButtons<KeyCode>>()
.map(|r| r.deref().clone()); .map(|r| r.deref().clone());

View File

@ -11,4 +11,182 @@ pub mod buttons;
pub use buttons::*; pub use buttons::*;
pub mod action; pub mod action;
pub use action::*; pub use action::*;
pub type KeyCode = winit::event::VirtualKeyCode;
/// Parses a [`KeyCode`] from a [`&str`].
///
/// There are some changes to a few keycodes. All the number keys `Key1`, `Key2`, etc., have
/// the `Key` prefix removed; so they are expected to be `1`, `2`, etc.
pub fn keycode_from_str(s: &str) -> Option<KeyCode> {
let s = s.to_lowercase();
let s = s.as_str();
match s {
"1" => Some(KeyCode::Key1),
"2" => Some(KeyCode::Key2),
"3" => Some(KeyCode::Key3),
"4" => Some(KeyCode::Key4),
"5" => Some(KeyCode::Key5),
"6" => Some(KeyCode::Key6),
"7" => Some(KeyCode::Key7),
"8" => Some(KeyCode::Key8),
"9" => Some(KeyCode::Key9),
"0" => Some(KeyCode::Key0),
"a" => Some(KeyCode::A),
"b" => Some(KeyCode::B),
"c" => Some(KeyCode::C),
"d" => Some(KeyCode::D),
"e" => Some(KeyCode::E),
"f" => Some(KeyCode::F),
"g" => Some(KeyCode::G),
"h" => Some(KeyCode::H),
"i" => Some(KeyCode::I),
"j" => Some(KeyCode::J),
"k" => Some(KeyCode::K),
"l" => Some(KeyCode::L),
"m" => Some(KeyCode::M),
"n" => Some(KeyCode::N),
"o" => Some(KeyCode::O),
"p" => Some(KeyCode::P),
"q" => Some(KeyCode::Q),
"r" => Some(KeyCode::R),
"s" => Some(KeyCode::S),
"t" => Some(KeyCode::T),
"u" => Some(KeyCode::U),
"v" => Some(KeyCode::V),
"w" => Some(KeyCode::W),
"x" => Some(KeyCode::X),
"y" => Some(KeyCode::Y),
"z" => Some(KeyCode::Z),
"escape" => Some(KeyCode::Escape),
"f1" => Some(KeyCode::F1),
"f2" => Some(KeyCode::F2),
"f3" => Some(KeyCode::F3),
"f4" => Some(KeyCode::F4),
"f5" => Some(KeyCode::F5),
"f6" => Some(KeyCode::F6),
"f7" => Some(KeyCode::F7),
"f8" => Some(KeyCode::F8),
"f9" => Some(KeyCode::F9),
"f10" => Some(KeyCode::F10),
"f11" => Some(KeyCode::F11),
"f12" => Some(KeyCode::F12),
"f13" => Some(KeyCode::F13),
"f14" => Some(KeyCode::F14),
"f15" => Some(KeyCode::F15),
"f16" => Some(KeyCode::F16),
"f17" => Some(KeyCode::F17),
"f18" => Some(KeyCode::F18),
"f19" => Some(KeyCode::F19),
"f20" => Some(KeyCode::F20),
"f21" => Some(KeyCode::F21),
"f22" => Some(KeyCode::F22),
"f23" => Some(KeyCode::F23),
"f24" => Some(KeyCode::F24),
"snapshot" => Some(KeyCode::Snapshot),
"scroll" => Some(KeyCode::Scroll),
"pause" => Some(KeyCode::Pause),
"insert" => Some(KeyCode::Insert),
"home" => Some(KeyCode::Home),
"delete" => Some(KeyCode::Delete),
"end" => Some(KeyCode::End),
"pagedown" => Some(KeyCode::PageDown),
"pageup" => Some(KeyCode::PageUp),
"left" => Some(KeyCode::Left),
"up" => Some(KeyCode::Up),
"right" => Some(KeyCode::Right),
"down" => Some(KeyCode::Down),
"back" => Some(KeyCode::Back),
"return" => Some(KeyCode::Return),
"space" => Some(KeyCode::Space),
"compose" => Some(KeyCode::Compose),
"caret" => Some(KeyCode::Caret),
"numlock" => Some(KeyCode::Numlock),
"numpad0" => Some(KeyCode::Numpad0),
"numpad1" => Some(KeyCode::Numpad1),
"numpad2" => Some(KeyCode::Numpad2),
"numpad3" => Some(KeyCode::Numpad3),
"numpad4" => Some(KeyCode::Numpad4),
"numpad5" => Some(KeyCode::Numpad5),
"numpad6" => Some(KeyCode::Numpad6),
"numpad7" => Some(KeyCode::Numpad7),
"numpad8" => Some(KeyCode::Numpad8),
"numpad9" => Some(KeyCode::Numpad9),
"numpadadd" => Some(KeyCode::NumpadAdd),
"numpaddivide" => Some(KeyCode::NumpadDivide),
"numpaddecimal" => Some(KeyCode::NumpadDecimal),
"numpadcomma" => Some(KeyCode::NumpadComma),
"numpadenter" => Some(KeyCode::NumpadEnter),
"numpadequals" => Some(KeyCode::NumpadEquals),
"numpadmultiply" => Some(KeyCode::NumpadMultiply),
"numpadsubtract" => Some(KeyCode::NumpadSubtract),
"abntc1" => Some(KeyCode::AbntC1),
"abntc2" => Some(KeyCode::AbntC2),
"apostrophe" => Some(KeyCode::Apostrophe),
"apps" => Some(KeyCode::Apps),
"asterisk" => Some(KeyCode::Asterisk),
"at" => Some(KeyCode::At),
"ax" => Some(KeyCode::Ax),
"backslash" => Some(KeyCode::Backslash),
"calculator" => Some(KeyCode::Calculator),
"capital" => Some(KeyCode::Capital),
"colon" => Some(KeyCode::Colon),
"comma" => Some(KeyCode::Comma),
"convert" => Some(KeyCode::Convert),
"equals" => Some(KeyCode::Equals),
"grave" => Some(KeyCode::Grave),
"kana" => Some(KeyCode::Kana),
"kanji" => Some(KeyCode::Kanji),
"lalt" => Some(KeyCode::LAlt),
"lbracket" => Some(KeyCode::LBracket),
"lcontrol" => Some(KeyCode::LControl),
"lshift" => Some(KeyCode::LShift),
"lwin" => Some(KeyCode::LWin),
"mail" => Some(KeyCode::Mail),
"mediaselect" => Some(KeyCode::MediaSelect),
"mediastop" => Some(KeyCode::MediaStop),
"minus" => Some(KeyCode::Minus),
"mute" => Some(KeyCode::Mute),
"mycomputer" => Some(KeyCode::MyComputer),
"navigateforward" => Some(KeyCode::NavigateForward),
"navigatebackward" => Some(KeyCode::NavigateBackward),
"nexttrack" => Some(KeyCode::NextTrack),
"noconvert" => Some(KeyCode::NoConvert),
"oem102" => Some(KeyCode::OEM102),
"period" => Some(KeyCode::Period),
"playpause" => Some(KeyCode::PlayPause),
"plus" => Some(KeyCode::Plus),
"power" => Some(KeyCode::Power),
"prevtrack" => Some(KeyCode::PrevTrack),
"ralt" => Some(KeyCode::RAlt),
"rbracket" => Some(KeyCode::RBracket),
"rcontrol" => Some(KeyCode::RControl),
"rshift" => Some(KeyCode::RShift),
"rwin" => Some(KeyCode::RWin),
"semicolon" => Some(KeyCode::Semicolon),
"slash" => Some(KeyCode::Slash),
"sleep" => Some(KeyCode::Sleep),
"stop" => Some(KeyCode::Stop),
"sysrq" => Some(KeyCode::Sysrq),
"tab" => Some(KeyCode::Tab),
"underline" => Some(KeyCode::Underline),
"unlabeled" => Some(KeyCode::Unlabeled),
"volumedown" => Some(KeyCode::VolumeDown),
"volumeup" => Some(KeyCode::VolumeUp),
"wake" => Some(KeyCode::Wake),
"webback" => Some(KeyCode::WebBack),
"webfavorites" => Some(KeyCode::WebFavorites),
"webforward" => Some(KeyCode::WebForward),
"webhome" => Some(KeyCode::WebHome),
"webrefresh" => Some(KeyCode::WebRefresh),
"websearch" => Some(KeyCode::WebSearch),
"webstop" => Some(KeyCode::WebStop),
"yen" => Some(KeyCode::Yen),
"copy" => Some(KeyCode::Copy),
"paste" => Some(KeyCode::Paste),
"cut" => Some(KeyCode::Cut),
_ => None
}
}

View File

@ -8,8 +8,6 @@ use crate::{EventQueue, plugin::Plugin, game::GameStages};
use super::{events::*, InputButtons, InputEvent}; use super::{events::*, InputButtons, InputEvent};
pub type KeyCode = winit::event::VirtualKeyCode;
#[derive(Default)] #[derive(Default)]
pub struct InputSystem; pub struct InputSystem;

View File

@ -1,4 +1,4 @@
use std::{any::TypeId, cell::{Ref, RefMut}, ptr::NonNull}; use std::{any::{Any, TypeId}, cell::{Ref, RefMut}, ptr::NonNull};
use lyra_ecs::{World, ResourceObject}; use lyra_ecs::{World, ResourceObject};
@ -11,6 +11,7 @@ pub struct ReflectedResource {
fn_reflect: for<'a> fn (world: &'a World) -> Option<Ref<'a, dyn Reflect>>, fn_reflect: for<'a> fn (world: &'a World) -> Option<Ref<'a, dyn Reflect>>,
fn_reflect_mut: for<'a> fn (world: &'a mut World) -> Option<RefMut<'a, dyn Reflect>>, fn_reflect_mut: for<'a> fn (world: &'a mut World) -> Option<RefMut<'a, dyn Reflect>>,
fn_reflect_ptr: fn (world: &mut World) -> Option<NonNull<u8>>, fn_reflect_ptr: fn (world: &mut World) -> Option<NonNull<u8>>,
fn_refl_insert: fn (world: &mut World, this: Box<dyn Reflect>),
} }
impl ReflectedResource { impl ReflectedResource {
@ -27,6 +28,11 @@ impl ReflectedResource {
pub fn reflect_ptr(&self, world: &mut World) -> Option<NonNull<u8>> { pub fn reflect_ptr(&self, world: &mut World) -> Option<NonNull<u8>> {
(self.fn_reflect_ptr)(world) (self.fn_reflect_ptr)(world)
} }
/// Insert the resource into the world.
pub fn insert(&self, world: &mut World, this: Box<dyn Reflect>) {
(self.fn_refl_insert)(world, this)
}
} }
impl<T: ResourceObject + Reflect> FromType<T> for ReflectedResource { impl<T: ResourceObject + Reflect> FromType<T> for ReflectedResource {
@ -45,6 +51,13 @@ impl<T: ResourceObject + Reflect> FromType<T> for ReflectedResource {
world.try_get_resource_ptr::<T>() world.try_get_resource_ptr::<T>()
.map(|ptr| ptr.cast::<u8>()) .map(|ptr| ptr.cast::<u8>())
}, },
fn_refl_insert: |world: &mut World, this: Box<dyn Reflect>| {
let res = this as Box<dyn Any>;
let res = res.downcast::<T>()
.expect("Provided a non-matching type to ReflectedResource insert method!");
let res = *res;
world.add_resource(res);
}
} }
} }
} }

View File

@ -23,6 +23,8 @@ tracing = "0.1.37"
#mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? #mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe?
elua = { path = "./elua", optional = true } elua = { path = "./elua", optional = true }
itertools = "0.12.0" itertools = "0.12.0"
regex = "1.10.3"
lazy_static = "1.4.0"
[dev-dependencies] [dev-dependencies]

@ -1 +1 @@
Subproject commit 70e2985cc44fdb30cdf2157c50d2f0e3385e08fa Subproject commit beea6c33fcb2f5bd16f40e1919b61a89b4aaecb6

View File

@ -61,6 +61,14 @@ impl ReflectBranch {
} }
} }
/// Gets self as a [`ReflectedResource`], returning `None` if self is not an instance of it.
pub fn as_resource(&self) -> Option<&ReflectedResource> {
match self {
ReflectBranch::Resource(v) => Some(v),
_ => None
}
}
/// Returns a boolean indicating if `self` is a reflection of a Resource. /// Returns a boolean indicating if `self` is a reflection of a Resource.
pub fn is_resource(&self) -> bool { pub fn is_resource(&self) -> bool {
matches!(self, ReflectBranch::Resource(_)) matches!(self, ReflectBranch::Resource(_))

View File

@ -1,7 +1,7 @@
use lyra_ecs::ResourceObject; use lyra_ecs::ResourceObject;
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use crate::{lua::{wrappers::{LuaDeltaTime, LuaModelComponent}, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; use crate::{lua::{wrappers::{LuaActionHandler, LuaDeltaTime, LuaModelComponent}, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
#[derive(Default)] #[derive(Default)]
pub struct LyraEcsApiProvider; pub struct LyraEcsApiProvider;
@ -21,6 +21,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?; globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?; globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;
globals.set("ModelComponent", ctx.create_proxy::<LuaModelComponent>()?)?; globals.set("ModelComponent", ctx.create_proxy::<LuaModelComponent>()?)?;
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?; let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?;
globals.set("DeltaTime", dt_table)?; globals.set("DeltaTime", dt_table)?;

View File

@ -1,7 +1,7 @@
use std::{ptr::NonNull, sync::Arc}; use std::{ptr::NonNull, sync::Arc};
use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr}; use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr};
use elua::AsLua; use elua::{AnyUserdata, AsLua, Table};
use lyra_ecs::{query::dynamic::QueryDynamicType, CommandQueue, Commands, DynamicBundle, World}; use lyra_ecs::{query::dynamic::QueryDynamicType, CommandQueue, Commands, DynamicBundle, World};
use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
@ -279,6 +279,32 @@ impl elua::Userdata for ScriptWorldPtr {
Ok(elua::Value::Nil) Ok(elua::Value::Nil)
} }
}) })
.method_mut("add_resource", |_, this, res: elua::Value| {
let reflect = match res {
elua::Value::Userdata(ud) => ud
.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.expect("Type does not implement 'reflect_type' properly"),
elua::Value::Table(t) => {
let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT)?;
f.exec::<_, ScriptBorrow>(())
.expect("Type does not implement 'reflect_type' properly")
}
_ => {
panic!("how");
}
};
let data = reflect.data
.expect("Its expected that 'FN_NAME_INTERNAL_REFLECT' returns data in World:add_resource");
let res = reflect.reflect_branch.as_resource()
.ok_or(elua::Error::runtime("Provided type is not a resource!"))?;
let world = this.as_mut();
res.insert(world, data);
Ok(())
})
.method_mut("request_res", |_, this, path: String| { .method_mut("request_res", |_, this, path: String| {
let world = this.as_mut(); let world = this.as_mut();
let mut man = world.get_resource_mut::<ResourceManager>(); let mut man = world.get_resource_mut::<ResourceManager>();

View File

@ -0,0 +1,147 @@
use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, LayoutId, MouseAxis, MouseInput};
use lazy_static::lazy_static;
use regex::Regex;
use crate::{lua::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow};
lazy_static! {
static ref BINDING_INPUT_RE: Regex = Regex::new(r"(\w+):(\w+)(?:=(-?\d+(?:.\d+)?))?").unwrap();
}
#[derive(Clone)]
pub struct LuaActionHandler {
handler: ActionHandler
}
impl elua::Userdata for LuaActionHandler {
fn name() -> String {
"ActionHandler".to_string()
}
fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> {
builder.function("new", |_, table: elua::Table| {
let mut handler = ActionHandler::new();
// create the layouts
let layouts = table.get::<_, elua::Table>("layouts")
.map_err(|_| elua::Error::runtime("missing 'layouts' in ActionHandler table"))?;
for layout_id in layouts.sequence_iter::<u32>() {
let layout_id = layout_id?;
handler.add_layout(LayoutId(layout_id));
}
let actions = table.get::<_, elua::Table>("actions")
.map_err(|_| elua::Error::runtime("missing 'actions' in ActionHandler table"))?;
for pair in actions.pairs::<String, String>() {
let (action_lbl, action_type) = pair?;
let action_type = action_type.to_lowercase();
let action_type = match action_type.as_str() {
"axis" => ActionKind::Axis,
"button" => ActionKind::Button,
_ => todo!("Handle invalid axis type"),
};
handler.add_action(action_lbl, Action::new(action_type));
}
let mappings= table.get::<_, elua::Table>("mappings")
.map_err(|_| elua::Error::runtime("missing 'mappings' in ActionHandler table"))?;
for (map_id, tbl) in mappings.sequence_iter::<elua::Table>().enumerate() {
let tbl = tbl?;
let layout_id = tbl.get::<_, u32>("layout")?;
let mut mapping = ActionMapping::new(LayoutId(layout_id), ActionMappingId(map_id as u32));
let binds_tbl = tbl.get::<_, elua::Table>("binds")
.map_err(|_| elua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?;
for pair in binds_tbl.pairs::<String, elua::Table>() {
let (action_lbl, input_binds) = pair?;
for input in input_binds.sequence_iter::<String>() {
let input = input?.to_lowercase();
let action = handler.action(&action_lbl)
.ok_or(elua::Error::Runtime(format!("Unknown action specified in mapping binds: {}", action_lbl)))?;
let mut binds = Vec::new();
let input_split: Vec<&str> = input.split(":").collect();
let input_name = input_split[0];
let button = input_split[1];
if action.kind == ActionKind::Axis {
if button == "axis" {
let axis_name = input_split[2];
let src = process_axis_string(input_name, axis_name)
.ok_or(elua::Error::Runtime(format!("invalid bind '{input_name}', unable to find device or axis for device")))?;
binds.push(src.into_binding());
} else {
// splits 'down=1' into 'down' and '1'
let (button, val_str) = button.split_once("=")
.ok_or(elua::Error::Runtime(format!("invalid bind string for Axis Action: '{input}' (expected '=' with float)")))?;
let val = val_str.parse::<f32>()
.map_err(|e| elua::Error::Runtime(format!("invalid bind string for Axis Action: '{input}' ({e})")))?;
let src = process_keyboard_string(button)
.ok_or(elua::Error::Runtime(format!("invalid key in bind: '{button}'")))?;
binds.push(src.into_binding_modifier(val));
}
} else {
todo!("Process bindings for Button Actions");
}
mapping.bind(action_lbl.clone(), &binds);
}
}
handler.add_mapping(mapping);
}
Ok(LuaActionHandler {
handler,
})
})
.method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(ScriptBorrow::from_resource::<ActionHandler>(Some(this.handler.clone())))
})
.method(FN_NAME_INTERNAL_REFLECT_TYPE, |_, _, ()| {
Ok(ScriptBorrow::from_resource::<ActionHandler>(None))
});
Ok(())
}
}
impl<'a> elua::FromLua<'a> for LuaActionHandler {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("ActionHandler", &tyname))?;
let handle = ud.as_ref::<LuaActionHandler>()?;
Ok(handle.clone())
}
}
fn process_keyboard_string(key_name: &str) -> Option<ActionSource> {
let key = keycode_from_str(key_name)?;
Some(ActionSource::Keyboard(key))
}
fn process_axis_string(device: &str, axis_name: &str) -> Option<ActionSource> {
match device {
"mouse" => match axis_name {
"y" => Some(ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y))),
"x" => Some(ActionSource::Mouse(MouseInput::Axis(MouseAxis::X))),
"scroll" | "scrollwheel" => Some(ActionSource::Mouse(MouseInput::Axis(MouseAxis::ScrollWheel))),
_ => None,
},
_ => None
}
}

View File

@ -8,4 +8,7 @@ pub mod res_handle;
pub use res_handle::*; pub use res_handle::*;
pub mod model_comp; pub mod model_comp;
pub use model_comp::*; pub use model_comp::*;
pub mod input_actions;
pub use input_actions::*;