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);