Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Jun 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}