1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 11:47:45 +00:00

Allow reloading Global.lua with the new !reload debug command

Scripts are normally loaded when they are needed, with the exception of
Global.lua that's only loaded once at start-up. But now you can reload
it in-game with the !reload command, which should make event and command
scripting way less of a hassle.
This commit is contained in:
Joshua Goins 2025-06-21 12:16:27 -04:00
parent 0c967f8a56
commit f07d16b949
7 changed files with 86 additions and 57 deletions

View file

@ -112,6 +112,7 @@ These special debug commands start with `!` and are custom to Kawari.
* `!equip <name>`: Forcefully equip an item, useful for bypassing class/job and other client restrictions. This will *overwrite* any item in that slot!
* `!nudge <distance> <up/down (optional)>`: Teleport forward, back, up or down `distance` yalms. Specifying up or down will move the player up or down instead of forward or back. Examples: `!nudge 5 up` to move up 5 yalms, `!nudge 5` to move forward 5 yalms, `!nudge -5` to move backward 5 yalms.
* `!festival <id1> <id2> <id3> <id4>`: Sets the festival in the current zone. Multiple festivals can be set together to create interesting effects.
* `!reload`: Reloads `Global.lua` that is normally only loaded once at start-up.
### GM commands

View file

@ -81,3 +81,4 @@ registerCommand("setspeed", "commands/debug/SetSpeed.lua")
registerCommand("nudge", "commands/debug/Nudge.lua")
registerCommand("festival", "commands/debug/Festival.lua")
registerCommand("permtest", "commands/debug/PermissionTest.lua")
registerCommand("reload", "commands/debug/Reload.lua")

View file

@ -0,0 +1,6 @@
required_rank = GM_RANK_DEBUG
function onCommand(args, player)
player:reload_scripts()
player:send_message("Scripts reloaded!")
end

View file

@ -20,10 +20,10 @@ use kawari::packet::oodle::OodleNetwork;
use kawari::packet::{
ConnectionType, PacketSegment, PacketState, SegmentData, SegmentType, send_keep_alive,
};
use kawari::world::{ChatHandler, ExtraLuaState, Zone, ZoneConnection};
use kawari::world::{ChatHandler, ExtraLuaState, Zone, ZoneConnection, load_global_script};
use kawari::world::{
ClientHandle, EffectsBuilder, Event, FromServer, LuaPlayer, PlayerData, ServerHandle,
StatusEffects, ToServer, WorldDatabase, handle_custom_ipc, server_main_loop,
ClientHandle, Event, FromServer, LuaPlayer, PlayerData, ServerHandle, StatusEffects, ToServer,
WorldDatabase, handle_custom_ipc, server_main_loop,
};
use mlua::{Function, Lua};
@ -888,58 +888,8 @@ async fn main() {
let game_data = Arc::new(Mutex::new(GameData::new()));
{
let lua = lua.lock().unwrap();
let register_action_func = lua
.create_function(|lua, (action_id, action_script): (u32, String)| {
tracing::info!("Registering {action_id} with {action_script}!");
let mut state = lua.app_data_mut::<ExtraLuaState>().unwrap();
let _ = state.action_scripts.insert(action_id, action_script);
Ok(())
})
.unwrap();
let register_event_func = lua
.create_function(|lua, (event_id, event_script): (u32, String)| {
tracing::info!("Registering {event_id} with {event_script}!");
let mut state = lua.app_data_mut::<ExtraLuaState>().unwrap();
let _ = state.event_scripts.insert(event_id, event_script);
Ok(())
})
.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::<ExtraLuaState>().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)
.unwrap();
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()))
.unwrap();
lua.globals()
.set("EffectsBuilder", effectsbuilder_constructor)
.unwrap();
let file_name = format!("{}/Global.lua", &config.world.scripts_location);
lua.load(std::fs::read(&file_name).expect("Failed to locate scripts directory!"))
.set_name("@".to_string() + &file_name)
.exec()
.unwrap();
let mut lua = lua.lock().unwrap();
load_global_script(&mut lua);
}
let (handle, _) = spawn_main_loop();

