Skip to content

Commit

Permalink
[Telnet] Add cross zone/world wide cast and move functionality to Tel…
Browse files Browse the repository at this point in the history
…net (#3409)

* [Telnet] Add cross zone/world wide cast and move functionality to Telnet

# Notes
- Add `czcast`, `czmove`, `wwcast`, and `wwmove` to Telnet functionality.
- Allows operators to cast spells across zone/world-wide and move players across zone/world-wide from telnet.

* Update console.cpp

* Update console.cpp

* Validation

* Update console.cpp
  • Loading branch information
Kinglykrab authored Jun 17, 2023
1 parent 1100668 commit 57243c6
Showing 1 changed file with 353 additions and 2 deletions.
355 changes: 353 additions & 2 deletions world/console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,352 @@ void ConsoleQuit(
connection->Close();
}

void ConsoleCrossZoneCastSpell(
EQ::Net::ConsoleServerConnection *connection,
const std::string &command,
const std::vector<std::string> &args
) {
if (args.size() < 3) {
connection->SendLine("czcast character [character_id] [spell_id]");
connection->SendLine("czcast expedition [expedition_id] [spell_id]");
connection->SendLine("czcast group [group_id] [spell_id]");
connection->SendLine("czcast guild [guild_id] [spell_id]");
connection->SendLine("czcast name [character_name] [spell_id]");
connection->SendLine("czcast raid [raid_id] [spell_id]");
return;
}

const auto& type = Strings::ToLower(args[0]);

const auto is_character = type == "character";
const auto is_expedition = type == "expedition";
const auto is_group = type == "group";
const auto is_guild = type == "guild";
const auto is_name = type == "name";
const auto is_raid = type == "raid";

if (
!is_character &&
!is_expedition &&
!is_group &&
!is_guild &&
!is_name &
!is_raid
) {
connection->SendLine("czcast character [character_id] [spell_id]");
connection->SendLine("czcast expedition [expedition_id] [spell_id]");
connection->SendLine("czcast group [group_id] [spell_id]");
connection->SendLine("czcast guild [guild_id] [spell_id]");
connection->SendLine("czcast name [character_name] [spell_id]");
connection->SendLine("czcast raid [raid_id] [spell_id]");
return;
}

std::string name;
int update_identifier = 0;

if (!is_name) {
if (Strings::IsNumber(args[1])) {
update_identifier = Strings::ToInt(args[1]);
}

if (!update_identifier) {
connection->SendLine(fmt::format("Identifier is invalid for '{}'.", type));
return;
}
} else if (is_name) {
if (!Strings::IsNumber(args[1])) {
name = Strings::UcFirst(Strings::ToLower(args[1]));
}

if (name.empty()) {
connection->SendLine("Empty name is invalid.");
return;
}
}

const auto spell_id = Strings::IsNumber(args[2]) ? Strings::ToUnsignedInt(args[2]) : 0;

if (!spell_id) {
connection->SendLine("Spell ID is invalid.");
return;
}

uint8 update_type;

if (is_character) {
update_type = CZUpdateType_Character;
} else if (is_expedition) {
update_type = CZUpdateType_Expedition;
} else if (is_group) {
update_type = CZUpdateType_Group;
} else if (is_guild) {
update_type = CZUpdateType_Guild;
} else if (is_name) {
update_type = CZUpdateType_ClientName;
} else if (is_raid) {
update_type = CZUpdateType_Raid;
}

auto pack = new ServerPacket(ServerOP_CZSpell, sizeof(CZSpell_Struct));
auto* CZS = (CZSpell_Struct*) pack->pBuffer;

CZS->update_type = update_type;
CZS->update_subtype = CZSpellUpdateSubtype_Cast;
CZS->update_identifier = update_identifier;
CZS->spell_id = spell_id;

strn0cpy(CZS->client_name, name.c_str(), sizeof(CZS->client_name));

zoneserver_list.SendPacket(pack);
safe_delete(pack);

connection->SendLine(
fmt::format(
"Casting spell ID {} across zones by {} with an identifier of {}.",
spell_id,
type,
!is_name ? std::to_string(update_identifier) : name
)
);
}

void ConsoleWorldWideCastSpell(
EQ::Net::ConsoleServerConnection *connection,
const std::string &command,
const std::vector<std::string> &args
) {
if (args.size() < 1) {
connection->SendLine("wwcast [spell_id]");
connection->SendLine("wwcast [spell_id] [min_status]");
connection->SendLine("wwcast [spell_id] [min_status] [max_status]");
return;
}

const auto spell_id = Strings::IsNumber(args[0]) ? Strings::ToUnsignedInt(args[0]) : 0;

if (!spell_id) {
connection->SendLine("Spell ID 0 is invalid.");
return;
}

uint8 min_status = AccountStatus::Player;
uint8 max_status = AccountStatus::Player;

if (args.size() >= 2 && Strings::IsNumber(args[1])) {
min_status = static_cast<uint8>(Strings::ToUnsignedInt(args[1]));
}

if (args.size() >= 3 && Strings::IsNumber(args[2])) {
max_status = static_cast<uint8>(Strings::ToUnsignedInt(args[2]));
}

auto pack = new ServerPacket(ServerOP_WWSpell, sizeof(WWSpell_Struct));
auto* WWS = (WWSpell_Struct*) pack->pBuffer;

WWS->update_type = WWSpellUpdateType_Cast;
WWS->spell_id = spell_id;
WWS->min_status = min_status;
WWS->max_status = max_status;

zoneserver_list.SendPacket(pack);
safe_delete(pack);

connection->SendLine(
fmt::format(
"Casting spell ID {} world wide for players with a status between {} and {}.",
spell_id,
min_status,
max_status
)
);
}

void ConsoleCrossZoneMove(
EQ::Net::ConsoleServerConnection *connection,
const std::string &command,
const std::vector<std::string> &args
) {
if (args.size() < 3) {
connection->SendLine("czmove character [character_id] [instance_id]");
connection->SendLine("czmove character [character_id] [zone_short_name]");
connection->SendLine("czmove expedition [expedition_id] [instance_id]");
connection->SendLine("czmove expedition [expedition_id] [zone_short_name]");
connection->SendLine("czmove group [group_id] [instance_id]");
connection->SendLine("czmove group [group_id] [zone_short_name]");
connection->SendLine("czmove guild [guild_id] [instance_id]");
connection->SendLine("czmove guild [guild_id] [zone_short_name]");
connection->SendLine("czmove name [character_name] [instance_id]");
connection->SendLine("czmove name [character_name] [zone_short_name]");
connection->SendLine("czmove raid [raid_id] [instance_id]");
connection->SendLine("czmove raid [raid_id] [zone_short_name]");
return;
}

const auto& type = Strings::ToLower(args[0]);

const auto is_character = type == "character";
const auto is_expedition = type == "expedition";
const auto is_group = type == "group";
const auto is_guild = type == "guild";
const auto is_name = type == "name";
const auto is_raid = type == "raid";

if (
!is_character &&
!is_expedition &&
!is_group &&
!is_guild &&
!is_name &
!is_raid
) {
connection->SendLine("czmove character [character_id] [instance_id]");
connection->SendLine("czmove character [character_id] [zone_short_name]");
connection->SendLine("czmove expedition [expedition_id] [instance_id]");
connection->SendLine("czmove expedition [expedition_id] [zone_short_name]");
connection->SendLine("czmove group [group_id] [instance_id]");
connection->SendLine("czmove group [group_id] [zone_short_name]");
connection->SendLine("czmove guild [guild_id] [instance_id]");
connection->SendLine("czmove guild [guild_id] [zone_short_name]");
connection->SendLine("czmove name [character_name] [instance_id]");
connection->SendLine("czmove name [character_name] [zone_short_name]");
connection->SendLine("czmove raid [raid_id] [instance_id]");
connection->SendLine("czmove raid [raid_id] [zone_short_name]");
return;
}

std::string name;
int update_identifier = 0;

if (!is_name) {
if (Strings::IsNumber(args[1])) {
update_identifier = Strings::ToInt(args[1]);
}

if (!update_identifier) {
connection->SendLine(fmt::format("Identifier invalid for '{}'.", type));
return;
}
} else if (is_name) {
if (!Strings::IsNumber(args[1])) {
name = Strings::UcFirst(Strings::ToLower(args[1]));
}

if (name.empty()) {
connection->SendLine("Empty name is invalid.");
return;
}
}

const auto& zone_short_name = !Strings::IsNumber(args[2]) ? args[2] : "";
const uint16 instance_id = Strings::IsNumber(args[2]) ? static_cast<uint16>(Strings::ToUnsignedInt(args[2])) : 0;

const auto& z = !zone_short_name.empty() ? zone_store.GetZone(zone_short_name) : nullptr;

if (z && !z->id) {
connection->SendLine(fmt::format("No zone with the short name '{}' exists.", zone_short_name));
return;
}

uint8 update_type;

if (is_character) {
update_type = CZUpdateType_Character;
} else if (is_expedition) {
update_type = CZUpdateType_Expedition;
} else if (is_group) {
update_type = CZUpdateType_Group;
} else if (is_guild) {
update_type = CZUpdateType_Guild;
} else if (is_name) {
update_type = CZUpdateType_ClientName;
} else if (is_raid) {
update_type = CZUpdateType_Raid;
}

auto pack = new ServerPacket(ServerOP_CZMove, sizeof(CZMove_Struct));
auto* CZM = (CZMove_Struct*) pack->pBuffer;

CZM->update_type = update_type;
CZM->update_subtype = !instance_id ? CZMoveUpdateSubtype_MoveZone : CZMoveUpdateSubtype_MoveZoneInstance;
CZM->update_identifier = update_identifier;
CZM->instance_id = instance_id;

strn0cpy(CZM->zone_short_name, zone_short_name.c_str(), sizeof(CZM->zone_short_name));
strn0cpy(CZM->client_name, name.c_str(), sizeof(CZM->client_name));

zoneserver_list.SendPacket(pack);
safe_delete(pack);

connection->SendLine(
fmt::format(
"Moving player(s) to {} by {} with an identifier of {}.",
!instance_id ? fmt::format("{} ({})", z->long_name, z->short_name) : fmt::format("Instance ID {}", instance_id),
type,
!is_name ? std::to_string(update_identifier) : name
)
);
}

void ConsoleWorldWideMove(
EQ::Net::ConsoleServerConnection *connection,
const std::string &command,
const std::vector<std::string> &args
) {
if (args.size() < 1) {
connection->SendLine("wwmove [instance_id]");
connection->SendLine("wwmove [instance_id] [min_status]");
connection->SendLine("wwmove [instance_id] [min_status] [max_status]");
connection->SendLine("wwmove [zone_short_name]");
connection->SendLine("wwmove [zone_short_name] [min_status]");
connection->SendLine("wwmove [zone_short_name] [min_status] [max_status]");
return;
}
\
const auto& zone_short_name = !Strings::IsNumber(args[2]) ? args[2] : "";
const uint16 instance_id = Strings::IsNumber(args[2]) ? static_cast<uint16>(Strings::ToUnsignedInt(args[2])) : 0;

const auto& z = !zone_short_name.empty() ? zone_store.GetZone(zone_short_name) : nullptr;

if (z && !z->id) {
connection->SendLine(fmt::format("No zone with the short name '{}' exists.", zone_short_name));
return;
}

uint8 min_status = AccountStatus::Player;
uint8 max_status = AccountStatus::Player;

if (args.size() >= 2 && Strings::IsNumber(args[1])) {
min_status = static_cast<uint8>(Strings::ToUnsignedInt(args[1]));
}

if (args.size() >= 3 && Strings::IsNumber(args[2])) {
max_status = static_cast<uint8>(Strings::ToUnsignedInt(args[2]));
}

auto pack = new ServerPacket(ServerOP_WWMove, sizeof(WWMove_Struct));
auto* WWM = (WWMove_Struct*) pack->pBuffer;

WWM->update_type = !instance_id ? WWMoveUpdateType_MoveZone : WWMoveUpdateType_MoveZoneInstance;
WWM->instance_id = instance_id;
WWM->min_status = min_status;
WWM->max_status = max_status;

strn0cpy(WWM->zone_short_name, zone_short_name.c_str(), sizeof(WWM->zone_short_name));

zoneserver_list.SendPacket(pack);
safe_delete(pack);

connection->SendLine(
fmt::format(
"Moving player(s) to {} for players with a status between {} and {}.",
!instance_id ? fmt::format("{} ({})", z->long_name, z->short_name) : fmt::format("Instance ID {}", instance_id),
min_status,
max_status
)
);
}


/**
* @param console
Expand All @@ -973,17 +1319,20 @@ void RegisterConsoleFunctions(std::unique_ptr<EQ::Net::ConsoleServer>& console)
console->RegisterCall("api", 200, "api", std::bind(ConsoleApi, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("auction", 50, "auction [message]", std::bind(ConsoleAuction, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("broadcast", 50, "broadcast [message]", std::bind(ConsoleBroadcast, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("czcast", 50, "czcast [type] [identifier] [spell_id]", std::bind(ConsoleCrossZoneCastSpell, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("czmove", 50, "czmove [type] [identifier] [instance_id|zone_short_name] - instance_id and zone_short_name are interchangeable", std::bind(ConsoleCrossZoneMove, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("echo", 50, "echo [on/off]", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("emote", 50, "emote [zonename or charname or world] [type] [message]", std::bind(ConsoleEmote, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("flag", 200, "flag [status] [accountname]", std::bind(ConsoleFlag, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("gmsay", 50, "gmsay [message]", std::bind(ConsoleGMSay, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("guildsay", 50, "guildsay [Character Name] [Guild ID] [Message]", std::bind(ConsoleGuildSay, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("iplookup", 50, "IPLookup [name]", std::bind(ConsoleIpLookup, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("iplookup", 50, "iplookup [name]", std::bind(ConsoleIpLookup, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("kick", 150, "kick [charname]", std::bind(ConsoleKick, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("lock", 150, "lock", std::bind(ConsoleLock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("lsreconnect", 50, "LSReconnect", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("md5", 50, "md5", std::bind(ConsoleMd5, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("ooc", 50, "ooc [message]", std::bind(ConsoleOOC, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("ping", 50, "ping", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("reloadworld", 200, "reloadworld", std::bind(ConsoleReloadWorld, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("reloadzonequests", 200, "reloadzonequests [zone_short_name]", std::bind(ConsoleReloadZoneQuests, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("setpass", 200, "setpass [account_name] [new_password]", std::bind(ConsoleSetPass, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
Expand All @@ -995,10 +1344,12 @@ void RegisterConsoleFunctions(std::unique_ptr<EQ::Net::ConsoleServer>& console)
console->RegisterCall("who", 50, "who", std::bind(ConsoleWho, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("whoami", 50, "whoami", std::bind(ConsoleWhoami, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("worldshutdown", 200, "worldshutdown", std::bind(ConsoleWorldShutdown, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("wwcast", 50, "wwcast [spell_id] [min_status] [max_status] - min_status and max_status are optional", std::bind(ConsoleWorldWideCastSpell, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("wwmove", 50, "wwmove [instance_id|zone_short_name] [min_status] [max_status] - min_status and max_status are optional, instance_id and zone_short_name are interchangeable", std::bind(ConsoleWorldWideMove, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("zonebootup", 150, "zonebootup [zone_server_id] [zone_short_name]", std::bind(ConsoleZoneBootup, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("zonelock", 150, "zonelock [list|lock|unlock] [zone_short_name]", std::bind(ConsoleZoneLock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("zoneshutdown", 150, "zoneshutdown [zone_short_name or zone_server_id]", std::bind(ConsoleZoneShutdown, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("zonestatus", 50, "zonestatus", std::bind(ConsoleZoneStatus, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));console->RegisterCall("ping", 50, "ping", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("zonestatus", 50, "zonestatus", std::bind(ConsoleZoneStatus, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("quit", 50, "quit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
console->RegisterCall("exit", 50, "exit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}

0 comments on commit 57243c6

Please sign in to comment.