mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
Let world handle expedition leader changes
This should eliminate race conditions caused by zones trying to set a leader when members in different zones quit at the same time Zone still detects when leader goes offline to trigger a change since it's easier than having world process expedition member status updates and perform expedition lookups
This commit is contained in:
+69
-2
@@ -20,26 +20,83 @@
|
||||
|
||||
#include "expedition.h"
|
||||
#include "expedition_database.h"
|
||||
#include "cliententry.h"
|
||||
#include "clientlist.h"
|
||||
#include "zonelist.h"
|
||||
#include "zoneserver.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
|
||||
extern ClientList client_list;
|
||||
extern ZSList zoneserver_list;
|
||||
|
||||
Expedition::Expedition(uint32_t expedition_id, uint32_t dz_id, uint32_t dz_instance_id,
|
||||
uint32_t dz_zone_id, uint32_t start_time, uint32_t duration
|
||||
uint32_t dz_zone_id, uint32_t start_time, uint32_t duration, uint32_t leader_id
|
||||
) :
|
||||
m_expedition_id(expedition_id),
|
||||
m_dz_id(dz_id),
|
||||
m_dz_instance_id(dz_instance_id),
|
||||
m_dz_zone_id(dz_zone_id),
|
||||
m_start_time(std::chrono::system_clock::from_time_t(start_time)),
|
||||
m_duration(duration)
|
||||
m_duration(duration),
|
||||
m_leader_id(leader_id)
|
||||
{
|
||||
m_expire_time = m_start_time + m_duration;
|
||||
m_warning_cooldown_timer.Enable();
|
||||
}
|
||||
|
||||
void Expedition::AddMember(uint32_t character_id)
|
||||
{
|
||||
auto it = std::find_if(m_member_ids.begin(), m_member_ids.end(),
|
||||
[&](uint32_t member_id) { return member_id == character_id; });
|
||||
|
||||
if (it == m_member_ids.end())
|
||||
{
|
||||
m_member_ids.emplace_back(character_id);
|
||||
}
|
||||
}
|
||||
|
||||
void Expedition::RemoveMember(uint32_t character_id)
|
||||
{
|
||||
m_member_ids.erase(std::remove_if(m_member_ids.begin(), m_member_ids.end(),
|
||||
[&](uint32_t member_id) { return member_id == character_id; }
|
||||
), m_member_ids.end());
|
||||
|
||||
if (!m_member_ids.empty() && character_id == m_leader_id)
|
||||
{
|
||||
ChooseNewLeader();
|
||||
}
|
||||
}
|
||||
|
||||
void Expedition::ChooseNewLeader()
|
||||
{
|
||||
// we don't track expedition member status in world so may choose a linkdead member
|
||||
// this is fine since it will trigger another change when that member goes offline
|
||||
auto it = std::find_if(m_member_ids.begin(), m_member_ids.end(), [&](uint32_t member_id) {
|
||||
auto member_cle = (member_id != m_leader_id) ? client_list.FindCLEByCharacterID(member_id) : nullptr;
|
||||
return (member_id != m_leader_id && member_cle && member_cle->GetOnline() == CLE_Status::InZone);
|
||||
});
|
||||
|
||||
if (it == m_member_ids.end())
|
||||
{
|
||||
// no online members found, fallback to choosing any member
|
||||
it = std::find_if(m_member_ids.begin(), m_member_ids.end(),
|
||||
[&](uint32_t member_id) { return (member_id != m_leader_id); });
|
||||
}
|
||||
|
||||
if (it != m_member_ids.end())
|
||||
{
|
||||
SetNewLeader(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void Expedition::SetNewLeader(uint32_t character_id)
|
||||
{
|
||||
LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_expedition_id, m_leader_id, character_id);
|
||||
ExpeditionDatabase::UpdateLeaderID(m_expedition_id, character_id);
|
||||
m_leader_id = character_id;
|
||||
SendZonesLeaderChanged();
|
||||
}
|
||||
|
||||
void Expedition::SendZonesExpeditionDeleted()
|
||||
{
|
||||
uint32_t pack_size = sizeof(ServerExpeditionID_Struct);
|
||||
@@ -69,6 +126,16 @@ void Expedition::SendZonesExpireWarning(uint32_t minutes_remaining)
|
||||
zoneserver_list.SendPacket(pack.get());
|
||||
}
|
||||
|
||||
void Expedition::SendZonesLeaderChanged()
|
||||
{
|
||||
uint32_t pack_size = sizeof(ServerExpeditionLeaderID_Struct);
|
||||
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_ExpeditionLeaderChanged, pack_size));
|
||||
auto buf = reinterpret_cast<ServerExpeditionLeaderID_Struct*>(pack->pBuffer);
|
||||
buf->expedition_id = GetID();
|
||||
buf->leader_id = m_leader_id;
|
||||
zoneserver_list.SendPacket(pack.get());
|
||||
}
|
||||
|
||||
void Expedition::UpdateDzSecondsRemaining(uint32_t seconds_remaining)
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
+10
-5
@@ -24,19 +24,20 @@
|
||||
#include "../common/timer.h"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class Expedition
|
||||
{
|
||||
public:
|
||||
Expedition() = default;
|
||||
Expedition(uint32_t expedition_id, uint32_t dz_id, uint32_t dz_instance_id,
|
||||
uint32_t dz_zone_id, uint32_t expire_time, uint32_t duration);
|
||||
uint32_t dz_zone_id, uint32_t expire_time, uint32_t duration, uint32_t leader_id);
|
||||
|
||||
void AddMember(uint32_t character_id) { m_member_ids.emplace(character_id); }
|
||||
void RemoveMember(uint32_t character_id) { m_member_ids.erase(character_id); }
|
||||
void AddMember(uint32_t character_id);
|
||||
void RemoveMember(uint32_t character_id);
|
||||
void RemoveAllMembers() { m_member_ids.clear(); }
|
||||
void CheckExpireWarning();
|
||||
void ChooseNewLeader();
|
||||
uint32_t GetID() const { return m_expedition_id; }
|
||||
uint16_t GetInstanceID() const { return static_cast<uint16_t>(m_dz_instance_id); }
|
||||
uint16_t GetZoneID() const { return static_cast<uint16_t>(m_dz_zone_id); }
|
||||
@@ -47,18 +48,22 @@ public:
|
||||
void SendZonesDurationUpdate();
|
||||
void SendZonesExpeditionDeleted();
|
||||
void SendZonesExpireWarning(uint32_t minutes_remaining);
|
||||
void SetNewLeader(uint32_t new_leader_id);
|
||||
void SetPendingDelete(bool pending) { m_pending_delete = pending; }
|
||||
void UpdateDzSecondsRemaining(uint32_t seconds_remaining);
|
||||
std::chrono::system_clock::duration GetRemainingDuration() const;
|
||||
|
||||
private:
|
||||
void SendZonesLeaderChanged();
|
||||
|
||||
uint32_t m_expedition_id = 0;
|
||||
uint32_t m_dz_id = 0;
|
||||
uint32_t m_dz_instance_id = 0;
|
||||
uint32_t m_dz_zone_id = 0;
|
||||
uint32_t m_leader_id = 0;
|
||||
bool m_pending_delete = false;
|
||||
Timer m_warning_cooldown_timer;
|
||||
std::unordered_set<uint32_t> m_member_ids;
|
||||
std::vector<uint32_t> m_member_ids;
|
||||
std::chrono::seconds m_duration;
|
||||
std::chrono::time_point<std::chrono::system_clock> m_start_time;
|
||||
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
|
||||
|
||||
@@ -77,6 +77,7 @@ std::vector<Expedition> ExpeditionDatabase::LoadExpeditions(uint32_t select_expe
|
||||
instance_list.zone,
|
||||
instance_list.start_time,
|
||||
instance_list.duration,
|
||||
expeditions.leader_id,
|
||||
expedition_members.character_id
|
||||
FROM expeditions
|
||||
INNER JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id
|
||||
@@ -110,13 +111,14 @@ std::vector<Expedition> ExpeditionDatabase::LoadExpeditions(uint32_t select_expe
|
||||
static_cast<uint32_t>(strtoul(row[2], nullptr, 10)), // dz_instance_id
|
||||
static_cast<uint32_t>(strtoul(row[3], nullptr, 10)), // dz_zone_id
|
||||
static_cast<uint32_t>(strtoul(row[4], nullptr, 10)), // start_time
|
||||
static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) // duration
|
||||
static_cast<uint32_t>(strtoul(row[5], nullptr, 10)), // duration
|
||||
static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) // leader_id
|
||||
);
|
||||
}
|
||||
|
||||
last_expedition_id = expedition_id;
|
||||
|
||||
uint32_t member_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
|
||||
uint32_t member_id = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
|
||||
expeditions.back().AddMember(member_id);
|
||||
}
|
||||
}
|
||||
@@ -167,3 +169,14 @@ void ExpeditionDatabase::UpdateDzDuration(uint16_t instance_id, uint32_t new_dur
|
||||
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
|
||||
void ExpeditionDatabase::UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id)
|
||||
{
|
||||
LogExpeditionsDetail("Updating leader [{}] for expedition [{}]", leader_id, expedition_id);
|
||||
|
||||
auto query = fmt::format(SQL(
|
||||
UPDATE expeditions SET leader_id = {} WHERE id = {};
|
||||
), leader_id, expedition_id);
|
||||
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace ExpeditionDatabase
|
||||
void PurgeExpiredExpeditions();
|
||||
void PurgeExpiredCharacterLockouts();
|
||||
void UpdateDzDuration(uint16_t instance_id, uint32_t new_duration);
|
||||
void UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "expedition.h"
|
||||
#include "expedition_message.h"
|
||||
#include "expedition_state.h"
|
||||
#include "cliententry.h"
|
||||
@@ -34,6 +35,11 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack)
|
||||
{
|
||||
switch (pack->opcode)
|
||||
{
|
||||
case ServerOP_ExpeditionChooseNewLeader:
|
||||
{
|
||||
ExpeditionMessage::ChooseNewLeader(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_ExpeditionCreate:
|
||||
{
|
||||
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
|
||||
@@ -51,8 +57,8 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack)
|
||||
case ServerOP_ExpeditionMemberSwap:
|
||||
{
|
||||
auto buf = reinterpret_cast<ServerExpeditionMemberSwap_Struct*>(pack->pBuffer);
|
||||
expedition_state.MemberChange(buf->expedition_id, buf->remove_char_id, true);
|
||||
expedition_state.MemberChange(buf->expedition_id, buf->add_char_id, false);
|
||||
expedition_state.MemberChange(buf->expedition_id, buf->remove_char_id, true);
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
@@ -141,6 +147,12 @@ void ExpeditionMessage::MakeLeader(ServerPacket* pack)
|
||||
buf->is_char_online = true;
|
||||
new_leader_zs = new_leader_cle->Server();
|
||||
new_leader_zs->SendPacket(pack);
|
||||
|
||||
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
|
||||
if (expedition)
|
||||
{
|
||||
expedition->SetNewLeader(new_leader_cle->CharID());
|
||||
}
|
||||
}
|
||||
|
||||
// if old and new leader are in the same zone only send one message
|
||||
@@ -205,3 +217,13 @@ void ExpeditionMessage::RequestInvite(ServerPacket* pack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExpeditionMessage::ChooseNewLeader(ServerPacket* pack)
|
||||
{
|
||||
auto buf = reinterpret_cast<ServerExpeditionID_Struct*>(pack->pBuffer);
|
||||
auto expedition = expedition_state.GetExpedition(buf->expedition_id);
|
||||
if (expedition)
|
||||
{
|
||||
expedition->ChooseNewLeader();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ class ServerPacket;
|
||||
namespace ExpeditionMessage
|
||||
{
|
||||
void AddPlayer(ServerPacket* pack);
|
||||
void ChooseNewLeader(ServerPacket* pack);
|
||||
void GetOnlineMembers(ServerPacket* pack);
|
||||
void HandleZoneMessage(ServerPacket* pack);
|
||||
void MakeLeader(ServerPacket* pack);
|
||||
|
||||
+20
-24
@@ -30,6 +30,14 @@ extern ZSList zoneserver_list;
|
||||
|
||||
ExpeditionState expedition_state;
|
||||
|
||||
Expedition* ExpeditionState::GetExpedition(uint32_t expedition_id)
|
||||
{
|
||||
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(),
|
||||
[&](const Expedition& expedition) { return expedition.GetID() == expedition_id; });
|
||||
|
||||
return (it != m_expeditions.end()) ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
void ExpeditionState::LoadActiveExpeditions()
|
||||
{
|
||||
BenchTimer benchmark;
|
||||
@@ -51,11 +59,8 @@ void ExpeditionState::AddExpedition(uint32_t expedition_id)
|
||||
|
||||
if (expedition.IsValid())
|
||||
{
|
||||
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), [&](const Expedition& expedition) {
|
||||
return expedition.GetID() == expedition_id;
|
||||
});
|
||||
|
||||
if (it == m_expeditions.end())
|
||||
auto existing_expedition = GetExpedition(expedition_id);
|
||||
if (!existing_expedition)
|
||||
{
|
||||
m_expeditions.emplace_back(expedition);
|
||||
}
|
||||
@@ -73,41 +78,32 @@ void ExpeditionState::RemoveExpedition(uint32_t expedition_id)
|
||||
|
||||
void ExpeditionState::MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove)
|
||||
{
|
||||
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), [&](const Expedition& expedition) {
|
||||
return expedition.GetID() == expedition_id;
|
||||
});
|
||||
|
||||
if (it != m_expeditions.end())
|
||||
auto expedition = GetExpedition(expedition_id);
|
||||
if (expedition)
|
||||
{
|
||||
if (remove) {
|
||||
it->RemoveMember(character_id);
|
||||
expedition->RemoveMember(character_id);
|
||||
} else {
|
||||
it->AddMember(character_id);
|
||||
expedition->AddMember(character_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExpeditionState::RemoveAllMembers(uint32_t expedition_id)
|
||||
{
|
||||
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), [&](const Expedition& expedition) {
|
||||
return expedition.GetID() == expedition_id;
|
||||
});
|
||||
|
||||
if (it != m_expeditions.end())
|
||||
auto expedition = GetExpedition(expedition_id);
|
||||
if (expedition)
|
||||
{
|
||||
it->RemoveAllMembers();
|
||||
expedition->RemoveAllMembers();
|
||||
}
|
||||
}
|
||||
|
||||
void ExpeditionState::SetSecondsRemaining(uint32_t expedition_id, uint32_t seconds_remaining)
|
||||
{
|
||||
auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), [&](const Expedition& expedition) {
|
||||
return expedition.GetID() == expedition_id;
|
||||
});
|
||||
|
||||
if (it != m_expeditions.end())
|
||||
auto expedition = GetExpedition(expedition_id);
|
||||
if (expedition)
|
||||
{
|
||||
it->UpdateDzSecondsRemaining(seconds_remaining);
|
||||
expedition->UpdateDzSecondsRemaining(seconds_remaining);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class ExpeditionState
|
||||
{
|
||||
public:
|
||||
void AddExpedition(uint32_t expedition_id);
|
||||
Expedition* GetExpedition(uint32_t expedition_id);
|
||||
void LoadActiveExpeditions();
|
||||
void MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove);
|
||||
void Process();
|
||||
|
||||
@@ -1362,7 +1362,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
client_list.SendPacket(buf->character_name, pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_ExpeditionLeaderChanged:
|
||||
case ServerOP_ExpeditionLockout:
|
||||
case ServerOP_ExpeditionLockoutDuration:
|
||||
case ServerOP_ExpeditionLockState:
|
||||
@@ -1376,6 +1375,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_ExpeditionChooseNewLeader:
|
||||
case ServerOP_ExpeditionCreate:
|
||||
case ServerOP_ExpeditionGetOnlineMembers:
|
||||
case ServerOP_ExpeditionMemberChange:
|
||||
|
||||
Reference in New Issue
Block a user