mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 10:31:29 +00:00
[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.
This commit is contained in:
parent
de5b7f472d
commit
f854137ca0
@ -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",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
126
zone/groups.cpp
126
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)
|
||||
|
||||
@ -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<Mob*>& member_list, bool clear_list = true);
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user