lua: expose DeviceEvent

This commit is contained in:
SeanOMik 2024-10-13 11:43:49 -04:00
parent 8e56ee1f0f
commit 6a47cd2671
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
14 changed files with 526 additions and 71 deletions

View File

@ -14,6 +14,19 @@ function udname(val)
return tbl.__name return tbl.__name
end end
function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
function on_init() function on_init()
@ -46,6 +59,17 @@ function on_first()
end, Window end, Window
) )
end end
---@type EventReader
local reader = world:read_event(DeviceEvent)
---@param ev DeviceEvent
for ev in reader:read() do
if ev.event.kind == DeviceEventKind.MOTION then
local motion_ev = ev.event --[[@as DeviceEventMotion]]
print("axis: " .. tostring(motion_ev.axis) .. " = " .. tostring(motion_ev.value))
end
end
end end
--[[ function on_pre_update() --[[ function on_pre_update()

View File

@ -1,4 +1,4 @@
use std::{cell::RefCell, rc::Rc, sync::Arc}; use std::sync::Arc;
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use lyra_ecs::{query::{ResMut, WorldTick}, system::FnArgFetcher, Tick}; use lyra_ecs::{query::{ResMut, WorldTick}, system::FnArgFetcher, Tick};
@ -99,7 +99,7 @@ impl<T: Event> Events<T> {
pub fn reader(&self) -> EventReader<T> { pub fn reader(&self) -> EventReader<T> {
EventReader { EventReader {
events: self.events.clone(), events: self.events.clone(),
cursor: Rc::new(RefCell::new(0)), cursor: Arc::new(AtomicRefCell::new(0)),
} }
} }
@ -112,11 +112,11 @@ impl<T: Event> Events<T> {
pub struct EventReader<T: Event> { pub struct EventReader<T: Event> {
events: Arc<AtomicRefCell<WaterfallVec<T>>>, events: Arc<AtomicRefCell<WaterfallVec<T>>>,
cursor: Rc<RefCell<usize>>, cursor: Arc<AtomicRefCell<usize>>,
} }
impl<T: Event> EventReader<T> { impl<T: Event> EventReader<T> {
pub fn read(&mut self) -> Option<atomic_refcell::AtomicRef<T>> { pub fn read(&self) -> Option<atomic_refcell::AtomicRef<T>> {
let events = self.events.borrow(); let events = self.events.borrow();
let mut cursor = self.cursor.borrow_mut(); let mut cursor = self.cursor.borrow_mut();
@ -136,7 +136,7 @@ pub struct EventWriter<T: Event> {
} }
impl<T: Event> EventWriter<T> { impl<T: Event> EventWriter<T> {
pub fn write(&mut self, event: T) { pub fn write(&self, event: T) {
let mut events = self.events.borrow_mut(); let mut events = self.events.borrow_mut();
events.push(event); events.push(event);
} }
@ -167,12 +167,12 @@ where
} }
impl<T: Event> FnArgFetcher for EventReader<T> { impl<T: Event> FnArgFetcher for EventReader<T> {
type State = Rc<RefCell<usize>>; type State = Arc<AtomicRefCell<usize>>;
type Arg<'a, 'state> = EventReader<T>; type Arg<'a, 'state> = EventReader<T>;
fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State { fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State {
Rc::new(RefCell::new(0)) Arc::new(AtomicRefCell::new(0))
} }
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> { unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {

View File

@ -520,7 +520,8 @@ impl KeyCode {
pub fn as_physical_key(&self) -> winit::keyboard::PhysicalKey { pub fn as_physical_key(&self) -> winit::keyboard::PhysicalKey {
match self { match self {
KeyCode::Unknown(v) => winit::keyboard::PhysicalKey::Unidentified((*v).into()), KeyCode::Unknown(v) => winit::keyboard::PhysicalKey::Unidentified((*v).into()),
_ => winit::keyboard::PhysicalKey::Code(self.into()) // unwrap is safe here since we know self is not an `Unknown` key.
_ => winit::keyboard::PhysicalKey::Code(self.as_winit_keycode().unwrap())
} }
} }

View File

@ -6,7 +6,7 @@ use winit::{event::{MouseScrollDelta, WindowEvent}, keyboard::PhysicalKey};
use crate::{game::GameStages, plugin::Plugin, winit::DeviceEventPair, EventReader, EventWriter}; use crate::{game::GameStages, plugin::Plugin, winit::DeviceEventPair, EventReader, EventWriter};
use super::{events::*, InputButtons}; use super::{events::*, InputButtons, KeyCode};
fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &MouseScrollDelta) { fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &MouseScrollDelta) {
let event = match delta { let event = match delta {
@ -21,24 +21,24 @@ fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &Mo
mouse_scroll_ev.write(event); mouse_scroll_ev.write(event);
} }
fn write_key_event(key_buttons: &mut ResMut<InputButtons<winit::keyboard::KeyCode>>, physical_key: winit::keyboard::PhysicalKey, state: winit::event::ElementState) { fn write_key_event(key_buttons: &mut ResMut<InputButtons<KeyCode>>, physical_key: PhysicalKey, state: winit::event::ElementState) {
if let PhysicalKey::Code(code) = physical_key { if let PhysicalKey::Code(code) = physical_key {
key_buttons.add_input_from_winit(code, state); key_buttons.add_input_from_winit(KeyCode::from(code), state);
} }
} }
pub fn input_system( pub fn input_system(
mut key_code_res: ResMut<InputButtons<winit::keyboard::KeyCode>>, mut key_code_res: ResMut<InputButtons<KeyCode>>,
mut mouse_btn_res: ResMut<InputButtons<MouseButton>>, mut mouse_btn_res: ResMut<InputButtons<MouseButton>>,
mut touches_res: ResMut<Touches>, mut touches_res: ResMut<Touches>,
mut window_ev: EventReader<WindowEvent>, window_ev: EventReader<WindowEvent>,
mut device_ev: EventReader<DeviceEventPair>, device_ev: EventReader<DeviceEventPair>,
mut mouse_scroll_ev: EventWriter<MouseScroll>, mut mouse_scroll_ev: EventWriter<MouseScroll>,
mut mouse_btn_ev: EventWriter<MouseButton>, mouse_btn_ev: EventWriter<MouseButton>,
mut mouse_exact_ev: EventWriter<MouseExact>, mouse_exact_ev: EventWriter<MouseExact>,
mut mouse_entered_ev: EventWriter<CursorEnteredWindow>, mouse_entered_ev: EventWriter<CursorEnteredWindow>,
mut mouse_left_ev: EventWriter<CursorLeftWindow>, mouse_left_ev: EventWriter<CursorLeftWindow>,
mut mouse_motion_ev: EventWriter<MouseMotion>, mouse_motion_ev: EventWriter<MouseMotion>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
while let Some(event) = window_ev.read() { while let Some(event) = window_ev.read() {
match event.deref() { match event.deref() {
@ -122,7 +122,7 @@ pub struct InputPlugin;
impl Plugin for InputPlugin { impl Plugin for InputPlugin {
fn setup(&mut self, app: &mut crate::game::App) { fn setup(&mut self, app: &mut crate::game::App) {
app.add_resource(InputButtons::<winit::keyboard::KeyCode>::default()); app.add_resource(InputButtons::<KeyCode>::default());
app.add_resource(InputButtons::<MouseButton>::default()); app.add_resource(InputButtons::<MouseButton>::default());
app.add_resource(Touches::default()); app.add_resource(Touches::default());

View File

@ -13,7 +13,7 @@ use winit::{
window::{Window, WindowAttributes, WindowId}, window::{Window, WindowAttributes, WindowId},
}; };
pub use winit::event::{DeviceId, DeviceEvent, MouseScrollDelta, ElementState}; pub use winit::event::{DeviceId, DeviceEvent, MouseScrollDelta, ElementState, RawKeyEvent};
pub use winit::keyboard::PhysicalKey; pub use winit::keyboard::PhysicalKey;
use crate::{ use crate::{

View File

@ -66,4 +66,29 @@ WrappingMode = {
CameraProjectionMode = { CameraProjectionMode = {
PERSPECTIVE = "perspective", PERSPECTIVE = "perspective",
ORTHOGRAPHIC = "orthographic", ORTHOGRAPHIC = "orthographic",
}
---@enum DeviceEventKind
DeviceEventKind = {
ADDED = "added",
REMOVED = "removed",
MOUSE_MOTION = "mouse_motion",
MOUSE_WHEEL = "mouse_wheel",
MOTION = "motion",
BUTTON = "button",
KEY = "key",
}
---@enum NativeKeyCodeKind
NativeKeyCodeKind = {
ANDROID = "android",
MACOS = "macos",
WINDOWS = "windows",
XKB = "xkb",
}
---@enum ElementState
ElementState = {
PRESSED = "pressed",
RELEASED = "released",
} }

View File

@ -0,0 +1,140 @@
---@meta
---@class DeviceEventAdded: table
DeviceEventAdded = {
---@type DeviceEventKind
kind = DeviceEventKind.ADDED,
}
---@class DeviceEventRemoved: table
DeviceEventRemoved = {
---@type DeviceEventKind
kind = DeviceEventKind.REMOVED,
}
---@class DeviceEventMouseMotion: table
DeviceEventMouseMotion = {
---@type DeviceEventKind
kind = DeviceEventKind.MOUSE_MOTION,
---The change in physical position of a pointing device.
---
---This represents raw, unfiltered physical motion.
---
---@type Vec2
delta = nil,
}
---A physical scroll event from a device.
---
---`line_delta` and `pixel_delta` are exclusive, only one is non-nil at a time.
---
---@class DeviceEventMouseWheel: table
DeviceEventMouseWheel = {
---@type DeviceEventKind
kind = DeviceEventKind.MOUSE_WHEEL,
---Amount in lines or rows to scroll.
---
---Positive values indicate that the content that is being scrolled should move right
---and down (revealing more content left and up).
---
---@type Vec2?
line_delta = nil,
---Amount in pixels to scroll in the horizontal and vertical direction.
---
---Positive values indicate that the content being scrolled should move right/down.
---
---@type Vec2?
pixel_delta = nil,
}
---Device event that was triggered by motion on an analog axis.
---@class DeviceEventMotion: table
DeviceEventMotion = {
---@type DeviceEventKind
kind = DeviceEventKind.MOTION,
---The analog axis that motion was triggered from.
---@type number
axis = nil,
---The amount of motion.
---@type number
value = nil,
}
---@class DeviceEventButton: table
DeviceEventButton = {
---@type DeviceEventKind
kind = DeviceEventKind.BUTTON,
---The button id that the event is from.
---@type number
button = nil,
---The state of the button.
---@type ElementState
state = nil,
}
---A device event triggered from a key press.
---
---The field `code` will be nil if the key code identifier is unknown.
---When the identifier is unknown, it can be retrieved with `native_code`. The field
---`native` specifies the kind of platform the code is from.
---
---@class DeviceEventKey: table
DeviceEventKey = {
---@type DeviceEventKind
kind = DeviceEventKind.KEY,
---@type ElementState
state = nil,
---The known key name.
---
---This is `nil` if `native` or `native_code` is specified.
---
---@type string
code = nil,
---The kind of native platform this code is for.
---
---This is `nil` if `code` is specified.
---
---@type NativeKeyCodeKind
native = nil,
---The platform-native physical key identifier.
---
---This is `nil` if `code` is specified.
---
---@type number
native_code = nil,
}
---@alias DeviceEventRaw
---| DeviceEventAdded
---| DeviceEventRemoved
---| DeviceEventMotion
---| DeviceEventMouseMotion
---| DeviceEventMouseWheel
---| DeviceEventButton
---| DeviceEventKey
DeviceEventRaw = { }
---@class DeviceId: userdata
DeviceId = {}
---@class DeviceEvent: table
DeviceEvent = {
---The device id that the event came from
---@type DeviceId
source = nil,
---The actual device event
---@type DeviceEventRaw
event = nil,
}

View File

@ -0,0 +1,18 @@
---@meta
---@class EventReader: userdata
EventReader = {}
---Returns an iterator over the events in the reader.
---@return fun(): any
function EventReader:read() end
---@class EventWriter: userdata
EventWriter = {}
---Writes an event.
---
---The event must be the same type that this writer wraps.
---
---@param event any
function EventWriter:write(event) end

View File

@ -1,10 +1,11 @@
use std::any::TypeId; use std::any::TypeId;
use lyra_ecs::ResourceObject; use lyra_ecs::ResourceObject;
use lyra_reflect::{Reflect, TypeRegistry}; use lyra_game::winit::DeviceEventPair;
use lyra_reflect::{FromType, Reflect, TypeRegistry};
use lyra_resource::gltf::Gltf; use lyra_resource::gltf::Gltf;
use crate::{lua::{wrappers::*, LuaContext, LuaWrapper, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; use crate::{lua::{wrappers::*, LuaContext, LuaWrapper, ReflectLuaProxy, RegisterLuaType, TypeLookup, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
//fn register_lua_proxy::<T: //fn register_lua_proxy::<T:
@ -21,12 +22,25 @@ impl ScriptApiProvider for LyraEcsApiProvider {
world.register_lua_wrapper::<LuaWindow>(); world.register_lua_wrapper::<LuaWindow>();
world.register_lua_convert_component::<LuaCamera>("Camera"); world.register_lua_convert_component::<LuaCamera>("Camera");
world.register_lua_convert_component::<LuaFreeFlyCamera>("FreeFlyCamera"); world.register_lua_convert_component::<LuaFreeFlyCamera>("FreeFlyCamera");
world.register_lua_wrapper::<LuaDeviceId>();
world.register_lua_convert::<LuaDeviceEventRaw>();
world.register_lua_convert::<LuaDeviceEvent>();
// Add typeid of 'DeviceEvent'
let mut lookup = world.get_resource_or_default::<TypeLookup>();
lookup.typeid_from_name.insert("DeviceEvent".into(), std::any::TypeId::of::<DeviceEventPair>());
drop(lookup);
let mut registry = world.get_resource_mut::<TypeRegistry>().unwrap(); let mut registry = world.get_resource_mut::<TypeRegistry>().unwrap();
// add LuaWrappedEventProxy
let reg_type = registry.get_type_or_default(TypeId::of::<DeviceEventPair>());
let l: LuaWrappedEventProxy = FromType::<LuaDeviceEvent>::from_type();
reg_type.add_data(l);
// add Gltf handle
let reg_type = registry.get_type_or_default(TypeId::of::<Gltf>()); let reg_type = registry.get_type_or_default(TypeId::of::<Gltf>());
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>()); reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>());
let l = LuaResHandleToComponent::new( let l = LuaResHandleToComponent::new(
|lua, res| { |lua, res| {
if let Some(gltf) = res.as_typed::<Gltf>() { if let Some(gltf) = res.as_typed::<Gltf>() {
@ -53,8 +67,9 @@ impl ScriptApiProvider for LyraEcsApiProvider {
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?; globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?; globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?;
expose_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?; expose_comp_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?;
expose_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?; expose_comp_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?;
expose_table_wrapper::<LuaDeviceEvent>(&ctx, &globals, "DeviceEvent")?;
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)?;
@ -104,12 +119,23 @@ where
/// ///
/// This creates the reflection functions on a table specified in globals. /// This creates the reflection functions on a table specified in globals.
/// The table name is set to `name`, which is also how the script will use the table. /// The table name is set to `name`, which is also how the script will use the table.
fn expose_table_wrapper<T>(lua: &mlua::Lua, globals: &mlua::Table, name: &str) -> mlua::Result<()> fn expose_comp_table_wrapper<T>(lua: &mlua::Lua, globals: &mlua::Table, name: &str) -> mlua::Result<()>
where where
T: LuaWrapper + mlua::FromLua, T: LuaWrapper + mlua::FromLua,
T::Wrap: lyra_ecs::Component + Reflect T::Wrap: lyra_ecs::Component + Reflect
{ {
let table = create_reflect_comp_table::<T>(&lua, name)?; let table = create_reflect_comp_table::<T>(&lua, name)?;
globals.set(name, table)?; globals.set(name, table)?;
Ok(())
}
fn expose_table_wrapper<T>(lua: &mlua::Lua, globals: &mlua::Table, name: &str) -> mlua::Result<()>
where
T: LuaWrapper,
{
let table = lua.create_table()?;
table.set(mlua::MetaMethod::Type.name(), name.to_string())?;
globals.set(name, table)?;
Ok(()) Ok(())
} }

View File

@ -8,8 +8,8 @@ use crate::{ScriptBorrow, ScriptDynamicBundle};
use super::{Error, FN_NAME_INTERNAL_REFLECT}; use super::{Error, FN_NAME_INTERNAL_REFLECT};
pub trait LuaWrapper { pub trait LuaWrapper: Sized {
type Wrap: Reflect + 'static; type Wrap: 'static;
/// The type id of the wrapped type. /// The type id of the wrapped type.
#[inline(always)] #[inline(always)]
@ -18,6 +18,12 @@ pub trait LuaWrapper {
} }
fn into_wrapped(self) -> Self::Wrap; fn into_wrapped(self) -> Self::Wrap;
#[inline(always)]
fn from_wrapped(wrap: Self::Wrap) -> Option<Self> {
let _ = wrap;
None
}
} }
/// A trait that used to convert something into lua, or to set something to a value from lua. /// A trait that used to convert something into lua, or to set something to a value from lua.
@ -36,7 +42,7 @@ pub trait LuaProxy {
impl<'a, T> LuaProxy for T impl<'a, T> LuaProxy for T
where where
T: Reflect + Clone + mlua::FromLua + mlua::UserData T: Reflect + Clone + mlua::FromLua + mlua::IntoLua
{ {
fn as_lua_value( fn as_lua_value(
lua: &mlua::Lua, lua: &mlua::Lua,
@ -47,16 +53,14 @@ where
} }
fn apply( fn apply(
_: &mlua::Lua, lua: &mlua::Lua,
this: &mut dyn Reflect, this: &mut dyn Reflect,
apply: &mlua::Value, apply: &mlua::Value,
) -> mlua::Result<()> { ) -> mlua::Result<()> {
let this = this.as_any_mut().downcast_mut::<T>().unwrap(); let this = this.as_any_mut().downcast_mut::<T>().unwrap();
let apply = apply.as_userdata() let apply = T::from_lua(apply.clone(), lua)?;
.expect("Somehow a non-userdata Lua Value was provided to a LuaProxy")
.borrow::<T>()?;
*this = apply.clone(); *this = apply;
Ok(()) Ok(())
} }

View File

@ -10,14 +10,20 @@ use lyra_resource::ResourceManager;
use mlua::{IntoLua, ObjectLike}; use mlua::{IntoLua, ObjectLike};
use super::{ use super::{
reflect_user_data, wrappers::LuaResHandleToComponent, Error, TypeLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE reflect_user_data,
wrappers::{LuaResHandleToComponent, LuaWrappedEventProxy},
Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT,
FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE,
}; };
impl mlua::FromLua for ScriptEntity { impl mlua::FromLua for ScriptEntity {
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> { fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match value { match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptEntity", "Nil"))), mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch(
"ScriptEntity",
"Nil",
))),
_ => panic!(), _ => panic!(),
} }
} }
@ -41,7 +47,10 @@ impl mlua::FromLua for ScriptWorldPtr {
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> { fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match val { match val {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptWorldPtr", "Nil"))), mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch(
"ScriptWorldPtr",
"Nil",
))),
_ => panic!(), _ => panic!(),
} }
} }
@ -64,8 +73,7 @@ impl mlua::UserData for ScriptWorldPtr {
})?; })?;
let comp_borrow = { let comp_borrow = {
if let Ok(as_comp) = ud.get::<mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) if let Ok(as_comp) = ud.get::<mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) {
{
let ud = match as_comp.call(ud.clone())? { let ud = match as_comp.call(ud.clone())? {
mlua::Value::UserData(ud) => ud, mlua::Value::UserData(ud) => ud,
mlua::Value::Nil => ud.clone(), mlua::Value::Nil => ud.clone(),
@ -115,11 +123,12 @@ impl mlua::UserData for ScriptWorldPtr {
mlua::Value::Table(t) => { mlua::Value::Table(t) => {
let name: String = t.get(mlua::MetaMethod::Type.name())?; let name: String = t.get(mlua::MetaMethod::Type.name())?;
let lookup = world.get_resource::<TypeLookup>().ok_or( let lookup =
mlua::Error::runtime( world
"Unable to lookup table proxy, none were ever registered!", .get_resource::<TypeLookup>()
), .ok_or(mlua::Error::runtime(
)?; "Unable to lookup table proxy, none were ever registered!",
))?;
let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| { let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| {
mlua::Error::BadArgument { mlua::Error::BadArgument {
to: Some("World:view".into()), to: Some("World:view".into()),
@ -139,10 +148,7 @@ impl mlua::UserData for ScriptWorldPtr {
} }
mlua::Value::UserData(ud) => { mlua::Value::UserData(ud) => {
let reflect = ud let reflect = ud
.call_function::<ScriptBorrow>( .call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
FN_NAME_INTERNAL_REFLECT_TYPE,
(),
)
.expect("Type does not implement 'reflect_type' properly"); .expect("Type does not implement 'reflect_type' properly");
let refl_comp = reflect.reflect_branch.as_component_unchecked(); let refl_comp = reflect.reflect_branch.as_component_unchecked();
@ -172,7 +178,9 @@ impl mlua::UserData for ScriptWorldPtr {
let mut world = this.write(); let mut world = this.write();
while let Some(row) = reflected_iter.next_lua(lua) { while let Some(row) = reflected_iter.next_lua(lua) {
let r = row.row.into_iter() let r = row
.row
.into_iter()
.into_iter() .into_iter()
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>())) .map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -183,7 +191,6 @@ impl mlua::UserData for ScriptWorldPtr {
// if values were returned, find the type in the type registry, and apply the new values // if values were returned, find the type in the type registry, and apply the new values
if res.len() <= ptrs.len() { if res.len() <= ptrs.len() {
for (comp, ptr) in res.into_iter().zip(ptrs) { for (comp, ptr) in res.into_iter().zip(ptrs) {
let lua_typeid = match &comp { let lua_typeid = match &comp {
mlua::Value::UserData(ud) => { mlua::Value::UserData(ud) => {
@ -310,5 +317,41 @@ impl mlua::UserData for ScriptWorldPtr {
Ok((data.fn_to_lua)(lua, handle).unwrap()) Ok((data.fn_to_lua)(lua, handle).unwrap())
}); });
methods.add_method_mut(
"read_event",
|lua, this, either: mlua::Either<String, mlua::Table>| {
let mut world = this.write();
// get the type name
let name = match either {
mlua::Either::Left(name) => name,
mlua::Either::Right(table) => table.get(mlua::MetaMethod::Type.name())?,
};
// lookup the type id using the name, return an error if not found
let lookup = world.get_resource::<TypeLookup>().unwrap();
let tyid = lookup.typeid_from_name.get(&name).cloned();
if tyid.is_none() {
return Err(mlua::Error::runtime(format!(
"failed to lookup type id from type name: {}",
name
)));
}
let tyid = tyid.unwrap();
drop(lookup);
let registry = world.get_resource::<TypeRegistry>().unwrap();
let ty = registry
.get_type(tyid)
.expect("Could not find asset type in registry");
let data = ty
.get_data::<LuaWrappedEventProxy>()
.expect("Asset type does not have 'LuaWrappedEventProxy' as TypeData")
.clone();
drop(registry);
data.reader(&mut world).into_lua(lua)
},
);
} }
} }

View File

@ -4,16 +4,18 @@ use lyra_game::{
math::Vec2, math::Vec2,
winit::{ winit::{
dpi::PhysicalPosition, DeviceEvent, DeviceEventPair, DeviceId, MouseScrollDelta, dpi::PhysicalPosition, DeviceEvent, DeviceEventPair, DeviceId, MouseScrollDelta,
PhysicalKey, PhysicalKey, RawKeyEvent,
}, },
}; };
use lyra_reflect::Reflect;
use lyra_scripting_derive::wrap_lua_struct; use lyra_scripting_derive::wrap_lua_struct;
use crate as lyra_scripting;
use crate::lyra_engine; use crate::lyra_engine;
use super::LuaVec2; use super::LuaVec2;
wrap_lua_struct!(DeviceId, skip(lua_reflect, lua_wrapper)); wrap_lua_struct!(DeviceId, skip(lua_reflect));
/// Wraps [`DeviceEvent`] and implements [`IntoLua`] /// Wraps [`DeviceEvent`] and implements [`IntoLua`]
#[derive(Clone)] #[derive(Clone)]
@ -42,7 +44,7 @@ impl mlua::FromLua for LuaDeviceEventRaw {
message: Some("expected Lua Table".into()), message: Some("expected Lua Table".into()),
})?; })?;
let type_str: String = table.get("type")?; let type_str: String = table.get("kind")?;
let type_str = type_str.as_str(); let type_str = type_str.as_str();
let ret = match type_str { let ret = match type_str {
@ -125,8 +127,9 @@ impl mlua::FromLua for LuaDeviceEventRaw {
message: Some(format!("unknown key code: '{}'", code_str)), message: Some(format!("unknown key code: '{}'", code_str)),
}); });
} }
let code = code.unwrap();
let phy = PhysicalKey::Code(code); let phy = PhysicalKey::Code(code.as_winit_keycode().unwrap());
Self(DeviceEvent::Key(RawKeyEvent { Self(DeviceEvent::Key(RawKeyEvent {
physical_key: phy, physical_key: phy,
state, state,
@ -140,12 +143,19 @@ impl mlua::FromLua for LuaDeviceEventRaw {
"unknown" => NativeKeyCode::Unidentified, "unknown" => NativeKeyCode::Unidentified,
"android" => NativeKeyCode::Android(native_code), "android" => NativeKeyCode::Android(native_code),
"macos" => NativeKeyCode::MacOS(native_code as u16), "macos" => NativeKeyCode::MacOS(native_code as u16),
"windows" => NativeKeyCode::Windows(native_code), "windows" => NativeKeyCode::Windows(native_code as u16),
"xkb" => NativeKeyCode::Xkb(native_code), "xkb" => NativeKeyCode::Xkb(native_code),
_ => {
return Err(mlua::Error::FromLuaConversionError {
from: ty,
to: "LuaDeviceEventRaw".into(),
message: Some(format!("unknown native code type: {}", native_str)),
});
}
}; };
Self(DeviceEvent::Key(RawKeyEvent { Self(DeviceEvent::Key(RawKeyEvent {
physical_key: PhysicalKey::Unidentified(native_code), physical_key: PhysicalKey::Unidentified(native_code.into()),
state, state,
})) }))
} else { } else {
@ -179,17 +189,17 @@ impl mlua::IntoLua for LuaDeviceEventRaw {
match self.0 { match self.0 {
DeviceEvent::Added => { DeviceEvent::Added => {
table.set("type", "added")?; table.set("kind", "added")?;
} }
DeviceEvent::Removed => { DeviceEvent::Removed => {
table.set("type", "removed")?; table.set("kind", "removed")?;
} }
DeviceEvent::MouseMotion { delta } => { DeviceEvent::MouseMotion { delta } => {
table.set("type", "mouse_motion")?; table.set("kind", "mouse_motion")?;
table.set("delta", LuaVec2(Vec2::new(delta.0 as _, delta.1 as _)))?; table.set("delta", LuaVec2(Vec2::new(delta.0 as _, delta.1 as _)))?;
} }
DeviceEvent::MouseWheel { delta } => { DeviceEvent::MouseWheel { delta } => {
table.set("type", "mouse_wheel")?; table.set("kind", "mouse_wheel")?;
match delta { match delta {
MouseScrollDelta::LineDelta(x, y) => { MouseScrollDelta::LineDelta(x, y) => {
@ -204,12 +214,12 @@ impl mlua::IntoLua for LuaDeviceEventRaw {
} }
} }
DeviceEvent::Motion { axis, value } => { DeviceEvent::Motion { axis, value } => {
table.set("type", "motion")?; table.set("kind", "motion")?;
table.set("axis", axis)?; table.set("axis", axis)?;
table.set("value", value)?; table.set("value", value)?;
} }
DeviceEvent::Button { button, state } => { DeviceEvent::Button { button, state } => {
table.set("type", "button")?; table.set("kind", "button")?;
table.set("button", button)?; table.set("button", button)?;
let state = match state { let state = match state {
@ -219,7 +229,7 @@ impl mlua::IntoLua for LuaDeviceEventRaw {
table.set("state", state)?; table.set("state", state)?;
} }
DeviceEvent::Key(raw_key_event) => { DeviceEvent::Key(raw_key_event) => {
table.set("type", "key")?; table.set("kind", "key")?;
let state = match raw_key_event.state { let state = match raw_key_event.state {
lyra_game::winit::ElementState::Pressed => "pressed", lyra_game::winit::ElementState::Pressed => "pressed",
@ -262,7 +272,16 @@ impl mlua::IntoLua for LuaDeviceEventRaw {
} }
} }
#[derive(Clone)] impl LuaWrapper for LuaDeviceEventRaw {
type Wrap = DeviceEvent;
#[inline(always)]
fn into_wrapped(self) -> Self::Wrap {
self.0
}
}
#[derive(Clone, Reflect)]
pub struct LuaDeviceEvent(pub(crate) DeviceEventPair); pub struct LuaDeviceEvent(pub(crate) DeviceEventPair);
impl std::ops::Deref for LuaDeviceEvent { impl std::ops::Deref for LuaDeviceEvent {
@ -292,8 +311,8 @@ impl mlua::FromLua for LuaDeviceEvent {
let ev: LuaDeviceEventRaw = table.get("event")?; let ev: LuaDeviceEventRaw = table.get("event")?;
Ok(Self(DeviceEventPair { Ok(Self(DeviceEventPair {
device_src: id, device_src: id.0,
event: ev, event: ev.0,
})) }))
} }
} }
@ -303,7 +322,6 @@ impl mlua::IntoLua for LuaDeviceEvent {
let table = lua.create_table()?; let table = lua.create_table()?;
table.set("device", LuaDeviceId(self.device_src))?; table.set("device", LuaDeviceId(self.device_src))?;
table.set("event", LuaDeviceEventRaw(self.event.clone()))?; table.set("event", LuaDeviceEventRaw(self.event.clone()))?;
table.into_lua(lua) table.into_lua(lua)
} }
} }
@ -315,4 +333,9 @@ impl LuaWrapper for LuaDeviceEvent {
fn into_wrapped(self) -> Self::Wrap { fn into_wrapped(self) -> Self::Wrap {
self.0 self.0
} }
}
#[inline(always)]
fn from_wrapped(wrap: Self::Wrap) -> Option<Self> {
Some(Self(wrap))
}
}

View File

@ -0,0 +1,151 @@
use std::{marker::PhantomData, sync::Arc};
use crate::lua::LuaWrapper;
use atomic_refcell::AtomicRefCell;
use lyra_ecs::World;
use lyra_game::{
Event, EventReader, EventWriter, Events,
};
use lyra_reflect::{FromType, Reflect};
use mlua::{FromLua, IntoLua};
use super::LuaVec2;
mod device_event;
pub use device_event::*;
/// Helper trait to type erase EventReader<T>
trait UntypedEventReader: Send + Sync {
fn next(&self, lua: &mlua::Lua) -> mlua::Result<mlua::Value>;
}
/// Helper trait to type erase EventWriter<T>
trait UntypedEventWriter: Send + Sync {
fn push(&self, lua: &mlua::Lua, event: mlua::Value) -> mlua::Result<()>;
}
/// Event reader exposed to Lua
#[derive(Clone)]
pub struct LuaEventReader(Arc<AtomicRefCell<dyn UntypedEventReader>>);
impl mlua::FromLua for LuaEventReader {
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let ty = value.type_name();
let ud = value.as_userdata()
.ok_or(mlua::Error::FromLuaConversionError { from: ty, to: "EventReader".into(), message: None })?;
let reader = ud.borrow::<Self>()?;
Ok(reader.clone())
}
}
impl mlua::UserData for LuaEventReader {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("read", |lua, this, ()| {
let reg = lua.create_registry_value(this.clone())?;
lua.create_function(move |lua: &mlua::Lua, ()| {
let reader = lua.registry_value::<Self>(&reg)?;
let reader = reader.0.borrow();
reader.next(lua)
})
});
}
}
/// Event writer exposed to Lua
#[derive(Clone)]
pub struct LuaEventWriter(Arc<AtomicRefCell<dyn UntypedEventWriter>>);
impl mlua::UserData for LuaEventWriter {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("write", |lua, this, event: mlua::Value| {
let writer = this.0.borrow();
writer.push(lua, event)?;
Ok(())
});
}
}
struct TypedReader<T>
where
T: Reflect + LuaWrapper + IntoLua + Send + Sync,
T::Wrap: Clone + Event,
{
reader: EventReader<T::Wrap>,
_marker: PhantomData<T>,
}
impl<T> UntypedEventReader for TypedReader<T>
where
T: Reflect + LuaWrapper + IntoLua + Send + Sync,
T::Wrap: Clone + Event,
{
fn next(&self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
self.reader
.read()
.map(|e| T::from_wrapped(e.clone()).into_lua(lua))
.unwrap_or(Ok(mlua::Value::Nil))
}
}
struct TypedWriter<T>
where
T: Reflect + LuaWrapper + FromLua,
T::Wrap: Clone + Event,
{
writer: EventWriter<T::Wrap>,
_marker: PhantomData<T>,
}
impl<T> UntypedEventWriter for TypedWriter<T>
where
T: Reflect + LuaWrapper + FromLua,
T::Wrap: Clone + Event,
{
fn push(&self, lua: &mlua::Lua, event: mlua::Value) -> mlua::Result<()> {
let ev = T::from_lua(event, lua)?.into_wrapped();
self.writer.write(ev);
Ok(())
}
}
#[derive(Clone)]
pub struct LuaWrappedEventProxy {
fn_reader: for<'a> fn(world: &'a mut World) -> Arc<AtomicRefCell<dyn UntypedEventReader>>,
fn_writer: for<'a> fn(world: &'a mut World) -> Arc<AtomicRefCell<dyn UntypedEventWriter>>,
}
impl<T> FromType<T> for LuaWrappedEventProxy
where
T: Reflect + LuaWrapper + FromLua + IntoLua + Send + Sync,
T::Wrap: Clone + Event,
{
fn from_type() -> Self {
Self {
fn_reader: |world| {
let events = world.get_resource_or_default::<Events<T::Wrap>>();
Arc::new(AtomicRefCell::new(TypedReader {
reader: events.reader(),
_marker: PhantomData::<T>,
}))
},
fn_writer: |world| {
let events = world.get_resource_or_default::<Events<T::Wrap>>();
Arc::new(AtomicRefCell::new(TypedWriter {
writer: events.writer(),
_marker: PhantomData::<T>,
}))
},
}
}
}
impl LuaWrappedEventProxy {
pub fn reader(&self, world: &mut World) -> LuaEventReader {
LuaEventReader((self.fn_reader)(world))
}
pub fn writer(&self, world: &mut World) -> LuaEventWriter {
LuaEventWriter((self.fn_writer)(world))
}
}

View File

@ -1,4 +1,4 @@
use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, ActionState, LayoutId, MouseAxis, MouseInput}; use lyra_game::input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, ActionState, KeyCode, LayoutId, MouseAxis, MouseInput};
use mlua::IntoLua; use mlua::IntoLua;
use crate::{lua::Error, lyra_engine}; use crate::{lua::Error, lyra_engine};
@ -185,7 +185,7 @@ impl LuaWrapper for LuaActionHandler {
} }
fn process_keyboard_string(key_name: &str) -> Option<ActionSource> { fn process_keyboard_string(key_name: &str) -> Option<ActionSource> {
let key = keycode_from_str(key_name)?; let key = KeyCode::from_str(key_name)?;
Some(ActionSource::Keyboard(key)) Some(ActionSource::Keyboard(key))
} }