From 4f9eaf75748d52f80b2754d4a34f164a3dc272fd Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 8 Nov 2020 22:31:41 -0500 Subject: [PATCH] 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 --- utils/sql/git/required/wip_expeditions.sql | 1 - zone/client.cpp | 18 ++-- zone/client.h | 1 - zone/expedition.cpp | 86 ++++++++++++------- zone/expedition.h | 1 + zone/expedition_database.cpp | 98 +++------------------- zone/expedition_database.h | 21 +++-- 7 files changed, 83 insertions(+), 143 deletions(-) diff --git a/utils/sql/git/required/wip_expeditions.sql b/utils/sql/git/required/wip_expeditions.sql index 64f99eecd..5524a2919 100644 --- a/utils/sql/git/required/wip_expeditions.sql +++ b/utils/sql/git/required/wip_expeditions.sql @@ -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`) ) diff --git a/zone/client.cpp b/zone/client.cpp index 1fb124799..06f04ec1b 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -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 lockout_entries; diff --git a/zone/client.h b/zone/client.h index 8d786eb6e..f2b1892c9 100644 --- a/zone/client.h +++ b/zone/client.h @@ -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); diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 6c7101be6..b9e54a9ea 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -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,39 +847,20 @@ 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 pending_lockouts; - for (const auto& lockout_iter : m_lockouts) + // replay timers are optionally added to new members on join with fresh expire time + if (m_add_replay_on_join) { - const ExpeditionLockoutTimer& lockout = lockout_iter.second; - if (lockout.IsFromExpedition(m_uuid) && - !add_client->HasExpeditionLockout(m_expedition_name, lockout.GetEventName())) + 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)) { - // replay timers are optionally added to new members immediately on - // join with a fresh expire time using the original duration. - if (lockout.IsReplayTimer()) - { - if (m_add_replay_on_join) - { - ExpeditionLockoutTimer replay_timer = lockout; - replay_timer.Reset(); - add_client->AddExpeditionLockout(replay_timer, true); - } - } - else if (!lockout.IsExpired()) - { - pending_lockouts.emplace_back(lockout); - } + ExpeditionLockoutTimer replay_timer = replay_lockout->second; // copy + replay_timer.Reset(); + add_client->AddExpeditionLockout(replay_timer, true); } } - ExpeditionDatabase::InsertCharacterLockouts( - add_client->CharacterID(), pending_lockouts, false, true); - if (was_swap_invite) { SwapMember(add_client, swap_remove_name); @@ -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& 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()); +} diff --git a/zone/expedition.h b/zone/expedition.h index 5a0d6da56..7e6c4a048 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -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& 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); diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp index 782e96517..69a1ebfa7 100644 --- a/zone/expedition_database.cpp +++ b/zone/expedition_database.cpp @@ -110,7 +110,7 @@ std::vector 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 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& 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& lockouts, - bool replace_timer, bool is_pending) +void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id, + const std::vector& 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); } diff --git a/zone/expedition_database.h b/zone/expedition_database.h index a774ac2c5..2efeba2f6 100644 --- a/zone/expedition_database.h +++ b/zone/expedition_database.h @@ -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& 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& members); + void DeleteMembersLockout(const std::vector& members, + const std::string& expedition_name, const std::string& event_name); uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id); std::pair, std::vector> GetMembersLockout( const std::vector& members, const std::string& expedition_name, const std::string& event_name); - void InsertCharacterLockouts( - uint32_t character_id, const std::vector& lockouts, - bool replace_timer, bool is_pending = false); - void InsertMembersLockout(const std::vector& members, const ExpeditionLockoutTimer& lockout); + void InsertCharacterLockouts(uint32_t character_id, + const std::vector& lockouts); + void InsertMembersLockout(const std::vector& members, + const ExpeditionLockoutTimer& lockout); void InsertLockout(uint32_t expedition_id, const ExpeditionLockoutTimer& lockout); - void InsertLockouts(uint32_t expedition_id, const std::unordered_map& lockouts); + void InsertLockouts(uint32_t expedition_id, + const std::unordered_map& lockouts); void InsertMember(uint32_t expedition_id, uint32_t character_id); void InsertMembers(uint32_t expedition_id, const std::vector& members); void UpdateLockState(uint32_t expedition_id, bool is_locked);