/* EQ2Emulator: Everquest II Server Emulator Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) This file is part of EQ2Emulator. EQ2Emulator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. EQ2Emulator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ #include "PlayerGroups.h" #include "../common/Log.h" #include "World.h" #include "Spells.h" #include "LuaInterface.h" #include "Bots/Bot.h" #include "SpellProcess.h" #include "Rules/Rules.h" #include "Web/PeerManager.h" #include "WorldDatabase.h" extern ConfigReader configReader; extern ZoneList zone_list; extern RuleManager rule_manager; extern PeerManager peer_manager; extern WorldDatabase database; /******************************************************** PlayerGroup ********************************************************/ PlayerGroup::PlayerGroup(int32 id) { m_id = id; MGroupMembers.SetName("MGroupMembers"); SetDefaultGroupOptions(); } PlayerGroup::~PlayerGroup() { Disband(); } bool PlayerGroup::AddMember(Entity* member, bool is_leader) { // Check to make sure the entity we are adding is valid if (!member) { LogWrite(GROUP__ERROR, 0, "Group", "New member is null"); return false; } // Make sure entity we are adding isn't already in a group by checking if it has a GroupMemberInfo pointer if (member->GetGroupMemberInfo()) { LogWrite(GROUP__ERROR, 0, "Group", "New member (%s) already has a group", member->GetName()); return false; } // Create a new GroupMemberInfo and assign it to the new member GroupMemberInfo* gmi = new GroupMemberInfo; gmi->group_id = m_id; gmi->member = member; gmi->leader = is_leader; if (member->IsPlayer()) { gmi->is_client = true; gmi->client = ((Player*)member)->GetClient(); } else { gmi->is_client = false; gmi->client = 0; } gmi->mentor_target_char_id = 0; if (member->GetZone()) { gmi->zone_id = member->GetZone()->GetZoneID(); gmi->instance_id = member->GetZone()->GetInstanceID(); } gmi->client_peer_address = std::string(net.GetWorldAddress()); gmi->client_peer_port = net.GetWorldPort(); gmi->is_raid_looter = false; member->SetGroupMemberInfo(gmi); member->group_id = gmi->group_id; MGroupMembers.writelock(); m_members.push_back(gmi); member->UpdateGroupMemberInfo(true, true); MGroupMembers.releasewritelock(); SendGroupUpdate(); return true; } bool PlayerGroup::AddMemberFromPeer(std::string name, bool isleader, bool isclient, int8 class_id, sint32 hp_cur, sint32 hp_max, int16 level_cur, int16 level_max, sint32 power_cur, sint32 power_max, int8 race_id, std::string zonename, int32 mentor_target_char_id, int32 zone_id, int32 instance_id, std::string peer_client_address, int16 peer_client_port, bool is_raid_looter) { // Create a new GroupMemberInfo and assign it to the new member GroupMemberInfo* gmi = new GroupMemberInfo; gmi->group_id = m_id; gmi->member = nullptr; gmi->leader = isleader; gmi->is_client = isclient; gmi->client = 0; gmi->mentor_target_char_id = 0; gmi->class_id = class_id; gmi->hp_max = hp_max; gmi->hp_current = hp_cur; gmi->level_max = level_max; gmi->level_current = level_cur; gmi->name = name; gmi->power_current = power_cur; gmi->power_max = power_max; gmi->race_id = race_id; gmi->zone = zonename; gmi->zone_id = zone_id; gmi->instance_id = instance_id; gmi->mentor_target_char_id = mentor_target_char_id; gmi->client_peer_address = peer_client_address; gmi->client_peer_port = peer_client_port; gmi->is_raid_looter = is_raid_looter; MGroupMembers.writelock(); m_members.push_back(gmi); MGroupMembers.releasewritelock(); SendGroupUpdate(); return true; } bool PlayerGroup::RemoveMember(Entity* member) { GroupMemberInfo* gmi = member->GetGroupMemberInfo(); if (!gmi) { return false; } bool ret = false; bool selfInRaid = IsInRaidGroup(gmi->group_id, false); MGroupMembers.writelock(); member->SetGroupMemberInfo(0); deque::iterator erase_itr = m_members.end(); deque::iterator itr; for (itr = m_members.begin(); itr != m_members.end(); itr++) { if (gmi == *itr) erase_itr = itr; if (member->IsPlayer() && (*itr)->mentor_target_char_id == ((Player*)member)->GetCharacterID() && (*itr)->client) { (*itr)->mentor_target_char_id = 0; (*itr)->client->GetPlayer()->EnableResetMentorship(); } if ((*itr)->client) { (*itr)->client->GetPlayer()->SetCharSheetChanged(true); if (selfInRaid) (*itr)->client->GetPlayer()->SetRaidSheetChanged(true); } } if (erase_itr != m_members.end()) { ret = true; m_members.erase(erase_itr); } MGroupMembers.releasewritelock(); member->SetGroupMemberInfo(nullptr); safe_delete(gmi); if (member->IsBot()) ((Bot*)member)->Camp(); return ret; } bool PlayerGroup::RemoveMember(std::string name, bool is_client, int32 charID) { bool ret = false; GroupMemberInfo* gmi = nullptr; bool selfInRaid = IsInRaidGroup(GetID(), false); deque::iterator erase_itr = m_members.end(); deque::iterator itr; for (itr = m_members.begin(); itr != m_members.end(); itr++) { if ((*itr)->name == name && (*itr)->is_client == is_client) { gmi = (*itr); erase_itr = itr; } if (is_client && charID > 0 && (*itr)->mentor_target_char_id == charID && (*itr)->client) { (*itr)->mentor_target_char_id = 0; (*itr)->client->GetPlayer()->EnableResetMentorship(); } if ((*itr)->client) { (*itr)->client->GetPlayer()->SetCharSheetChanged(true); if (selfInRaid) (*itr)->client->GetPlayer()->SetRaidSheetChanged(true); } } if (erase_itr != m_members.end()) { ret = true; m_members.erase(erase_itr); } MGroupMembers.releasewritelock(); safe_delete(gmi); return ret; } void PlayerGroup::Disband() { m_raidgroups.clear(); deque::iterator itr; MGroupMembers.writelock(); bool selfInRaid = IsInRaidGroup(GetID(), false); for (itr = m_members.begin(); itr != m_members.end(); itr++) { if ((*itr)->member) { (*itr)->member->SetGroupMemberInfo(0); if ((*itr)->member->IsBot()) ((Bot*)(*itr)->member)->Camp(); } if ((*itr)->mentor_target_char_id && (*itr)->client) { (*itr)->mentor_target_char_id = 0; (*itr)->client->GetPlayer()->EnableResetMentorship(); } if ((*itr)->client) { (*itr)->client->GetPlayer()->SetCharSheetChanged(true); if (selfInRaid) (*itr)->client->GetPlayer()->SetRaidSheetChanged(true); } safe_delete(*itr); } m_members.clear(); MGroupMembers.releasewritelock(); } void PlayerGroup::SendGroupUpdate(Client* exclude, bool forceRaidUpdate) { bool selfInRaid = IsInRaidGroup(GetID(), false); deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* gmi = *itr; if (gmi->client && gmi->client != exclude && !gmi->client->IsZoning()) { gmi->client->GetPlayer()->SetCharSheetChanged(true); if (selfInRaid || forceRaidUpdate) gmi->client->GetPlayer()->SetRaidSheetChanged(true); } } MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } void PlayerGroup::SimpleGroupMessage(const char* message) { deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* info = *itr; if (info->client) info->client->SimpleMessage(CHANNEL_GROUP_CHAT, message); } MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } void PlayerGroup::SendGroupMessage(int8 type, const char* message, ...) { va_list argptr; char buffer[4096]; buffer[0] = 0; va_start(argptr, message); vsnprintf(buffer, sizeof(buffer), message, argptr); va_end(argptr); deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* info = *itr; if (info->client) info->client->SimpleMessage(type, buffer); } MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } void PlayerGroup::GroupChatMessage(Spawn* from, int32 language, const char* message, int16 channel) { deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* info = *itr; if (info && info->client && info->client->GetCurrentZone()) info->client->GetCurrentZone()->HandleChatMessage(info->client, from, 0, channel, message, 0, 0, true, language); } MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } void PlayerGroup::GroupChatMessage(std::string fromName, int32 language, const char* message, int16 channel) { deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* info = *itr; if (info && info->client && info->client->GetCurrentZone()) info->client->GetCurrentZone()->HandleChatMessage(info->client, fromName, "", channel, message, 0, 0, language); } MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } bool PlayerGroup::MakeLeader(Entity* new_leader) { if (!new_leader || new_leader->GetGroupMemberInfo()) return false; bool selfInRaid = IsInRaidGroup(GetID(), false); deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); new_leader->GetGroupMemberInfo()->leader = true; for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* info = *itr; if (info && info != new_leader->GetGroupMemberInfo() && info->leader) { info->leader = false; peer_manager.sendPeersGroupMember(GetID(), info, true); break; } if ((*itr)->client) { (*itr)->client->GetPlayer()->SetCharSheetChanged(true); if (selfInRaid) (*itr)->client->GetPlayer()->SetRaidSheetChanged(true); } } peer_manager.sendPeersGroupMember(GetID(), new_leader->GetGroupMemberInfo(), true); MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); SendGroupUpdate(); return true; } std::string PlayerGroup::GetLeaderName() { std::string name(""); deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* info = *itr; if (info->leader && info->name.size() > 0) { name = info->name; break; } } MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); return name; } bool PlayerGroup::ShareQuestWithGroup(Client* quest_sharer, Quest* quest) { if (!quest || !quest_sharer) return false; bool canShare = quest->CanShareQuestCriteria(quest_sharer); if (!canShare) { return false; } deque::iterator itr; MGroupMembers.readlock(__FUNCTION__, __LINE__); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* info = *itr; if (info && info->client && info->client->GetCurrentZone()) { if (quest_sharer != info->client && info->client->GetPlayer()->HasAnyQuest(quest->GetQuestID()) == 0) { info->client->AddPendingQuest(new Quest(quest)); info->client->Message(CHANNEL_COLOR_YELLOW, "%s has shared the quest %s with you.", quest_sharer->GetPlayer()->GetName(), quest->GetName()); } } } MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); return true; } /******************************************************** PlayerGroupManager ********************************************************/ PlayerGroupManager::PlayerGroupManager() { m_nextGroupID = 1; MPendingInvites.SetName("PlayerGroupManager::m_pendingInvites"); } PlayerGroupManager::~PlayerGroupManager() { MPendingInvites.writelock(__FUNCTION__, __LINE__); m_pendingInvites.clear(); MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); std::unique_lock lock(MGroups); map::iterator itr; for (itr = m_groups.begin(); itr != m_groups.end(); itr++) safe_delete(itr->second); m_groups.clear(); } bool PlayerGroupManager::AddGroupMember(int32 group_id, Entity* member, bool is_leader) { std::shared_lock lock(MGroups); bool ret = false; if (m_groups.count(group_id) > 0) { PlayerGroup* group = m_groups[group_id]; ret = group->AddMember(member, is_leader); peer_manager.sendPeersGroupMember(group_id, member->GetGroupMemberInfo()); } return ret; } bool PlayerGroupManager::AddGroupMemberFromPeer(int32 group_id, GroupMemberInfo* info) { if (!info) return false; std::shared_lock lock(MGroups); bool ret = false; if (m_groups.count(group_id) > 0) { PlayerGroup* group = m_groups[group_id]; ret = group->AddMemberFromPeer(info->name, info->leader, info->is_client, info->class_id, info->hp_current, info->hp_max, info->level_current, info->level_max, info->power_current, info->power_max, info->race_id, info->zone, info->mentor_target_char_id, info->zone_id, info->instance_id, info->client_peer_address, info->client_peer_port, info->is_raid_looter); } return ret; } bool PlayerGroupManager::RemoveGroupMember(int32 group_id, Entity* member) { if (!member) return false; bool ret = false; bool remove = false; Client* client = 0; if (member->GetGroupMemberInfo() && member->GetGroupMemberInfo()->mentor_target_char_id) { if (member->IsPlayer()) { Player* tmpPlayer = (Player*)member; member->GetGroupMemberInfo()->mentor_target_char_id = 0; tmpPlayer->EnableResetMentorship(); } } GroupLock(__FUNCTION__, __LINE__); if (m_groups.count(group_id) > 0) { PlayerGroup* group = m_groups[group_id]; if (member->IsPlayer()) client = member->GetGroupMemberInfo()->client; ret = group->RemoveMember(member); // If only 1 person left in the group set a flag to remove the group if (group->Size() == 1) remove = true; } ReleaseGroupLock(__FUNCTION__, __LINE__); if (client) RemoveGroupBuffs(group_id, client); peer_manager.sendPeersRemoveGroupMember(group_id, std::string(member->GetName()), (member->IsPlayer() ? ((Player*)member)->GetCharacterID() : 0), member->IsPlayer()); // Call RemoveGroup outside the locks as it uses the same locks if (remove) { RemoveGroup(group_id); peer_manager.sendPeersDisbandGroup(group_id); } return ret; } bool PlayerGroupManager::RemoveGroupMember(int32 group_id, std::string name, bool is_client, int32 charID) { bool ret = false; bool remove = false; Client* client = 0; GroupLock(__FUNCTION__, __LINE__); if (m_groups.count(group_id) > 0) { PlayerGroup* group = m_groups[group_id]; ret = group->RemoveMember(name, is_client, charID); // If only 1 person left in the group set a flag to remove the group if (group->Size() == 1) remove = true; } ReleaseGroupLock(__FUNCTION__, __LINE__); //if (client) // RemoveGroupBuffs(group_id, client); // Call RemoveGroup outside the locks as it uses the same locks if (remove) RemoveGroup(group_id); else { GroupMessage(group_id, "%s has left the group.", name.c_str()); } return ret; } int32 PlayerGroupManager::NewGroup(Entity* leader, GroupOptions* goptions, int32 override_group_id) { std::unique_lock lock(MGroups); int32 groupID = 0; // Highly doubt this will ever be needed but putting it in any way, basically bump the id and ensure // no active group is currently using this id, if we hit the max for an int32 then reset the id to 1 if (!override_group_id) { do { groupID = peer_manager.getUniqueGroupId(); } while (m_groups.count(groupID) > 0); } else if (override_group_id) { groupID = override_group_id; if (m_groups.count(groupID)) return 0; // group already exists } // last resort if the unique group id is not working out while (m_groups.count(groupID) > 0) { // If m_nextGroupID is at its max then reset it to 1, else increment it if (groupID == 4294967295) groupID = 1; else groupID++; } // Create a new group with the valid ID we got from above PlayerGroup* new_group = new PlayerGroup(groupID); new_group->SetDefaultGroupOptions(goptions); // Add the new group to the list (need to do this first, AddMember needs ref to the PlayerGroup ptr -> UpdateGroupMemberInfo) m_groups[groupID] = new_group; return groupID; } void PlayerGroupManager::RemoveGroup(int32 group_id) { std::unique_lock lock(MGroups); // Check to see if the id is in the list if (m_groups.count(group_id) > 0) { // Get a pointer to the group PlayerGroup* group = m_groups[group_id]; std::vector raidGroups; m_groups[group_id]->GetRaidGroups(&raidGroups); if (raidGroups.size() > 0) { std::vector::iterator group_itr; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { if (m_groups.count((*group_itr))) { m_groups[(*group_itr)]->ReplaceRaidGroups(&raidGroups); } } } m_groups[group_id]->ClearGroupRaid(); // Erase the group from the list m_groups.erase(group_id); // Delete the group safe_delete(group); } } int8 PlayerGroupManager::Invite(Player* leader, Entity* member) { int8 ret = 255; // Should be changed, if it is not then we have an unknown error // Lock the pending invite list so we can work with it MPendingInvites.writelock(__FUNCTION__, __LINE__); if (!member || (member->IsBot() && ((Bot*)member)->IsImmediateCamp())) ret = 6; // failure, not a valid target else if (member->IsNPC() && (!member->IsBot() /*|| !member->IsMec()*/)) ret = 6; else if (leader == member) ret = 5; // failure, can't invite yourself else if (member->GetGroupMemberInfo()) ret = 1; // failure, member already in a group // Check to see if the target of the invite already has a pending invite else if (m_pendingInvites.count(member->GetName()) > 0) ret = 2; // Target already has an invite // Check to see if the player that invited is already in a group else if (leader->GetGroupMemberInfo()) { // Read lock the group list so we can get the size of the inviters group GroupLock(__FUNCTION__, __LINE__); int32 group_size = m_groups[leader->GetGroupMemberInfo()->group_id]->Size(); ReleaseGroupLock(__FUNCTION__, __LINE__); // Check to see if the group is full if (m_groups[leader->GetGroupMemberInfo()->group_id]->Size() >= 6) ret = 3; // Group full // Group isn't full so add the member to the pending invite list else { m_pendingInvites[member->GetName()] = leader->GetName(); ret = 0; // Success } } // Inviter is not in a group else { // Check to see if the inviter has a pending invite himself if (m_pendingInvites.count(leader->GetName()) > 0) ret = 4; // inviter already has a pending group invite // No pending invites for the inviter add both the inviter and the target of the invite to the list else { m_pendingInvites[leader->GetName()] = leader->GetName(); m_pendingInvites[member->GetName()] = leader->GetName(); ret = 0; // success } } // Release the lock on pending invites MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); bool group_existed = false; if (leader && leader->GetGroupMemberInfo()) { group_existed = true; } /* testing purposes only */ if (ret == 0 && member->IsNPC()) { if (net.is_primary) { int32 group_id = 0; int8 result = AcceptInvite(member, &group_id, false); if (result == 0) { if (leader && leader->GetClient()) { if (group_existed) { group_id = leader->GetGroupMemberInfo()->group_id; GroupOptions options; leader->GetClient()->SetGroupOptionsReference(&options); peer_manager.sendPeersNewGroupRequest("", 0, group_id, std::string(leader->GetName()), std::string(member->GetName()), &options); } else { AddGroupMember(group_id, leader, true); } GroupMessage(group_id, "%s has joined the group.", member->GetName()); AddGroupMember(group_id, member); } } } else { GroupOptions options; if (leader && leader->GetClient()) { GroupOptions options; leader->GetClient()->SetGroupOptionsReference(&options); peer_manager.sendPrimaryNewGroupRequest(std::string(leader->GetName()), std::string(member->GetName()), member->GetID(), &options); } } } return ret; } bool PlayerGroupManager::AddInvite(Player* leader, Entity* member) { bool ret = true; MPendingInvites.writelock(__FUNCTION__, __LINE__); if (leader == member) ret = false; // failure, can't invite yourself else if (member->GetGroupMemberInfo()) ret = false; // Check to see if the target of the invite already has a pending invite else if (m_pendingInvites.count(member->GetName()) > 0) ret = false; // Target already has an invite if (leader->GetGroupMemberInfo()) { m_pendingInvites[member->GetName()] = leader->GetName(); } else { m_pendingInvites[leader->GetName()] = leader->GetName(); m_pendingInvites[member->GetName()] = leader->GetName(); } MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); return ret; } int8 PlayerGroupManager::AcceptInvite(Entity* member, int32* group_override_id, bool auto_add_group) { int8 ret = 3; // default to unknown error int32 groupResultID = 0; MPendingInvites.writelock(__FUNCTION__, __LINE__); if (m_pendingInvites.count(member->GetName()) > 0) { string leader = m_pendingInvites[member->GetName()]; Client* client_leader = zone_list.GetClientByCharName(leader); if (client_leader) { if (m_pendingInvites.count(leader) > 0) { if (!client_leader->GetPlayer()->GetGroupMemberInfo()) { GroupOptions goptions; goptions.loot_method = client_leader->GetPlayer()->GetInfoStruct()->get_group_loot_method(); goptions.loot_items_rarity = client_leader->GetPlayer()->GetInfoStruct()->get_group_loot_items_rarity(); goptions.auto_split = client_leader->GetPlayer()->GetInfoStruct()->get_group_auto_split(); goptions.default_yell = client_leader->GetPlayer()->GetInfoStruct()->get_group_default_yell(); goptions.group_autolock = client_leader->GetPlayer()->GetInfoStruct()->get_group_autolock(); goptions.group_lock_method = client_leader->GetPlayer()->GetInfoStruct()->get_group_lock_method(); goptions.solo_autolock = client_leader->GetPlayer()->GetInfoStruct()->get_group_solo_autolock(); goptions.auto_loot_method = client_leader->GetPlayer()->GetInfoStruct()->get_group_auto_loot_method(); groupResultID = NewGroup(client_leader->GetPlayer(), &goptions, (group_override_id != nullptr) ? (*group_override_id) : 0); if (group_override_id != nullptr && *group_override_id == 0) { *group_override_id = groupResultID; } } else { if (group_override_id != nullptr && *group_override_id == 0) { if (client_leader->GetPlayer()->GetGroupMemberInfo()) *group_override_id = client_leader->GetPlayer()->GetGroupMemberInfo()->group_id; } } m_pendingInvites.erase(leader); } // Remove from invite list and add to the group if (m_pendingInvites.count(member->GetName())) { m_pendingInvites.erase(member->GetName()); } int32 result_group_id = 0; if (*group_override_id && *group_override_id > 0) { result_group_id = *group_override_id; } else result_group_id = groupResultID; if (auto_add_group && result_group_id) { AddGroupMember(result_group_id, client_leader->GetPlayer()); GroupMessage(result_group_id, "%s has joined the group.", member->GetName()); AddGroupMember(result_group_id, member); } ret = 0; // success } else { // Was unable to find the leader, remove from the invite list if (m_pendingInvites.count(member->GetName())) { m_pendingInvites.erase(member->GetName()); } ret = 2; // failure, can't find leader } } else ret = 1; // failure, no pending invite MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); return ret; } void PlayerGroupManager::DeclineInvite(Entity* member) { MPendingInvites.writelock(__FUNCTION__, __LINE__); if (m_pendingInvites.count(member->GetName()) > 0) { string leader = m_pendingInvites[member->GetName()]; // send decline to leader m_pendingInvites.erase(member->GetName()); if (m_pendingInvites.count(leader) > 0) m_pendingInvites.erase(leader); } if (m_raidPendingInvites.count(member->GetName()) > 0) { string leader = m_raidPendingInvites[member->GetName()]; // send decline to leader m_raidPendingInvites.erase(member->GetName()); } MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); } bool PlayerGroupManager::IsGroupIDValid(int32 group_id) { std::shared_lock lock(MGroups); bool ret = false; ret = m_groups.count(group_id) > 0; return ret; } void PlayerGroupManager::SendGroupUpdate(int32 group_id, Client* exclude, bool forceRaidUpdate) { std::shared_lock lock(MGroups); if (m_groups.count(group_id) > 0) { std::vector raidGroups; m_groups[group_id]->GetRaidGroups(&raidGroups); if (raidGroups.size() < 1) raidGroups.push_back(group_id); std::vector::iterator group_itr; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { if (m_groups.count((*group_itr))) { m_groups[(*group_itr)]->SendGroupUpdate(exclude, forceRaidUpdate); } } } } PlayerGroup* PlayerGroupManager::GetGroup(int32 group_id) { if (m_groups.count(group_id) > 0) return m_groups[group_id]; return 0; } void PlayerGroupManager::ClearPendingInvite(Entity* member) { MPendingInvites.writelock(__FUNCTION__, __LINE__); if (m_pendingInvites.count(member->GetName()) > 0) m_pendingInvites.erase(member->GetName()); MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); } std::string PlayerGroupManager::HasPendingInvite(Entity* member) { std::string leader(""); MPendingInvites.writelock(__FUNCTION__, __LINE__); if (m_pendingInvites.count(member->GetName()) > 0) { leader = m_pendingInvites[member->GetName()]; } MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); return leader; } void PlayerGroupManager::RemoveGroupBuffs(int32 group_id, Client* client) { SpellEffects* se = 0; Spell* spell = 0; LuaSpell* luaspell = 0; EQ2Packet* packet = 0; Entity* pet = 0; Player* player = 0; Entity* charmed_pet = 0; PlayerGroup* group = 0; MGroups.lock_shared(); if (m_groups.count(group_id) > 0) group = m_groups[group_id]; if (group && client) { /* first remove all spell effects this group member has on them from other group members */ player = client->GetPlayer(); bool recoup_lock = true; for (int i = 0; i < NUM_SPELL_EFFECTS; i++) { if (recoup_lock) { player->GetSpellEffectMutex()->readlock(__FUNCTION__, __LINE__); recoup_lock = false; se = player->GetSpellEffects(); } if (se && se[i].spell_id != 0xFFFFFFFF) { //If the client is the caster, don't remove the spell if (se[i].caster == player) continue; luaspell = se[i].spell; spell = luaspell->spell; /* is this a friendly group spell? */ if (spell && spell->GetSpellData()->group_spell && spell->GetSpellData()->friendly_spell) { player->GetSpellEffectMutex()->releasereadlock(__FUNCTION__, __LINE__); recoup_lock = true; // we have to remove our spell effect mutex lock since RemoveSpellEffect needs a write lock to remove it //Remove all group buffs not cast by this player player->RemoveSpellEffect(luaspell); player->RemoveSpellBonus(luaspell); player->RemoveSkillBonus(spell->GetSpellID()); //Also remove group buffs from pets pet = 0; charmed_pet = 0; if (player->HasPet()) { pet = player->GetPet(); pet = player->GetCharmedPet(); } if (pet) { pet->RemoveSpellEffect(luaspell); pet->RemoveSpellBonus(luaspell); } if (charmed_pet) { charmed_pet->RemoveSpellEffect(luaspell); charmed_pet->RemoveSpellBonus(luaspell); } } } } if (!recoup_lock) { // we previously set a readlock that we now release player->GetSpellEffectMutex()->releasereadlock(__FUNCTION__, __LINE__); } packet = client->GetPlayer()->GetSkills()->GetSkillPacket(client->GetVersion()); if (packet) client->QueuePacket(packet); } MGroups.unlock_shared(); } int32 PlayerGroupManager::GetGroupSize(int32 group_id) { std::shared_lock lock(MGroups); int32 ret = 0; if (m_groups.count(group_id) > 0) ret = m_groups[group_id]->Size(); return ret; } void PlayerGroupManager::SendGroupQuests(int32 group_id, Client* client) { std::shared_lock lock(MGroups); GroupMemberInfo* info = 0; if (m_groups.count(group_id) > 0) { m_groups[group_id]->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = m_groups[group_id]->GetMembers(); deque::iterator itr; for (itr = members->begin(); itr != members->end(); itr++) { info = *itr; if (info->client) { LogWrite(PLAYER__DEBUG, 0, "Player", "Send Quest Journal..."); info->client->SendQuestJournal(false, client); client->SendQuestJournal(false, info->client); } } m_groups[group_id]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } } bool PlayerGroupManager::HasGroupCompletedQuest(int32 group_id, int32 quest_id) { std::shared_lock lock(MGroups); bool questComplete = true; GroupMemberInfo* info = 0; if (m_groups.count(group_id) > 0) { m_groups[group_id]->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = m_groups[group_id]->GetMembers(); deque::iterator itr; for (itr = members->begin(); itr != members->end(); itr++) { info = *itr; if (info->client) { bool isComplete = info->client->GetPlayer()->HasQuestBeenCompleted(quest_id); if (!isComplete) { questComplete = isComplete; break; } } } m_groups[group_id]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } return questComplete; } void PlayerGroupManager::SimpleGroupMessage(int32 group_id, const char* message) { std::shared_lock lock(MGroups); if (m_groups.count(group_id) > 0) m_groups[group_id]->SimpleGroupMessage(message); } void PlayerGroupManager::SendGroupMessage(int32 group_id, int8 type, const char* message, ...) { std::shared_lock lock(MGroups); va_list argptr; char buffer[4096]; buffer[0] = 0; va_start(argptr, message); vsnprintf(buffer, sizeof(buffer), message, argptr); va_end(argptr); if (m_groups.count(group_id) > 0) m_groups[group_id]->SendGroupMessage(type, buffer); } void PlayerGroupManager::GroupMessage(int32 group_id, const char* message, ...) { va_list argptr; char buffer[4096]; buffer[0] = 0; va_start(argptr, message); vsnprintf(buffer, sizeof(buffer), message, argptr); va_end(argptr); SimpleGroupMessage(group_id, buffer); } void PlayerGroupManager::GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message, int16 channel) { std::shared_lock lock(MGroups); if (m_groups.count(group_id) > 0) { std::vector raidGroups; if (channel == CHANNEL_RAID_SAY) m_groups[group_id]->GetRaidGroups(&raidGroups); if (raidGroups.size() < 1) raidGroups.push_back(group_id); std::vector::iterator group_itr; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { if (m_groups.count((*group_itr))) { m_groups[(*group_itr)]->GroupChatMessage(from, language, message, channel); } } } } void PlayerGroupManager::GroupChatMessage(int32 group_id, std::string fromName, int32 language, const char* message, int16 channel) { std::shared_lock lock(MGroups); if (m_groups.count(group_id) > 0) { std::vector raidGroups; if (channel == CHANNEL_RAID_SAY) m_groups[group_id]->GetRaidGroups(&raidGroups); if (raidGroups.size() < 1) raidGroups.push_back(group_id); std::vector::iterator group_itr; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { if (m_groups.count((*group_itr))) { m_groups[(*group_itr)]->GroupChatMessage(fromName, language, message, channel); } } } } void PlayerGroupManager::SendGroupChatMessage(int32 group_id, int16 channel, const char* message, ...) { std::shared_lock lock(MGroups); va_list argptr; char buffer[4096]; buffer[0] = 0; va_start(argptr, message); vsnprintf(buffer, sizeof(buffer), message, argptr); va_end(argptr); if (m_groups.count(group_id) > 0) { std::vector raidGroups; if (channel == CHANNEL_RAID_SAY || channel == CHANNEL_LOOT_ROLLS) m_groups[group_id]->GetRaidGroups(&raidGroups); if (raidGroups.size() < 1) raidGroups.push_back(group_id); std::vector::iterator group_itr; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { if (m_groups.count((*group_itr))) { m_groups[(*group_itr)]->SendGroupMessage(channel, buffer); } } } } bool PlayerGroupManager::MakeLeader(int32 group_id, Entity* new_leader) { std::shared_lock lock(MGroups); if (m_groups.count(group_id) > 0) return m_groups[group_id]->MakeLeader(new_leader); return false; } void PlayerGroupManager::UpdateGroupBuffs() { map::iterator itr; deque::iterator member_itr; deque::iterator target_itr; map::iterator itr_skills; MaintainedEffects* me = nullptr; LuaSpell* luaspell = nullptr; Spell* spell = nullptr; Entity* group_member = nullptr; SkillBonus* sb = nullptr; EQ2Packet* packet = nullptr; int32 i = 0; PlayerGroup* group = nullptr; Player* caster = nullptr; vector new_target_list; vector char_list; Client* client = nullptr; bool has_effect = false; vector* sb_list = nullptr; BonusValues* bv = nullptr; Entity* pet = nullptr; Entity* charmed_pet = nullptr; for (itr = m_groups.begin(); itr != m_groups.end(); itr++) { group = itr->second; /* loop through the group members and see if any of them have any maintained spells that are group buffs and friendly. if so, update the list of targets and apply/remove effects as needed */ vector players; group->MGroupMembers.readlock(__FUNCTION__, __LINE__); for (member_itr = group->GetMembers()->begin(); member_itr != group->GetMembers()->end(); member_itr++) { if ((*member_itr)->client) caster = (*member_itr)->client->GetPlayer(); else caster = 0; if (!caster) continue; if (!caster->GetMaintainedSpellBySlot(0)) continue; players.push_back(caster); } group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); vector::iterator vitr; for (vitr = players.begin(); vitr != players.end(); vitr++) { caster = *vitr; caster->GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__); // go through the player's maintained spells me = caster->GetMaintainedSpells(); for (i = 0; i < NUM_MAINTAINED_EFFECTS; i++) { if (me[i].spell_id == 0xFFFFFFFF) continue; luaspell = me[i].spell; if (!luaspell) continue; if (!luaspell->caster) { LogWrite(PLAYER__ERROR, 0, "Player", "Bad luaspell, caster is NULL, spellid: %u", me[i].spell_id); continue; } spell = luaspell->spell; if (spell && spell->GetSpellData()->group_spell && spell->GetSpellData()->friendly_spell && (spell->GetSpellData()->target_type == SPELL_TARGET_GROUP_AE || spell->GetSpellData()->target_type == SPELL_TARGET_RAID_AE)) { luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__); luaspell->char_id_targets.clear(); for (target_itr = group->GetMembers()->begin(); target_itr != group->GetMembers()->end(); target_itr++) { group_member = (*target_itr)->member; if (!group_member) continue; if (group_member == caster) continue; client = (*target_itr)->client; has_effect = false; if (group_member->GetSpellEffect(spell->GetSpellID(), caster)) { has_effect = true; } if (!has_effect && (std::find(luaspell->removed_targets.begin(), luaspell->removed_targets.end(), group_member->GetID()) != luaspell->removed_targets.end())) { continue; } // Check if player is within range of the caster if (!rule_manager.GetGlobalRule(R_Spells, EnableCrossZoneGroupBuffs)->GetInt8() && (group_member->GetZone() != caster->GetZone() || caster->GetDistance(group_member) > spell->GetSpellData()->radius)) { if (has_effect) { group_member->RemoveSpellEffect(luaspell); group_member->RemoveSpellBonus(luaspell); group_member->RemoveSkillBonus(spell->GetSpellID()); if (client) { packet = ((Player*)group_member)->GetSkills()->GetSkillPacket(client->GetVersion()); if (packet) client->QueuePacket(packet); } //Also remove group buffs from pet if (group_member->HasPet()) { pet = group_member->GetPet(); charmed_pet = group_member->GetCharmedPet(); if (pet) { pet->RemoveSpellEffect(luaspell); pet->RemoveSpellBonus(luaspell); } if (charmed_pet) { charmed_pet->RemoveSpellEffect(luaspell); charmed_pet->RemoveSpellBonus(luaspell); } } } continue; } if (group_member->GetZone() != caster->GetZone()) { SpellProcess::AddSelfAndPetToCharTargets(luaspell, group_member); } else { //this group member is a target of the spell new_target_list.push_back(group_member->GetID()); } if (has_effect) continue; pet = 0; charmed_pet = 0; if (group_member->HasPet()) { pet = group_member->GetPet(); charmed_pet = group_member->GetCharmedPet(); } group_member->AddSpellEffect(luaspell, luaspell->timer.GetRemainingTime() != 0 ? luaspell->timer.GetRemainingTime() : 0); if (pet) pet->AddSpellEffect(luaspell, luaspell->timer.GetRemainingTime() != 0 ? luaspell->timer.GetRemainingTime() : 0); if (charmed_pet) charmed_pet->AddSpellEffect(luaspell, luaspell->timer.GetRemainingTime() != 0 ? luaspell->timer.GetRemainingTime() : 0); if (pet) new_target_list.push_back(pet->GetID()); if (charmed_pet) new_target_list.push_back(charmed_pet->GetID()); // look for a spell bonus on caster's spell sb_list = caster->GetAllSpellBonuses(luaspell); for (int32 x = 0; x < sb_list->size(); x++) { bv = sb_list->at(x); group_member->AddSpellBonus(luaspell, bv->type, bv->value, bv->class_req, bv->race_req, bv->faction_req); if (pet) pet->AddSpellBonus(luaspell, bv->type, bv->value, bv->class_req, bv->race_req, bv->faction_req); if (charmed_pet) charmed_pet->AddSpellBonus(luaspell, bv->type, bv->value, bv->class_req, bv->race_req, bv->faction_req); } sb_list->clear(); safe_delete(sb_list); // look for a skill bonus on the caster's spell sb = caster->GetSkillBonus(me[i].spell_id); if (sb) { for (itr_skills = sb->skills.begin(); itr_skills != sb->skills.end(); itr_skills++) group_member->AddSkillBonus(sb->spell_id, (*itr_skills).second->skill_id, (*itr_skills).second->value); } if (client) { packet = ((Player*)group_member)->GetSkills()->GetSkillPacket(client->GetVersion()); if (packet) client->QueuePacket(packet); } } luaspell->targets.swap(new_target_list); SpellProcess::AddSelfAndPet(luaspell, caster); luaspell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); new_target_list.clear(); } } caster->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__); } } } bool PlayerGroupManager::IsInGroup(int32 group_id, Entity* member) { std::shared_lock lock(MGroups); bool ret = false; if (m_groups.count(group_id) > 0) { m_groups[group_id]->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = m_groups[group_id]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { if (member == members->at(i)->member) { ret = true; break; } } m_groups[group_id]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } return ret; } Entity* PlayerGroupManager::IsPlayerInGroup(int32 group_id, int32 character_id) { std::shared_lock lock(MGroups); Entity* ret = nullptr; if (m_groups.count(group_id) > 0) { m_groups[group_id]->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = m_groups[group_id]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { if (members->at(i)->member && members->at(i)->member->IsPlayer() && character_id == ((Player*)members->at(i)->member)->GetCharacterID()) { ret = members->at(i)->member; break; } } m_groups[group_id]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } return ret; } void PlayerGroupManager::SendPeerGroupData(std::string peerId) { if (peerId.size() < 1) // must provide a peerId return; std::shared_lock lock(MGroups); // unique lock to avoid updates while we push to a new peer map::iterator itr; for (itr = m_groups.begin(); itr != m_groups.end(); itr++) { PlayerGroup* group = itr->second; if (group) { int32 groupID = group->GetID(); std::vector raidGroups; group->GetRaidGroups(&raidGroups); peer_manager.sendPeersNewGroupRequest("", 0, groupID, "", "", group->GetGroupOptions(), peerId, &raidGroups); m_groups[groupID]->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = m_groups[groupID]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { GroupMemberInfo* info = members->at(i); if (info) peer_manager.sendPeersGroupMember(groupID, info, false, peerId); } m_groups[groupID]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } } } void PlayerGroup::RemoveClientReference(Client* remove) { deque::iterator itr; MGroupMembers.writelock(); for (itr = m_members.begin(); itr != m_members.end(); itr++) { GroupMemberInfo* gmi = *itr; if (gmi->client && gmi->client == remove) { gmi->client = 0; gmi->member = 0; break; } } MGroupMembers.releasewritelock(); } void PlayerGroup::UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked) { Player* player = (Player*)ent; if (!player || !player->GetGroupMemberInfo()) return; if (!groupMembersLocked) MGroupMembers.writelock(); GroupMemberInfo* group_member_info = player->GetGroupMemberInfo(); player->GetGroupMemberInfo()->class_id = player->GetAdventureClass(); group_member_info->hp_max = player->GetTotalHP(); group_member_info->hp_current = player->GetHP(); group_member_info->level_max = player->GetLevel(); group_member_info->level_current = player->GetLevel(); group_member_info->name = string(player->GetName()); group_member_info->power_current = player->GetPower(); group_member_info->power_max = player->GetTotalPower(); group_member_info->race_id = player->GetRace(); if (player->GetZone()) { group_member_info->zone = player->GetZone()->GetZoneDescription(); group_member_info->zone_id = player->GetZone()->GetZoneID(); group_member_info->instance_id = player->GetZone()->GetInstanceID(); } else { group_member_info->zone = "Unknown"; group_member_info->zone_id = 0; group_member_info->instance_id = 0; } peer_manager.sendPeersGroupMember(group_member_info->group_id, group_member_info, true); if (!groupMembersLocked) MGroupMembers.releasewritelock(); } void PlayerGroup::GetRaidGroups(std::vector* groups) { std::shared_lock lock(MRaidGroups); if (groups) groups->insert(groups->end(), m_raidgroups.begin(), m_raidgroups.end()); } void PlayerGroup::ReplaceRaidGroups(std::vector* groups) { std::unique_lock lock(MRaidGroups); if (groups) { m_raidgroups.clear(); m_raidgroups.insert(m_raidgroups.end(), groups->begin(), groups->end()); } } bool PlayerGroup::IsInRaidGroup(int32 groupID, bool isLeaderGroup) { std::unique_lock lock(MRaidGroups); if (isLeaderGroup) { if (m_raidgroups.size() > 0) { int32 group = m_raidgroups[0]; if (group == groupID) return true; } } else if (std::find(m_raidgroups.begin(), m_raidgroups.end(), groupID) != m_raidgroups.end()) { return true; } return false; } void PlayerGroup::AddGroupToRaid(int32 groupID) { std::unique_lock lock(MRaidGroups); if (std::find(m_raidgroups.begin(), m_raidgroups.end(), groupID) == m_raidgroups.end()) { m_raidgroups.push_back(groupID); } } void PlayerGroup::RemoveGroupFromRaid(int32 groupID) { std::unique_lock lock(MRaidGroups); std::vector::iterator group_itr = std::find(m_raidgroups.begin(), m_raidgroups.end(), groupID); if (group_itr != m_raidgroups.end()) { m_raidgroups.erase(group_itr); } } bool PlayerGroup::IsGroupRaid() { std::unique_lock lock(MRaidGroups); return (m_raidgroups.size() > 1); } void PlayerGroup::ClearGroupRaid() { std::unique_lock lock(MRaidGroups); m_raidgroups.clear(); } void PlayerGroupManager::UpdateGroupMemberInfoFromPeer(int32 group_id, std::string name, bool is_client, GroupMemberInfo* updateinfo) { std::shared_lock lock(MGroups); if (m_groups.count(group_id) > 0) { m_groups[group_id]->MGroupMembers.writelock(__FUNCTION__, __LINE__); deque* members = m_groups[group_id]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { GroupMemberInfo* curinfo = members->at(i); if (curinfo && curinfo->name == name && curinfo->is_client == is_client) { curinfo->class_id = updateinfo->class_id; curinfo->hp_max = updateinfo->hp_max; curinfo->hp_current = updateinfo->hp_current; curinfo->level_max = updateinfo->level_max; curinfo->level_current = updateinfo->level_current; curinfo->name = updateinfo->name; curinfo->power_current = updateinfo->power_current; curinfo->power_max = updateinfo->power_max; curinfo->race_id = updateinfo->race_id; curinfo->zone = updateinfo->zone; curinfo->mentor_target_char_id = updateinfo->mentor_target_char_id; curinfo->is_client = updateinfo->is_client; curinfo->leader = updateinfo->leader; curinfo->client_peer_address = updateinfo->client_peer_address; curinfo->client_peer_port = updateinfo->client_peer_port; curinfo->is_raid_looter = updateinfo->is_raid_looter; break; } } m_groups[group_id]->MGroupMembers.releasewritelock(__FUNCTION__, __LINE__); } } void PlayerGroupManager::ClearGroupRaid(int32 groupID) { std::shared_lock lock(MGroups); // Check to see if the id is in the list if (m_groups.count(groupID) > 0) { m_groups[groupID]->ClearGroupRaid(); } SendGroupUpdate(groupID); } void PlayerGroupManager::RemoveGroupFromRaid(int32 groupID, int32 targetGroupID) { std::shared_lock lock(MGroups); // Check to see if the id is in the list if (m_groups.count(groupID) > 0) { // Get a pointer to the group PlayerGroup* group = m_groups[groupID]; std::vector raidGroups; m_groups[groupID]->GetRaidGroups(&raidGroups); if (raidGroups.size() > 0) { std::vector::iterator group_itr; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { if (m_groups.count((*group_itr))) { m_groups[(*group_itr)]->RemoveGroupFromRaid(targetGroupID); } } } } } bool PlayerGroupManager::IsInRaidGroup(int32 groupID, int32 targetGroupID, bool isLeaderGroup) { std::shared_lock lock(MGroups); if (m_groups.count(groupID)) return m_groups[groupID]->IsInRaidGroup(targetGroupID, isLeaderGroup); return false; } bool PlayerGroupManager::GetDefaultGroupOptions(int32 groupID, GroupOptions* options) { std::shared_lock lock(MGroups); if (m_groups.count(groupID)) return m_groups[groupID]->GetDefaultGroupOptions(options); return false; } void PlayerGroupManager::GetRaidGroups(int32 groupID, std::vector* newGroups) { std::shared_lock lock(MGroups); if (m_groups.count(groupID)) m_groups[groupID]->GetRaidGroups(newGroups); } void PlayerGroupManager::ReplaceRaidGroups(int32 groupID, std::vector* newGroups) { std::shared_lock lock(MGroups); if (m_groups.count(groupID)) m_groups[groupID]->ReplaceRaidGroups(newGroups); SendGroupUpdate(groupID); } void PlayerGroupManager::SetGroupOptions(int32 groupID, GroupOptions* options) { std::shared_lock lock(MGroups); if (m_groups.count(groupID)) m_groups[groupID]->SetDefaultGroupOptions(options); } void PlayerGroupManager::SendWhoGroupMembers(Client* client, int32 groupID) { std::shared_lock lock(MGroups); if (m_groups.count(groupID) > 0) { PacketStruct* packet = configReader.getStruct("WS_WhoQueryReply", client->GetVersion()); if (packet) { packet->setDataByName("account_id", client->GetAccountID()); packet->setDataByName("unknown", 0xFFFFFFFF); int8 num_characters = 0; m_groups[groupID]->MGroupMembers.writelock(__FUNCTION__, __LINE__); deque* members = m_groups[groupID]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { GroupMemberInfo* curinfo = members->at(i); if (curinfo && curinfo->name.size() > 0) { num_characters++; } } packet->setDataByName("response", 2); packet->setArrayLengthByName("num_characters", (int8)num_characters); for (int8 i = 0; i < members->size(); i++) { GroupMemberInfo* curinfo = members->at(i); if (curinfo && curinfo->name.size() > 0) { packet->setArrayDataByName("char_name", curinfo->name.c_str(), i); packet->setArrayDataByName("level", curinfo->level_current, i); packet->setArrayDataByName("class", curinfo->class_id, i); packet->setArrayDataByName("unknown4", 0xFF, i); //probably tradeskill class packet->setArrayDataByName("race", curinfo->race_id, i); packet->setArrayDataByName("zone", curinfo->zone.c_str(), i); } } client->QueuePacket(packet->serialize()); safe_delete(packet); } m_groups[groupID]->MGroupMembers.releasewritelock(__FUNCTION__, __LINE__); } } void PlayerGroupManager::SendWhoRaidMembers(Client* client, int32 groupID) { std::shared_lock lock(MGroups); PlayerGroup* group = nullptr; if (m_groups.count(groupID)) group = m_groups[groupID]; std::vector groups; std::vector::iterator group_itr; if (group) group->GetRaidGroups(&groups); if (groups.size() > 0) { PacketStruct* packet = configReader.getStruct("WS_WhoQueryReply", client->GetVersion()); if (packet) { packet->setDataByName("account_id", client->GetAccountID()); packet->setDataByName("unknown", 0xFFFFFFFF); int8 num_characters = 0; for (group_itr = groups.begin(); group_itr != groups.end(); group_itr++) { if (m_groups.count((*group_itr))) { // we will release this lock after we submit the data out below in the second group_itr m_groups[(*group_itr)]->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = m_groups[(*group_itr)]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { GroupMemberInfo* curinfo = members->at(i); if (curinfo && curinfo->name.size() > 0) { num_characters++; } } } } packet->setDataByName("response", 2); packet->setArrayLengthByName("num_characters", (int8)num_characters); int8 pos = 0; for (group_itr = groups.begin(); group_itr != groups.end(); group_itr++) { if (m_groups.count((*group_itr))) { deque* members = m_groups[(*group_itr)]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { GroupMemberInfo* curinfo = members->at(i); if (curinfo && curinfo->name.size() > 0) { packet->setArrayDataByName("char_name", curinfo->name.c_str(), pos); packet->setArrayDataByName("level", curinfo->level_current, pos); packet->setArrayDataByName("class", curinfo->class_id, pos); packet->setArrayDataByName("unknown4", 0xFF, pos); //probably tradeskill class packet->setArrayDataByName("race", curinfo->race_id, pos); packet->setArrayDataByName("zone", curinfo->zone.c_str(), pos); pos++; } } // release previously established lock during the count m_groups[(*group_itr)]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } } client->QueuePacket(packet->serialize()); safe_delete(packet); } } else { client->SimpleMessage(CHANNEL_COLOR_RED, "You are not currently in a raid."); } } int8 PlayerGroupManager::AcceptRaidInvite(std::string acceptorName, int32 groupID) { std::unique_lock lock(MGroups); std::string raidLeaderName(""); MPendingInvites.writelock(__FUNCTION__, __LINE__); bool inviteAccept = true; if (m_raidPendingInvites.count(acceptorName.c_str()) > 0) { raidLeaderName = m_raidPendingInvites[acceptorName.c_str()]; m_raidPendingInvites.erase(acceptorName.c_str()); } MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); if (raidLeaderName.size() < 1) return 0; Client* client_leader = zone_list.GetClientByCharName(raidLeaderName.c_str()); if (client_leader) { if (client_leader->GetPlayer()->GetGroupMemberInfo() && m_groups.count(groupID) && m_groups.count(client_leader->GetPlayer()->GetGroupMemberInfo()->group_id)) { Player* player = client_leader->GetPlayer(); PlayerGroup* spawn_group = m_groups[groupID]; PlayerGroup* player_group = m_groups[player->GetGroupMemberInfo()->group_id]; if (spawn_group && player_group) { std::vector groups; spawn_group->GetRaidGroups(&groups); if (groups.size() > 0) { return 0; } player_group->GetRaidGroups(&groups); if (groups.size() > 3) { return 0; } spawn_group->SetDefaultGroupOptions(player_group->GetGroupOptions()); if (groups.size() < 1) { player_group->AddGroupToRaid(player_group->GetID()); player_group->AddGroupToRaid(spawn_group->GetID()); player_group->GetRaidGroups(&groups); spawn_group->ReplaceRaidGroups(&groups); return 1; } else { groups.clear(); player_group->AddGroupToRaid(spawn_group->GetID()); player_group->GetRaidGroups(&groups); std::vector::iterator group_itr; for (group_itr = groups.begin(); group_itr != groups.end(); group_itr++) { int32 cur_group_id = (*group_itr); if (cur_group_id == player_group->GetID() || m_groups.count(cur_group_id) < 1) // we already set the player_group above, skip continue; PlayerGroup* temp_group = m_groups[cur_group_id]; temp_group->ReplaceRaidGroups(&groups); } return 1; } } } } else { // must be somewhere else in the world } return 0; } bool PlayerGroupManager::SendRaidInvite(Client* sender, Entity* target) { std::shared_lock lock(MGroups); if (!sender || !target) return false; if (!target->IsPlayer() || !((Player*)target)->GetClient() || !target->GetGroupMemberInfo() || !sender->GetPlayer()->GetGroupMemberInfo()) return false; Player* player = sender->GetPlayer(); int32 spawn_group_id = target->GetGroupMemberInfo()->group_id; if (m_groups.count(spawn_group_id) < 1) return false; PlayerGroup* spawn_group = m_groups[spawn_group_id]; int32 player_group_id = player->GetGroupMemberInfo() ? player->GetGroupMemberInfo()->group_id : 0; PlayerGroup* player_group = nullptr; if (m_groups.count(player_group_id)) player_group = m_groups[player_group_id]; // check if already invited if (spawn_group && !player->IsGroupMember((Player*)target) && !spawn_group->IsGroupRaid() && player_group && player->GetGroupMemberInfo()->leader && (!player_group->IsInRaidGroup(spawn_group_id) || player_group->IsInRaidGroup(player_group_id, true))) { std::string leader = spawn_group->GetLeaderName(); std::vector groups; player_group->GetRaidGroups(&groups); if (groups.size() > 3) { sender->SimpleMessage(CHANNEL_COLOR_RED, "You are currently in a full raid."); return false; } if (leader.size() < 1) { sender->SimpleMessage(CHANNEL_COLOR_RED, "Your target has no leader."); return false; } MPendingInvites.readlock(__FUNCTION__, __LINE__); bool inviteAccept = true; if (m_raidPendingInvites.count(leader.c_str()) > 0) { inviteAccept = false; sender->SimpleMessage(CHANNEL_COLOR_RED, "Leader of the other group has a pending raid invite."); } MPendingInvites.releasereadlock(__FUNCTION__, __LINE__); if (!inviteAccept) return false; MPendingInvites.writelock(__FUNCTION__, __LINE__); m_raidPendingInvites[leader] = player->GetGroupMemberInfo()->name.c_str(); MPendingInvites.releasewritelock(__FUNCTION__, __LINE__); sender->SendReceiveOffer(((Player*)target)->GetClient(), 3, std::string(sender->GetPlayer()->GetName()), 1); return true; } return false; } void PlayerGroupManager::SplitWithGroupOrRaid(Client* client, int32 coin_plat, int32 coin_gold, int32 coin_silver, int32 coin_copper) { std::shared_lock lock(MGroups); bool startWithLooter = true; if (!client->GetPlayer()->GetGroupMemberInfo()) return; if (!coin_plat && !coin_gold && !coin_silver && !coin_copper) return; if (client->GetPlayer()->GetInfoStruct()->get_coin_plat() < coin_plat && client->GetPlayer()->GetInfoStruct()->get_coin_gold() < coin_gold && client->GetPlayer()->GetInfoStruct()->get_coin_silver() < coin_silver && client->GetPlayer()->GetInfoStruct()->get_coin_copper() < coin_copper) { // lacking coin return; } int32 groupID = client->GetPlayer()->GetGroupMemberInfo()->group_id; if (m_groups.count(groupID) < 1) { return; } PlayerGroup* group = m_groups[groupID]; if (group) { bool isLeadGroup = group->IsInRaidGroup(group->GetID(), true); bool isInRaid = group->IsInRaidGroup(group->GetID()); std::vector raidGroups; group->GetRaidGroups(&raidGroups); if (!isInRaid && raidGroups.size() < 1) { raidGroups.push_back(group->GetID()); } std::vector::iterator group_itr; int32 split_coin_per_player = 0; int32 actual_coins = coin_plat * 1000000 + coin_gold * 10000 + coin_silver * 100 + coin_copper; int32 coins_remain_after_split = actual_coins; int32 total_coins = actual_coins; bool foundLooterResetRaidRun = false; int8 members_in_zone = 0; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { group = m_groups[(*group_itr)]; if (!group) continue; group->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = group->GetMembers(); for (int8 i = 0; i < members->size(); i++) { Entity* member = members->at(i)->member; if (!member || !member->IsPlayer()) continue; if (member->GetZone() != client->GetPlayer()->GetZone()) continue; members_in_zone++; } group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } if (members_in_zone < 1) // this should not happen, but divide by zero checked members_in_zone = 1; split_coin_per_player = actual_coins / members_in_zone; coins_remain_after_split = actual_coins - (split_coin_per_player * members_in_zone); // try to individually take each tier of coin and not split the coin in inventory client->GetPlayer()->GetInfoStruct()->set_coin_plat(client->GetPlayer()->GetInfoStruct()->get_coin_plat() - coin_plat); client->GetPlayer()->GetInfoStruct()->set_coin_gold(client->GetPlayer()->GetInfoStruct()->get_coin_gold() - coin_gold); client->GetPlayer()->GetInfoStruct()->set_coin_silver(client->GetPlayer()->GetInfoStruct()->get_coin_silver() - coin_silver); client->GetPlayer()->GetInfoStruct()->set_coin_copper(client->GetPlayer()->GetInfoStruct()->get_coin_copper() - coin_copper); int32 lootGroup = 0; for (group_itr = raidGroups.begin(); group_itr != raidGroups.end();) { group = m_groups[(*group_itr)]; if (!group) continue; isLeadGroup = group->IsInRaidGroup((*group_itr), true); group->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = group->GetMembers(); LogWrite(LOOT__INFO, 0, "Loot", "%s: Group SplitWithGroupOrRaid, split coin per player %u, remaining coin after split %u", client->GetPlayer()->GetName(), split_coin_per_player, coins_remain_after_split); for (int8 i = 0; i < members->size(); i++) { Entity* member = members->at(i)->member; if (!member || !member->IsPlayer()) continue; if (member->GetZone() != client->GetPlayer()->GetZone()) continue; // this will make sure we properly send the loot window to the initial requester if there is no item rarity matches if (startWithLooter && member != client->GetPlayer()) continue; else if (!startWithLooter && member == client->GetPlayer()) continue; int32 coin_recv = 0; if (member == client->GetPlayer() && (split_coin_per_player + coins_remain_after_split) > 0) { coin_recv = split_coin_per_player + coins_remain_after_split; ((Player*)member)->AddCoins(split_coin_per_player + coins_remain_after_split); if (coins_remain_after_split > 0) // overflow of coin division went to the first player coins_remain_after_split = 0; } else if (split_coin_per_player > 0) { coin_recv = split_coin_per_player; ((Player*)member)->AddCoins(split_coin_per_player); } if (coin_recv && ((Player*)member)->GetClient()) { ((Player*)member)->GetClient()->Message(CHANNEL_MONEY_SPLIT, "Your share of the %s split from %s is %s.", client->GetCoinMessage(total_coins).c_str(), client->GetPlayer()->GetName(), client->GetCoinMessage(coin_recv).c_str()); } if (startWithLooter) { startWithLooter = false; foundLooterResetRaidRun = true; // we got it, shouldn't hit this again break; } } group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); if (foundLooterResetRaidRun) { group_itr = raidGroups.begin(); foundLooterResetRaidRun = false; // disable running it again } else group_itr++; } // end raid groups } // end group } bool PlayerGroupManager::IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, Client* client, int32 zoneID, int32 instanceID) { std::shared_lock lock(MGroups); ZoneServer* ret = nullptr; PlayerGroup* group = nullptr; bool succeed = false; if (client->GetPlayer()->GetGroupMemberInfo() && m_groups.count(client->GetPlayer()->GetGroupMemberInfo()->group_id)) group = m_groups[client->GetPlayer()->GetGroupMemberInfo()->group_id]; else return false; std::vector raidGroups; std::vector::iterator group_itr; if (group) group->GetRaidGroups(&raidGroups); group->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = group->GetMembers(); deque::iterator itr; for (itr = members->begin(); itr != members->end(); itr++) { // If a group member matches a target if ((*itr)->is_client && (*itr)->member && (*itr)->member->GetZone() && (*itr)->zone_id == zoneID && (*itr)->instance_id == instanceID) { // toggle the flag and break the loop ret = (*itr)->member->GetZone(); break; } else if ((*itr)->is_client && (*itr)->zone_id == zoneID && (*itr)->instance_id == instanceID) { // toggle the flag and break the loop std::string id = peer_manager.isPeer((*itr)->client_peer_address, (*itr)->client_peer_port); std::shared_ptr peer = peer_manager.getPeerById(id); if (peer) { ZoneServer* tmp = new ZoneServer((*itr)->zone.c_str()); database.LoadZoneInfo(tmp); peer_manager.setZonePeerData(details, peer->id, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, std::string(tmp->GetZoneFile()), std::string(tmp->GetZoneName()), tmp->GetZoneID(), tmp->GetInstanceID(), tmp->GetSafeX(), tmp->GetSafeY(), tmp->GetSafeZ(), tmp->GetSafeHeading(), tmp->GetZoneLockState(), tmp->GetMinimumStatus(), tmp->GetMinimumLevel(), tmp->GetMaximumLevel(), tmp->GetMinimumVersion(), tmp->GetDefaultLockoutTime(), tmp->GetDefaultReenterTime(), tmp->GetInstanceType(), tmp->NumPlayers()); safe_delete(tmp); succeed = true; break; } } } group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); if (succeed) { return true; } else if (ret) { peer_manager.setZonePeerDataSelf(details, std::string(ret->GetZoneFile()), std::string(ret->GetZoneName()), ret->GetZoneID(), ret->GetInstanceID(), ret->GetSafeX(), ret->GetSafeY(), ret->GetSafeZ(), ret->GetSafeHeading(), ret->GetZoneLockState(), ret->GetMinimumStatus(), ret->GetMinimumLevel(), ret->GetMaximumLevel(), ret->GetMinimumVersion(), ret->GetDefaultLockoutTime(), ret->GetDefaultReenterTime(), ret->GetInstanceType(), ret->NumPlayers(), ret); return true; } for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) { if (m_groups.count((*group_itr))) { group->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = group->GetMembers(); deque::iterator itr; for (itr = members->begin(); itr != members->end(); itr++) { // If a group member matches a target if ((*itr)->is_client && (*itr)->zone_id == zoneID && (*itr)->instance_id == instanceID) { // toggle the flag and break the loop std::string id = peer_manager.isPeer((*itr)->client_peer_address, (*itr)->client_peer_port); std::shared_ptr peer = peer_manager.getPeerById(id); if (peer) { ZoneServer* tmp = new ZoneServer((*itr)->zone.c_str()); database.LoadZoneInfo(tmp); peer_manager.setZonePeerData(details, peer->id, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, std::string(tmp->GetZoneFile()), std::string(tmp->GetZoneName()), tmp->GetZoneID(), tmp->GetInstanceID(), tmp->GetSafeX(), tmp->GetSafeY(), tmp->GetSafeZ(), tmp->GetSafeHeading(), tmp->GetZoneLockState(), tmp->GetMinimumStatus(), tmp->GetMinimumLevel(), tmp->GetMaximumLevel(), tmp->GetMinimumVersion(), tmp->GetDefaultLockoutTime(), tmp->GetDefaultReenterTime(), tmp->GetInstanceType(), tmp->NumPlayers()); safe_delete(tmp); succeed = true; break; } } } group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); if (succeed) break; } } return succeed; } void PlayerGroupManager::ClearGroupRaidLooterFlag(int32 groupID) { std::shared_lock lock(MGroups); if (m_groups.count(groupID) > 0) { m_groups[groupID]->MGroupMembers.readlock(__FUNCTION__, __LINE__); deque* members = m_groups[groupID]->GetMembers(); for (int8 i = 0; i < members->size(); i++) { GroupMemberInfo* curinfo = members->at(i); if (curinfo) { curinfo->is_raid_looter = false; } } m_groups[groupID]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); } } Entity* PlayerGroup::GetGroupMemberByPosition(Entity* seeker, int32 mapped_position) { Entity* ret = nullptr; deque::iterator itr; MGroupMembers.readlock(); int32 count = 1; for (itr = m_members.begin(); itr != m_members.end(); itr++) { if ((*itr)->member == seeker) { continue; } count++; if (count >= mapped_position) { ret = (Entity*)(*itr)->member; break; } } MGroupMembers.releasereadlock(); return ret; } void PlayerGroup::SetDefaultGroupOptions(GroupOptions* options) { MGroupMembers.writelock(); if (options != nullptr) { group_options.loot_method = options->loot_method; group_options.loot_items_rarity = options->loot_items_rarity; group_options.auto_split = options->auto_split; group_options.default_yell = options->default_yell; group_options.group_lock_method = options->group_lock_method; group_options.group_autolock = options->group_autolock; group_options.solo_autolock = options->solo_autolock; group_options.auto_loot_method = options->auto_loot_method; } else { group_options.loot_method = 1; group_options.loot_items_rarity = 0; group_options.auto_split = 1; group_options.default_yell = 1; group_options.group_lock_method = 0; group_options.group_autolock = 0; group_options.solo_autolock = 0; group_options.auto_loot_method = 0; group_options.last_looted_index = 0; } MGroupMembers.releasewritelock(); } bool PlayerGroup::GetDefaultGroupOptions(GroupOptions* options) { bool setOptions = false; MGroupMembers.readlock(); if (options != nullptr) { options->loot_method = group_options.loot_method; options->loot_items_rarity = group_options.loot_items_rarity; options->auto_split = group_options.auto_split; options->default_yell = group_options.default_yell; options->group_lock_method = group_options.group_lock_method; options->group_autolock = group_options.group_autolock; options->solo_autolock = group_options.solo_autolock; options->auto_loot_method = group_options.auto_loot_method; setOptions = true; } MGroupMembers.releasereadlock(); return setOptions; }