mirror of
https://github.com/redstrate/Kawari.git
synced 2025-06-30 11:47:45 +00:00
Reimplement several commands in Lua (#37)
* Reimplement !unlockaction in Lua Rebased on upstream master * Unlockaction: display the action id that was unlocked * Reimplement GM speed in Lua * Fix warnings and errors * Run cargo fmt * Reimplement GM wireframe in Lua * Reimplement GM invis command, with a caveat It can't toggle the invis state yet, and I'm not sure where to update it. * Lua invis: add the gm_invisible toggle, but it still doesn't revert back to false... * Reimplement GM aetheryte in Lua, with a caveat It's seemingly not working right now though: it doesn't add any new aetherytes to the teleport menu. But I can't get the command `//gm aetheryte on X` to do it either, so it's possible Kawari isn't responding correctly? Either way this needs further testing. * Lua invis: add the forgotten Lua file * Reimplement GM teri in Lua Also add a TODO for UnlockAetheryte * Make comment in lua.rs more useful * Run cargo fmt again * Teri: range check the territory ID * Update USAGE.md to reflect the new commands Rebased on upstream master * Clarify unlockaetheryte USAGE and in-script usage * Refactor UnlockAetheryte.lua, and make ToggleInvisibility actually work properly. I opted to create a Task for this, because sticking it in kawari-world.rs felt like a hack to me. * Run cargo fmt for hopefully the last time today * Move lua.ra:toggle_invisibility down with the other queued tasks * Fix spaces in USAGE.md, remove stray rebase message
This commit is contained in:
parent
6561e63fd4
commit
61616df842
11 changed files with 243 additions and 11 deletions
4
USAGE.md
4
USAGE.md
|
@ -113,6 +113,10 @@ These special debug commands start with `!` and are custom to Kawari.
|
||||||
* `!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.
|
* `!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.
|
* `!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.
|
* `!reload`: Reloads `Global.lua` that is normally only loaded once at start-up.
|
||||||
|
* `!wireframe: Toggles the hidden GM wireframe rendering mode on or off.`
|
||||||
|
* `!invis: Toggles the hidden GM invisibility mode on or off for your character.`
|
||||||
|
* `!unlockaetheryte <on/off> <id>`: Unlock an aetheryte. For the first parameter, the literal words are required.
|
||||||
|
* `!teri <id>`: Changes to the specified territory.
|
||||||
|
|
||||||
### GM commands
|
### GM commands
|
||||||
|
|
||||||
|
|
|
@ -86,3 +86,8 @@ registerCommand("nudge", "commands/debug/Nudge.lua")
|
||||||
registerCommand("festival", "commands/debug/Festival.lua")
|
registerCommand("festival", "commands/debug/Festival.lua")
|
||||||
registerCommand("permtest", "commands/debug/PermissionTest.lua")
|
registerCommand("permtest", "commands/debug/PermissionTest.lua")
|
||||||
registerCommand("reload", "commands/debug/Reload.lua")
|
registerCommand("reload", "commands/debug/Reload.lua")
|
||||||
|
registerCommand("unlockaction", "commands/debug/UnlockAction.lua")
|
||||||
|
registerCommand("wireframe", "commands/debug/ToggleWireframe.lua")
|
||||||
|
registerCommand("invis", "commands/debug/ToggleInvisibility.lua")
|
||||||
|
registerCommand("unlockaetheryte", "commands/debug/UnlockAetheryte.lua")
|
||||||
|
registerCommand("teri", "commands/debug/ChangeTerritory.lua")
|
||||||
|
|
22
resources/scripts/commands/debug/ChangeTerritory.lua
Normal file
22
resources/scripts/commands/debug/ChangeTerritory.lua
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
required_rank = GM_RANK_DEBUG
|
||||||
|
|
||||||
|
function onCommand(args, player)
|
||||||
|
local parts = split(args)
|
||||||
|
local argc = table.getn(parts)
|
||||||
|
local sender = "[teri] "
|
||||||
|
local usage = "\nThis command moves the user to a new zone/territory.\nUsage: !teri <id>"
|
||||||
|
|
||||||
|
if argc == 0 then
|
||||||
|
player:send_message(sender.."A territory id is required to use this command."..usage)
|
||||||
|
end
|
||||||
|
|
||||||
|
local id = tonumber(parts[1])
|
||||||
|
|
||||||
|
if not id or id < 0 or id > 65535 then -- Must be in range of unsigned 16-bit value
|
||||||
|
player:send_message(sender.."Error parsing territory id! Make sure your input is an integer between 0 and 65535."..usage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
player:change_territory(id)
|
||||||
|
player:send_message(string.format("%s Changing territory to %s.", sender, id))
|
||||||
|
end
|
26
resources/scripts/commands/debug/SetSpeed.lua
Normal file
26
resources/scripts/commands/debug/SetSpeed.lua
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
required_rank = GM_RANK_DEBUG
|
||||||
|
|
||||||
|
function onCommand(args, player)
|
||||||
|
local parts = split(args)
|
||||||
|
local argc = table.getn(parts)
|
||||||
|
local sender = "[setspeed] "
|
||||||
|
local usage = "\nThis command sets the user's speed to a desired multiplier.\nUsage: !setspeed <multiplier>"
|
||||||
|
local SPEED_MAX = 10 -- Arbitrary, but it's more or less unplayable even at this amount
|
||||||
|
local speed_multiplier = tonumber(parts[1])
|
||||||
|
|
||||||
|
if argc == 1 and not speed_multiplier then
|
||||||
|
player:send_message(sender.."Error parsing speed multiplier! Make sure the multiplier is an integer."..usage)
|
||||||
|
return
|
||||||
|
elseif argc == 0 then
|
||||||
|
speed_multiplier = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if speed_multiplier <= 0 then
|
||||||
|
speed_multiplier = 1
|
||||||
|
elseif speed_multiplier > SPEED_MAX then
|
||||||
|
speed_multiplier = SPEED_MAX
|
||||||
|
end
|
||||||
|
|
||||||
|
player:set_speed(speed_multiplier)
|
||||||
|
player:send_message(string.format("%sSpeed multiplier set to %s.", sender, speed_multiplier))
|
||||||
|
end
|
11
resources/scripts/commands/debug/ToggleInvisibility.lua
Normal file
11
resources/scripts/commands/debug/ToggleInvisibility.lua
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
required_rank = GM_RANK_DEBUG
|
||||||
|
|
||||||
|
function onCommand(args, player)
|
||||||
|
local parts = split(args)
|
||||||
|
local argc = table.getn(parts)
|
||||||
|
local sender = "[invis] "
|
||||||
|
local usage = "\nThis command makes the user invisible to all other actors."
|
||||||
|
|
||||||
|
player:toggle_invisibility()
|
||||||
|
player:send_message(sender.."Invisibility toggled.")
|
||||||
|
end
|
11
resources/scripts/commands/debug/ToggleWireframe.lua
Normal file
11
resources/scripts/commands/debug/ToggleWireframe.lua
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
required_rank = GM_RANK_DEBUG
|
||||||
|
|
||||||
|
function onCommand(args, player)
|
||||||
|
local parts = split(args)
|
||||||
|
local argc = table.getn(parts)
|
||||||
|
local sender = "[wireframe] "
|
||||||
|
local usage = "\nThis command allows the user to view the world in wireframe mode."
|
||||||
|
|
||||||
|
player:toggle_wireframe()
|
||||||
|
player:send_message(sender.."Wireframe mode toggled.")
|
||||||
|
end
|
23
resources/scripts/commands/debug/UnlockAction.lua
Normal file
23
resources/scripts/commands/debug/UnlockAction.lua
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
required_rank = GM_RANK_DEBUG
|
||||||
|
|
||||||
|
function onCommand(args, player)
|
||||||
|
local parts = split(args)
|
||||||
|
local argc = table.getn(parts)
|
||||||
|
local sender = "[unlockaction] "
|
||||||
|
local usage = "\nThis command teaches the user an action.\nUsage: !useaction <id>"
|
||||||
|
|
||||||
|
if argc < 1 then
|
||||||
|
player:send_message(sender.."This command requires 1 parameter."..usage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local id = tonumber(parts[1])
|
||||||
|
|
||||||
|
if not id then
|
||||||
|
player:send_message(sender.."Error parsing action id! Make sure the id is an integer."..usage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
player:unlock_action(id)
|
||||||
|
player:send_message(string.format("%s Action %s unlocked!", sender, id))
|
||||||
|
end
|
39
resources/scripts/commands/debug/UnlockAetheryte.lua
Normal file
39
resources/scripts/commands/debug/UnlockAetheryte.lua
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
required_rank = GM_RANK_DEBUG
|
||||||
|
|
||||||
|
function onCommand(args, player)
|
||||||
|
local parts = split(args)
|
||||||
|
local argc = table.getn(parts)
|
||||||
|
local sender = "[unlockaetheryte] "
|
||||||
|
local usage = "\nThis command unlocks an aetheryte for the user.\nUsage: !unlockaetheryte <on/off> <id>"
|
||||||
|
|
||||||
|
if argc < 2 then
|
||||||
|
player:send_message(sender.."This command requires two parameters."..usage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local on = parts[1]
|
||||||
|
|
||||||
|
if on == "on" then
|
||||||
|
on = 0
|
||||||
|
elseif on == "off" then
|
||||||
|
on = 1
|
||||||
|
else
|
||||||
|
player:send_message(sender.."Error parsing first parameter. Must be either of the words: 'on' or 'off'."..usage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local id = tonumber(parts[2])
|
||||||
|
|
||||||
|
if not id then
|
||||||
|
id = parts[2]
|
||||||
|
if id == "all" then
|
||||||
|
id = 0
|
||||||
|
else
|
||||||
|
player:send_message(sender.."Error parsing id parameter. Must be a territory id or the word 'all'."..usage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
player:unlock_aetheryte(on, id)
|
||||||
|
player:send_message(string.format("%s Aetheryte(s) %s had their unlocked status changed!", sender, id))
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
inventory::Storage,
|
inventory::Storage,
|
||||||
ipc::zone::{ActorControlCategory, ActorControlSelf, ChatMessage, GameMasterRank},
|
ipc::zone::{ChatMessage, GameMasterRank},
|
||||||
world::ToServer,
|
world::ToServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,16 +44,6 @@ impl ChatHandler {
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
"!unlockaction" => {
|
|
||||||
let parts: Vec<&str> = chat_message.message.split(' ').collect();
|
|
||||||
let id = parts[1].parse::<u32>().unwrap();
|
|
||||||
|
|
||||||
connection
|
|
||||||
.actor_control_self(ActorControlSelf {
|
|
||||||
category: ActorControlCategory::ToggleActionUnlock { id, unlocked: true },
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
"!equip" => {
|
"!equip" => {
|
||||||
let (_, name) = chat_message.message.split_once(' ').unwrap();
|
let (_, name) = chat_message.message.split_once(' ').unwrap();
|
||||||
|
|
||||||
|
|
|
@ -568,6 +568,26 @@ impl ZoneConnection {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn toggle_invisibility(&mut self, invisible: bool) {
|
||||||
|
self.player_data.gm_invisible = invisible;
|
||||||
|
let ipc = ServerZoneIpcSegment {
|
||||||
|
op_code: ServerZoneIpcType::ActorControlSelf,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
||||||
|
category: ActorControlCategory::ToggleInvisibility { invisible },
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.send_segment(PacketSegment {
|
||||||
|
source_actor: self.player_data.actor_id,
|
||||||
|
target_actor: self.player_data.actor_id,
|
||||||
|
segment_type: SegmentType::Ipc,
|
||||||
|
data: SegmentData::Ipc { data: ipc },
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn process_lua_player(&mut self, player: &mut LuaPlayer) {
|
pub async fn process_lua_player(&mut self, player: &mut LuaPlayer) {
|
||||||
for segment in &player.queued_segments {
|
for segment in &player.queued_segments {
|
||||||
self.send_segment(segment.clone()).await;
|
self.send_segment(segment.clone()).await;
|
||||||
|
@ -596,6 +616,9 @@ impl ZoneConnection {
|
||||||
let mut lua = self.lua.lock().unwrap();
|
let mut lua = self.lua.lock().unwrap();
|
||||||
load_global_script(&mut lua);
|
load_global_script(&mut lua);
|
||||||
}
|
}
|
||||||
|
Task::ToggleInvisibility { invisible } => {
|
||||||
|
self.toggle_invisibility(*invisible).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
player.queued_tasks.clear();
|
player.queued_tasks.clear();
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub enum Task {
|
||||||
SetClassJob { classjob_id: u8 },
|
SetClassJob { classjob_id: u8 },
|
||||||
WarpAetheryte { aetheryte_id: u32 },
|
WarpAetheryte { aetheryte_id: u32 },
|
||||||
ReloadScripts,
|
ReloadScripts,
|
||||||
|
ToggleInvisibility { invisible: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -132,6 +133,58 @@ impl LuaPlayer {
|
||||||
self.create_segment_self(op_code, data);
|
self.create_segment_self(op_code, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unlock_action(&mut self, id: u32) {
|
||||||
|
let op_code = ServerZoneIpcType::ActorControlSelf;
|
||||||
|
let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
||||||
|
category: ActorControlCategory::ToggleActionUnlock { id, unlocked: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
self.create_segment_self(op_code, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_speed(&mut self, speed: u16) {
|
||||||
|
let op_code = ServerZoneIpcType::ActorControlSelf;
|
||||||
|
let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
||||||
|
category: ActorControlCategory::Flee { speed },
|
||||||
|
});
|
||||||
|
|
||||||
|
self.create_segment_self(op_code, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_wireframe(&mut self) {
|
||||||
|
let op_code = ServerZoneIpcType::ActorControlSelf;
|
||||||
|
let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
||||||
|
category: ActorControlCategory::ToggleWireframeRendering(),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.create_segment_self(op_code, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock_aetheryte(&mut self, unlocked: u32, id: u32) {
|
||||||
|
let op_code = ServerZoneIpcType::ActorControlSelf;
|
||||||
|
let on = unlocked == 0;
|
||||||
|
if id == 0 {
|
||||||
|
for i in 1..239 {
|
||||||
|
let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
||||||
|
category: ActorControlCategory::LearnTeleport {
|
||||||
|
id: i,
|
||||||
|
unlocked: on,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Unknown if this will make the server panic from a flood of packets.
|
||||||
|
* Needs testing once toggling aetherytes actually works. */
|
||||||
|
self.create_segment_self(op_code.clone(), data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
||||||
|
category: ActorControlCategory::LearnTeleport { id, unlocked: on },
|
||||||
|
});
|
||||||
|
|
||||||
|
self.create_segment_self(op_code, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn change_territory(&mut self, zone_id: u16) {
|
fn change_territory(&mut self, zone_id: u16) {
|
||||||
self.queued_tasks.push(Task::ChangeTerritory { zone_id });
|
self.queued_tasks.push(Task::ChangeTerritory { zone_id });
|
||||||
}
|
}
|
||||||
|
@ -163,6 +216,11 @@ impl LuaPlayer {
|
||||||
fn reload_scripts(&mut self) {
|
fn reload_scripts(&mut self) {
|
||||||
self.queued_tasks.push(Task::ReloadScripts);
|
self.queued_tasks.push(Task::ReloadScripts);
|
||||||
}
|
}
|
||||||
|
fn toggle_invisiblity(&mut self) {
|
||||||
|
self.queued_tasks.push(Task::ToggleInvisibility {
|
||||||
|
invisible: !self.player_data.gm_invisible,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserData for LuaPlayer {
|
impl UserData for LuaPlayer {
|
||||||
|
@ -201,6 +259,26 @@ impl UserData for LuaPlayer {
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
methods.add_method_mut("unlock_aetheryte", |_, this, (unlock, id): (u32, u32)| {
|
||||||
|
this.unlock_aetheryte(unlock, id);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_method_mut("unlock_action", |_, this, action_id: u32| {
|
||||||
|
this.unlock_action(action_id);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_method_mut("set_speed", |_, this, speed: u16| {
|
||||||
|
this.set_speed(speed);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_method_mut("toggle_wireframe", |_, this, _: Value| {
|
||||||
|
this.toggle_wireframe();
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_method_mut("toggle_invisibility", |_, this, _: Value| {
|
||||||
|
this.toggle_invisiblity();
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
methods.add_method_mut("change_territory", |_, this, zone_id: u16| {
|
methods.add_method_mut("change_territory", |_, this, zone_id: u16| {
|
||||||
this.change_territory(zone_id);
|
this.change_territory(zone_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Add table
Reference in a new issue