diff --git a/resources/scripts/Global.lua b/resources/scripts/Global.lua index 406c197..77d89cf 100644 --- a/resources/scripts/Global.lua +++ b/resources/scripts/Global.lua @@ -3,6 +3,19 @@ function onBeginLogin(player) player:send_message("Welcome to Kawari!") end +function split(input, separator) + if separator == nil then + separator = '%s' + end + + local t = {} + for str in string.gmatch(input, '([^'..separator..']+)') do + table.insert(t, str) + end + + return t +end + -- please keep these ids sorted! -- Actions @@ -28,3 +41,6 @@ registerEvent(1245186, "opening/OpeningGridania.lua") registerEvent(1245187, "opening/OpeningUldah.lua") -- TODO: Generic warps might be decided through ArrayEventHandler? + +-- Commands +registerCommand("setpos", "commands/debug/SetPos.lua") diff --git a/resources/scripts/commands/debug/SetPos.lua b/resources/scripts/commands/debug/SetPos.lua new file mode 100644 index 0000000..960bf14 --- /dev/null +++ b/resources/scripts/commands/debug/SetPos.lua @@ -0,0 +1,4 @@ +function onCommand(args, player) + local parts = split(args) + player:set_position({ x = tonumber(parts[1]), y = tonumber(parts[2]), z = tonumber(parts[3]) }) +end diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 446b0c2..025118a 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -38,6 +38,7 @@ use tokio::task::JoinHandle; struct ExtraLuaState { action_scripts: HashMap, event_scripts: HashMap, + command_scripts: HashMap, } fn spawn_main_loop() -> (ServerHandle, JoinHandle<()>) { @@ -478,12 +479,60 @@ async fn client_loop( ClientZoneIpcData::ChatMessage(chat_message) => { connection.handle.send(ToServer::Message(connection.id, chat_message.message.clone())).await; - ChatHandler::handle_chat_message( - &mut connection, - &mut lua_player, - chat_message, - ) - .await + let mut handled = false; + { + let parts: Vec<&str> = chat_message.message.split(' ').collect(); + let command_name = &parts[0][1..]; + + let lua = lua.lock().unwrap(); + let state = lua.app_data_ref::().unwrap(); + + if let Some(command_script) = + state.command_scripts.get(command_name) + { + handled = true; + + lua.scope(|scope| { + let connection_data = scope + .create_userdata_ref_mut(&mut lua_player) + .unwrap(); + + let config = get_config(); + + let file_name = format!( + "{}/{}", + &config.world.scripts_location, command_script + ); + lua.load( + std::fs::read(&file_name) + .expect("Failed to locate scripts directory!"), + ) + .set_name("@".to_string() + &file_name) + .exec() + .unwrap(); + + let func: Function = + lua.globals().get("onCommand").unwrap(); + + tracing::info!("{}", &chat_message.message[command_name.len() + 2..]); + + func.call::<()>((&chat_message.message[command_name.len() + 2..], connection_data)) + .unwrap(); + + Ok(()) + }) + .unwrap(); + } + } + + if !handled { + ChatHandler::handle_chat_message( + &mut connection, + &mut lua_player, + chat_message, + ) + .await; + } } ClientZoneIpcData::GMCommand { command, arg0, .. } => { tracing::info!("Got a game master command!"); @@ -866,6 +915,15 @@ async fn main() { }) .unwrap(); + let register_command_func = lua + .create_function(|lua, (command_name, command_script): (String, String)| { + tracing::info!("Registering {command_name} with {command_script}!"); + let mut state = lua.app_data_mut::().unwrap(); + let _ = state.command_scripts.insert(command_name, command_script); + Ok(()) + }) + .unwrap(); + lua.set_app_data(ExtraLuaState::default()); lua.globals() .set("registerAction", register_action_func) @@ -873,6 +931,9 @@ async fn main() { lua.globals() .set("registerEvent", register_event_func) .unwrap(); + lua.globals() + .set("registerCommand", register_command_func) + .unwrap(); let effectsbuilder_constructor = lua .create_function(|_, ()| Ok(EffectsBuilder::default())) diff --git a/src/common/position.rs b/src/common/position.rs index 07a9bc0..6e3a7e6 100644 --- a/src/common/position.rs +++ b/src/common/position.rs @@ -1,7 +1,8 @@ use binrw::binrw; +use serde::{Deserialize, Serialize}; #[binrw] -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)] pub struct Position { pub x: f32, pub y: f32, diff --git a/src/world/chat_handler.rs b/src/world/chat_handler.rs index 59171cd..ae5cfe3 100644 --- a/src/world/chat_handler.rs +++ b/src/world/chat_handler.rs @@ -54,19 +54,6 @@ impl ChatHandler { let parts: Vec<&str> = chat_message.message.split(' ').collect(); match parts[0] { - "!setpos" => { - let pos_x = parts[1].parse::().unwrap(); - let pos_y = parts[2].parse::().unwrap(); - let pos_z = parts[3].parse::().unwrap(); - - connection - .set_player_position(Position { - x: pos_x, - y: pos_y, - z: pos_z, - }) - .await; - } "!spawnplayer" => { let config = get_config(); diff --git a/src/world/lua.rs b/src/world/lua.rs index 1337052..de8ccc1 100644 --- a/src/world/lua.rs +++ b/src/world/lua.rs @@ -146,7 +146,8 @@ impl UserData for LuaPlayer { Ok(()) }, ); - methods.add_method_mut("set_position", |_, this, position: Position| { + methods.add_method_mut("set_position", |lua, this, position: Value| { + let position: Position = lua.from_value(position).unwrap(); this.set_position(position); Ok(()) }); @@ -185,15 +186,6 @@ impl UserData for LuaPlayer { impl UserData for Position {} -impl FromLua for Position { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::UserData(ud) => Ok(*ud.borrow::()?), - _ => unreachable!(), - } - } -} - impl UserData for ObjectTypeId {} impl FromLua for ObjectTypeId {