eqemu-server/queryserv/lfguild.cpp
Mitch Freeman 91f5932c6d
[Feature] Add RoF2 Guild features (#3699)
* [Feature] Add additional Guild Features

This adds the following guild features and design pattern
- the existing guild system was used
- guild features are based on RoF2 within source with translaters used to converted between client differences
- backward compatible with Ti and UF, and allows for mixed client servers
- Guild Back for Ti and UF is based on RoF2 Permissions for banking if Guild Leader does not use Ti/UF
- Guild Ranks and Permissions are enabled.
- Guild Tributes are enabled.
- Event logging via rules for donating tribute items and plat
- Rules to limit Guild Tributes based on max level of server
- Rewrote guild communications to client using specific opcodes
-- Server no longer sends a guild member list on each zone
-- Guild window is updated when a member levels, rank changes, zone changes, banker/alt status using individual opcodes
-- When a member is removed or added to a guild, a single opcode is sent to each guild member
-- This reduces network traffic considerably

Known issues:
- Visual bug only. Guild Tributes window will display a 0 for level if tribute is above max level rule setting.
- Visual bug only. Guild Mgmt Window will not display an online member if the player has 'show offline' unchecked and a guild member zones within the Notes/Tribute tab.  This is resolved by selecting and de-selecting the 'Show Offline' checkbox.

* Updated RoF2 Guild Comms

Updated RoF2 Guild Comms
Update RoF2 Opcodes
Rewrote RoF2 Guild Communications using specific opcodes.
Added database changes - they are irreversible

* Formatting

* Update base_guild_members_repository.h

* Format GuildInfo

* Format GuildAction enum

* Formatting in clientlist

* quantity vs quantity

* desc vs description

* Format structs

* Inline struct values

* Formatting

* Formatting

* Formatting fixes

* Formatting items

* Formatting

* Formatting

* struct formatting updates

* Updated formatting

* Updated
- std:string items
- naming conventions
- magic numbers

* Repo refactors
Other formatting updates

* Remove test guild commands

* Updated #guild info command

* Add new repo methods for Neckolla ReplaceOne and ReplaceMany

* Fix guild_tributes repo

* Update database_update_manifest.cpp

* Phase 1 of final testing with RoF2 -> RoF2.
Next phase will be inter compatibility review

* Remove #guild testing commands

* Fix uf translator error
Rewrite LoadGuilds

* Use extended repository

* FIx guild window on member add

* LoadGuild Changes

* Update guild_base.cpp

* Few small fixes for display issue with UF

* Update guild_base.cpp

* Update guild_members_repository.h

* Update zoneserver.cpp

* Update guild.cpp

* Update entity.h

* Switch formatting

* Formatting

* Update worldserver.cpp

* Switch formatting

* Formatting switch statement

* Update guild.cpp

* Formatting in guild_base

* We don't need to validate m_db everywhere

* More formatting / spacing issues

* Switch format

* Update guild_base.cpp

* Fix an UF issue displaying incorrect guildtag as <>

* Updated several constants, fixed a few issues with Ti/UF and guild tributes not being removed or sent when a member is removed/disbands from a guild.

* Formatting and logging updates

* Fix for Loadguilds and permissions after repo updates.

* Cleanup unnecessary m_db checks

* Updated logging to use player_event_logs

* Updated to use the single opcodes for guild traffic for Ti/UF/RoF2.  Several enhancements for guild functionality for more reusable code and readability.

* Update to fix Demote Self and guild invites declining when option set to not accept guild invites

* Potential fix for guild notes/tribute display issues when client has 'Show Offline' unchecked.

* Updates to fox recent master changes

Updates to fix recent master changes

* Updates in response to comments

* Further Updates in response to comments

* Comment updates and refactor for SendAppearance functions

* Comment updates

* Update client spawn process for show guild name

Add show guild tag to default spawn process

* Update to use zone spawn packets for RoF2
Removed several unused functions as a result
Updated MemberRankUpdate to properly update guild_show on rank change.
Updated OP_GuildURLAndChannel opcode for UF/RoF2

* Cleanup of world changes
Created function for repetitive zonelist sendpackets to only booted zones
Re-Inserted accidental delete of scanclosemobs

* Fixes

* Further world cleanup

* Fix a few test guild bank cases for backward compat
Removed a duplicate db call
Fixed a fallthrough issue

* Update guild_mgr.cpp

* Cleanup

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2024-02-10 03:27:58 -06:00

406 lines
12 KiB
C++

#include <stdlib.h>
#include "lfguild.h"
#include "database.h"
#include "worldserver.h"
#include "../common/eqemu_logsys.h"
#include "../common/strings.h"
#include "../common/packet_dump.h"
#include "../common/rulesys.h"
extern WorldServer *worldserver;
extern QSDatabase database;
PlayerLookingForGuild::PlayerLookingForGuild(char *name, char *comments, uint32 level, uint32 classes, uint32 aa_count, uint32 time_zone, uint32 time_posted)
{
Name = std::string(name);
Comments = std::string(comments);
Level = level;
Class = classes;
AACount = aa_count;
TimeZone = time_zone;
TimePosted = time_posted;
}
GuildLookingForPlayers::GuildLookingForPlayers(char * name, char * comments, uint32 from_level, uint32 to_level, uint32 classes, uint32 aa_count, uint32 time_zone, uint32 time_posted)
{
Name = std::string(name);
Comments = std::string(comments);
FromLevel = from_level;
ToLevel = to_level;
Classes = classes;
AACount = aa_count;
TimeZone = time_zone;
TimePosted = time_posted;
}
bool LFGuildManager::LoadDatabase()
{
std::string query = "SELECT `type`,`name`,`comment`, "
"`fromlevel`, `tolevel`, `classes`, "
"`aacount`, `timezone`, `timeposted` FROM `lfguild`";
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 type = Strings::ToUnsignedInt(row[0]);
if(type == 0)
{
PlayerLookingForGuild p(row[1], row[2], Strings::ToUnsignedInt(row[3]), Strings::ToUnsignedInt(row[5]), Strings::ToUnsignedInt(row[6]), Strings::ToUnsignedInt(row[7]), Strings::ToUnsignedInt(row[8]));
Players.push_back(p);
continue;
}
GuildLookingForPlayers g(row[1], row[2], Strings::ToUnsignedInt(row[3]), Strings::ToUnsignedInt(row[4]), Strings::ToUnsignedInt(row[5]), Strings::ToUnsignedInt(row[6]), Strings::ToUnsignedInt(row[7]), Strings::ToUnsignedInt(row[8]));
Guilds.push_back(g);
}
return true;
}
void LFGuildManager::HandlePacket(ServerPacket *pack)
{
char From[64];
pack->SetReadPosition(0);
uint32 FromZoneID = pack->ReadUInt32();
uint32 FromInstanceID = pack->ReadUInt32();
pack->ReadString(From);
pack->ReadSkipBytes(4);
uint32 SubType = pack->ReadUInt32();
switch(SubType)
{
case QSG_LFGuild_PlayerMatches:
{
uint32 FromLevel = pack->ReadUInt32();
uint32 ToLevel = pack->ReadUInt32();
uint32 MinAA = pack->ReadUInt32();
uint32 TimeZone = pack->ReadUInt32();
uint32 Classes = pack->ReadUInt32();
SendPlayerMatches(FromZoneID, FromInstanceID, From, FromLevel, ToLevel, MinAA, TimeZone, Classes);
break;
}
case QSG_LFGuild_UpdatePlayerInfo:
{
char Comments[257];
uint32 Class = pack->ReadUInt32();
uint32 Level = pack->ReadUInt32();
uint32 AAPoints = pack->ReadUInt32();
pack->ReadString(Comments);
uint32 Toggle = pack->ReadUInt32();
uint32 TimeZone = pack->ReadUInt32();
TogglePlayer(FromZoneID, FromInstanceID, From, Class, Level, AAPoints, Comments, Toggle, TimeZone);
break;
}
case QSG_LFGuild_RequestPlayerInfo:
{
SendPlayerStatus(FromZoneID, FromInstanceID, From);
break;
}
case QSG_LFGuild_UpdateGuildInfo:
{
char GuildName[33], Comments[257];
pack->ReadString(GuildName);
pack->ReadString(Comments);
uint32 FromLevel = pack->ReadUInt32();
uint32 ToLevel = pack->ReadUInt32();
uint32 Classes = pack->ReadUInt32();
uint32 AACount = pack->ReadUInt32();
uint32 Toggle = pack->ReadUInt32();
uint32 TimeZone = pack->ReadUInt32();
ToggleGuild(FromZoneID, FromInstanceID, From, GuildName, Comments, FromLevel, ToLevel, Classes, AACount, Toggle, TimeZone);
break;
}
case QSG_LFGuild_GuildMatches:
{
uint32 Level = pack->ReadUInt32();
uint32 AAPoints = pack->ReadUInt32();
uint32 TimeZone = pack->ReadUInt32();
uint32 Class = pack->ReadUInt32();
SendGuildMatches(FromZoneID, FromInstanceID, From, Level, AAPoints, TimeZone, Class);
break;
}
case QSG_LFGuild_RequestGuildInfo:
{
char GuildName[33];
pack->ReadString(GuildName);
SendGuildStatus(FromZoneID, FromInstanceID, From, GuildName);
break;
}
default:
break;
}
}
void LFGuildManager::SendPlayerMatches(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 FromLevel, uint32 ToLevel, uint32 MinAA, uint32 TimeZone, uint32 Classes)
{
std::list<PlayerLookingForGuild>::iterator it;
std::list<PlayerLookingForGuild> Matches;
uint32 PacketSize = strlen(From) + 21, NumberOfMatches = 0;
for(it = Players.begin(); it != Players.end(); ++it)
{
uint32 bitmask = 1 << (*it).Class ;
if(((*it).Level >= FromLevel) && ((*it).Level <= ToLevel) && ((*it).AACount >= MinAA) && (bitmask & Classes) && ((TimeZone == 0xFFFFFFFF) || (TimeZone == (*it).TimeZone)))
{
++NumberOfMatches;
Matches.push_back(*it);
PacketSize += (*it).Name.length() + (*it).Comments.length() + 18;
}
}
auto pack = new ServerPacket(ServerOP_QueryServGeneric, PacketSize);
pack->WriteUInt32(FromZoneID);
pack->WriteUInt32(FromInstanceID);
pack->WriteString(From);
pack->WriteUInt32(QSG_LFGuild);
pack->WriteUInt32(QSG_LFGuild_PlayerMatches);
pack->WriteUInt32(NumberOfMatches);
for(it = Matches.begin(); it != Matches.end(); ++it)
{
pack->WriteString((*it).Name.c_str());
pack->WriteString((*it).Comments.c_str());
pack->WriteUInt32((*it).Level);
pack->WriteUInt32((*it).Class);
pack->WriteUInt32((*it).AACount);
pack->WriteUInt32((*it).TimeZone);
}
worldserver->SendPacket(pack);
safe_delete(pack);
}
void LFGuildManager::SendGuildMatches(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 Level, uint32 AAPoints, uint32 TimeZone, uint32 Class)
{
std::list<GuildLookingForPlayers>::iterator it;
std::list<GuildLookingForPlayers> Matches;
uint32 bitmask = 1 << Class ;
uint32 PacketSize = strlen(From) + 21, NumberOfMatches = 0;
for(it = Guilds.begin(); it != Guilds.end(); ++it)
{
if((Level >= (*it).FromLevel) && (Level <= (*it).ToLevel) && (AAPoints >= (*it).AACount) && (bitmask & (*it).Classes) && (((*it).TimeZone == 0xFFFFFFFF) || (TimeZone == 0xFFFFFFFF) || (TimeZone == (*it).TimeZone)))
{
++NumberOfMatches;
Matches.push_back(*it);
PacketSize += (*it).Name.length() + (*it).Comments.length() + 6;
}
}
auto pack = new ServerPacket(ServerOP_QueryServGeneric, PacketSize);
pack->WriteUInt32(FromZoneID);
pack->WriteUInt32(FromInstanceID);
pack->WriteString(From);
pack->WriteUInt32(QSG_LFGuild);
pack->WriteUInt32(QSG_LFGuild_GuildMatches);
pack->WriteUInt32(NumberOfMatches);
for(it = Matches.begin(); it != Matches.end(); ++it)
{
pack->WriteString((*it).Name.c_str());
pack->WriteUInt32((*it).TimeZone);
pack->WriteString((*it).Comments.c_str());
}
worldserver->SendPacket(pack);
safe_delete(pack);
}
void LFGuildManager::TogglePlayer(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 Class, uint32 Level, uint32 AAPoints, char *Comments, uint32 Toggle, uint32 TimeZone)
{
for(auto it = Players.begin(); it != Players.end(); ++it)
if(!strcasecmp((*it).Name.c_str(), From)) {
Players.erase(it);
break;
}
std::string query = StringFormat("DELETE FROM `lfguild` WHERE `type` = 0 AND `name` = '%s'", From);
auto results = database.QueryDatabase(query);
uint32 Now = time(nullptr);
if(Toggle == 1) {
PlayerLookingForGuild p(From, Comments, Level, Class, AAPoints, TimeZone, Now);
Players.push_back(p);
query = StringFormat("INSERT INTO `lfguild` "
"(`type`, `name`, `comment`, `fromlevel`, `tolevel`, "
"`classes`, `aacount`, `timezone`, `timeposted`) "
"VALUES (0, '%s', '%s', %u, 0, %u, %u, %u, %u)",
From, Comments, Level, Class, AAPoints, TimeZone, Now);
auto results = database.QueryDatabase(query);
}
auto pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(From) + strlen(Comments) + 30);
pack->WriteUInt32(FromZoneID);
pack->WriteUInt32(FromInstanceID);
pack->WriteString(From);
pack->WriteUInt32(QSG_LFGuild);
pack->WriteUInt32(QSG_LFGuild_RequestPlayerInfo);
pack->WriteString(Comments);
pack->WriteUInt32(TimeZone);
pack->WriteUInt32(Now);
pack->WriteUInt32(Toggle);
worldserver->SendPacket(pack);
safe_delete(pack);
}
void LFGuildManager::ToggleGuild(uint32 FromZoneID, uint32 FromInstanceID, char *From, char* GuildName, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Toggle, uint32 TimeZone)
{
for(auto it = Guilds.begin(); it != Guilds.end(); ++it)
if(!strcasecmp((*it).Name.c_str(), GuildName))
{
Guilds.erase(it);
break;
}
std::string query = StringFormat("DELETE FROM `lfguild` WHERE `type` = 1 AND `name` = '%s'", GuildName);
auto results = database.QueryDatabase(query);
uint32 Now = time(nullptr);
if(Toggle == 1)
{
GuildLookingForPlayers g(GuildName, Comments, FromLevel, ToLevel, Classes, AACount, TimeZone, Now);
Guilds.push_back(g);
query = StringFormat("INSERT INTO `lfguild` "
"(`type`, `name`, `comment`, `fromlevel`, `tolevel`, "
"`classes`, `aacount`, `timezone`, `timeposted`) "
"VALUES (1, '%s', '%s', %u, %u, %u, %u, %u, %u)",
GuildName, Comments, FromLevel, ToLevel,
Classes, AACount, TimeZone, Now);
auto results = database.QueryDatabase(query);
}
auto pack = new ServerPacket(ServerOP_LFGuildUpdate, strlen(GuildName) + strlen(Comments) + 30);
pack->WriteString(GuildName);
pack->WriteString(Comments);
pack->WriteUInt32(FromLevel);
pack->WriteUInt32(ToLevel);
pack->WriteUInt32(Classes);
pack->WriteUInt32(AACount);
pack->WriteUInt32(TimeZone);
pack->WriteUInt32(Now);
pack->WriteUInt32(Toggle);
worldserver->SendPacket(pack);
safe_delete(pack);
}
void LFGuildManager::ExpireEntries()
{
for(auto it = Players.begin(); it != Players.end(); ++it)
{
if((*it).TimePosted + 604800 > (uint32)time(nullptr))
continue;
std::string query = StringFormat("DELETE from `lfguild` WHERE `type` = 0 AND `name` = '%s'", (*it).Name.c_str());
auto results = database.QueryDatabase(query);
if(!results.Success())
it = Players.erase(it);
}
for(auto it2 = Guilds.begin(); it2 != Guilds.end(); ++it2)
{
if((*it2).TimePosted + 2592000 > time(nullptr))
continue;
std::string query = StringFormat("DELETE from `lfguild` WHERE `type` = 1 AND `name` = '%s'", (*it2).Name.c_str());
auto results = database.QueryDatabase(query);
if(!results.Success())
it2 = Guilds.erase(it2);
}
}
void LFGuildManager::SendPlayerStatus(uint32 FromZoneID, uint32 FromInstanceID, char *From)
{
std::list<PlayerLookingForGuild>::iterator it;
for(it = Players.begin(); it != Players.end(); ++it)
{
if(!strcasecmp((*it).Name.c_str(), From))
{
auto pack =
new ServerPacket(ServerOP_QueryServGeneric, strlen(From) + (*it).Comments.length() + 30);
pack->WriteUInt32(FromZoneID);
pack->WriteUInt32(FromInstanceID);
pack->WriteString(From);
pack->WriteUInt32(QSG_LFGuild);
pack->WriteUInt32(QSG_LFGuild_RequestPlayerInfo);
pack->WriteString((*it).Comments.c_str());
pack->WriteUInt32((*it).TimeZone);
pack->WriteUInt32((*it).TimePosted);
pack->WriteUInt32(1);
worldserver->SendPacket(pack);
safe_delete(pack);
break;
}
}
}
void LFGuildManager::SendGuildStatus(uint32 FromZoneID, uint32 FromInstanceID, char *From, char *GuildName)
{
std::list<GuildLookingForPlayers>::iterator it;
for(it = Guilds.begin(); it != Guilds.end(); ++it)
{
if(!strcasecmp((*it).Name.c_str(), GuildName))
{
auto pack =
new ServerPacket(ServerOP_QueryServGeneric, strlen(From) + (*it).Comments.length() + 42);
pack->WriteUInt32(FromZoneID);
pack->WriteUInt32(FromInstanceID);
pack->WriteString(From);
pack->WriteUInt32(QSG_LFGuild);
pack->WriteUInt32(QSG_LFGuild_RequestGuildInfo);
pack->WriteString((*it).Comments.c_str());
pack->WriteUInt32((*it).FromLevel);
pack->WriteUInt32((*it).ToLevel);
pack->WriteUInt32((*it).Classes);
pack->WriteUInt32((*it).AACount);
pack->WriteUInt32((*it).TimeZone);
pack->WriteUInt32((*it).TimePosted);
worldserver->SendPacket(pack);
safe_delete(pack);
break;
}
}
}