diff --git a/common/database.cpp b/common/database.cpp index 98fa2c5df..322908e00 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -35,6 +35,8 @@ #include "../common/repositories/character_languages_repository.h" #include "../common/repositories/character_leadership_abilities_repository.h" #include "../common/repositories/character_skills_repository.h" +#include "../common/repositories/group_id_repository.h" +#include "../common/repositories/group_leaders_repository.h" // Disgrace: for windows compile #ifdef _WINDOWS @@ -1280,24 +1282,6 @@ void Database::AddReport(std::string who, std::string against, std::string lines safe_delete_array(escape_str); } -void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc) { - std::string query; - if (id == 0) { - // removing from group - query = StringFormat("delete from group_id where charid=%i and name='%s' and ismerc=%i",charid, name, ismerc); - auto results = QueryDatabase(query); - - if (!results.Success()) - LogError("Error deleting character from group id: {}", results.ErrorMessage().c_str()); - - return; - } - - /* Add to the Group */ - query = StringFormat("REPLACE INTO `group_id` SET `charid` = %i, `groupid` = %i, `name` = '%s', `ismerc` = '%i'", charid, id, name, ismerc); - QueryDatabase(query); -} - void Database::ClearAllGroups(void) { std::string query("DELETE FROM `group_id`"); @@ -1305,71 +1289,65 @@ void Database::ClearAllGroups(void) return; } -void Database::ClearGroup(uint32 gid) { - ClearGroupLeader(gid); +void Database::ClearGroup(uint32 group_id) { + ClearGroupLeader(group_id); - if(gid == 0) - { + if (!group_id) { //clear all groups ClearAllGroups(); return; } //clear a specific group - std::string query = StringFormat("delete from group_id where groupid = %lu", (unsigned long)gid); - QueryDatabase(query); -} - -uint32 Database::GetGroupID(const char* name){ - std::string query = StringFormat("SELECT groupid from group_id where name='%s'", name); - auto results = QueryDatabase(query); - - if (!results.Success()) { - return 0; - } - - if (results.RowCount() == 0) - { - // Commenting this out until logging levels can prevent this from going to console - //LogDebug(, "Character not in a group: [{}]", name); - return 0; - } - - auto row = results.begin(); - - return Strings::ToUnsignedInt(row[0]); -} - -std::string Database::GetGroupLeaderForLogin(std::string character_name) { - uint32 group_id = 0; - - auto query = fmt::format( - "SELECT `groupid` FROM `group_id` WHERE `name` = '{}'", - character_name + GroupIdRepository::DeleteWhere( + *this, + fmt::format( + "`group_id` = {}", + group_id + ) ); - auto results = QueryDatabase(query); +} - if (results.Success() && results.RowCount()) { - auto row = results.begin(); - group_id = Strings::ToUnsignedInt(row[0]); +uint32 Database::GetGroupID(const std::string& name) +{ + const auto& l = GroupIdRepository::GetWhere( + *this, + fmt::format( + "`name` = '{}'", + Strings::Escape(name) + ) + ); + + if (l.empty()) { + return 0; } - if (!group_id) { + auto e = l.front(); + + return e.group_id; +} + +std::string Database::GetGroupLeaderForLogin(const std::string& character_name) +{ + const auto& g = GroupIdRepository::GetWhere( + *this, + fmt::format( + "`name` = '{}'", + Strings::Escape(character_name) + ) + ); + + if (g.empty()) { return std::string(); } - query = fmt::format( - "SELECT `leadername` FROM `group_leaders` WHERE `gid` = {} LIMIT 1", - group_id - ); - results = QueryDatabase(query); + auto group = g.front(); - if (results.Success() && results.RowCount()) { - auto row = results.begin(); - return row[0]; - } + const uint32 group_id = group.group_id; - return std::string(); + const auto& e = GroupLeadersRepository::FindOne(*this, group_id); + + return e.gid ? e.leadername : std::string(); } void Database::SetGroupLeaderName(uint32 gid, const char* name) { diff --git a/common/database.h b/common/database.h index 850632b7f..81f92c97a 100644 --- a/common/database.h +++ b/common/database.h @@ -202,16 +202,15 @@ public: /* Groups */ - std::string GetGroupLeaderForLogin(std::string character_name); + std::string GetGroupLeaderForLogin(const std::string& character_name); char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); std::string GetGroupLeaderName(uint32 group_id); - uint32 GetGroupID(const char* name); + uint32 GetGroupID(const std::string& name); - void ClearGroup(uint32 gid = 0); + void ClearGroup(uint32 group_id = 0); void ClearGroupLeader(uint32 gid = 0); - void SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc = false); void SetGroupLeaderName(uint32 gid, const char* name); /* Raids */ diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 28dee5029..a674afbef 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5433,6 +5433,25 @@ ADD PRIMARY KEY (`id`); ALTER TABLE `rule_values` MODIFY COLUMN `rule_value` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `rule_name`; )" + }, + ManifestEntry{ + .version = 9267, + .description = "2024_02_18_group_id_bot_id.sql", + .check = "SHOW COLUMNS FROM `group_id` LIKE 'bot_id'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `group_id` +CHANGE COLUMN `groupid` `group_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST, +CHANGE COLUMN `charid` `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `group_id`, +CHANGE COLUMN `ismerc` `merc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `name`, +ADD COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`, +MODIFY COLUMN `name` varchar(64) NOT NULL DEFAULT '' AFTER `character_id`, +DROP PRIMARY KEY, +ADD PRIMARY KEY (`group_id`, `character_id`, `bot_id`, `merc_id`) USING BTREE; +ALTER TABLE `group_id` +MODIFY COLUMN `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `name`; +)" } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ diff --git a/common/database_instances.cpp b/common/database_instances.cpp index 7d90f0923..85b26a917 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -421,20 +421,25 @@ void Database::AssignGroupToInstance(uint32 group_id, uint32 instance_id) auto zone_id = GetInstanceZoneID(instance_id); auto version = GetInstanceVersion(instance_id); - auto l = GroupIdRepository::GetWhere( + const auto& l = GroupIdRepository::GetWhere( *this, fmt::format( - "groupid = {}", + "`group_id` = {}", group_id ) ); + if (l.empty()) { return; } for (const auto& e : l) { - if (!GetInstanceID(zone_id, e.charid, version)) { - AddClientToInstance(instance_id, e.charid); + if (!e.character_id) { + continue; + } + + if (!GetInstanceID(zone_id, e.character_id, version)) { + AddClientToInstance(instance_id, e.character_id); } } } diff --git a/common/repositories/base/base_group_id_repository.h b/common/repositories/base/base_group_id_repository.h index eb590fccf..689d5ae70 100644 --- a/common/repositories/base/base_group_id_repository.h +++ b/common/repositories/base/base_group_id_repository.h @@ -19,34 +19,37 @@ class BaseGroupIdRepository { public: struct GroupId { - int32_t groupid; - int32_t charid; + uint32_t group_id; std::string name; - int8_t ismerc; + uint32_t character_id; + uint32_t bot_id; + uint32_t merc_id; }; static std::string PrimaryKey() { - return std::string("groupid"); + return std::string("group_id"); } static std::vector Columns() { return { - "groupid", - "charid", + "group_id", "name", - "ismerc", + "character_id", + "bot_id", + "merc_id", }; } static std::vector SelectColumns() { return { - "groupid", - "charid", + "group_id", "name", - "ismerc", + "character_id", + "bot_id", + "merc_id", }; } @@ -87,10 +90,11 @@ public: { GroupId e{}; - e.groupid = 0; - e.charid = 0; - e.name = ""; - e.ismerc = 0; + e.group_id = 0; + e.name = ""; + e.character_id = 0; + e.bot_id = 0; + e.merc_id = 0; return e; } @@ -101,7 +105,7 @@ public: ) { for (auto &group_id : group_ids) { - if (group_id.groupid == group_id_id) { + if (group_id.group_id == group_id_id) { return group_id; } } @@ -127,10 +131,11 @@ public: if (results.RowCount() == 1) { GroupId e{}; - e.groupid = row[0] ? static_cast(atoi(row[0])) : 0; - e.charid = row[1] ? static_cast(atoi(row[1])) : 0; - e.name = row[2] ? row[2] : ""; - e.ismerc = row[3] ? static_cast(atoi(row[3])) : 0; + e.group_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.name = row[1] ? row[1] : ""; + e.character_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.bot_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.merc_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; return e; } @@ -164,10 +169,11 @@ public: auto columns = Columns(); - v.push_back(columns[0] + " = " + std::to_string(e.groupid)); - v.push_back(columns[1] + " = " + std::to_string(e.charid)); - v.push_back(columns[2] + " = '" + Strings::Escape(e.name) + "'"); - v.push_back(columns[3] + " = " + std::to_string(e.ismerc)); + v.push_back(columns[0] + " = " + std::to_string(e.group_id)); + v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'"); + v.push_back(columns[2] + " = " + std::to_string(e.character_id)); + v.push_back(columns[3] + " = " + std::to_string(e.bot_id)); + v.push_back(columns[4] + " = " + std::to_string(e.merc_id)); auto results = db.QueryDatabase( fmt::format( @@ -175,7 +181,7 @@ public: TableName(), Strings::Implode(", ", v), PrimaryKey(), - e.groupid + e.group_id ) ); @@ -189,10 +195,11 @@ public: { std::vector v; - v.push_back(std::to_string(e.groupid)); - v.push_back(std::to_string(e.charid)); + v.push_back(std::to_string(e.group_id)); v.push_back("'" + Strings::Escape(e.name) + "'"); - v.push_back(std::to_string(e.ismerc)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.merc_id)); auto results = db.QueryDatabase( fmt::format( @@ -203,7 +210,7 @@ public: ); if (results.Success()) { - e.groupid = results.LastInsertedID(); + e.group_id = results.LastInsertedID(); return e; } @@ -222,10 +229,11 @@ public: for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.groupid)); - v.push_back(std::to_string(e.charid)); + v.push_back(std::to_string(e.group_id)); v.push_back("'" + Strings::Escape(e.name) + "'"); - v.push_back(std::to_string(e.ismerc)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.merc_id)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -259,10 +267,11 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { GroupId e{}; - e.groupid = row[0] ? static_cast(atoi(row[0])) : 0; - e.charid = row[1] ? static_cast(atoi(row[1])) : 0; - e.name = row[2] ? row[2] : ""; - e.ismerc = row[3] ? static_cast(atoi(row[3])) : 0; + e.group_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.name = row[1] ? row[1] : ""; + e.character_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.bot_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.merc_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -287,10 +296,11 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { GroupId e{}; - e.groupid = row[0] ? static_cast(atoi(row[0])) : 0; - e.charid = row[1] ? static_cast(atoi(row[1])) : 0; - e.name = row[2] ? row[2] : ""; - e.ismerc = row[3] ? static_cast(atoi(row[3])) : 0; + e.group_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.name = row[1] ? row[1] : ""; + e.character_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.bot_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.merc_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -365,10 +375,11 @@ public: { std::vector v; - v.push_back(std::to_string(e.groupid)); - v.push_back(std::to_string(e.charid)); + v.push_back(std::to_string(e.group_id)); v.push_back("'" + Strings::Escape(e.name) + "'"); - v.push_back(std::to_string(e.ismerc)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.merc_id)); auto results = db.QueryDatabase( fmt::format( @@ -391,10 +402,11 @@ public: for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.groupid)); - v.push_back(std::to_string(e.charid)); + v.push_back(std::to_string(e.group_id)); v.push_back("'" + Strings::Escape(e.name) + "'"); - v.push_back(std::to_string(e.ismerc)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.merc_id)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/base/base_group_leaders_repository.h b/common/repositories/base/base_group_leaders_repository.h index ca5dae8ec..b233d7f2c 100644 --- a/common/repositories/base/base_group_leaders_repository.h +++ b/common/repositories/base/base_group_leaders_repository.h @@ -105,7 +105,7 @@ public: e.gid = 0; e.leadername = ""; e.marknpc = ""; - e.leadershipaa = 0; + e.leadershipaa = ""; e.maintank = ""; e.assist = ""; e.puller = ""; @@ -150,7 +150,7 @@ public: e.gid = row[0] ? static_cast(atoi(row[0])) : 0; e.leadername = row[1] ? row[1] : ""; e.marknpc = row[2] ? row[2] : ""; - e.leadershipaa = row[3] ? row[3] : 0; + e.leadershipaa = row[3] ? row[3] : ""; e.maintank = row[4] ? row[4] : ""; e.assist = row[5] ? row[5] : ""; e.puller = row[6] ? row[6] : ""; @@ -302,7 +302,7 @@ public: e.gid = row[0] ? static_cast(atoi(row[0])) : 0; e.leadername = row[1] ? row[1] : ""; e.marknpc = row[2] ? row[2] : ""; - e.leadershipaa = row[3] ? row[3] : 0; + e.leadershipaa = row[3] ? row[3] : ""; e.maintank = row[4] ? row[4] : ""; e.assist = row[5] ? row[5] : ""; e.puller = row[6] ? row[6] : ""; @@ -335,7 +335,7 @@ public: e.gid = row[0] ? static_cast(atoi(row[0])) : 0; e.leadername = row[1] ? row[1] : ""; e.marknpc = row[2] ? row[2] : ""; - e.leadershipaa = row[3] ? row[3] : 0; + e.leadershipaa = row[3] ? row[3] : ""; e.maintank = row[4] ? row[4] : ""; e.assist = row[5] ? row[5] : ""; e.puller = row[6] ? row[6] : ""; diff --git a/common/shared_tasks.cpp b/common/shared_tasks.cpp index 844cf4cc7..dde9cd874 100644 --- a/common/shared_tasks.cpp +++ b/common/shared_tasks.cpp @@ -60,7 +60,7 @@ SharedTaskRequest SharedTask::GetRequestCharacters(Database &db, uint32_t reques request.group_type = SharedTaskRequestGroupType::Group; auto characters = CharacterDataRepository::GetWhere( db, fmt::format( - "id IN (select charid from group_id where groupid = (select groupid from group_id where charid = {}))", + "id IN (select charid from group_id where group_id = (select group_id from group_id where charid = {}))", requested_character_id ) ); diff --git a/common/version.h b/common/version.h index 9c9ad9cbb..790d43932 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9266 +#define CURRENT_BINARY_DATABASE_VERSION 9267 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043 #endif diff --git a/world/client.cpp b/world/client.cpp index 90937fac4..9fd97f98e 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -53,6 +53,7 @@ #include "../common/repositories/inventory_repository.h" #include "../common/events/player_event_logs.h" #include "../common/content/world_content_service.h" +#include "../common/repositories/group_id_repository.h" #include #include @@ -881,7 +882,14 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { } if(!is_player_zoning) { - database.SetGroupID(char_name, 0, charid); + GroupIdRepository::DeleteWhere( + database, + fmt::format( + "`character_id` = {} AND `name` = '{}'", + charid, + Strings::Escape(char_name) + ) + ); database.SetLoginFlags(charid, false, false, 1); } else { auto group_id = database.GetGroupID(char_name); diff --git a/zone/bot.cpp b/zone/bot.cpp index 0857a9a1c..334e6b275 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3343,7 +3343,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) { } if (bot_spawn_limit >= 0 && spawned_bots_count >= bot_spawn_limit) { - database.SetGroupID(b->GetCleanName(), 0, b->GetBotID()); + Group::RemoveFromGroup(b); g->UpdatePlayer(bot_owner); continue; } @@ -3355,7 +3355,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) { bot_spawn_limit_class >= 0 && spawned_bot_count_class >= bot_spawn_limit_class ) { - database.SetGroupID(b->GetCleanName(), 0, b->GetBotID()); + Group::RemoveFromGroup(b); g->UpdatePlayer(bot_owner); continue; } @@ -3375,7 +3375,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) { } if (!bot_owner->HasGroup()) { - database.SetGroupID(b->GetCleanName(), 0, b->GetBotID()); + Group::RemoveFromGroup(b); } } } @@ -3632,7 +3632,7 @@ bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) { bot->SetFollowID(0); if (group->DelMember(bot)) { group->DelMemberOOZ(bot->GetName()); - database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID()); + Group::RemoveFromGroup(bot); if (group->GroupCount() < 2) { group->DisbandGroup(); } @@ -3646,7 +3646,7 @@ bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) { group->members[i]->SetFollowID(0); } group->DisbandGroup(); - database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID()); + Group::RemoveFromGroup(bot); } Result = true; } @@ -6600,14 +6600,14 @@ void Bot::ProcessBotGroupInvite(Client* c, std::string const& botName) { entity_list.AddGroup(g); database.SetGroupLeaderName(g->GetID(), c->GetName()); g->SaveGroupLeaderAA(); - database.SetGroupID(c->GetName(), g->GetID(), c->CharacterID()); - database.SetGroupID(invitedBot->GetCleanName(), g->GetID(), invitedBot->GetBotID()); + g->AddToGroup(c); + g->AddToGroup(invitedBot); } else { delete g; } } else { if (AddBotToGroup(invitedBot, c->GetGroup())) { - database.SetGroupID(invitedBot->GetCleanName(), c->GetGroup()->GetID(), invitedBot->GetBotID()); + c->GetGroup()->AddToGroup(invitedBot); } } } else if (invitedBot->HasGroup()) { diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 498e16653..2090c8100 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -1922,18 +1922,14 @@ bool BotDatabase::LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 g const auto& l = GroupIdRepository::GetWhere( database, fmt::format( - "`groupid` = {} AND `name` IN (SELECT `name` FROM `bot_data` WHERE `owner_id` = {})", + "`group_id` = {} AND `bot_id` != 0 AND `name` IN (SELECT `name` FROM `bot_data` WHERE `owner_id` = {})", group_id, owner_id ) ); - if (l.empty()) { - return true; - } - for (const auto& e : l) { - group_list.push_back(e.charid); + group_list.emplace_back(e.bot_id); } return true; diff --git a/zone/bot_raid.cpp b/zone/bot_raid.cpp index 0bcf5cd95..ac15e2156 100644 --- a/zone/bot_raid.cpp +++ b/zone/bot_raid.cpp @@ -98,7 +98,7 @@ void Raid::HandleBotGroupDisband(uint32 owner, uint32 gid) auto r_group_members = GetRaidGroupMembers(GetGroup(b->GetName())); auto g = new Group(b); entity_list.AddGroup(g); - database.SetGroupID(b->GetCleanName(), g->GetID(), b->GetBotID()); + g->AddToGroup(b); database.SetGroupLeaderName(g->GetID(), b->GetName()); for (auto m: r_group_members) { diff --git a/zone/client.cpp b/zone/client.cpp index 1bc2760e2..fabf9bf32 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -4442,7 +4442,7 @@ bool Client::GroupFollow(Client* inviter) { } //now we have a group id, can set inviter's id - database.SetGroupID(inviter->GetName(), group->GetID(), inviter->CharacterID(), false); + group->AddToGroup(inviter); database.SetGroupLeaderName(group->GetID(), inviter->GetName()); group->UpdateGroupAAs(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0ecc5a597..e9da4fa12 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1530,8 +1530,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) } } //else, somebody from our group is already here... - if (!group) - database.SetGroupID(GetName(), 0, CharacterID(), false); //cannot re-establish group, kill it + if (!group) { //cannot re-establish group, kill it + Group::RemoveFromGroup(this); + } } else { //no group id @@ -7189,7 +7190,7 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) if (!GetMerc()) { - database.SetGroupID(GetName(), 0, CharacterID(), false); + Group::RemoveFromGroup(this); } return; } diff --git a/zone/groups.cpp b/zone/groups.cpp index 6ffa06655..4cb160698 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -203,155 +203,141 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu } } -bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 CharacterID, bool ismerc) +bool Group::AddMember(Mob* new_member, std::string new_member_name, uint32 character_id, bool is_merc) { - bool InZone = true; + bool in_zone = true; // This method should either be passed a Mob*, if the new member is in this zone, or a nullptr Mob* - // and the name and CharacterID of the new member, if they are out of zone. - if(!newmember && !NewMemberName) - { + // and the name and character_id of the new member, if they are out of zone. + if (!new_member && new_member_name.empty()) { return false; } - if(GroupCount() >= MAX_GROUP_MEMBERS) //Sanity check for merging groups together. - { + if (GroupCount() >= MAX_GROUP_MEMBERS) { //Sanity check for merging groups together. return false; } - if(!newmember) - { - InZone = false; - } - else - { - NewMemberName = newmember->GetCleanName(); + if (!new_member) { + in_zone = false; + } else { + new_member_name = new_member->GetCleanName(); - if(newmember->IsClient()) - { - CharacterID = newmember->CastToClient()->CharacterID(); + if (new_member->IsClient()) { + character_id = new_member->CastToClient()->CharacterID(); } - if(newmember->IsMerc()) - { - Client* owner = newmember->CastToMerc()->GetMercenaryOwner(); - if(owner) - { - CharacterID = owner->CastToClient()->CharacterID(); + + if (new_member->IsMerc()) { + Client* o = new_member->CastToMerc()->GetMercenaryOwner(); + if (o) { + character_id = o->CastToClient()->CharacterID(); } - ismerc = true; + + is_merc = true; } } // See if they are already in the group - uint32 i = 0; - for (i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if (!strcasecmp(membername[i], NewMemberName)) - { + for (const auto& m : membername) { + if (Strings::EqualFold(m, new_member_name)) { return false; } } // Put them in the group - for (i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if (membername[i][0] == '\0') - { - if(InZone) - { - members[i] = newmember; + for (int slot_id = 0; slot_id < MAX_GROUP_MEMBERS; ++slot_id) { + if (membername[slot_id][0] == '\0') { + if (in_zone) { + members[slot_id] = new_member; } - strcpy(membername[i], NewMemberName); - MemberRoles[i] = 0; + + strcpy(membername[slot_id], new_member_name.c_str()); + MemberRoles[slot_id] = 0; break; } } - // Is this even possible based on the above loops? Remove? - if (i == MAX_GROUP_MEMBERS) - { - return false; - } - - int x=1; + int x = 1; //build the template join packet auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); - GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; - strcpy(gj->membername, NewMemberName); - gj->action = groupActJoin; + + auto gj = (GroupJoin_Struct*) outapp->pBuffer; + + strcpy(gj->membername, new_member_name.c_str()); + + gj->action = groupActJoin; gj->leader_aas = LeaderAbilities; - for (i = 0;i < MAX_GROUP_MEMBERS; i++) - { - if (members[i] != nullptr && members[i] != newmember) - { + for (int slot_id = 0; slot_id < MAX_GROUP_MEMBERS; slot_id++) { + if (members[slot_id] && members[slot_id] != new_member) { //fill in group join & send it - strcpy(gj->yourname, members[i]->GetCleanName()); - if(members[i]->IsClient()) - { - members[i]->CastToClient()->QueuePacket(outapp); + strcpy(gj->yourname, members[slot_id]->GetCleanName()); + if (members[slot_id]->IsClient()) { + members[slot_id]->CastToClient()->QueuePacket(outapp); //put new member into existing group members' list(s) - strcpy(members[i]->CastToClient()->GetPP().groupMembers[GroupCount()-1], NewMemberName); + strcpy( + members[slot_id]->CastToClient()->GetPP().groupMembers[GroupCount() - 1], + new_member_name.c_str() + ); } //put existing group member(s) into the new member's list - if(InZone && newmember && newmember->IsClient()) - { - if(IsLeader(members[i])) - { - strcpy(newmember->CastToClient()->GetPP().groupMembers[0], members[i]->GetCleanName()); - } - else - { - strcpy(newmember->CastToClient()->GetPP().groupMembers[x], members[i]->GetCleanName()); + if (in_zone && new_member && new_member->IsClient()) { + if (IsLeader(members[slot_id])) { + strcpy(new_member->CastToClient()->GetPP().groupMembers[0], members[slot_id]->GetCleanName()); + } else { + strcpy(new_member->CastToClient()->GetPP().groupMembers[x], members[slot_id]->GetCleanName()); ++x; } } } } - if(InZone && newmember) - { + if (in_zone && new_member) { //put new member in his own list. - newmember->SetGrouped(true); + new_member->SetGrouped(true); - if(newmember->IsClient()) - { - strcpy(newmember->CastToClient()->GetPP().groupMembers[x], NewMemberName); - newmember->CastToClient()->Save(); - database.SetGroupID(NewMemberName, GetID(), newmember->CastToClient()->CharacterID(), false); - SendMarkedNPCsToMember(newmember->CastToClient()); + if (new_member->IsClient()) { + strcpy(new_member->CastToClient()->GetPP().groupMembers[x], new_member_name.c_str()); - NotifyMainTank(newmember->CastToClient(), 1); - NotifyMainAssist(newmember->CastToClient(), 1); - NotifyPuller(newmember->CastToClient(), 1); + new_member->CastToClient()->Save(); + + AddToGroup(new_member); + + SendMarkedNPCsToMember(new_member->CastToClient()); + + NotifyMainTank(new_member->CastToClient(), 1); + NotifyMainAssist(new_member->CastToClient(), 1); + NotifyPuller(new_member->CastToClient(), 1); } - if(newmember->IsMerc()) - { - Client* owner = newmember->CastToMerc()->GetMercenaryOwner(); - if(owner) - { - database.SetGroupID(NewMemberName, GetID(), owner->CharacterID(), true); + if (new_member->IsMerc()) { + Client* o = new_member->CastToMerc()->GetMercenaryOwner(); + if (o) { + AddToGroup(new_member); } } - Group* group = newmember->CastToClient()->GetGroup(); - if (group) { - group->SendHPManaEndPacketsTo(newmember); - group->SendHPPacketsFrom(newmember); + Group* g = new_member->CastToClient()->GetGroup(); + if (g) { + g->SendHPManaEndPacketsTo(new_member); + g->SendHPPacketsFrom(new_member); } - } - else - { - database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc); + } else { + AddToGroup( + AddToGroupRequest{ + .mob = nullptr, + .member_name = new_member_name, + .character_id = character_id, + } + ); } - if (newmember && newmember->IsClient()) - newmember->CastToClient()->JoinGroupXTargets(this); + if (new_member && new_member->IsClient()) { + new_member->CastToClient()->JoinGroupXTargets(this); + } safe_delete(outapp); @@ -362,20 +348,18 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte return true; } -void Group::AddMember(const char *NewMemberName) +void Group::AddMember(const std::string& new_member_name) { // This method should be called when both the new member and the group leader are in a different zone to this one. - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(!strcasecmp(membername[i], NewMemberName)) - { + for (const auto& m : membername) { + if (Strings::EqualFold(m, new_member_name)) { return; } + } - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if (membername[i][0] == '\0') - { - strcpy(membername[i], NewMemberName); + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if (membername[i][0] == '\0') { + strcpy(membername[i], new_member_name.c_str()); MemberRoles[i] = 0; break; } @@ -752,7 +736,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) if(oldmember->IsClient()) { - database.SetGroupID(oldmember->GetCleanName(), 0, oldmember->CastToClient()->CharacterID(), false); + RemoveFromGroup(oldmember); } if(oldmember->IsMerc()) @@ -760,7 +744,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) Client* owner = oldmember->CastToMerc()->GetMercenaryOwner(); if(owner) { - database.SetGroupID(oldmember->GetCleanName(), 0, owner->CharacterID(), true); + RemoveFromGroup(oldmember); } } @@ -943,7 +927,7 @@ void Group::DisbandGroup(bool joinraid) { } strcpy(gu->yourname, members[i]->GetCleanName()); - database.SetGroupID(members[i]->GetCleanName(), 0, members[i]->CastToClient()->CharacterID(), false); + RemoveFromGroup(members[i]); members[i]->CastToClient()->QueuePacket(outapp); SendMarkedNPCsToMember(members[i]->CastToClient(), true); if (!joinraid) @@ -955,7 +939,7 @@ void Group::DisbandGroup(bool joinraid) { Client* owner = members[i]->CastToMerc()->GetMercenaryOwner(); if(owner) { - database.SetGroupID(members[i]->GetCleanName(), 0, owner->CharacterID(), true); + RemoveFromGroup(members[i]); } } @@ -1148,31 +1132,30 @@ void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float bool Group::LearnMembers() { - auto rows = GroupIdRepository::GetWhere( + const auto& l = GroupIdRepository::GetWhere( database, fmt::format( - "groupid = {}", + "`group_id` = {}", GetID() ) ); - if (rows.empty()) { + if (l.empty()) { LogError( "Error getting group members for group [{}]", GetID() ); } - for(int i = 0; i < MAX_GROUP_MEMBERS; ++i) - { + for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { members[i] = nullptr; - memset(membername[i],0,64); + memset(membername[i], 0, 64); MemberRoles[i] = 0; } - int memberIndex = 0; - for (const auto& member : rows) { - if (memberIndex >= MAX_GROUP_MEMBERS) { + int member_index = 0; + for (const auto& e : l) { + if (member_index >= MAX_GROUP_MEMBERS) { LogError( "Too many members in group [{}]", GetID() @@ -1180,14 +1163,15 @@ bool Group::LearnMembers() { break; } - if (member.name.empty()) { - members[memberIndex] = nullptr; - membername[memberIndex][0] = '\0'; + if (e.name.empty()) { + members[member_index] = nullptr; + membername[member_index][0] = '\0'; } else { - members[memberIndex] = nullptr; - strn0cpy(membername[memberIndex], member.name.c_str(), 64); + members[member_index] = nullptr; + strn0cpy(membername[member_index], e.name.c_str(), 64); } - ++memberIndex; + + ++member_index; } VerifyGroup(); @@ -1264,10 +1248,10 @@ void Client::LeaveGroup() { else { //force things a little - database.SetGroupID(GetCleanName(), 0, CharacterID(), false); - if (GetMerc()) - { - database.SetGroupID(GetMerc()->GetCleanName(), 0, CharacterID(), true); + Group::RemoveFromGroup(this); + + if (GetMerc()) { + Group::RemoveFromGroup(GetMerc()); } } @@ -2514,3 +2498,69 @@ bool Group::IsLeader(const char* name) { std::string Group::GetLeaderName() { return database.GetGroupLeaderName(GetID()); } + +void Group::RemoveFromGroup(Mob* m) +{ + uint32 bot_id = 0; + uint32 character_id = 0; + uint32 merc_id = 0; + + if (m->IsBot()) { + bot_id = m->CastToBot()->GetBotID(); + } else if (m->IsClient()) { + character_id = m->CastToClient()->CharacterID(); + } else if (m->IsMerc()) { + merc_id = m->CastToMerc()->GetMercenaryID(); + } + + GroupIdRepository::DeleteWhere( + database, + fmt::format( + "`character_id` = {} AND `bot_id` = {} AND `merc_id` = {}", + character_id, + bot_id, + merc_id + ) + ); +} + +void Group::AddToGroup(Mob* m) +{ + AddToGroup( + AddToGroupRequest{ + .mob = m + } + ); +} + +// Handles database-side, should eventually be consolidated to handle memory-based group stuff as well +void Group::AddToGroup(AddToGroupRequest r) +{ + uint32 bot_id = 0; + uint32 character_id = 0; + uint32 merc_id = 0; + std::string name = ""; + + if (r.mob) { + if (r.mob->IsBot()) { + bot_id = r.mob->CastToBot()->GetBotID(); + } else if (r.mob->IsClient()) { + character_id = r.mob->CastToClient()->CharacterID(); + } else if (r.mob->IsMerc()) { + merc_id = r.mob->CastToMerc()->GetMercenaryID(); + } + + name = r.mob->GetCleanName(); + } + + GroupIdRepository::ReplaceOne( + database, + GroupIdRepository::GroupId{ + .group_id = GetID(), + .name = name, + .character_id = character_id, + .bot_id = bot_id, + .merc_id = merc_id + } + ); +} diff --git a/zone/groups.h b/zone/groups.h index 0232e74f6..b1b781fae 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -52,8 +52,15 @@ public: Group(uint32 gid); ~Group(); - bool AddMember(Mob* newmember, const char* NewMemberName = nullptr, uint32 CharacterID = 0, bool ismerc = false); - void AddMember(const char* NewMemberName); + struct AddToGroupRequest { + Mob* mob = nullptr; + // Only used cross-zone, otherwise use Mob* mob + std::string member_name = std::string(); + uint32 character_id = 0; + }; + + bool AddMember(Mob* new_member, std::string new_member_name = std::string(), uint32 character_id = 0, bool is_merc = false); + void AddMember(const std::string& new_member_name); void SendUpdate(uint32 type,Mob* member); void SendLeadershipAAUpdate(); void SendWorldGroup(uint32 zone_id,Mob* zoningmember); @@ -145,6 +152,9 @@ public: void SetDirtyAutoHaters(); inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; } void JoinRaidXTarget(Raid *raid, bool first = false); + void AddToGroup(AddToGroupRequest r); + void AddToGroup(Mob* m); + static void RemoveFromGroup(Mob* m); void SetGroupMentor(int percent, char *name); void ClearGroupMentor(); diff --git a/zone/merc.cpp b/zone/merc.cpp index b3cd88a37..93b3002d1 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -5377,7 +5377,7 @@ bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { { if(merc->GetMercenaryCharacterID() != 0) { - database.SetGroupID(merc->GetName(), 0, merc->GetMercenaryCharacterID(), true); + Group::RemoveFromGroup(merc); } } } @@ -5462,7 +5462,7 @@ bool Merc::MercJoinClientGroup() { if (AddMercToGroup(this, g)) { - database.SetGroupID(mercOwner->GetName(), g->GetID(), mercOwner->CharacterID(), false); + g->AddToGroup(mercOwner); database.SetGroupLeaderName(g->GetID(), mercOwner->GetName()); database.RefreshGroupFromDB(mercOwner); g->SaveGroupLeaderAA(); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index b9b26bfc9..d63cd14bb 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1092,7 +1092,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } - database.SetGroupID(Inviter->GetName(), group->GetID(), Inviter->CastToClient()->CharacterID(), false); + group->AddToGroup(Inviter); database.SetGroupLeaderName(group->GetID(), Inviter->GetName()); group->UpdateGroupAAs(); @@ -1192,9 +1192,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) group->UpdatePlayer(client); else { - if (client->GetMerc()) - database.SetGroupID(client->GetMerc()->GetCleanName(), 0, client->CharacterID(), true); - database.SetGroupID(client->GetName(), 0, client->CharacterID(), false); //cannot re-establish group, kill it + if (client->GetMerc()) { + Group::RemoveFromGroup(client->GetMerc()); + } + + Group::RemoveFromGroup(client); //cannot re-establish group, kill it } } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 2c22b9143..f603656ff 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2649,7 +2649,7 @@ void ZoneDatabase::RefreshGroupFromDB(Client *client){ int index = 0; auto query = fmt::format( - "SELECT name FROM group_id WHERE groupid = {}", + "SELECT name FROM group_id WHERE group_id = {}", group->GetID() ); auto results = QueryDatabase(query);