Sync character lockouts when entering dz

This removes the is_pending column from character lockouts table

Synchronizing character lockout timers with the expedition's when zoning
into the dynamic zone simplifies adding missing lockouts to new members.
This also matches live behavior that replaces any character lockout
timers from another expedition with ones from the current expedition
This commit is contained in:
hg 2020-11-08 22:31:41 -05:00
parent fc7d8a82e5
commit 4f9eaf7574
7 changed files with 83 additions and 143 deletions

View File

@ -48,7 +48,6 @@ CREATE TABLE `character_expedition_lockouts` (
`expire_time` DATETIME NOT NULL DEFAULT current_timestamp(),
`duration` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`from_expedition_uuid` VARCHAR(36) NOT NULL,
`is_pending` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `character_id_expedition_name_event_name` (`character_id`, `expedition_name`, `event_name`)
)

View File

@ -9563,16 +9563,18 @@ void Client::UpdateExpeditionInfoAndLockouts()
// this is processed by client after entering a zone
SendDzCompassUpdate();
m_expedition_lockouts = ExpeditionDatabase::LoadCharacterLockouts(CharacterID());
auto expedition = GetExpedition();
if (expedition)
{
expedition->SendClientExpeditionInfo(this);
// live only adds lockouts obtained during the active expedition to new
// live synchronizes lockouts obtained during the active expedition to
// members once they zone into the expedition's dynamic zone instance
if (expedition->GetDynamicZone().IsCurrentZoneDzInstance())
{
ExpeditionDatabase::AssignPendingLockouts(CharacterID(), expedition->GetName());
expedition->SyncCharacterLockouts(CharacterID(), m_expedition_lockouts);
expedition->SetMemberStatus(this, ExpeditionMemberStatus::InDynamicZone);
}
else
@ -9581,7 +9583,7 @@ void Client::UpdateExpeditionInfoAndLockouts()
}
}
LoadAllExpeditionLockouts();
SendExpeditionLockoutTimers();
// ask world for any pending invite we saved from a previous zone
RequestPendingExpeditionInvite();
@ -9629,7 +9631,7 @@ void Client::AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool up
if (update_db) // for quest api
{
ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { lockout }, true);
ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { lockout });
}
SendExpeditionLockoutTimers();
@ -9662,7 +9664,7 @@ void Client::AddExpeditionLockoutDuration(
if (update_db)
{
ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { *it }, true);
ExpeditionDatabase::InsertCharacterLockouts(CharacterID(), { *it });
}
SendExpeditionLockoutTimers();
@ -9753,12 +9755,6 @@ bool Client::HasExpeditionLockout(
return (GetExpeditionLockout(expedition_name, event_name, include_expired) != nullptr);
}
void Client::LoadAllExpeditionLockouts()
{
m_expedition_lockouts = ExpeditionDatabase::LoadCharacterLockouts(CharacterID());
SendExpeditionLockoutTimers();
}
void Client::SendExpeditionLockoutTimers()
{
std::vector<ExpeditionLockoutTimerEntry_Struct> lockout_entries;

View File

@ -1143,7 +1143,6 @@ public:
void SendExpeditionLockoutTimers();
void SetExpeditionID(uint32 expedition_id) { m_expedition_id = expedition_id; };
void SetPendingExpeditionInvite(ExpeditionInvite&& invite) { m_pending_expedition_invite = invite; }
void LoadAllExpeditionLockouts();
void UpdateExpeditionInfoAndLockouts();
void DzListTimers();
void SetDzRemovalTimer(bool enable_timer);

View File

@ -298,7 +298,6 @@ void Expedition::SaveMembers(ExpeditionRequest& request)
}
ExpeditionDatabase::InsertMembers(m_id, m_members);
ExpeditionDatabase::DeleteAllMembersPendingLockouts(m_members);
m_dynamiczone.SaveInstanceMembersToDatabase(member_ids);
}
@ -555,7 +554,6 @@ void Expedition::RemoveAllMembers(bool enable_removal_timers)
{
m_dynamiczone.RemoveAllCharacters(enable_removal_timers);
ExpeditionDatabase::DeleteAllMembersPendingLockouts(m_members);
ExpeditionDatabase::DeleteAllMembers(m_id);
SendUpdatesToZoneMembers(true);
@ -849,38 +847,19 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std::
{
SendLeaderMessage(leader_client, Chat::Yellow, EXPEDITION_INVITE_ACCEPTED, { add_client->GetName() });
// insert pending lockouts client will receive when entering dynamic zone.
// only lockouts missing from client when they join are added. client may
// have a lockout that expires after joining and shouldn't receive it again
ExpeditionDatabase::DeletePendingLockouts(add_client->CharacterID());
std::vector<ExpeditionLockoutTimer> pending_lockouts;
for (const auto& lockout_iter : m_lockouts)
{
const ExpeditionLockoutTimer& lockout = lockout_iter.second;
if (lockout.IsFromExpedition(m_uuid) &&
!add_client->HasExpeditionLockout(m_expedition_name, lockout.GetEventName()))
{
// replay timers are optionally added to new members immediately on
// join with a fresh expire time using the original duration.
if (lockout.IsReplayTimer())
{
// replay timers are optionally added to new members on join with fresh expire time
if (m_add_replay_on_join)
{
ExpeditionLockoutTimer replay_timer = lockout;
auto replay_lockout = m_lockouts.find(DZ_REPLAY_TIMER_NAME);
if (replay_lockout != m_lockouts.end() &&
replay_lockout->second.IsFromExpedition(m_uuid) &&
!add_client->HasExpeditionLockout(m_expedition_name, DZ_REPLAY_TIMER_NAME))
{
ExpeditionLockoutTimer replay_timer = replay_lockout->second; // copy
replay_timer.Reset();
add_client->AddExpeditionLockout(replay_timer, true);
}
}
else if (!lockout.IsExpired())
{
pending_lockouts.emplace_back(lockout);
}
}
}
ExpeditionDatabase::InsertCharacterLockouts(
add_client->CharacterID(), pending_lockouts, false, true);
if (was_swap_invite)
{
@ -1237,7 +1216,6 @@ void Expedition::ProcessMemberRemoved(const std::string& removed_char_name, uint
// live doesn't clear expedition info on clients removed while inside dz.
// it instead let's the dz kick timer do it even if character zones out
// before it triggers. for simplicity we'll always clear immediately
ExpeditionDatabase::DeletePendingLockouts(member_client->CharacterID());
member_client->SetExpeditionID(0);
member_client->SendDzCompassUpdate();
member_client->QueuePacket(CreateInfoPacket(true).get());
@ -1734,7 +1712,7 @@ void Expedition::AddLockoutByCharacterID(
if (character_id)
{
auto lockout = ExpeditionLockoutTimer::CreateLockout(expedition_name, event_name, seconds, uuid);
ExpeditionDatabase::InsertCharacterLockouts(character_id, { lockout }, true);
ExpeditionDatabase::InsertCharacterLockouts(character_id, { lockout });
SendWorldCharacterLockout(character_id, lockout, false);
}
}
@ -2256,3 +2234,47 @@ void Expedition::SendMembersExpireWarning(uint32_t minutes_remaining)
}
}
}
void Expedition::SyncCharacterLockouts(
uint32_t character_id, std::vector<ExpeditionLockoutTimer>& client_lockouts)
{
// adds missing event lockouts to client for this expedition and replaces
// client timers that are both shorter and from another expedition
BenchTimer benchmark;
bool modified = false;
for (const auto& lockout_iter : m_lockouts)
{
const ExpeditionLockoutTimer& lockout = lockout_iter.second;
if (lockout.IsReplayTimer() || lockout.IsExpired() || lockout.GetExpeditionUUID() != m_uuid)
{
continue;
}
auto client_lockout_iter = std::find_if(client_lockouts.begin(), client_lockouts.end(),
[&](const ExpeditionLockoutTimer& client_lockout) {
return client_lockout.IsSameLockout(lockout);
});
if (client_lockout_iter == client_lockouts.end())
{
modified = true;
client_lockouts.emplace_back(lockout); // insert missing
}
else if (client_lockout_iter->GetSecondsRemaining() < lockout.GetSecondsRemaining() &&
client_lockout_iter->GetExpeditionUUID() != m_uuid)
{
modified = true;
client_lockouts.erase(client_lockout_iter);
client_lockouts.emplace_back(lockout); // replaced existing
}
}
if (modified)
{
ExpeditionDatabase::InsertCharacterLockouts(character_id, client_lockouts);
}
LogExpeditionsDetail("Syncing character lockouts with expedition took [{}] s", benchmark.elapsed());
}

View File

@ -136,6 +136,7 @@ public:
bool HasReplayLockout();
void RemoveLockout(const std::string& event_name);
void SetReplayLockoutOnMemberJoin(bool add_on_join, bool update_db = false);
void SyncCharacterLockouts(uint32_t character_id, std::vector<ExpeditionLockoutTimer>& client_lockouts);
void UpdateLockoutDuration(const std::string& event_name, uint32_t seconds, bool members_only = true);
bool CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id);

View File

@ -110,7 +110,7 @@ std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(ui
UNIX_TIMESTAMP(expire_time),
duration
FROM character_expedition_lockouts
WHERE character_id = {} AND is_pending = FALSE AND expire_time > NOW();
WHERE character_id = {} AND expire_time > NOW();
), character_id);
auto results = database.QueryDatabase(query);
@ -147,7 +147,6 @@ std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(
FROM character_expedition_lockouts
WHERE
character_id = {}
AND is_pending = FALSE
AND expire_time > NOW()
AND expedition_name = '{}';
), character_id, EscapeString(expedition_name));
@ -250,7 +249,6 @@ MySQLRequestResult ExpeditionDatabase::LoadMembersForCreateRequest(
FROM character_data
LEFT JOIN character_expedition_lockouts lockout
ON character_data.id = lockout.character_id
AND lockout.is_pending = FALSE
AND lockout.expire_time > NOW()
AND lockout.expedition_name = '{}'
LEFT JOIN expedition_members member ON character_data.id = member.character_id
@ -306,7 +304,6 @@ void ExpeditionDatabase::DeleteCharacterLockout(
DELETE FROM character_expedition_lockouts
WHERE
character_id = {}
AND is_pending = FALSE
AND expedition_name = '{}'
AND event_name = '{}';
), character_id, EscapeString(expedition_name), EscapeString(event_name));
@ -334,7 +331,6 @@ void ExpeditionDatabase::DeleteMembersLockout(
DELETE FROM character_expedition_lockouts
WHERE character_id
IN ({})
AND is_pending = FALSE
AND expedition_name = '{}'
AND event_name = '{}';
), query_character_ids, EscapeString(expedition_name), EscapeString(event_name));
@ -343,57 +339,6 @@ void ExpeditionDatabase::DeleteMembersLockout(
}
}
void ExpeditionDatabase::AssignPendingLockouts(uint32_t character_id, const std::string& expedition_name)
{
LogExpeditionsDetail("Assigning character [{}] pending lockouts [{}]", character_id, expedition_name);
auto query = fmt::format(SQL(
UPDATE character_expedition_lockouts
SET is_pending = FALSE
WHERE
character_id = {}
AND is_pending = TRUE
AND expedition_name = '{}';
), character_id, EscapeString(expedition_name));
database.QueryDatabase(query);
}
void ExpeditionDatabase::DeletePendingLockouts(uint32_t character_id)
{
LogExpeditionsDetail("Deleting character [{}] pending lockouts", character_id);
auto query = fmt::format(SQL(
DELETE FROM character_expedition_lockouts
WHERE character_id = {} AND is_pending = TRUE;
), character_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::DeleteAllMembersPendingLockouts(const std::vector<ExpeditionMember>& members)
{
LogExpeditionsDetail("Deleting pending lockouts for [{}] characters", members.size());
std::string query_character_ids;
for (const auto& member : members)
{
fmt::format_to(std::back_inserter(query_character_ids), "{},", member.char_id);
}
if (!query_character_ids.empty())
{
query_character_ids.pop_back(); // trailing comma
auto query = fmt::format(SQL(
DELETE FROM character_expedition_lockouts
WHERE character_id IN ({}) AND is_pending = TRUE;
), query_character_ids);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::DeleteLockout(uint32_t expedition_id, const std::string& event_name)
{
LogExpeditionsDetail("Deleting expedition [{}] lockout event [{}]", expedition_id, event_name);
@ -424,9 +369,8 @@ uint32_t ExpeditionDatabase::GetExpeditionIDFromCharacterID(uint32_t character_i
return expedition_id;
}
void ExpeditionDatabase::InsertCharacterLockouts(
uint32_t character_id, const std::vector<ExpeditionLockoutTimer>& lockouts,
bool replace_timer, bool is_pending)
void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts)
{
LogExpeditionsDetail("Inserting [{}] lockouts for character [{}]", lockouts.size(), character_id);
@ -434,14 +378,13 @@ void ExpeditionDatabase::InsertCharacterLockouts(
for (const auto& lockout : lockouts)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}', {}),",
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
character_id,
lockout.GetExpireTime(),
lockout.GetDuration(),
lockout.GetExpeditionUUID(),
EscapeString(lockout.GetExpeditionName()),
EscapeString(lockout.GetEventName()),
is_pending
EscapeString(lockout.GetEventName())
);
}
@ -449,34 +392,15 @@ void ExpeditionDatabase::InsertCharacterLockouts(
{
insert_values.pop_back(); // trailing comma
std::string on_duplicate;
if (replace_timer)
{
on_duplicate = SQL(
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration)
);
}
else
{
on_duplicate = "character_id = VALUES(character_id)";
}
auto query = fmt::format(SQL(
INSERT INTO character_expedition_lockouts
(
character_id,
expire_time,
duration,
from_expedition_uuid,
expedition_name,
event_name,
is_pending
)
(character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name)
VALUES {}
ON DUPLICATE KEY UPDATE {};
), insert_values, on_duplicate);
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
), insert_values);
database.QueryDatabase(query);
}

View File

@ -52,23 +52,22 @@ namespace ExpeditionDatabase
void DeleteMember(uint32_t expedition_id, uint32_t character_id);
void DeleteAllCharacterLockouts(uint32_t character_id);
void DeleteAllCharacterLockouts(uint32_t character_id, const std::string& expedition_name);
void DeleteCharacterLockout(uint32_t character_id, const std::string& expedition_name, const std::string& event_name);
void DeleteCharacterLockout(uint32_t character_id, const std::string& expedition_name,
const std::string& event_name);
void DeleteLockout(uint32_t expedition_id, const std::string& event_name);
void DeleteMembersLockout(
const std::vector<ExpeditionMember>& members, const std::string& expedition_name, const std::string& event_name);
void AssignPendingLockouts(uint32_t character_id, const std::string& expedition_name);
void DeletePendingLockouts(uint32_t character_id);
void DeleteAllMembersPendingLockouts(const std::vector<ExpeditionMember>& members);
void DeleteMembersLockout(const std::vector<ExpeditionMember>& members,
const std::string& expedition_name, const std::string& event_name);
uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id);
std::pair<std::vector<ExpeditionLockoutTimer>, std::vector<uint32_t>> GetMembersLockout(
const std::vector<ExpeditionMember>& members, const std::string& expedition_name,
const std::string& event_name);
void InsertCharacterLockouts(
uint32_t character_id, const std::vector<ExpeditionLockoutTimer>& lockouts,
bool replace_timer, bool is_pending = false);
void InsertMembersLockout(const std::vector<ExpeditionMember>& members, const ExpeditionLockoutTimer& lockout);
void InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts);
void InsertMembersLockout(const std::vector<ExpeditionMember>& members,
const ExpeditionLockoutTimer& lockout);
void InsertLockout(uint32_t expedition_id, const ExpeditionLockoutTimer& lockout);
void InsertLockouts(uint32_t expedition_id, const std::unordered_map<std::string, ExpeditionLockoutTimer>& lockouts);
void InsertLockouts(uint32_t expedition_id,
const std::unordered_map<std::string, ExpeditionLockoutTimer>& lockouts);
void InsertMember(uint32_t expedition_id, uint32_t character_id);
void InsertMembers(uint32_t expedition_id, const std::vector<ExpeditionMember>& members);
void UpdateLockState(uint32_t expedition_id, bool is_locked);