View file

@ -34,6 +34,7 @@ use super::{
Actor, CharacterData, EffectsBuilder, Event, LuaPlayer, StatusEffects, ToServer, WorldDatabase,
Zone,
common::{ClientId, ServerHandle},
load_global_script,
lua::Task,
};
@ -591,6 +592,10 @@ impl ZoneConnection {
Task::WarpAetheryte { aetheryte_id } => {
self.warp_aetheryte(*aetheryte_id).await;
}
Task::ReloadScripts => {
let mut lua = self.lua.lock().unwrap();
load_global_script(&mut lua);
}
}
}
player.queued_tasks.clear();

View file

@ -5,12 +5,14 @@ use crate::{
ObjectId, ObjectTypeId, Position, timestamp_secs, workdefinitions::RemakeMode,
write_quantized_rotation,
},
config::get_config,
ipc::zone::{
ActionEffect, ActorControlCategory, ActorControlSelf, DamageElement, DamageKind,
DamageType, EffectKind, EventScene, ServerZoneIpcData, ServerZoneIpcSegment, Warp,
},
opcodes::ServerZoneIpcType,
packet::{PacketSegment, SegmentData, SegmentType},
world::ExtraLuaState,
};
use super::{PlayerData, StatusEffects, Zone, connection::TeleportQuery};
@ -23,6 +25,7 @@ pub enum Task {
FinishEvent { handler_id: u32 },
SetClassJob { classjob_id: u8 },
WarpAetheryte { aetheryte_id: u32 },
ReloadScripts,
}
#[derive(Default)]
@ -156,6 +159,10 @@ impl LuaPlayer {
fn warp_aetheryte(&mut self, aetheryte_id: u32) {
self.queued_tasks.push(Task::WarpAetheryte { aetheryte_id });
}
fn reload_scripts(&mut self) {
self.queued_tasks.push(Task::ReloadScripts);
}
}
impl UserData for LuaPlayer {
@ -223,6 +230,10 @@ impl UserData for LuaPlayer {
this.warp_aetheryte(aetheryte_id);
Ok(())
});
methods.add_method_mut("reload_scripts", |_, this, _: ()| {
this.reload_scripts();
Ok(())
});
}
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
@ -324,3 +335,58 @@ impl FromLua for EffectsBuilder {
}
}
}
/// Loads `Global.lua`
pub fn load_global_script(lua: &mut Lua) {
let register_action_func = lua
.create_function(|lua, (action_id, action_script): (u32, String)| {
tracing::info!("Registering {action_id} with {action_script}!");
let mut state = lua.app_data_mut::<ExtraLuaState>().unwrap();
let _ = state.action_scripts.insert(action_id, action_script);
Ok(())
})
.unwrap();
let register_event_func = lua
.create_function(|lua, (event_id, event_script): (u32, String)| {
tracing::info!("Registering {event_id} with {event_script}!");
let mut state = lua.app_data_mut::<ExtraLuaState>().unwrap();
let _ = state.event_scripts.insert(event_id, event_script);
Ok(())
})
.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::<ExtraLuaState>().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)
.unwrap();
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()))
.unwrap();
lua.globals()
.set("EffectsBuilder", effectsbuilder_constructor)
.unwrap();
let config = get_config();
let file_name = format!("{}/Global.lua", &config.world.scripts_location);
lua.load(std::fs::read(&file_name).expect("Failed to locate scripts directory!"))
.set_name("@".to_string() + &file_name)
.exec()
.unwrap();
}

View file

@ -11,7 +11,7 @@ mod database;
pub use database::{CharacterData, WorldDatabase};
mod lua;
pub use lua::{EffectsBuilder, LuaPlayer};
pub use lua::{EffectsBuilder, LuaPlayer, load_global_script};
mod event;
pub use event::Event;