diff --git a/Cargo.lock b/Cargo.lock index 7938f4a..bad4420 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1706,6 +1706,7 @@ dependencies = [ "tokio", "tokio-util", "toml", + "uuid", "walkdir", "windows 0.58.0", "zip", @@ -3069,17 +3070,18 @@ dependencies = [ [[package]] name = "mlua" -version = "0.9.9" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7" +checksum = "0f6ddbd668297c46be4bdea6c599dcc1f001a129586272d53170b7ac0a62961e" dependencies = [ "bstr", + "either", "erased-serde", "futures-util", "libloading 0.8.5", "mlua-sys", "num-traits", - "once_cell", + "parking_lot 0.12.3", "rustc-hash 2.0.0", "serde", "serde-value", @@ -5426,6 +5428,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 1d777d4..35167e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,4 @@ members = [ resolver = "2" [profile.release.package.futuremod_hook] -opt-level = 0 # Completely disable optimizations for dll due to issues with raw memory modification \ No newline at end of file +opt-level = 0 # Completely disable optimizations for dll due to issues with raw memory modification diff --git a/examples/settings/info.toml b/examples/settings/info.toml new file mode 100644 index 0000000..dbf01ec --- /dev/null +++ b/examples/settings/info.toml @@ -0,0 +1,10 @@ +name = "Settings Test" +version = "0.0.1" +authors = ["Simon Kurz"] +dependencies = ["settings", "system"] +description = """\ +# Settings Test +This plugin tests the settings function. + +It does nothing more than registering some settings and logging changes to them. +""" diff --git a/examples/settings/main.lua b/examples/settings/main.lua new file mode 100644 index 0000000..cb9a7f7 --- /dev/null +++ b/examples/settings/main.lua @@ -0,0 +1,24 @@ +local settings = require("settings") + +function onLoad() + createSettings() + + print(`Creation took {diff} ms`) +end + +function createSettings() + settings:create({ + settings.Section({ + settings.Text("Example Settings"), + settings.Text("By clicking the button below, you should be able to trigger some code of the plugin. How neat!!!"), + settings.Button("Click Me"):onClick(function() + print("Clicked") + end) + :withID("button") + }), + settings.Section({ + settings.Text("Next Section"), + settings.Button("Other butotn"):withDisabled(true) + }) + }) +end \ No newline at end of file diff --git a/futuremod/src/api.rs b/futuremod/src/api.rs index 51925fd..5047bde 100644 --- a/futuremod/src/api.rs +++ b/futuremod/src/api.rs @@ -4,11 +4,11 @@ use crate::config; use anyhow::{anyhow, bail}; use log::info; use reqwest::Body; -use serde::de::DeserializeOwned; +use serde::{de::DeserializeOwned, Serialize}; use tokio::fs; use tokio_util::codec::{BytesCodec, FramedRead}; -use futuremod_data::plugin::{Plugin, PluginInfo}; +use futuremod_data::plugin::{settings::{Event, PluginSettings, SettingsEvent}, Plugin, PluginInfo}; pub fn build_url(path: &str) -> String { let config = config::get(); @@ -236,3 +236,63 @@ pub async fn disable_plugin(name: String) -> Result<(), anyhow::Error> { Ok(()) } + +#[derive(Debug, Clone, Serialize)] +pub struct PluginByName { + pub name: String, +} + + +pub async fn get_plugin_settings(name: String) -> Result { + let body = PluginByName { + name, + }; + + let response = reqwest::Client::new() + .put(build_url("/plugin/settings")) + .json(&body) + .send() + .await + .map_err(|e| anyhow!("Could not send request to get settings: {}", e))?; + + if !response.status().is_success() { + let response_text = response + .text() + .await + .map_err(|e| anyhow!("Could nto get response content: {}", e))?; + + bail!("{}", response_text); + } + + response + .json() + .await + .map_err(|e| anyhow!("Plugin settings has unexpected format: {}", e)) +} + +pub async fn send_settings_event(name: String, id: String, event: Event) -> Result<(), anyhow::Error> { + let body = SettingsEvent { + name, + event, + target_id: id, + }; + + let response = reqwest::Client::new() + .put(build_url("/plugin/settings/event")) + .json(&body) + .send() + .await + .map_err(|e| anyhow!("Could not send request for settings event: {}", e))?; + + if !response.status().is_success() { + let response_text = response + .text() + .await + + .map_err(|e| anyhow!("Could not get response content: {}", e))?; + + bail!("{}", response_text); + } + + Ok(()) +} \ No newline at end of file diff --git a/futuremod/src/view/dashboard/state.rs b/futuremod/src/view/dashboard/state.rs index ffabae3..aa68ed5 100644 --- a/futuremod/src/view/dashboard/state.rs +++ b/futuremod/src/view/dashboard/state.rs @@ -258,7 +258,9 @@ pub fn update(dashboard: &mut Dashboard, message: Message) -> Task { let plugin = dashboard.plugins.get(&name); match plugin { Some(plugin) => { - dashboard.view = View::Plugin(view::plugin::Plugin::new(plugin)); + let (view, message) = view::plugin::Plugin::new(plugin); + dashboard.view = View::Plugin(view); + return message.map(Message::Plugin); } None => {} } @@ -290,12 +292,17 @@ pub fn update(dashboard: &mut Dashboard, message: Message) -> Task { }, _ => (), }, - View::Plugin(_) => match message { + View::Plugin(plugin_view) => match message { Message::Plugin(plugin_message) => match plugin_message { - view::plugin::Message::GoBack => { - return Task::done(Message::ToPluginList); + view::plugin::Message::GoBack => return Task::done(Message::ToPluginList), + plugin_message => { + if let Some(plugin) = dashboard.plugins.get(&plugin_view.name) { + return plugin_view.update(plugin, plugin_message) + .map(Message::Plugin); + } else { + warn!("Plugin view active but plugin '{}' not found", plugin_view.name); + } } - _ => (), }, _ => (), }, diff --git a/futuremod/src/view/plugin/components.rs b/futuremod/src/view/plugin/components.rs index 606c61d..cf5925c 100644 --- a/futuremod/src/view/plugin/components.rs +++ b/futuremod/src/view/plugin/components.rs @@ -1,6 +1,6 @@ -use futuremod_data::plugin::{Plugin, PluginDependency, PluginState}; +use futuremod_data::plugin::{settings::{self, Component, PluginSettings}, Plugin, PluginDependency, PluginState}; use iced::{ - widget::{column, container, markdown, row, text, Scrollable, Toggler}, + widget::{column, container, markdown, row, scrollable, text, Scrollable, Toggler}, Alignment, Length, Padding, }; use iced_fonts::Bootstrap; @@ -14,7 +14,7 @@ use crate::{ }, }; -use super::Message; +use super::{view::FutureResult, Message}; fn plugin_reload_button<'a>(plugin: &Plugin) -> Element<'a, Message> { icon_text_button(Bootstrap::ArrowClockwise, "Reload") @@ -111,6 +111,7 @@ pub fn plugin_details_view<'a>( ]) .padding(8), plugin_details_content(&plugin_view.description, plugin), + plugin_info_box(loading_settings(&plugin_view.settings)), ] .into() } @@ -207,3 +208,51 @@ fn plugin_toggle_button<'a>(plugin: &Plugin) -> Option> { .into(), ) } + +fn loading_settings<'a>(loading_settings: &'a FutureResult) -> Element<'a, Message> { + match loading_settings { + FutureResult::Loading => text("Loading...").into(), + FutureResult::Finished(plugin_settings) => settings(plugin_settings), + FutureResult::Error(e) => text(format!("Could not load plugin settings: {}", e)).into(), + } +} + +fn settings<'a>(settings: &'a PluginSettings) -> Element<'a, Message> { + scrollable(render_component(&settings.root)).into() +} + +fn render_component<'a>(component: &'a settings::Component) -> Element<'a, Message> { + match component { + Component::Button(button) => render_button(button), + Component::Section(section) => render_section(section), + Component::Text(text) => render_text(text), + } +} + +fn render_button<'a>(settings_button: &'a settings::Button) -> Element<'a, Message> { + button(text(&settings_button.text)) + .on_press_maybe(if settings_button.disabled { + None + } else { + Some(Message::SettingsEvent(settings_button.id.clone(), settings::Event::ClickButton)) + }) + .into() +} + +fn render_section<'a>(settings_section: &'a settings::Section) -> Element<'a, Message> { + let mut section = Column::new(); + + for component in settings_section.content.iter() { + section = section.push(render_component(&component)); + } + + section + .spacing(8) + .padding(Padding{top: 16.0, right: 0.0, bottom: 16.0, left: 0.0}) + .into() +} + +fn render_text<'a>(settings_text: &'a settings::Text) -> Element<'a, Message> { + text(&settings_text.text) + .into() +} \ No newline at end of file diff --git a/futuremod/src/view/plugin/mod.rs b/futuremod/src/view/plugin/mod.rs index e4e14b8..4ba2f11 100644 --- a/futuremod/src/view/plugin/mod.rs +++ b/futuremod/src/view/plugin/mod.rs @@ -1,4 +1,5 @@ mod components; mod view; +mod state; pub use view::{Message, Plugin}; diff --git a/futuremod/src/view/plugin/state.rs b/futuremod/src/view/plugin/state.rs new file mode 100644 index 0000000..c05f625 --- /dev/null +++ b/futuremod/src/view/plugin/state.rs @@ -0,0 +1,25 @@ +use futures::TryFutureExt; +use iced::Task; +use log::info; + +use crate::api::send_settings_event; + +use super::{view::FutureResult, Message}; + +pub(super) fn update(view: &mut super::Plugin, plugin: &futuremod_data::plugin::Plugin, message: Message) -> Task { + match message { + Message::HandlePluginSettingsResponse(result) => match result { + Ok(settings) => view.settings = FutureResult::Finished(settings), + Err(e) => view.settings = FutureResult::Error(e), + }, + Message::SettingsEvent(id, event) => { + return Task::perform(send_settings_event(plugin.info.name.clone(), id, event).map_err(|e| e.to_string()), Message::EventResponse); + }, + Message::EventResponse(response) => { + info!("Event response: {:?}", response); + } + _ => (), + } + + Task::none() +} \ No newline at end of file diff --git a/futuremod/src/view/plugin/view.rs b/futuremod/src/view/plugin/view.rs index 79d0257..2902424 100644 --- a/futuremod/src/view/plugin/view.rs +++ b/futuremod/src/view/plugin/view.rs @@ -1,13 +1,23 @@ -use iced::widget::markdown; +use futuremod_data::plugin::settings::{Event, PluginSettings}; +use futures::TryFutureExt; +use iced::{widget::markdown, Task}; -use crate::widget::Element; +use crate::{api::get_plugin_settings, widget::Element}; -use super::components::plugin_details_view; +use super::{components::plugin_details_view, state::update}; + +#[derive(Debug, Clone)] +pub enum FutureResult { + Loading, + Finished(T), + Error(E), +} #[derive(Debug, Clone)] pub struct Plugin { pub name: String, pub description: Vec, + pub settings: FutureResult, } #[derive(Debug, Clone)] @@ -18,16 +28,27 @@ pub enum Message { Reload(String), UninstallPrompt(String), OpenUrl(reqwest::Url), + HandlePluginSettingsResponse(Result), + SettingsEvent(String, Event), + EventResponse(Result<(), String>), } impl Plugin { - pub fn new(plugin: &futuremod_data::plugin::Plugin) -> Self { + pub fn new(plugin: &futuremod_data::plugin::Plugin) -> (Self, Task) { let description = markdown::parse(&plugin.info.description).collect(); - Plugin { - name: plugin.info.name.clone(), - description, - } + ( + Plugin { + name: plugin.info.name.clone(), + description, + settings: FutureResult::Loading, + }, + Task::perform(get_plugin_settings(plugin.info.name.clone()).map_err(|e| e.to_string()), Message::HandlePluginSettingsResponse), + ) + } + + pub fn update(&mut self, plugin: &futuremod_data::plugin::Plugin, message: Message) -> Task { + update(self, plugin, message) } pub fn view<'a>(&'a self, plugin: &futuremod_data::plugin::Plugin) -> Element<'a, Message> { diff --git a/futuremod_data/src/plugin/mod.rs b/futuremod_data/src/plugin/mod.rs new file mode 100644 index 0000000..3be63d8 --- /dev/null +++ b/futuremod_data/src/plugin/mod.rs @@ -0,0 +1,4 @@ +pub mod plugin; +pub mod settings; + +pub use plugin::*; \ No newline at end of file diff --git a/futuremod_data/src/plugin.rs b/futuremod_data/src/plugin/plugin.rs similarity index 97% rename from futuremod_data/src/plugin.rs rename to futuremod_data/src/plugin/plugin.rs index 777f647..401c7c4 100644 --- a/futuremod_data/src/plugin.rs +++ b/futuremod_data/src/plugin/plugin.rs @@ -12,6 +12,7 @@ pub enum PluginDependency { UI, System, Matrix, + Settings, // The following libraries are from the standard library Math, @@ -35,6 +36,7 @@ impl Display for PluginDependency { PluginDependency::String => f.write_str("String"), PluginDependency::Utf8 => f.write_str("Utf8"), PluginDependency::Matrix => f.write_str("Matrix"), + PluginDependency::Settings => f.write_str("Settings"), } } } diff --git a/futuremod_data/src/plugin/settings.rs b/futuremod_data/src/plugin/settings.rs new file mode 100644 index 0000000..5b58b9e --- /dev/null +++ b/futuremod_data/src/plugin/settings.rs @@ -0,0 +1,52 @@ +use serde::{Deserialize, Serialize}; + + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PluginSettings { + pub root: Component +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "camelCase")] +pub enum Component { + Button(Button), + Section(Section), + Text(Text), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Button { + pub text: String, + pub disabled: bool, + pub id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Section { + pub content: Vec, + pub id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Text { + pub text: String, + pub id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "type")] +pub enum Event { + ClickButton, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SettingsEvent { + pub name: String, + pub target_id: String, + pub event: Event, +} \ No newline at end of file diff --git a/futuremod_engine/Cargo.toml b/futuremod_engine/Cargo.toml index 31cd29a..009e5df 100644 --- a/futuremod_engine/Cargo.toml +++ b/futuremod_engine/Cargo.toml @@ -37,10 +37,11 @@ walkdir = "2.4.0" zip = "0.6.6" junction = "1.2.0" directories = "5.0.1" +uuid = { version = "1.11.0", features = ["serde", "v4"] } [dependencies.mlua] -version = "0.9.1" -features = ["luau", "async", "serialize", "unstable"] +version = "0.10.0" +features = ["luau", "async", "serialize"] [dependencies.windows] version = "0.*" diff --git a/futuremod_engine/src/plugins/library/dangerous/memory.rs b/futuremod_engine/src/plugins/library/dangerous/memory.rs index 2cfadce..252fd0f 100644 --- a/futuremod_engine/src/plugins/library/dangerous/memory.rs +++ b/futuremod_engine/src/plugins/library/dangerous/memory.rs @@ -1,6 +1,6 @@ use futuremod_hook::types::{Type, MAX_STRING}; use log::debug; -use mlua::{AnyUserDataExt, Lua}; +use mlua::{Lua, ObjectLike}; use crate::plugins::library::LuaResult; @@ -15,8 +15,8 @@ fn try_userdata_to_bytes(userdata: &mlua::AnyUserData) -> LuaResult> { /// **Very unsafe**. /// /// Wrong usage can easily lead to a panic. -pub fn write_memory_function<'lua>( - _: &'lua Lua, +pub fn write_memory_function( + _: &Lua, (address, data): (u32, mlua::Value), ) -> Result<(), mlua::Error> { debug!("Write memory to {}, value: {:?}", address, data); @@ -91,10 +91,10 @@ pub fn write_memory_function<'lua>( } /// Read any memory address and convert it to the given type in lua. -pub fn read_memory_function<'lua>( - lua: &'lua Lua, +pub fn read_memory_function( + lua: &Lua, (address, type_name): (u32, String), -) -> Result, mlua::Error> { +) -> Result { debug!("Read memory address {} with type {}", address, type_name); let value_type = match Type::try_from_str(type_name.as_str()) { Some(t) => t, diff --git a/futuremod_engine/src/plugins/library/dangerous/mod.rs b/futuremod_engine/src/plugins/library/dangerous/mod.rs index a8252e9..9baebbc 100644 --- a/futuremod_engine/src/plugins/library/dangerous/mod.rs +++ b/futuremod_engine/src/plugins/library/dangerous/mod.rs @@ -10,7 +10,7 @@ mod native; use futuremod_hook::lua::hook_function; use memory::*; -pub fn create_dangerous_library(lua: Arc) -> Result { +pub fn create_dangerous_library(lua: Arc) -> Result { let table = lua.create_table()?; let hook_fn = lua.create_function(hook_function)?; @@ -38,5 +38,5 @@ pub fn create_dangerous_library(lua: Arc) -> Result>(methods: &mut M) { + fn add_methods>(methods: &mut M) { methods.add_meta_function( MetaMethod::Index, |lua, (native_struct_userdata, field): (AnyUserData, String)| - -> Result, mlua::Error> { - let native_struct: Ref = + -> Result { + let native_struct: UserDataRef = native_struct_userdata.borrow().map_err(|_| { mlua::Error::RuntimeError( "Self must be a native struct definition".to_string(), @@ -73,7 +73,7 @@ impl UserData for NativeStruct { // Call the `getByteSize` method of the value to get the expected amount of byte the type allocates let byte_size = complex_type - .call_method::<_, u32>("getByteSize", ()) + .call_method::("getByteSize", ()) .map_err(|e| { mlua::Error::RuntimeError(format!( "getByteSize method errored: {}", @@ -93,15 +93,14 @@ impl UserData for NativeStruct { } // Call the type's 'fromBytes' function to construct an instance of the type from the bytes - let f = - complex_type - .get::<_, mlua::Function>("fromBytes") - .map_err(|_| { - mlua::Error::RuntimeError( - "Type userdata is missing 'fromBytes' function".to_string(), - ) - })?; - let value = f.call::<_, mlua::Value>((complex_type, byte_vec))?; + let f = complex_type + .get::("fromBytes") + .map_err(|_| { + mlua::Error::RuntimeError( + "Type userdata is missing 'fromBytes' function".to_string(), + ) + })?; + let value = f.call::((complex_type, byte_vec))?; Ok(value) } @@ -114,7 +113,7 @@ impl UserData for NativeStruct { |_, (native_struct_userdata, field, value): (AnyUserData, String, mlua::Value)| -> Result<(), mlua::Error> { - let native_struct: Ref = native_struct_userdata.borrow()?; + let native_struct: UserDataRef = native_struct_userdata.borrow()?; debug!( "Set field {} of struct at 0x{:x} to {:?}", @@ -213,7 +212,7 @@ impl UserData for NativeStruct { })?; let bytes = complex_type - .call_method::>("toBytes", value) + .call_method::>("toBytes", value) .map_err(|e| { mlua::Error::RuntimeError(format!( "toBytes function of complex type errored: {}", @@ -258,12 +257,12 @@ pub struct NativeStructDefinition { fields: HashMap, } -fn native_struct_from_definition<'a>( - lua: &'a Lua, +fn native_struct_from_definition( + lua: &Lua, address: u32, - definition_userdata: AnyUserData<'a>, -) -> LuaResult> { - let definition: Ref = definition_userdata.borrow()?; + definition_userdata: AnyUserData, +) -> LuaResult { + let definition: UserDataRef = definition_userdata.borrow()?; let fields = &definition.fields; let mut struct_fields: HashMap = HashMap::new(); @@ -301,22 +300,20 @@ fn native_struct_from_definition<'a>( } impl UserData for NativeStructDefinition { - fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + fn add_methods>(methods: &mut M) { methods.add_function( "cast", - |lua, - (definition, address): (AnyUserData, u32)| - -> Result, mlua::Error> { + |lua, (definition, address): (AnyUserData, u32)| -> Result { native_struct_from_definition(lua, address, definition) }, ); } } -pub fn create_native_struct_definition_fn<'lua>( - lua: &'lua Lua, - fields: mlua::Table<'lua>, -) -> Result, mlua::Error> { +pub fn create_native_struct_definition_fn( + lua: &Lua, + fields: mlua::Table, +) -> Result { debug!("Creating native struct def"); let mut native_fields: HashMap = HashMap::new(); @@ -340,7 +337,7 @@ pub fn create_native_struct_definition_fn<'lua>( })?; let native_type: FieldDefinitionType = match native_type_id.type_name() { "string" => match native_type_id.as_str() { - Some(native_type_str) => match Type::try_from_str(native_type_str) { + Some(native_type_str) => match Type::try_from_str(&native_type_str) { Some(value) => FieldDefinitionType::Primitive(value), None => return Err(mlua::Error::runtime("Unsupported type")), }, @@ -348,14 +345,12 @@ pub fn create_native_struct_definition_fn<'lua>( }, "userdata" => match native_type_id.as_userdata() { Some(userdata) => { - userdata.get::<_, mlua::Function>("toBytes").map_err(|_| { + userdata.get::("toBytes").map_err(|_| { mlua::Error::runtime("Complex type is missing function 'toBytes'") })?; - userdata - .get::<_, mlua::Function>("fromBytes") - .map_err(|_| { - mlua::Error::runtime("Complex type is missing function 'fromBytes'") - })?; + userdata.get::("fromBytes").map_err(|_| { + mlua::Error::runtime("Complex type is missing function 'fromBytes'") + })?; FieldDefinitionType::Complex(key.clone()) } @@ -389,7 +384,7 @@ pub fn create_native_struct_definition_fn<'lua>( Err(_) => return Err(mlua::Error::runtime("Field definition must be table")), }; - let field_definition_type = field_definition.get::<_, mlua::Value>("type")?; + let field_definition_type = field_definition.get::("type")?; let field_definition_type_type_name = field_definition_type.type_name(); if field_definition_type_type_name == "userdata" { @@ -401,10 +396,10 @@ pub fn create_native_struct_definition_fn<'lua>( Ok(definition_userdata) } -pub fn create_native_struct_fn<'lua>( - lua: &'lua Lua, - (address, definition_userdata): (u32, AnyUserData<'lua>), -) -> Result, mlua::Error> { +pub fn create_native_struct_fn( + lua: &Lua, + (address, definition_userdata): (u32, AnyUserData), +) -> Result { debug!("Create new native struct at 0x{:x}", address); native_struct_from_definition(lua, address, definition_userdata) diff --git a/futuremod_engine/src/plugins/library/game.rs b/futuremod_engine/src/plugins/library/game.rs index f55d446..7784589 100644 --- a/futuremod_engine/src/plugins/library/game.rs +++ b/futuremod_engine/src/plugins/library/game.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use log::debug; -use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, OwnedTable, UserData}; +use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData}; use serde::Serialize; use crate::futurecop::{self, global::GetterSetter, state::FUTURE_COP, PLAYER_ARRAY_ADDR}; @@ -44,7 +44,7 @@ struct PlayerEntity { } impl UserData for PlayerEntity { - fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fn add_fields>(fields: &mut F) { fields.add_field_method_get("health", |_, this| { Ok(unsafe { (*this.player_entity).health.health }) }); @@ -102,13 +102,13 @@ impl UserData for PlayerEntity { Ok(()) }); - fn create_getter_setter<'lua, T, F>( + fn create_getter_setter( name: &str, fields: &mut F, extractor: fn(*mut futurecop::PlayerEntity) -> *mut T, ) where - F: mlua::prelude::LuaUserDataFields<'lua, PlayerEntity>, - T: IntoLua<'lua> + FromLua<'lua> + 'static + Copy, + F: mlua::prelude::LuaUserDataFields, + T: IntoLua + FromLua + 'static + Copy, { fields.add_field_method_get(name, move |_, this| { let field: T; @@ -190,7 +190,7 @@ impl UserData for PlayerEntity { }); } - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + fn add_methods>(methods: &mut M) { methods.add_method("getMaxHealth", |_, this, ()| { Ok(unsafe { (*this.player_entity).health.max_health }) }) @@ -213,7 +213,7 @@ impl GameState { } } -pub fn create_game_library(lua: Arc) -> Result { +pub fn create_game_library(lua: Arc) -> Result { let functions = lua.create_table()?; let get_game_state = lua.create_function(|lua, ()| { @@ -248,5 +248,5 @@ pub fn create_game_library(lua: Arc) -> Result { })?; functions.set("getPlayer", get_player)?; - Ok(functions.into_owned()) + Ok(functions) } diff --git a/futuremod_engine/src/plugins/library/input.rs b/futuremod_engine/src/plugins/library/input.rs index 0a08601..ff9f99d 100644 --- a/futuremod_engine/src/plugins/library/input.rs +++ b/futuremod_engine/src/plugins/library/input.rs @@ -2,7 +2,7 @@ use std::{str::FromStr, sync::Arc}; use device_query::Keycode; use log::*; -use mlua::{Lua, OwnedTable}; +use mlua::{Lua, Table}; use crate::input::KeyState; @@ -126,7 +126,7 @@ fn insert_keycode(table: &mlua::Table, code: Keycode) -> Result<(), mlua::Error> table.set(code.clone(), code) } -pub fn create_input_library(lua: Arc) -> Result { +pub fn create_input_library(lua: Arc) -> Result { let library = lua.create_table()?; // Insert supported key codes into library table. @@ -156,5 +156,5 @@ pub fn create_input_library(lua: Arc) -> Result { })?; library.set("isKeyPressed", is_key_pressed_function)?; - Ok(library.into_owned()) + Ok(library) } diff --git a/futuremod_engine/src/plugins/library/matrix.rs b/futuremod_engine/src/plugins/library/matrix.rs index 3607036..85e02be 100644 --- a/futuremod_engine/src/plugins/library/matrix.rs +++ b/futuremod_engine/src/plugins/library/matrix.rs @@ -1,5 +1,4 @@ use std::{ - cell::Ref, fmt, marker::PhantomData, mem::size_of, @@ -8,7 +7,9 @@ use std::{ }; use log::info; -use mlua::{AnyUserData, FromLua, IntoLua, Lua, MetaMethod, OwnedTable, UserData, UserDataMethods}; +use mlua::{ + AnyUserData, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, UserDataRef, +}; use nalgebra::{DMatrix, Matrix4, Scalar, Vector3}; use num::{ traits::{FromBytes, ToBytes}, @@ -17,7 +18,7 @@ use num::{ use super::LuaResult; -pub fn create_matrix_library(lua: Arc) -> Result { +pub fn create_matrix_library(lua: Arc) -> Result { let table = lua.create_table()?; // Float-based dynamic matrix @@ -45,7 +46,7 @@ pub fn create_matrix_library(lua: Arc) -> Result { table.set("ModelMatrix", lua.create_proxy::()?)?; table.set("newModel", lua.create_function(create_model_matrix)?)?; - Ok(table.into_owned()) + Ok(table) } /// Trait for types that encapsulate their data in an arc and mutex. @@ -116,8 +117,8 @@ impl LuaMatrix { } } -impl<'a, T: 'static> FromLua<'a> for LuaMatrix { - fn from_lua(value: mlua::Value<'a>, lua: &'a Lua) -> mlua::Result { +impl FromLua for LuaMatrix { + fn from_lua(value: mlua::Value, lua: &Lua) -> mlua::Result { try_from_userdata::>(value, lua) } } @@ -126,7 +127,7 @@ impl<'a, T: 'static> FromLua<'a> for LuaMatrix { /// If value is a userdata of type T, this function returns a clone of the userdata. /// /// Errors if the given lua value is not a userdata and if the userdata is not of type T. -fn try_from_userdata<'a, T: 'static>(value: mlua::Value<'a>, _: &'a Lua) -> mlua::Result +fn try_from_userdata(value: mlua::Value, _: &Lua) -> mlua::Result where T: Clone, { @@ -139,7 +140,7 @@ where return Err(mlua::Error::RuntimeError("Not a matrix".to_string())); } - let m: Ref = userdata.borrow()?; + let m: UserDataRef = userdata.borrow()?; Ok(m.clone()) } @@ -147,8 +148,8 @@ where impl< T: Num + Copy - + for<'a> IntoLua<'a> - + for<'a> FromLua<'a> + + for<'a> IntoLua + + for<'a> FromLua + fmt::Debug + AddAssign + 'static @@ -156,7 +157,7 @@ impl< + MulAssign, > UserData for LuaMatrix { - fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { + fn add_fields>(fields: &mut F) { fields.add_field_method_get("ncols", |_, matrix| { matrix.with_matrix(|matrix| Ok(matrix.ncols())) }); @@ -166,10 +167,10 @@ impl< }); } - fn add_methods<'lua, M>(methods: &mut M) + fn add_methods(methods: &mut M) where M: Sized, - M: UserDataMethods<'lua, LuaMatrix>, + M: UserDataMethods>, { methods.add_method("at", |_, matrix, (row, col): (u8, u8)| -> LuaResult { matrix.check_bounds(row, col)?; @@ -232,8 +233,8 @@ impl< } /// Create zero matrix -fn create_zero_matrix<'lua, T: Scalar + Zero>( - _: &'lua Lua, +fn create_zero_matrix( + _: &Lua, (rows, columns): (u8, u8), ) -> LuaResult> { Ok(LuaMatrix(Arc::new(Mutex::new(DMatrix::::zeros( @@ -243,10 +244,7 @@ fn create_zero_matrix<'lua, T: Scalar + Zero>( } /// Create identify matrix -fn create_identity_matrix<'lua, T: Scalar + Zero + One>( - _: &'lua Lua, - size: u8, -) -> LuaResult> { +fn create_identity_matrix(_: &Lua, size: u8) -> LuaResult> { let size = size as usize; Ok(LuaMatrix(Arc::new(Mutex::new(DMatrix::::identity( @@ -269,7 +267,7 @@ fn create_identity_matrix<'lua, T: Scalar + Zero + One>( /// ``` /// /// All rows must have the same length, otherwise, this function panics. -fn create_matrix<'lua, T: Scalar>(_: &'lua Lua, data: Vec>) -> LuaResult> { +fn create_matrix(_: &Lua, data: Vec>) -> LuaResult> { let rows = data.len(); let cols = data.iter().map(|r| r.len()).fold(0, |l, r| l.max(r)); let data: Vec = data.into_iter().flatten().collect(); @@ -309,11 +307,11 @@ impl< + AddAssign + MulAssign + fmt::Debug - + for<'a> IntoLua<'a> - + for<'a> FromLua<'a>, + + for<'a> IntoLua + + for<'a> FromLua, > UserData for MatrixType { - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + fn add_methods>(methods: &mut M) { methods.add_function("new", |_, (nrows, ncols): (u32, u32)| { Ok(MatrixType::::new(nrows, ncols)) }); @@ -465,14 +463,14 @@ impl ModelMatrix { } } -impl<'a> FromLua<'a> for ModelMatrix { - fn from_lua(value: mlua::Value<'a>, lua: &'a Lua) -> mlua::Result { +impl FromLua for ModelMatrix { + fn from_lua(value: mlua::Value, lua: &Lua) -> mlua::Result { try_from_userdata::(value, lua) } } impl UserData for ModelMatrix { - fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { + fn add_fields>(fields: &mut F) { fields.add_field_method_get("ncols", |_, matrix| { matrix.with_matrix(|matrix| Ok(matrix.ncols())) }); @@ -482,7 +480,7 @@ impl UserData for ModelMatrix { }); } - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + fn add_methods>(methods: &mut M) { methods.add_method("at", |_, matrix, (row, col): (u8, u8)| -> LuaResult { matrix.check_bounds(row, col)?; @@ -523,7 +521,7 @@ impl UserData for ModelMatrix { methods.add_function( "toBytes", |_, (_, matrix): (AnyUserData, AnyUserData)| -> LuaResult> { - let matrix: Ref = matrix.borrow()?; + let matrix: UserDataRef = matrix.borrow()?; matrix.with_matrix(|matrix| { let mut bytes = Vec::::new(); diff --git a/futuremod_engine/src/plugins/library/mod.rs b/futuremod_engine/src/plugins/library/mod.rs index 20b64db..12f9520 100644 --- a/futuremod_engine/src/plugins/library/mod.rs +++ b/futuremod_engine/src/plugins/library/mod.rs @@ -2,6 +2,7 @@ pub mod dangerous; pub mod game; pub mod input; pub mod matrix; +pub mod settings; pub mod system; pub mod ui; diff --git a/futuremod_engine/src/plugins/library/settings/button.rs b/futuremod_engine/src/plugins/library/settings/button.rs new file mode 100644 index 0000000..3285a14 --- /dev/null +++ b/futuremod_engine/src/plugins/library/settings/button.rs @@ -0,0 +1,83 @@ + +use log::debug; +use mlua::{AnyUserData, Lua, UserData}; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct ButtonBuilder { + pub(super) text: String, + pub(super) disabled: bool, + #[serde(skip)] + pub(super) on_click: Option, + #[serde(skip)] + pub(super) manual_id: Option, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Button { + pub(super) text: String, + pub(super) disabled: bool, + #[serde(skip)] + pub(super) on_click: Option, + pub(super) id: String, +} + +pub fn create_button(_: &Lua, text: String) -> mlua::Result { + Ok(ButtonBuilder::new(text)) +} + +impl ButtonBuilder { + pub fn new(text: String) -> ButtonBuilder { + debug!("Create button with text '{}'", text); + + ButtonBuilder { + text, + disabled: false, + on_click: None, + manual_id: None, + } + } +} + +impl UserData for ButtonBuilder { + fn add_methods>(methods: &mut M) { + methods.add_function("withText", |_, (builder, text): (AnyUserData, String)| { + debug!("Change button text to '{}'", text); + let builder = builder.borrow::()?; + let mut new_buider = builder.clone(); + new_buider.text = text; + Ok(new_buider) + }); + + methods.add_function( + "withDisabled", + |_, (builder, value): (AnyUserData, bool)| { + debug!("Change button disabled to '{}'", value); + let builder = builder.borrow::()?; + let mut new_buider = builder.clone(); + new_buider.disabled = value; + Ok(new_buider) + }, + ); + + methods.add_function("withID", |_, (builder, id): (AnyUserData, String)| { + debug!("Change button ID to {}", id); + let builder = builder.borrow::()?; + let mut new_buider = builder.clone(); + new_buider.manual_id = Some(id); + Ok(new_buider) + }); + + methods.add_function( + "onClick", + |_, (builder, cb): (AnyUserData, mlua::Function)| { + debug!("Change button on click listener to '{:?}'", cb); + let builder = builder.borrow::()?; + let mut new_buider = builder.clone(); + new_buider.on_click = Some(cb); + Ok(new_buider) + }, + ); + } +} diff --git a/futuremod_engine/src/plugins/library/settings/mod.rs b/futuremod_engine/src/plugins/library/settings/mod.rs new file mode 100644 index 0000000..d8b8a33 --- /dev/null +++ b/futuremod_engine/src/plugins/library/settings/mod.rs @@ -0,0 +1,82 @@ +use std::{collections::HashMap, sync::Arc}; + +use anyhow::anyhow; +use log::debug; +use mlua::{Lua, UserData}; + +mod button; +mod section; +mod settings; +mod text; + +pub use settings::PluginSettings; + +use button::create_button; +use section::create_section; +use settings::{create_settings, ComponentBuilder}; +use text::create_text; + +#[derive(Debug, Clone)] +pub struct PluginSettingsLibrary { + settings: Option, +} + +impl PluginSettingsLibrary { + pub fn new() -> Self { + PluginSettingsLibrary { settings: None } + } +} + +impl UserData for PluginSettingsLibrary { + fn add_methods>(methods: &mut M) { + methods.add_method_mut( + "create", + |_lua, settings_library, components: Vec| { + let settings = create_settings(components)?; + + settings_library.settings = Some(settings); + + Ok(()) + }, + ); + + methods.add_function("Button", create_button); + methods.add_function("Text", create_text); + methods.add_function("Section", create_section); + } +} + +pub fn create_settings_library_new(_lua: Arc) -> Result { + Ok(PluginSettingsLibrary::new()) +} + +pub fn get_settings( + context: &HashMap<&'static str, mlua::Value>, +) -> Result, anyhow::Error> { + let has_settings = context.contains_key("settings"); + + if !has_settings { + debug!("Plugin has no settings in globals"); + return Ok(None); + } + + debug!("Settings key found in plugin's globals"); + + let settings_global_value: &mlua::Value = context.get("settings").ok_or(anyhow!( + "Could not access settings global in plugin context" + ))?; + + let settings_global = settings_global_value + .as_userdata() + .ok_or(anyhow!("Global settings has invalid format"))?; + + debug!("Got settings as userdata from globals"); + + let settings = settings_global + .borrow::() + .map_err(|e| anyhow!("Plugin context does not contain valid setting: {e}"))?; + + debug!("Could convert settings userdata to settings struct"); + + Ok(settings.settings.clone()) +} diff --git a/futuremod_engine/src/plugins/library/settings/section.rs b/futuremod_engine/src/plugins/library/settings/section.rs new file mode 100644 index 0000000..d232ef0 --- /dev/null +++ b/futuremod_engine/src/plugins/library/settings/section.rs @@ -0,0 +1,88 @@ +use std::sync::Weak; + +use mlua::{Lua, UserData}; +use serde::{ + ser::{SerializeSeq, SerializeStruct}, + Serialize, +}; + +use super::{button::ButtonBuilder, settings::Component, text::TextBuilder, ComponentBuilder}; + +#[derive(Debug, Clone, Serialize)] +pub struct SectionBuilder { + pub(super) content: Vec, +} + +#[derive(Debug, Clone)] +pub struct Section { + pub(super) content: Vec>, + pub(super) id: String, +} + +pub struct SectionContent<'a>(&'a Vec>); + +impl<'a> Serialize for SectionContent<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut content_state = serializer.serialize_seq(Some(self.0.len()))?; + + for child in self.0.iter() { + let strong_child = child + .upgrade() + .ok_or(serde::ser::Error::custom("Child dropped"))?; + + content_state.serialize_element(&*strong_child)?; + } + + content_state.end() + } +} + +impl Serialize for Section { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("section", 2)?; + state.serialize_field("id", &self.id)?; + state.serialize_field("content", &SectionContent(&self.content))?; + + state.end() + } +} + +impl SectionBuilder { + pub fn new(content: Vec) -> SectionBuilder { + SectionBuilder { content } + } +} + +pub fn create_section(_lua: &Lua, children: Vec) -> mlua::Result { + let mut components = Vec::::new(); + + for child in children { + let child_userdata = child + .as_userdata() + .ok_or(mlua::Error::RuntimeError("Component not userdata".into()))?; + + let compoment = if let Ok(v) = child_userdata.borrow::() { + ComponentBuilder::Button(v.clone()) + } else if let Ok(v) = child_userdata.borrow::() { + ComponentBuilder::Text(v.clone()) + } else if let Ok(v) = child_userdata.borrow::() { + ComponentBuilder::Section(v.clone()) + } else { + return Err(mlua::Error::RuntimeError("Unknown compoment".into())); + }; + + components.push(compoment) + } + + Ok(SectionBuilder { + content: components, + }) +} + +impl UserData for SectionBuilder {} diff --git a/futuremod_engine/src/plugins/library/settings/settings.rs b/futuremod_engine/src/plugins/library/settings/settings.rs new file mode 100644 index 0000000..b79da55 --- /dev/null +++ b/futuremod_engine/src/plugins/library/settings/settings.rs @@ -0,0 +1,218 @@ +use std::{ + collections::HashMap, + sync::{Arc, Weak}, +}; + +use anyhow::{anyhow, bail}; +use futuremod_data::plugin::settings::Event; +use log::debug; +use mlua::UserData; +use serde::{ser::SerializeStruct, Serialize}; +use uuid::Uuid; + +use super::{ + button::{Button, ButtonBuilder}, + section::{Section, SectionBuilder}, + text::{Text, TextBuilder}, +}; + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", rename_all = "camelCase")] +pub enum ComponentBuilder { + Button(ButtonBuilder), + Section(SectionBuilder), + Text(TextBuilder), +} + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", rename_all = "camelCase")] +pub enum Component { + Button(Button), + Section(Section), + Text(Text), +} + +#[derive(Debug, Clone)] +pub struct PluginSettings { + components: HashMap>, + root: Weak, +} + +impl Serialize for PluginSettings { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("settings", 1)?; + let strong_root = self + .root + .upgrade() + .ok_or(serde::ser::Error::custom("Root dropped"))?; + state.serialize_field("root", &*strong_root)?; + state.end() + } +} + +impl PluginSettings { + pub fn handle_event(&mut self, id: String, event: Event) -> Result<(), anyhow::Error> { + match event { + Event::ClickButton => { + let component = self.components.get(&id) + .ok_or(anyhow!("No component with id '{}' exists", id))?; + + if let Component::Button(button) = component.as_ref() { + match &button.on_click { + Some(button_callback) => { + button_callback.call::<()>(()) + .map_err(|e| anyhow!("onClick function errored: {}", e))?; + }, + None => bail!("Button '{}' has no onClick callback", id), + } + } + + return Ok(()) + }, + } + } +} + +impl UserData for PluginSettings {} + +pub(super) fn generate_new_id(map: &HashMap) -> String { + let mut key = Uuid::new_v4().to_string(); + + while map.contains_key(&key) { + key = Uuid::new_v4().to_string(); + } + + key +} + +pub(super) fn create_settings(components: Vec) -> mlua::Result { + debug!("Create settings from: {:?}", components); + + let mut setting_components = HashMap::>::new(); + + let mut root_components = Vec::>::new(); + + for unknown_component in components.into_iter() { + let component = build_component(&mut setting_components, unknown_component) + .map_err(|e| mlua::Error::RuntimeError(format!("{:?}", e)))?; + + root_components.push(component); + } + + let id = generate_new_id(&setting_components); + let root_section = Section { + content: root_components, + id: id.clone(), + }; + + let shared_root = Arc::new(Component::Section(root_section)); + + setting_components.insert(id, shared_root.clone()); + + let settings = PluginSettings { + components: setting_components, + root: Arc::downgrade(&shared_root), + }; + + Ok(settings) +} + +fn build_component( + components: &mut HashMap>, + component: mlua::Value, +) -> Result, anyhow::Error> { + let userdata = component + .as_userdata() + .ok_or(anyhow!("Component not a userdata"))?; + + if let Ok(button_builder) = userdata.borrow::() { + build_button(components, button_builder.clone()) + } else if let Ok(section_builder) = userdata.borrow::() { + build_section(components, section_builder.clone()) + } else { + bail!("Unkown component: {:?}", userdata); + } +} + +fn build_button( + components: &mut HashMap>, + button_builder: ButtonBuilder, +) -> Result, anyhow::Error> { + let id = match button_builder.manual_id { + Some(v) => { + if components.contains_key(&v) { + bail!("Duplicate id"); + } + + v + } + None => generate_new_id(&components), + }; + + let button = Button { + text: button_builder.text, + disabled: button_builder.disabled, + on_click: button_builder.on_click, + id: id.clone(), + }; + + let shared_component = Arc::new(Component::Button(button)); + + components.insert(id, shared_component.clone()); + + Ok(Arc::downgrade(&shared_component)) +} + +fn build_known_compoment( + components: &mut HashMap>, + builder: ComponentBuilder, +) -> Result, anyhow::Error> { + match builder { + ComponentBuilder::Button(button_builder) => build_button(components, button_builder), + ComponentBuilder::Section(section_builder) => build_section(components, section_builder), + ComponentBuilder::Text(text_builder) => build_text(components, text_builder), + } +} + +fn build_section( + components: &mut HashMap>, + section_builder: SectionBuilder, +) -> Result, anyhow::Error> { + let mut content = Vec::>::new(); + + for component_builder in section_builder.content { + let component = build_known_compoment(components, component_builder)?; + + content.push(component); + } + + let id = generate_new_id(&components); + let section = Section { + id: id.clone(), + content, + }; + + let shared_component = Arc::new(Component::Section(section)); + + components.insert(id, shared_component.clone()); + + Ok(Arc::downgrade(&shared_component)) +} + +fn build_text( + components: &mut HashMap>, + text_builder: TextBuilder, +) -> Result, anyhow::Error> { + let id = generate_new_id(components); + let text = Arc::new(Component::Text(Text { + id: id.clone(), + text: text_builder.text, + })); + + components.insert(id, text.clone()); + + Ok(Arc::downgrade(&text)) +} diff --git a/futuremod_engine/src/plugins/library/settings/text.rs b/futuremod_engine/src/plugins/library/settings/text.rs new file mode 100644 index 0000000..8ac6279 --- /dev/null +++ b/futuremod_engine/src/plugins/library/settings/text.rs @@ -0,0 +1,37 @@ +use mlua::{AnyUserData, Lua, UserData}; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct TextBuilder { + pub text: String, +} + +#[derive(Debug, Clone, Serialize)] +pub struct Text { + pub(super) id: String, + pub(super) text: String, +} + +pub fn create_text(_lua: &Lua, text: String) -> mlua::Result { + Ok(TextBuilder::new(text)) +} + +impl TextBuilder { + pub fn new(text: String) -> TextBuilder { + TextBuilder { text } + } +} + +impl UserData for TextBuilder { + fn add_methods>(methods: &mut M) { + methods.add_function( + "withText", + |_, (text_component, text): (AnyUserData, String)| { + let original = text_component.borrow::()?; + let mut new_component = original.clone(); + new_component.text = text; + Ok(new_component) + }, + ); + } +} diff --git a/futuremod_engine/src/plugins/library/system.rs b/futuremod_engine/src/plugins/library/system.rs index 03f2e35..190333f 100644 --- a/futuremod_engine/src/plugins/library/system.rs +++ b/futuremod_engine/src/plugins/library/system.rs @@ -3,9 +3,9 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -use mlua::{Lua, OwnedTable}; +use mlua::{Lua, Table}; -pub fn create_system_library(lua: Arc) -> Result { +pub fn create_system_library(lua: Arc) -> Result { let library = lua.create_table()?; let get_time_fn = lua.create_function(|_, ()| { @@ -21,5 +21,5 @@ pub fn create_system_library(lua: Arc) -> Result { })?; library.set("getTime", get_time_fn)?; - Ok(library.into_owned()) + Ok(library) } diff --git a/futuremod_engine/src/plugins/library/ui.rs b/futuremod_engine/src/plugins/library/ui.rs index 5d22a66..f2b663a 100644 --- a/futuremod_engine/src/plugins/library/ui.rs +++ b/futuremod_engine/src/plugins/library/ui.rs @@ -1,13 +1,13 @@ use std::sync::Arc; -use mlua::{Lua, LuaSerdeExt, OwnedTable, Value}; +use mlua::{Lua, LuaSerdeExt, Table, Value}; use crate::api::{ self, ui::{Color, TextPalette, TEXT_PALETTES}, }; -pub fn create_ui_library(lua: Arc) -> Result { +pub fn create_ui_library(lua: Arc) -> Result { let library = lua.create_table()?; let render_text = lua.create_function( @@ -43,5 +43,5 @@ pub fn create_ui_library(lua: Arc) -> Result { library.set(format!("Palette{}", palette), Into::::into(palette))?; } - Ok(library.into_owned()) + Ok(library) } diff --git a/futuremod_engine/src/plugins/mod.rs b/futuremod_engine/src/plugins/mod.rs index 46ac7c4..c786449 100644 --- a/futuremod_engine/src/plugins/mod.rs +++ b/futuremod_engine/src/plugins/mod.rs @@ -1,4 +1,4 @@ -mod library; +pub mod library; pub mod plugin; mod plugin_environment; pub mod plugin_info; diff --git a/futuremod_engine/src/plugins/plugin.rs b/futuremod_engine/src/plugins/plugin.rs index bf4856a..2532305 100644 --- a/futuremod_engine/src/plugins/plugin.rs +++ b/futuremod_engine/src/plugins/plugin.rs @@ -1,7 +1,10 @@ -use super::plugin_environment::PluginEnvironment; +use super::{ + library::settings::{get_settings, PluginSettings}, + plugin_environment::PluginEnvironment, +}; use futuremod_data::plugin::{PluginError, PluginInfo}; use log::*; -use mlua::{Function, Lua, OwnedFunction, Table}; +use mlua::{Function, Lua, Table}; use serde::{ser::SerializeStruct, Serialize}; use std::{fs, path::PathBuf, sync::Arc}; @@ -70,13 +73,13 @@ impl Into for PluginState { #[derive(Debug, Clone)] pub struct PluginContext { environment: PluginEnvironment, - on_load: Option, - on_unload: Option, - on_update: Option, - on_enable: Option, - on_disable: Option, - on_install: Option, - on_uninstall: Option, + on_load: Option, + on_unload: Option, + on_update: Option, + on_enable: Option, + on_disable: Option, + on_install: Option, + on_uninstall: Option, } impl Into for PluginContext { @@ -93,7 +96,7 @@ impl Into for PluginContext { } } -fn optional_lua_function_to_string(fun: &Option) -> &'static str { +fn optional_lua_function_to_string(fun: &Option) -> &'static str { if fun.is_some() { "set" } else { @@ -201,13 +204,13 @@ impl Plugin { } }; - let on_load = get_lua_function_or_none(&environment.table.to_ref(), "onLoad"); - let on_unload = get_lua_function_or_none(&environment.table.to_ref(), "onUnload"); - let on_update = get_lua_function_or_none(&environment.table.to_ref(), "onUpdate"); - let on_enable = get_lua_function_or_none(&environment.table.to_ref(), "onEnable"); - let on_disable = get_lua_function_or_none(&environment.table.to_ref(), "onDisable"); - let on_install = get_lua_function_or_none(&environment.table.to_ref(), "onInstall"); - let on_uninstall = get_lua_function_or_none(&environment.table.to_ref(), "onUninstall"); + let on_load = get_lua_function_or_none(&environment.table, "onLoad"); + let on_unload = get_lua_function_or_none(&environment.table, "onUnload"); + let on_update = get_lua_function_or_none(&environment.table, "onUpdate"); + let on_enable = get_lua_function_or_none(&environment.table, "onEnable"); + let on_disable = get_lua_function_or_none(&environment.table, "onDisable"); + let on_install = get_lua_function_or_none(&environment.table, "onInstall"); + let on_uninstall = get_lua_function_or_none(&environment.table, "onUninstall"); let context = PluginContext { environment, @@ -222,7 +225,7 @@ impl Plugin { debug!("Execute onLoad function"); match &context.on_load { - Some(main) => match main.call::<_, ()>(()) { + Some(main) => match main.call::<()>(()) { Ok(_) => debug!("Successfully called onLoad"), Err(e) => { warn!("Main function threw error: {:?}", e); @@ -375,14 +378,28 @@ impl Plugin { pub fn is_enabled(&self) -> bool { self.enabled } + + pub fn get_settings(&self) -> Result, PluginError> { + match &self.state { + PluginState::Loaded(context) => { + debug!("{:#?}", context.environment.libraries); + get_settings(&context.environment.libraries) + .map_err(|e| PluginError::Error(format!("{}", e))) + } + _ => { + debug!("Requested plugin settings of not loaded plugin"); + Ok(None) + } + } + } } -fn get_lua_function_or_none<'lua>(module: &'lua Table, name: &str) -> Option { - match module.get::<&str, Function>(name) { +fn get_lua_function_or_none(module: &Table, name: &str) -> Option { + match module.get::(name) { Ok(function) => { debug!("Module {:?} has attribute '{}'", module, name); - Some(function.into_owned()) + Some(function) } Err(_) => { debug!("Module {:?} has no attribute '{}'", module, name); diff --git a/futuremod_engine/src/plugins/plugin_environment.rs b/futuremod_engine/src/plugins/plugin_environment.rs index 44345b6..5464121 100644 --- a/futuremod_engine/src/plugins/plugin_environment.rs +++ b/futuremod_engine/src/plugins/plugin_environment.rs @@ -8,12 +8,13 @@ use std::{ use super::library::{ dangerous::create_dangerous_library, game::create_game_library, input::create_input_library, - matrix::create_matrix_library, system::create_system_library, ui::create_ui_library, + matrix::create_matrix_library, settings::create_settings_library_new, + system::create_system_library, ui::create_ui_library, }; use anyhow::bail; use futuremod_data::plugin::{PluginDependency, PluginInfo}; use log::*; -use mlua::{Lua, OwnedTable}; +use mlua::{IntoLua, Lua, Table}; /// Holds the entire plugin environment. /// @@ -24,9 +25,11 @@ use mlua::{Lua, OwnedTable}; #[derive(Clone)] pub struct PluginEnvironment { /// Plugin globals. - pub table: OwnedTable, + pub table: Table, /// Plugin package cache - package_cache: Arc>>, + package_cache: Arc>>, + + pub libraries: Arc>, } #[derive(Debug, Clone, Copy)] @@ -57,7 +60,7 @@ unsafe fn raw_to_lua<'a>( lua: &'a Lua, lua_type: Type, raw_value: u32, -) -> Result, mlua::Error> { +) -> Result { let value = match lua_type { Type::Integer => mlua::Value::Integer(raw_value as i32), Type::String => { @@ -115,27 +118,40 @@ unsafe fn lua_to_raw<'a>( fn prepare_libraries( lua: Arc, info: &PluginInfo, -) -> Result, mlua::Error> { - let mut libraries = HashMap::new(); +) -> Result, mlua::Error> { + let mut libraries: HashMap<&'static str, mlua::Value> = HashMap::new(); let globals = lua.globals(); for library in info.dependencies.iter() { match library { - PluginDependency::Dangerous => { - libraries.insert("dangerous", create_dangerous_library(lua.clone())?) - } - PluginDependency::Game => libraries.insert("game", create_game_library(lua.clone())?), - PluginDependency::Input => { - libraries.insert("input", create_input_library(lua.clone())?) - } - PluginDependency::UI => libraries.insert("ui", create_ui_library(lua.clone())?), - PluginDependency::System => { - libraries.insert("system", create_system_library(lua.clone())?) - } - PluginDependency::Matrix => { - libraries.insert("matrix", create_matrix_library(lua.clone())?) + PluginDependency::Dangerous => libraries.insert( + "dangerous", + mlua::Value::Table(create_dangerous_library(lua.clone())?), + ), + PluginDependency::Game => libraries.insert( + "game", + mlua::Value::Table(create_game_library(lua.clone())?), + ), + PluginDependency::Input => libraries.insert( + "input", + mlua::Value::Table(create_input_library(lua.clone())?), + ), + PluginDependency::UI => { + libraries.insert("ui", mlua::Value::Table(create_ui_library(lua.clone())?)) } + PluginDependency::System => libraries.insert( + "system", + mlua::Value::Table(create_system_library(lua.clone())?), + ), + PluginDependency::Matrix => libraries.insert( + "matrix", + mlua::Value::Table(create_matrix_library(lua.clone())?), + ), + PluginDependency::Settings => libraries.insert( + "settings", + create_settings_library_new(lua.clone())?.into_lua(&lua)?, + ), PluginDependency::Math => libraries.insert("math", globals.get("math").to_owned()?), PluginDependency::Bit32 => libraries.insert("bit32", globals.get("bit32").to_owned()?), PluginDependency::String => { @@ -154,7 +170,7 @@ fn link_global_by_name( src: &mlua::Table, dst: &mlua::Table, ) -> Result<(), mlua::Error> { - dst.set(name, src.get::<_, mlua::Value>(name)?) + dst.set(name, src.get::(name)?) } const DEFAULT_GLOBALS: [&str; 17] = [ @@ -196,21 +212,23 @@ impl PluginEnvironment { // Create and set functions let print_target = plugin_info.name.to_string(); let print_fn = lua.create_function(move |_, msg: mlua::Value| { - // Convert the message into a string. - // If the value cannot be converted to string, use it's debug representation - let msg = match msg.to_string() { - Ok(msg) => msg, - Err(_) => format!("{:?}", msg), - }; - let plugin_name = print_target.clone(); + // Convert the message into a string. + // If the value cannot be converted to string, use it's debug representation + let msg = match msg.to_string() { + Ok(msg) => msg, + Err(_) => format!("{:?}", msg), + }; + let plugin_name = print_target.clone(); + + info!(target: format!("plugin::{}", print_target).as_str(), plugin:% = plugin_name; "{}", msg); - info!(target: format!("plugin::{}", print_target).as_str(), plugin:% = plugin_name; "{}", msg); + Ok(()) + })?; - Ok(()) - })?; + let libraries = Arc::new(prepare_libraries(lua.clone(), &plugin_info)?); + let libraires_weak = Arc::downgrade(&libraries); - let libraries = prepare_libraries(lua.clone(), &plugin_info)?; - let package_cache: Arc>> = + let package_cache: Arc>> = Arc::new(Mutex::new(HashMap::new())); let require_fn_package_cache = Arc::downgrade(&package_cache); let plugin_info_clone = plugin_info.clone(); @@ -219,65 +237,68 @@ impl PluginEnvironment { let lua_ref = lua.clone(); let require_fn = lua.create_function(move |lua, name: String| { - debug!("Plugin '{}' required {}", plugin_name, name); + debug!("Plugin '{}' required {}", plugin_name, name); - // Check if a library with the given name exists - if let Some(library) = libraries.get(name.as_str()) { - debug!("Required name is a library"); - return Ok(library.clone()); - } + // Check if a library with the given name exists + let libraries = libraires_weak.upgrade() + .ok_or(mlua::Error::RuntimeError("Require is forbidden".into()))?; - debug!("Library doesn't exist, treating require statement as requiring a local file"); + if let Some(library) = libraries.get(name.as_str()) { + debug!("Required name is a library"); + return Ok(library.clone()); + } - // Check if the require statement should load another lua file - // Normalize the require path such that referencing the same file with a slightly different path - // will not load the same file multiple times. - // We enforce here that every require statement of a lua file is the relative path to that file - // starting from the root of the plugin. - let require_path = Path::new(&name).to_path_buf().with_extension("lua"); + debug!("Library doesn't exist, treating require statement as requiring a local file"); - debug!("Requiring file '{:?}'", require_path); + // Check if the require statement should load another lua file + // Normalize the require path such that referencing the same file with a slightly different path + // will not load the same file multiple times. + // We enforce here that every require statement of a lua file is the relative path to that file + // starting from the root of the plugin. + let require_path = Path::new(&name).to_path_buf().with_extension("lua"); - let absolute_require_path = Path::join(&plugin_path, require_path.clone()).canonicalize().map_err(|e| mlua::Error::RuntimeError(format!("Could not load library: {:?}", e)))?; + debug!("Requiring file '{:?}'", require_path); - let require_package_cache = match require_fn_package_cache.upgrade() { - Some(c) => c, - None => return Err(mlua::Error::RuntimeError("Require is forbidden: Plugin is destroyed".into())), - }; + let absolute_require_path = Path::join(&plugin_path, require_path.clone()).canonicalize().map_err(|e| mlua::Error::RuntimeError(format!("Could not load library: {:?}", e)))?; - let mut require_package_cache = require_package_cache.lock().map_err(|e| mlua::Error::RuntimeError(format!("Couldn't get lock to cache: {:?}", e)))?; + let require_package_cache = match require_fn_package_cache.upgrade() { + Some(c) => c, + None => return Err(mlua::Error::RuntimeError("Require is forbidden: Plugin is destroyed".into())), + }; - if let Some(cached_file) = require_package_cache.get(&require_path) { - debug!("Found required file in cache"); - return Ok(cached_file.clone()); - } + let mut require_package_cache = require_package_cache.lock().map_err(|e| mlua::Error::RuntimeError(format!("Couldn't get lock to cache: {:?}", e)))?; - if !absolute_require_path.starts_with(&plugin_path) { - warn!("Plugin {} required {:?} which is outside it's plugin folder", plugin_name, absolute_require_path); - return Err(mlua::Error::RuntimeError("Permission denied: Requiring a file outside of the plugin folder is not allowed".into())); - } + if let Some(cached_file) = require_package_cache.get(&require_path) { + debug!("Found required file in cache"); + return Ok(mlua::Value::Table(cached_file.clone())); + } - if !absolute_require_path.exists() { - warn!("Plugin {} required non-existing file {:?}", plugin_name, absolute_require_path); - return Err(mlua::Error::RuntimeError("Required file doesn't exist".into())); - } + if !absolute_require_path.starts_with(&plugin_path) { + warn!("Plugin {} required {:?} which is outside it's plugin folder", plugin_name, absolute_require_path); + return Err(mlua::Error::RuntimeError("Permission denied: Requiring a file outside of the plugin folder is not allowed".into())); + } + + if !absolute_require_path.exists() { + warn!("Plugin {} required non-existing file {:?}", plugin_name, absolute_require_path); + return Err(mlua::Error::RuntimeError("Required file doesn't exist".into())); + } - debug!("Preparing plugin environment for required file"); - let file_environment = PluginEnvironment::new(lua_ref.clone(), &plugin_info_clone)?; + debug!("Preparing plugin environment for required file"); + let file_environment = PluginEnvironment::new(lua_ref.clone(), &plugin_info_clone)?; - // Read the file content - let content = fs::read_to_string(&absolute_require_path).map_err(|e| mlua::Error::RuntimeError(format!("Could not require file: {:?}", e)))?; - let file_chunk = lua.load(content).set_environment(file_environment.table.clone()); + // Read the file content + let content = fs::read_to_string(&absolute_require_path).map_err(|e| mlua::Error::RuntimeError(format!("Could not require file: {:?}", e)))?; + let file_chunk = lua.load(content).set_environment(file_environment.table.clone()); - debug!("Executing required file"); - file_chunk.exec()?; + debug!("Executing required file"); + file_chunk.exec()?; - let file_globals = file_environment.table.clone(); + let file_globals = file_environment.table.clone(); - let _ = require_package_cache.insert(absolute_require_path, file_globals.clone()); + let _ = require_package_cache.insert(absolute_require_path, file_globals.clone()); - Ok(file_globals) - })?; + Ok(mlua::Value::Table(file_globals)) + })?; table.set("print", print_fn)?; table.set("require", require_fn)?; @@ -285,7 +306,8 @@ impl PluginEnvironment { add_default_globals(&table, &lua.globals())?; Ok(PluginEnvironment { - table: table.into_owned(), + table, + libraries, package_cache, }) } diff --git a/futuremod_engine/src/plugins/plugin_manager.rs b/futuremod_engine/src/plugins/plugin_manager.rs index c51d785..2b72a37 100644 --- a/futuremod_engine/src/plugins/plugin_manager.rs +++ b/futuremod_engine/src/plugins/plugin_manager.rs @@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex, OnceLock}; use std::{collections::HashMap, fs}; use walkdir::WalkDir; +use super::library::settings::PluginSettings; use super::plugin::*; use super::plugin_info::PluginInfoError; use super::plugin_persistence::{PersistedPlugin, PersistedPlugins, PersistentPluginState}; @@ -167,7 +168,7 @@ impl PluginManager { pub fn new(plugins_directory: PathBuf) -> Result { let lua = Arc::new(Lua::new()); if let Err(e) = - lua.load_from_std_lib(StdLib::STRING | StdLib::BIT | StdLib::MATH | StdLib::TABLE) + lua.load_std_libs(StdLib::STRING | StdLib::BIT | StdLib::MATH | StdLib::TABLE) { error!("Could not load subset of standard library: {}", e); return Err(PluginManagerError::Other(format!( @@ -421,6 +422,22 @@ impl PluginManager { return &self.plugins; } + pub fn get_plugin_settings( + &self, + name: &str, + ) -> Result, PluginManagerError> { + info!("Getting settings of plugin '{}'", name); + match self.plugins.get(name) { + Some(game_plugin) => game_plugin + .get_settings() + .map_err(PluginManagerError::Plugin), + None => { + warn!("Plugin doesn't exist"); + Err(PluginManagerError::PluginNotFound) + } + } + } + /// Install a plugin from a folder. /// /// This method will install the plugin stored at the specified `folder`. diff --git a/futuremod_engine/src/server.rs b/futuremod_engine/src/server.rs index 6f9c25e..06f66b4 100644 --- a/futuremod_engine/src/server.rs +++ b/futuremod_engine/src/server.rs @@ -10,7 +10,7 @@ use axum::{ routing::{get, post, put}, BoxError, Json, Router, }; -use futuremod_data::plugin::PluginInfo; +use futuremod_data::plugin::{settings::SettingsEvent, PluginInfo}; use futures::Stream; use futures::TryStreamExt; use kv::Key; @@ -76,6 +76,8 @@ fn serve(config: Config) -> Result<(), Error> { .route("/plugin/install-dev", post(install_plugin_in_dev_mode)) .route("/plugin/uninstall", post(uninstall_plugin)) .route("/plugin/info", put(get_plugin_info)) + .route("/plugin/settings", put(get_plugin_settings)) + .route("/plugin/settings/event", put(handle_settings_event)) .route("/log", get(log_handler)); axum::Server::bind( @@ -240,13 +242,16 @@ where } } -fn with_plugin_manager(f: F) -> Result +fn with_plugin_manager(f: F) -> Result where - F: Fn(&PluginManager) -> Result, + F: Fn(&PluginManager) -> R, { match GlobalPluginManager::get().lock() { - Ok(mut plugin_manager) => Ok(f(&mut plugin_manager)?), - Err(e) => Err(anyhow!("Could not get lock to plugin manager: {:?}", e)), + Ok(plugin_manager) => Ok(f(&plugin_manager)), + Err(e) => Err(AppError(anyhow!( + "Could not get lock to plugin manager: {:?}", + e + ))), } } @@ -330,6 +335,54 @@ async fn reload_plugin(Json(payload): Json) -> impl IntoResponse { }) } +fn get_plugin_settings_from_manager(name: &str, plugin_manager: &PluginManager) -> Result { + match plugin_manager.get_plugin_settings(name) { + Ok(optional_result) => match optional_result { + None => { + debug!("Plugin has not settings"); + Err(StatusCode::NOT_FOUND.into_response()) + } + Some(settings) => { + debug!("Plugin has settings, returning"); + Ok(settings) + } + }, + Err(e) => match e { + PluginManagerError::PluginNotFound => Err(StatusCode::NOT_FOUND.into_response()), + e => Err(( + StatusCode::INTERNAL_SERVER_ERROR, + AppError(anyhow!("{:?}", e)), + ) + .into_response()), + }, + } +} + +async fn get_plugin_settings(Json(payload): Json) -> impl IntoResponse { + debug!("Getting settings of '{}'", payload.name); + with_plugin_manager(|plugin_manager| -> Response { + match get_plugin_settings_from_manager(&payload.name, plugin_manager) { + Ok(settings) => Json(settings).into_response(), + Err(e) => e.into_response(), + } + }) +} + + +async fn handle_settings_event(Json(payload): Json) -> impl IntoResponse { + debug!("Handling settings event: {:?}", payload); + + with_plugin_manager(|plugin_manager| { + match get_plugin_settings_from_manager(&payload.name, plugin_manager) { + Err(e) => e.into_response(), + Ok(mut settings) => match settings.handle_event(payload.target_id.clone(), payload.event.clone()) { + Ok(_) => StatusCode::NO_CONTENT.into_response(), + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", e)).into_response(), + } + } + }) +} + const TEMPORARY_DIRECTORY: &str = "fcop"; enum InstallError { diff --git a/futuremod_hook/Cargo.toml b/futuremod_hook/Cargo.toml index 18ff0fd..f4f4af7 100644 --- a/futuremod_hook/Cargo.toml +++ b/futuremod_hook/Cargo.toml @@ -27,5 +27,5 @@ features = [ ] [dependencies.mlua] -version = "0.9.1" -features = ["luau", "async", "serialize", "unstable"] +version = "0.10.0" +features = ["luau", "async", "serialize"] diff --git a/futuremod_hook/src/lua.rs b/futuremod_hook/src/lua.rs index 79d6b3e..497919d 100644 --- a/futuremod_hook/src/lua.rs +++ b/futuremod_hook/src/lua.rs @@ -10,8 +10,8 @@ use crate::native::{memory_copy, Hook}; use crate::types::{lua_to_native, lua_to_native_implied, native_to_lua, Type}; /// Create a hook on any function with a given lua function. -pub fn hook_function<'lua>( - lua: &'lua Lua, +pub fn hook_function( + lua: &Lua, (address, arg_type_names, return_type_name, callback): (u32, Vec, String, Function), ) -> Result { debug!( @@ -72,12 +72,12 @@ pub fn hook_function<'lua>( // 1. Convert the arguments from lua values into native values // 2. Call the original function with the arguments // 3. Convert the return value back to a lua value and return it - let original_wrapper = match lua.create_function::<_, mlua::Value, _>( + let original_wrapper = match lua.create_function::<_, _, mlua::Value>( move |lua, args: MultiValue| { debug!("Lua called original function"); // Convert the arguments from lua values into actual native values. - let lua_args = args.into_vec(); + let lua_args: Vec<&mlua::Value> = args.iter().collect(); let mut converted_lua_args: Vec = Vec::new(); @@ -163,7 +163,7 @@ pub fn hook_function<'lua>( for i in 0..argument_types.len() { let arg_type = argument_types[i]; - match native_to_lua(lua, arg_type, *arg_pointer.byte_offset(i as isize * 4)) { + match native_to_lua(&lua, arg_type, *arg_pointer.byte_offset(i as isize * 4)) { Ok(value) => callback_args.push(value), Err(e) => { warn!( @@ -177,7 +177,7 @@ pub fn hook_function<'lua>( // Call the lua hook let return_value = - match callback.call::<_, mlua::Value>(mlua::MultiValue::from_vec(callback_args)) { + match callback.call::(mlua::MultiValue::from_iter(callback_args)) { Ok(value) => value, Err(e) => { warn!("Lua hook threw error: {:?}. Panicking...", e); @@ -243,12 +243,8 @@ impl NativeFunction { } } - pub fn call<'lua>( - &self, - lua: &'lua Lua, - args: mlua::MultiValue, - ) -> Result, mlua::Error> { - let args = args.into_vec(); + pub fn call(&self, lua: &Lua, args: mlua::MultiValue) -> Result { + let args = args.iter().collect::>(); debug!( "Calling function at address {:x} with ({:?}), expecting return type {:?}", @@ -312,7 +308,7 @@ impl NativeFunction { } impl UserData for NativeFunction { - fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + fn add_methods>(methods: &mut M) { methods.add_method("getAddress", |_, native_function, ()| { return Ok(native_function.address); }); @@ -324,8 +320,8 @@ impl UserData for NativeFunction { } } -pub fn create_native_function_function<'lua>( - lua: &'lua Lua, +pub fn create_native_function_function( + lua: &Lua, (arg_types, return_type, lua_fn): (Vec, String, mlua::Function), ) -> Result { debug!( @@ -385,8 +381,7 @@ pub fn create_native_function_function<'lua>( } } - let return_value = match lua_fn.call::<_, mlua::Value>(mlua::MultiValue::from_vec(lua_args)) - { + let return_value = match lua_fn.call::(mlua::MultiValue::from_iter(lua_args)) { Ok(value) => value, Err(e) => { warn!("Lua function threw unexpected error: {:?}. Panicking...", e); @@ -536,8 +531,8 @@ pub fn create_native_function_function<'lua>( } } -pub fn get_native_function<'lua>( - _: &'lua Lua, +pub fn get_native_function( + _: &Lua, (address, arg_types, return_type): (u32, Vec, String), ) -> Result { let mut lua_arg_types: Vec = Vec::new(); diff --git a/futuremod_hook/src/native.rs b/futuremod_hook/src/native.rs index c433a3e..30dc48f 100644 --- a/futuremod_hook/src/native.rs +++ b/futuremod_hook/src/native.rs @@ -723,7 +723,7 @@ impl Hook { } impl UserData for Hook { - fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + fn add_methods>(methods: &mut M) { methods.add_method_mut("unhook", |_, this, ()| { unsafe { this.unhook() diff --git a/futuremod_hook/src/types.rs b/futuremod_hook/src/types.rs index deba5c5..b44d81b 100644 --- a/futuremod_hook/src/types.rs +++ b/futuremod_hook/src/types.rs @@ -37,11 +37,11 @@ impl Type { pub const MAX_STRING: u16 = 1024; /// Convert a native value into its lua value given the type name. -pub unsafe fn native_to_lua<'a>( - lua: &'a Lua, +pub unsafe fn native_to_lua( + lua: &Lua, lua_type: Type, raw_value: u32, -) -> Result, mlua::Error> { +) -> Result { let value = match lua_type { Type::String => { let mut string_bytes: Vec = Vec::new();