Implemented server side #who

This commit is contained in:
Akkadius 2019-01-21 00:29:58 -06:00
parent 7b2c87c7ed
commit 48564a5789
5 changed files with 728 additions and 479 deletions

View File

@ -3,8 +3,21 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50)
== 1/20/2019 == == 1/20/2019 ==
Uleat: Added 'spells' entry to EQDictionary Uleat: Added 'spells' entry to EQDictionary
Akkadius: Implement and extended #goto via #goto <player_name> - this will work cross zone, cross instance, in zone etc. Akkadius:
It works on top of the original #goto (target) and #goto x y z - [Command] Extended #goto via #goto <player_name> -
- This will work cross zone, cross instance, in zone etc.
- It works on top of the original #goto (target) and #goto x y z
- [Command] Implemented server side #who Example: https://bit.ly/2TZ2hvI
- Searches can be filtered by
- Account Name
- Base Class Name
- Guild Name
- IP
- Player Name
- Race Name
- Zone Short Name
- Features a clickable (Goto) saylink that will bring you directly to a player regardless of
whether or not they are in an instance
== 1/15/2019 == == 1/15/2019 ==
Uleat: Activated per-expansion support for active inventory slot addressing Uleat: Activated per-expansion support for active inventory slot addressing

View File

@ -18,230 +18,332 @@
#include "../common/global_define.h" #include "../common/global_define.h"
#include "../common/classes.h" #include "../common/classes.h"
const char* GetClassIDName(uint8 class_id, uint8 level) const char *GetClassIDName(uint8 class_id, uint8 level)
{ {
switch (class_id) { switch (class_id) {
case WARRIOR: case WARRIOR:
if (level >= 70) if (level >= 70) {
return "Vanquisher"; return "Vanquisher";
else if (level >= 65) }
else if (level >= 65) {
return "Overlord"; //Baron-Sprite: LEAVE MY CLASSES ALONE. return "Overlord"; //Baron-Sprite: LEAVE MY CLASSES ALONE.
else if (level >= 60) }
else if (level >= 60) {
return "Warlord"; return "Warlord";
else if (level >= 55) }
else if (level >= 55) {
return "Myrmidon"; return "Myrmidon";
else if (level >= 51) }
else if (level >= 51) {
return "Champion"; return "Champion";
else }
else {
return "Warrior"; return "Warrior";
}
case CLERIC: case CLERIC:
if (level >= 70) if (level >= 70) {
return "Prelate"; return "Prelate";
else if (level >= 65) }
else if (level >= 65) {
return "Archon"; return "Archon";
else if (level >= 60) }
else if (level >= 60) {
return "High Priest"; return "High Priest";
else if (level >= 55) }
else if (level >= 55) {
return "Templar"; return "Templar";
else if (level >= 51) }
else if (level >= 51) {
return "Vicar"; return "Vicar";
else }
else {
return "Cleric"; return "Cleric";
}
case PALADIN: case PALADIN:
if (level >= 70) if (level >= 70) {
return "Lord"; return "Lord";
else if (level >= 65) }
else if (level >= 65) {
return "Lord Protector"; return "Lord Protector";
else if (level >= 60) }
else if (level >= 60) {
return "Crusader"; return "Crusader";
else if (level >= 55) }
else if (level >= 55) {
return "Knight"; return "Knight";
else if (level >= 51) }
else if (level >= 51) {
return "Cavalier"; return "Cavalier";
else }
else {
return "Paladin"; return "Paladin";
}
case RANGER: case RANGER:
if (level >= 70) if (level >= 70) {
return "Plainswalker"; return "Plainswalker";
else if (level >= 65) }
else if (level >= 65) {
return "Forest Stalker"; return "Forest Stalker";
else if (level >= 60) }
else if (level >= 60) {
return "Warder"; return "Warder";
else if (level >= 55) }
else if (level >= 55) {
return "Outrider"; return "Outrider";
else if (level >= 51) }
else if (level >= 51) {
return "Pathfinder"; return "Pathfinder";
else }
else {
return "Ranger"; return "Ranger";
}
case SHADOWKNIGHT: case SHADOWKNIGHT:
if (level >= 70) if (level >= 70) {
return "Scourge Knight"; return "Scourge Knight";
else if (level >= 65) }
else if (level >= 65) {
return "Dread Lord"; return "Dread Lord";
else if (level >= 60) }
else if (level >= 60) {
return "Grave Lord"; return "Grave Lord";
else if (level >= 55) }
else if (level >= 55) {
return "Revenant"; return "Revenant";
else if (level >= 51) }
else if (level >= 51) {
return "Reaver"; return "Reaver";
else }
else {
return "Shadowknight"; return "Shadowknight";
}
case DRUID: case DRUID:
if (level >= 70) if (level >= 70) {
return "Natureguard"; return "Natureguard";
else if (level >= 65) }
else if (level >= 65) {
return "Storm Warden"; return "Storm Warden";
else if (level >= 60) }
else if (level >= 60) {
return "Hierophant"; return "Hierophant";
else if (level >= 55) }
else if (level >= 55) {
return "Preserver"; return "Preserver";
else if (level >= 51) }
else if (level >= 51) {
return "Wanderer"; return "Wanderer";
else }
else {
return "Druid"; return "Druid";
}
case MONK: case MONK:
if (level >= 70) if (level >= 70) {
return "Stone Fist"; return "Stone Fist";
else if (level >= 65) }
else if (level >= 65) {
return "Transcendent"; return "Transcendent";
else if (level >= 60) }
else if (level >= 60) {
return "Grandmaster"; return "Grandmaster";
else if (level >= 55) }
else if (level >= 55) {
return "Master"; return "Master";
else if (level >= 51) }
else if (level >= 51) {
return "Disciple"; return "Disciple";
else }
else {
return "Monk"; return "Monk";
}
case BARD: case BARD:
if (level >= 70) if (level >= 70) {
return "Performer"; return "Performer";
else if (level >= 65) }
else if (level >= 65) {
return "Maestro"; return "Maestro";
else if (level >= 60) }
else if (level >= 60) {
return "Virtuoso"; return "Virtuoso";
else if (level >= 55) }
else if (level >= 55) {
return "Troubadour"; return "Troubadour";
else if (level >= 51) }
else if (level >= 51) {
return "Minstrel"; return "Minstrel";
else }
else {
return "Bard"; return "Bard";
}
case ROGUE: case ROGUE:
if (level >= 70) if (level >= 70) {
return "Nemesis"; return "Nemesis";
else if (level >= 65) }
else if (level >= 65) {
return "Deceiver"; return "Deceiver";
else if (level >= 60) }
else if (level >= 60) {
return "Assassin"; return "Assassin";
else if (level >= 55) }
else if (level >= 55) {
return "Blackguard"; return "Blackguard";
else if (level >= 51) }
else if (level >= 51) {
return "Rake"; return "Rake";
else }
else {
return "Rogue"; return "Rogue";
}
case SHAMAN: case SHAMAN:
if (level >= 70) if (level >= 70) {
return "Soothsayer"; return "Soothsayer";
else if (level >= 65) }
else if (level >= 65) {
return "Prophet"; return "Prophet";
else if (level >= 60) }
else if (level >= 60) {
return "Oracle"; return "Oracle";
else if (level >= 55) }
else if (level >= 55) {
return "Luminary"; return "Luminary";
else if (level >= 51) }
else if (level >= 51) {
return "Mystic"; return "Mystic";
else }
else {
return "Shaman"; return "Shaman";
}
case NECROMANCER: case NECROMANCER:
if (level >= 70) if (level >= 70) {
return "Wraith"; return "Wraith";
else if (level >= 65) }
else if (level >= 65) {
return "Arch Lich"; return "Arch Lich";
else if (level >= 60) }
else if (level >= 60) {
return "Warlock"; return "Warlock";
else if (level >= 55) }
else if (level >= 55) {
return "Defiler"; return "Defiler";
else if (level >= 51) }
else if (level >= 51) {
return "Heretic"; return "Heretic";
else }
else {
return "Necromancer"; return "Necromancer";
}
case WIZARD: case WIZARD:
if (level >= 70) if (level >= 70) {
return "Grand Arcanist"; return "Grand Arcanist";
else if (level >= 65) }
else if (level >= 65) {
return "Arcanist"; return "Arcanist";
else if (level >= 60) }
else if (level >= 60) {
return "Sorcerer"; return "Sorcerer";
else if (level >= 55) }
else if (level >= 55) {
return "Evoker"; return "Evoker";
else if (level >= 51) }
else if (level >= 51) {
return "Channeler"; return "Channeler";
else }
else {
return "Wizard"; return "Wizard";
}
case MAGICIAN: case MAGICIAN:
if (level >= 70) if (level >= 70) {
return "Arch Magus"; return "Arch Magus";
else if (level >= 65) }
else if (level >= 65) {
return "Arch Convoker"; return "Arch Convoker";
else if (level >= 60) }
else if (level >= 60) {
return "Arch Mage"; return "Arch Mage";
else if (level >= 55) }
else if (level >= 55) {
return "Conjurer"; return "Conjurer";
if (level >= 51) }
if (level >= 51) {
return "Elementalist"; return "Elementalist";
else }
else {
return "Magician"; return "Magician";
}
case ENCHANTER: case ENCHANTER:
if (level >= 70) if (level >= 70) {
return "Bedazzler"; return "Bedazzler";
else if (level >= 65) }
else if (level >= 65) {
return "Coercer"; return "Coercer";
else if (level >= 60) }
else if (level >= 60) {
return "Phantasmist"; return "Phantasmist";
else if (level >= 55) }
else if (level >= 55) {
return "Beguiler"; return "Beguiler";
else if (level >= 51) }
else if (level >= 51) {
return "Illusionist"; return "Illusionist";
else }
else {
return "Enchanter"; return "Enchanter";
}
case BEASTLORD: case BEASTLORD:
if (level >= 70) if (level >= 70) {
return "Wildblood"; return "Wildblood";
else if (level >= 65) }
else if (level >= 65) {
return "Feral Lord"; return "Feral Lord";
else if (level >= 60) }
else if (level >= 60) {
return "Savage Lord"; return "Savage Lord";
else if (level >= 55) }
else if (level >= 55) {
return "Animist"; return "Animist";
else if (level >= 51) }
else if (level >= 51) {
return "Primalist"; return "Primalist";
else }
else {
return "Beastlord"; return "Beastlord";
}
case BERSERKER: case BERSERKER:
if (level >= 70) if (level >= 70) {
return "Ravager"; return "Ravager";
else if (level >= 65) }
else if (level >= 65) {
return "Fury"; return "Fury";
else if (level >= 60) }
else if (level >= 60) {
return "Rager"; return "Rager";
else if (level >= 55) }
else if (level >= 55) {
return "Vehement"; return "Vehement";
else if (level >= 51) }
else if (level >= 51) {
return "Brawler"; return "Brawler";
else }
else {
return "Berserker"; return "Berserker";
}
case BANKER: case BANKER:
if (level >= 70) if (level >= 70) {
return "Master Banker"; return "Master Banker";
else if (level >= 65) }
else if (level >= 65) {
return "Elder Banker"; return "Elder Banker";
else if (level >= 60) }
else if (level >= 60) {
return "Oldest Banker"; return "Oldest Banker";
else if (level >= 55) }
else if (level >= 55) {
return "Older Banker"; return "Older Banker";
else if (level >= 51) }
else if (level >= 51) {
return "Old Banker"; return "Old Banker";
else }
else {
return "Banker"; return "Banker";
}
case WARRIORGM: case WARRIORGM:
return "Warrior Guildmaster"; return "Warrior Guildmaster";
case CLERICGM: case CLERICGM:
@ -291,7 +393,7 @@ const char* GetClassIDName(uint8 class_id, uint8 level)
} }
} }
const char* GetPlayerClassName(uint32 player_class_value, uint8 level) const char *GetPlayerClassName(uint32 player_class_value, uint8 level)
{ {
return GetClassIDName(GetClassIDFromPlayerClassValue(player_class_value), level); return GetClassIDName(GetClassIDFromPlayerClassValue(player_class_value), level);
} }

