From f854137ca029fa6e90cab5133bd8ee6194f8e1cb Mon Sep 17 00:00:00 2001 From: Trust Date: Fri, 4 Dec 2020 19:26:16 -0500 Subject: [PATCH 1/3] [BUG] Fix for Group Leader Disband Issue Added public/private class for oldleadername so we can save the previous leader name when the entity is destroyed then allow us to transfer leadership. Adjusted DelmemberOOZ and in zone functions to include removal of the old leader when disbanding. --- common/eqemu_logsys.h | 2 + common/servertalk.h | 12 ++++ world/zoneserver.cpp | 8 +++ zone/entity.cpp | 38 +++++++++++-- zone/entity.h | 3 +- zone/groups.cpp | 126 +++++++++++++++++++++++++----------------- zone/groups.h | 4 ++ zone/worldserver.cpp | 13 ++++- 8 files changed, 147 insertions(+), 59 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index d28478a97..c8f724a17 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -120,6 +120,7 @@ namespace Logs { Loot, Expeditions, DynamicZones, + Group, MaxCategoryID /* Don't Remove this */ }; @@ -199,6 +200,7 @@ namespace Logs { "Loot", "Expeditions", "DynamicZones", + "Group", }; } diff --git a/common/servertalk.h b/common/servertalk.h index 40be635ad..816807b56 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -80,6 +80,7 @@ #define ServerOP_UpdateSpawn 0x003f #define ServerOP_SpawnStatusChange 0x0040 #define ServerOP_DropClient 0x0041 // DropClient +#define ServerOP_ChangeGroupLeader 0x0042 #define ServerOP_ReloadTasks 0x0060 #define ServerOP_DepopAllPlayersCorpses 0x0061 #define ServerOP_ReloadTitles 0x0062 @@ -861,6 +862,7 @@ struct ServerGroupLeave_Struct { uint16 instance_id; uint32 gid; char member_name[64]; //kick this member from the group + bool checkleader; }; struct ServerGroupJoin_Struct { @@ -870,10 +872,20 @@ struct ServerGroupJoin_Struct { char member_name[64]; //this person is joining the group }; +struct ServerGroupLeader_Struct { + uint32 zoneid; + uint16 instance_id; + uint32 gid; + char leader_name[64]; + char oldleader_name[64]; +}; + struct ServerForceGroupUpdate_Struct { uint32 origZoneID; uint16 instance_id; uint32 gid; + char leader_name[64]; + char oldleader_name[64]; }; struct ServerGroupChannelMessage_Struct { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index f09d08db1..58bd9f177 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -277,6 +277,14 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } + case ServerOP_ChangeGroupLeader: { + if (pack->size != sizeof(ServerGroupLeader_Struct)) { + break; + } + zoneserver_list.SendPacket(pack); //bounce it to all zones + break; + } + case ServerOP_RaidAdd: { if (pack->size != sizeof(ServerRaidGeneralAction_Struct)) break; diff --git a/zone/entity.cpp b/zone/entity.cpp index 1ad03eea0..51d6043a4 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4209,7 +4209,7 @@ void EntityList::ForceGroupUpdate(uint32 gid) } } -void EntityList::SendGroupLeave(uint32 gid, const char *name) +void EntityList::SendGroupLeave(uint32 gid, const char *name, bool checkleader) { auto it = client_list.begin(); while (it != client_list.end()) { @@ -4225,13 +4225,39 @@ void EntityList::SendGroupLeave(uint32 gid, const char *name) gj->action = groupActLeave; strcpy(gj->yourname, c->GetName()); Mob *Leader = g->GetLeader(); - if (Leader) + if (Leader) { Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); + } c->QueuePacket(outapp); safe_delete(outapp); - g->DelMemberOOZ(name); - if (g->IsLeader(c) && c->IsLFP()) + g->DelMemberOOZ(name, checkleader); + if (g->IsLeader(c) && c->IsLFP()) { c->UpdateLFP(); + } + } + } + } + ++it; + } +} + +void EntityList::SendGroupLeader(uint32 gid, const char *lname, const char *oldlname) +{ + auto it = client_list.begin(); + while (it != client_list.end()) { + if (it->second){ + Group *g = nullptr; + g = it->second->GetGroup(); + if (g) { + if (g->GetID() == gid) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; + gj->action = groupActMakeLeader; + strcpy(gj->membername, lname); + strcpy(gj->yourname, oldlname); + it->second->QueuePacket(outapp); + Log(Logs::Detail, Logs::Group, "SendGroupLeader(): Entity loop leader update packet sent to: %s .", it->second->GetName()); + safe_delete(outapp); } } } @@ -4254,9 +4280,9 @@ void EntityList::SendGroupJoin(uint32 gid, const char *name) gj->action = groupActJoin; strcpy(gj->yourname, it->second->GetName()); Mob *Leader = g->GetLeader(); - if (Leader) + if (Leader) { Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); - + } it->second->QueuePacket(outapp); safe_delete(outapp); } diff --git a/zone/entity.h b/zone/entity.h index b7f90b6f9..ec617acf7 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -478,8 +478,9 @@ public: void CameraEffect(uint32 duration, uint32 intensity); Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType); void ForceGroupUpdate(uint32 gid); - void SendGroupLeave(uint32 gid, const char *name); + void SendGroupLeave(uint32 gid, const char *name, bool checkleader); void SendGroupJoin(uint32 gid, const char *name); + void SendGroupLeader(uint32 gid, const char *lname, const char *oldlname); void SaveAllClientsTaskState(); void ReloadAllClientsTaskState(int TaskID=0); diff --git a/zone/groups.cpp b/zone/groups.cpp index 4b8e25d09..abc868460 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -59,8 +59,12 @@ Group::Group(uint32 gid) } if(gid != 0) { - if(!LearnMembers()) + if(!LearnMembers()) { SetID(0); + } + if(GetLeader() != nullptr) { + SetOldLeaderName(GetLeaderName()); + } } for(int i = 0; i < MAX_MARKED_NPCS; ++i) MarkedNPCs[i] = 0; @@ -78,6 +82,8 @@ Group::Group(Mob* leader) members[0] = leader; leader->SetGrouped(true); SetLeader(leader); + SetOldLeaderName(leader->GetName()); + Log(Logs::Detail, Logs::Group, "Group:Group() Setting OldLeader to: %s and Leader to: %s", GetOldLeaderName(), leader->GetName()); AssistTargetID = 0; TankTargetID = 0; PullerTargetID = 0; @@ -604,39 +610,61 @@ void Group::SendGroupJoinOOZ(Mob* NewMember) { } -bool Group::DelMemberOOZ(const char *Name) { +bool Group::DelMemberOOZ(const char *Name, bool checkleader) { - if(!Name) return false; + if (!Name) return false; + bool removed = false; // If a member out of zone has disbanded, clear out their name. - // - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(!strcasecmp(Name, membername[i])) + for (unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (!strcasecmp(Name, membername[i])) { // This shouldn't be called if the member is in this zone. - if(!members[i]) { - if(!strncmp(GetLeaderName(), Name, 64)) - { + if (!members[i]) { + if (!strncmp(GetLeaderName(), Name, 64)) { //TODO: Transfer leadership if leader disbands OOZ. UpdateGroupAAs(); } - memset(membername[i], 0, 64); - MemberRoles[i] = 0; - if(GroupCount() < 3) - { + if (GroupCount() < 3) { UnDelegateMarkNPC(NPCMarkerName.c_str()); - if (GetLeader() && GetLeader()->IsClient() && GetLeader()->CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoD) { - UnDelegateMainAssist(MainAssistName.c_str()); + if (GetLeader() && GetLeader()->IsClient() && + GetLeader()->CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoD) { + UnDelegateMainAssist(MainAssistName.c_str()); } ClearAllNPCMarks(); } - if (Name == mentoree_name) + if (Name == mentoree_name) { ClearGroupMentor(); - return true; + } + + memset(membername[i], 0, 64); + MemberRoles[i] = 0; + removed = true; + Log(Logs::Detail, Logs::Group, "DelMemberOOZ: Removed Member: %s", Name); + break; } + } } - return false; + if (GroupCount() < 2) { + DisbandGroup(); + return true; + } + + if (checkleader) { + Log(Logs::Detail, Logs::Group, "DelMemberOOZ: Checking leader..."); + if (strcmp(GetOldLeaderName(), Name) == 0 && GroupCount() >= 2) { + for (uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++) { + if (members[nl]) { + if (members[nl]->IsClient()) { + ChangeLeader(members[nl]); + break; + } + } + } + } + } + return removed; } bool Group::DelMember(Mob* oldmember, bool ignoresender) @@ -646,16 +674,6 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) return false; } - // TODO: fix this shit - // okay, so there is code below that tries to handle this. It does not. - // So instead of figuring it out now, lets just disband the group so the client doesn't - // sit there with a broken group and there isn't any group leader shuffling going on - // since the code below doesn't work. - if (oldmember == GetLeader()) { - DisbandGroup(); - return true; - } - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { if (members[i] == oldmember) @@ -664,44 +682,36 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) membername[i][0] = '\0'; memset(membername[i],0,64); MemberRoles[i] = 0; + Log(Logs::Detail, Logs::Group, "DelMember: Removed Member: %s", oldmember->GetCleanName()); break; } } - /* This may seem pointless but the case above does not cover the following situation: - * Group has Leader a, member b, member c - * b and c are out of zone - * a disconnects/quits - * b or c zone back in and disconnects/quits - * a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b) - * Ultimately we should think up a better solution to this. - */ - if(oldmember == GetLeader()) + if(GroupCount() < 2) { - SetLeader(nullptr); + DisbandGroup(); + return true; } - //handle leader quitting group gracefully - if (oldmember == GetLeader() && GroupCount() >= 2) + // If the leader has quit and we have 2 or more players left in group, we want to first check the zone the old leader was in for a new leader. + // If a suitable replacement cannot be found, we need to go out of zone. If checkleader remains true after this method completes, another + // loop will be run in DelMemberOOZ. + bool checkleader = true; + if (strcmp(GetOldLeaderName(),oldmember->GetCleanName()) == 0 && GroupCount() >= 2) { for(uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++) { - if(members[nl]) + if(members[nl]) { if (members[nl]->IsClient()) { ChangeLeader(members[nl]); + checkleader = false; break; } } } } - - if (!GetLeaderName()) - { - DisbandGroup(); - return true; - } auto pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; @@ -709,6 +719,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) gl->zoneid = zone->GetZoneID(); gl->instance_id = zone->GetInstanceID(); strcpy(gl->member_name, oldmember->GetCleanName()); + gl->checkleader = checkleader; worldserver.SendPacket(pack); safe_delete(pack); @@ -800,6 +811,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) Bot::UpdateGroupCastingRoles(this); #endif + safe_delete(outapp); return true; } @@ -2330,17 +2342,16 @@ void Group::ChangeLeader(Mob* newleader) // this changes the current group leader, notifies other members, and updates leadship AA // if the new leader is invalid, do nothing - if (!newleader || !newleader->IsClient()) + if (!newleader) { return; - - Mob* oldleader = GetLeader(); + } auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer; gu->action = groupActMakeLeader; strcpy(gu->membername, newleader->GetName()); - strcpy(gu->yourname, oldleader->GetName()); + strcpy(gu->yourname, GetOldLeaderName()); SetLeader(newleader); database.SetGroupLeaderName(GetID(), newleader->GetName()); UpdateGroupAAs(); @@ -2352,9 +2363,22 @@ void Group::ChangeLeader(Mob* newleader) members[i]->CastToClient()->SendGroupLeaderChangePacket(newleader->GetName()); members[i]->CastToClient()->QueuePacket(outapp); + Log(Logs::Detail, Logs::Group, "ChangeLeader(): Local leader update packet sent to: %s .", members[i]->GetName()); } } safe_delete(outapp); + + Log(Logs::Detail, Logs::Group, "ChangeLeader(): Old Leader is: %s New leader is: %s", GetOldLeaderName(), newleader->GetName()); + + ServerPacket* pack = new ServerPacket(ServerOP_ChangeGroupLeader, sizeof(ServerGroupLeader_Struct)); + ServerGroupLeader_Struct* fgu = (ServerGroupLeader_Struct*)pack->pBuffer; + fgu->zoneid = zone->GetZoneID(); + fgu->gid = GetID(); + strcpy(fgu->leader_name, newleader->GetName()); + strcpy(fgu->oldleader_name, GetOldLeaderName()); + worldserver.SendPacket(pack); + + SetOldLeaderName(newleader->GetName()); } const char *Group::GetClientNameByIndex(uint8 index) diff --git a/zone/groups.h b/zone/groups.h index 57dfdbfb6..c27f9d59a 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -37,6 +37,8 @@ public: GroupIDConsumer() { id = 0; } GroupIDConsumer(uint32 gid) { id = gid; } inline const uint32 GetID() const { return id; } + void SetOldLeaderName(const char* oldleader) { strcpy(oldleadername, oldleader); } + char* GetOldLeaderName() { return oldleadername; } protected: friend class EntityList; @@ -44,6 +46,7 @@ protected: inline void SetID(uint32 set_id) { id = set_id; } private: uint32 id; + char oldleadername[64]; // Keeps the previous leader name, so when the entity is destroyed we can still transfer leadership. }; class Group : public GroupIDConsumer { @@ -58,6 +61,7 @@ public: void SendLeadershipAAUpdate(); void SendWorldGroup(uint32 zone_id,Mob* zoningmember); bool DelMemberOOZ(const char *Name); + bool DelMemberOOZ(const char *Name, bool checkleader); bool DelMember(Mob* oldmember,bool ignoresender = false); void DisbandGroup(bool joinraid = false); void GetMemberList(std::list& member_list, bool clear_list = true); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 920a3d958..eea45d057 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -877,7 +877,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (gl->zoneid == zone->GetZoneID() && gl->instance_id == zone->GetInstanceID()) break; - entity_list.SendGroupLeave(gl->gid, gl->member_name); + entity_list.SendGroupLeave(gl->gid, gl->member_name, gl->checkleader); } break; } @@ -1114,6 +1114,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } + case ServerOP_ChangeGroupLeader: { + ServerGroupLeader_Struct *fgu = (ServerGroupLeader_Struct *) pack->pBuffer; + if (zone) { + if (fgu->zoneid == zone->GetZoneID()) { + break; + } + entity_list.SendGroupLeader(fgu->gid, fgu->leader_name, fgu->oldleader_name); + } + break; + } + case ServerOP_OOZGroupMessage: { ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer; if (zone) { From 7fbf522aa3d7553bee3a673f00b34148c3abfd7d Mon Sep 17 00:00:00 2001 From: Trust Date: Sat, 2 Jan 2021 22:54:26 -0500 Subject: [PATCH 2/3] Added LogGroup to logging aliases. --- common/eqemu_logsys_log_aliases.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index efe25dabe..307cc9fc5 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -161,6 +161,11 @@ OutF(LogSys, Logs::Detail, Logs::Doors, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogGroup(message, ...) do {\ + if (LogSys.log_settings[Logs::Group].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Group, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define LogGuilds(message, ...) do {\ if (LogSys.log_settings[Logs::Guilds].is_category_enabled == 1)\ OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -719,6 +724,9 @@ #define LogDoorsDetail(message, ...) do {\ } while (0) +#define LogGroup(message, ...) do {\ +} while (0) + #define LogGuilds(message, ...) do {\ } while (0) From 29e693d443030325073b90e598ed41b4ce85083a Mon Sep 17 00:00:00 2001 From: Trust Date: Sat, 2 Jan 2021 22:57:57 -0500 Subject: [PATCH 3/3] Also added LogGroupDetail --- common/eqemu_logsys_log_aliases.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 307cc9fc5..e32b43b4f 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -166,6 +166,11 @@ OutF(LogSys, Logs::General, Logs::Group, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogGroupDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Group].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Group, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define LogGuilds(message, ...) do {\ if (LogSys.log_settings[Logs::Guilds].is_category_enabled == 1)\ OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -727,6 +732,9 @@ #define LogGroup(message, ...) do {\ } while (0) +#define LogGroupDetail(message, ...) do {\ +} while (0) + #define LogGuilds(message, ...) do {\ } while (0)