View File

@ -809,6 +809,16 @@ void Client::CompleteConnect()
std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc);
} }
/**
* Update last login since this doesn't get updated until a late save later so we can update online status
*/
database.QueryDatabase(
StringFormat(
"UPDATE `character_data` SET `last_login` = UNIX_TIMESTAMP() WHERE id = %u",
this->CharacterID()
)
);
} }
if (zone) { if (zone) {

View File

@ -413,6 +413,7 @@ int command_init(void)
command_add("viewpetition", "[petition number] - View a petition", 20, command_viewpetition) || command_add("viewpetition", "[petition number] - View a petition", 20, command_viewpetition) ||
command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", 200, command_wc) || command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", 200, command_wc) ||
command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", 80, command_weather) || command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", 80, command_weather) ||
command_add("who", "[search]", 20, command_who) ||
command_add("worldshutdown", "- Shut down world and all zones", 200, command_worldshutdown) || command_add("worldshutdown", "- Shut down world and all zones", 200, command_worldshutdown) ||
command_add("wp", "[add/delete] [grid_num] [pause] [wp_num] [-h] - Add/delete a waypoint to/from a wandering grid", 170, command_wp) || command_add("wp", "[add/delete] [grid_num] [pause] [wp_num] [-h] - Add/delete a waypoint to/from a wandering grid", 170, command_wp) ||
command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path", 170, command_wpadd) || command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path", 170, command_wpadd) ||
@ -2262,10 +2263,12 @@ void command_sendzonespawns(Client *c, const Seperator *sep)
void command_zsave(Client *c, const Seperator *sep) void command_zsave(Client *c, const Seperator *sep)
{ {
if(zone->SaveZoneCFG()) if (zone->SaveZoneCFG()) {
c->Message(13, "Zone header saved successfully."); c->Message(13, "Zone header saved successfully.");
else }
else {
c->Message(13, "ERROR: Zone header data was NOT saved."); c->Message(13, "ERROR: Zone header data was NOT saved.");
}
} }
void command_dbspawn2(Client *c, const Seperator *sep) void command_dbspawn2(Client *c, const Seperator *sep)
@ -11837,6 +11840,126 @@ void command_scale(Client *c, const Seperator *sep)
} }
} }
void command_who(Client *c, const Seperator *sep)
{
std::string query =
"SELECT\n"
" character_data.account_id,\n"
" character_data.name,\n"
" character_data.zone_id,\n"
" COALESCE((select zone.short_name from zone where zoneidnumber = character_data.zone_id LIMIT 1), \"Not Found\") as zone_name,\n"
" character_data.zone_instance,\n"
" COALESCE((select guilds.name from guilds where id = ((select guild_id from guild_members where char_id = character_data.id))), \"\") as guild_name,\n"
" character_data.level,\n"
" character_data.race,\n"
" character_data.class,\n"
" COALESCE((select account.status from account where account.id = character_data.account_id LIMIT 1), 0) as account_status,\n"
" COALESCE((select account.name from account where account.id = character_data.account_id LIMIT 1), \"\") as account_name,\n"
" COALESCE((select account_ip.ip from account_ip where account_ip.accid = character_data.account_id ORDER BY account_ip.lastused DESC LIMIT 1), \"\") as account_ip\n"
"FROM\n"
" character_data\n"
"WHERE\n"
" last_login > (UNIX_TIMESTAMP() - 600)\n"
"ORDER BY character_data.name;";
auto results = database.QueryDatabase(query);
if (!results.Success())
return;
if (results.RowCount() == 0) {
c->Message(15, "No results found");
return;
}
std::string search_string;
if (sep->arg[1]) {
search_string = str_tolower(sep->arg[1]);
}
int found_count = 0;
c->Message(5, "Players in EverQuest");
c->Message(5, "--------------------");
for (auto row = results.begin(); row != results.end(); ++row) {
auto account_id = static_cast<uint32>(atoi(row[0]));
std::string player_name = row[1];
auto zone_id = static_cast<uint32>(atoi(row[2]));
std::string zone_short_name = row[3];
auto zone_instance = static_cast<uint32>(atoi(row[4]));
std::string guild_name = row[5];
auto player_level = static_cast<uint32>(atoi(row[6]));
auto player_race = static_cast<uint32>(atoi(row[7]));
auto player_class = static_cast<uint32>(atoi(row[8]));
auto account_status = static_cast<uint32>(atoi(row[9]));
std::string account_name = row[10];
std::string account_ip = row[11];
std::string base_class_name = GetClassIDName(static_cast<uint8>(player_class), 1);
std::string displayed_race_name = GetRaceIDName(static_cast<uint16>(player_race));
if (search_string.length() > 0) {
bool found_search_term =
(
str_tolower(player_name).find(search_string) != std::string::npos ||
str_tolower(zone_short_name).find(search_string) != std::string::npos ||
str_tolower(displayed_race_name).find(search_string) != std::string::npos ||
str_tolower(base_class_name).find(search_string) != std::string::npos ||
str_tolower(guild_name).find(search_string) != std::string::npos ||
str_tolower(account_name).find(search_string) != std::string::npos ||
str_tolower(account_ip).find(search_string) != std::string::npos
);
if (!found_search_term) {
continue;
}
}
std::string displayed_guild_name;
if (guild_name.length() > 0) {
displayed_guild_name = EQEmu::SayLinkEngine::GenerateQuestSaylink(
StringFormat(
"#who \"%s\"",
guild_name.c_str()),
false,
StringFormat("<%s>", guild_name.c_str())
);
}
std::string goto_saylink = EQEmu::SayLinkEngine::GenerateQuestSaylink(
StringFormat("#goto %s", player_name.c_str()), false, "Goto"
);
std::string display_class_name = GetClassIDName(static_cast<uint8>(player_class), static_cast<uint8>(player_level));
c->Message(
5, "%s[%u %s] %s (%s) %s ZONE: %s (%u) (%s) (%s) (%s)",
(account_status > 0 ? "* GM * " : ""),
player_level,
EQEmu::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", base_class_name.c_str()), false, display_class_name).c_str(),
player_name.c_str(),
EQEmu::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", displayed_race_name.c_str()), false, displayed_race_name).c_str(),
displayed_guild_name.c_str(),
EQEmu::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", zone_short_name.c_str()), false, zone_short_name).c_str(),
zone_instance,
goto_saylink.c_str(),
EQEmu::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", account_name.c_str()), false, account_name).c_str(),
EQEmu::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", account_ip.c_str()), false, account_ip).c_str()
);
found_count++;
}
std::string message = (
found_count > 0 ?
StringFormat("There is %i player(s) in EverQuest", found_count).c_str() :
"There are no players in EverQuest that match those who filters."
);
c->Message(5, message.c_str());
}
// All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block.
#ifdef BOTS #ifdef BOTS
#include "bot_command.h" #include "bot_command.h"

View File

@ -325,6 +325,7 @@ void command_viewnpctype(Client *c, const Seperator *sep);
void command_viewpetition(Client *c, const Seperator *sep); void command_viewpetition(Client *c, const Seperator *sep);
void command_wc(Client *c, const Seperator *sep); void command_wc(Client *c, const Seperator *sep);
void command_weather(Client *c, const Seperator *sep); void command_weather(Client *c, const Seperator *sep);
void command_who(Client *c, const Seperator *sep);
void command_worldshutdown(Client *c, const Seperator *sep); void command_worldshutdown(Client *c, const Seperator *sep);
void command_wp(Client *c, const Seperator *sep); void command_wp(Client *c, const Seperator *sep);
void command_wpadd(Client *c, const Seperator *sep); void command_wpadd(Client *c, const Seperator *sep);