diff options
-rw-r--r-- | sql/base/characters_database.sql | 4 | ||||
-rw-r--r-- | sql/updates/characters/3.3.5/2025_07_20_00_characters_2022_07_03_00_characters.sql | 5 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 38 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 8 | ||||
-rw-r--r-- | src/server/game/Events/GameEventMgr.cpp | 17 | ||||
-rw-r--r-- | src/server/game/Events/GameEventMgr.h | 1 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 5 | ||||
-rw-r--r-- | src/server/game/World/World.h | 2 |
9 files changed, 64 insertions, 22 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index dddc50c1ff4..447fa2cb839 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -1088,6 +1088,7 @@ CREATE TABLE `character_queststatus_seasonal` ( `guid` int unsigned NOT NULL DEFAULT '0' COMMENT 'Global Unique Identifier', `quest` int unsigned NOT NULL DEFAULT '0' COMMENT 'Quest Identifier', `event` int unsigned NOT NULL DEFAULT '0' COMMENT 'Event Identifier', + `completedTime` bigint NOT NULL DEFAULT '0', PRIMARY KEY (`guid`,`quest`), KEY `idx_guid` (`guid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Player System'; @@ -2688,7 +2689,8 @@ INSERT INTO `updates` VALUES ('2024_04_10_00_characters.sql','E0D6E19ACE6759332402FA27C23B0F7745C49742','ARCHIVED','2024-04-10 16:07:02',0), ('2024_08_17_00_characters.sql','08705FBCB8504E8B1009FDAF955F56D734FAD782','ARCHIVED','2024-08-17 22:26:12',0), ('2024_10_03_00_characters.sql','408249A6992999A36EB94089D184972E8E0767A3','ARCHIVED','2024-10-03 11:10:18',0), -('2024_11_22_00_characters.sql','9EA2A4F88036D1D5F47EE8A6B634D52D0014986E','ARCHIVED','2024-11-22 23:18:14',0); +('2024_11_22_00_characters.sql','9EA2A4F88036D1D5F47EE8A6B634D52D0014986E','ARCHIVED','2024-11-22 23:18:14',0), +('2025_07_20_00_characters_2022_07_03_00_characters.sql','D3F04078C0846BCF7C8330AC20C39B8C3AEE7002','RELEASED','2022-07-03 23:37:24',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/3.3.5/2025_07_20_00_characters_2022_07_03_00_characters.sql b/sql/updates/characters/3.3.5/2025_07_20_00_characters_2022_07_03_00_characters.sql new file mode 100644 index 00000000000..11ed0f7e42c --- /dev/null +++ b/sql/updates/characters/3.3.5/2025_07_20_00_characters_2022_07_03_00_characters.sql @@ -0,0 +1,5 @@ +ALTER TABLE `character_queststatus_seasonal` ADD `completedTime` bigint NOT NULL DEFAULT '0' AFTER `event`; + +UPDATE `character_queststatus_seasonal` SET `completedTime` = UNIX_TIMESTAMP(); + +DELETE FROM `worldstates` WHERE `entry` BETWEEN 1 AND 85; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 776eb7dbe87..65c49183556 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -79,7 +79,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_DAILY, "SELECT quest, time FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_WEEKLY, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_MONTHLY, "SELECT quest FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_SEASONAL, "SELECT quest, event FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_SEASONAL, "SELECT quest, event, completedTime FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY, "DELETE FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_WEEKLY, "DELETE FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_MONTHLY, "DELETE FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC); @@ -87,11 +87,11 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_DAILY, "INSERT INTO character_queststatus_daily (guid, quest, time) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_WEEKLY, "INSERT INTO character_queststatus_weekly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_MONTHLY, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_SEASONAL, "INSERT INTO character_queststatus_seasonal (guid, quest, event) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_SEASONAL, "INSERT INTO character_queststatus_seasonal (guid, quest, event, completedTime) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY, "DELETE FROM character_queststatus_daily", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_WEEKLY, "DELETE FROM character_queststatus_weekly", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_MONTHLY, "DELETE FROM character_queststatus_monthly", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT, "DELETE FROM character_queststatus_seasonal WHERE event = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT, "DELETE FROM character_queststatus_seasonal WHERE event = ? AND completedTime < ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, " diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index e50c46ee1f2..fe8529835b4 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -18546,11 +18546,12 @@ void Player::_LoadSeasonalQuestStatus(PreparedQueryResult result) Field* fields = result->Fetch(); uint32 quest_id = fields[0].GetUInt32(); uint32 event_id = fields[1].GetUInt32(); + uint32 completedTime = fields[2].GetInt64(); Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id); if (!quest) continue; - m_seasonalquests[event_id].insert(quest_id); + m_seasonalquests[event_id][quest_id] = completedTime; TC_LOG_DEBUG("entities.player.loading", "Player::_LoadSeasonalQuestStatus: Loaded seasonal quest cooldown (QuestID: {}) for player '{}' ({})", quest_id, GetName(), GetGUID().ToString()); } @@ -19929,18 +19930,20 @@ void Player::_SaveSeasonalQuestStatus(CharacterDatabaseTransaction trans) if (m_seasonalquests.empty()) return; - for (SeasonalEventQuestMap::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter) + for (SeasonalQuestMapByEvent::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter) { uint16 eventId = iter->first; - for (SeasonalQuestSet::const_iterator itr = iter->second.begin(); itr != iter->second.end(); ++itr) + for (SeasonalQuestMapByQuest::const_iterator itr = iter->second.begin(); itr != iter->second.end(); ++itr) { - uint32 questId = *itr; + uint32 questId = itr->first; + time_t completedTime = itr->second; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_SEASONAL); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt32(1, questId); stmt->setUInt32(2, eventId); + stmt->setInt64(3, completedTime); trans->Append(stmt); } } @@ -23140,7 +23143,7 @@ void Player::SetSeasonalQuestStatus(uint32 quest_id) if (!quest) return; - m_seasonalquests[quest->GetEventIdForQuest()].insert(quest_id); + m_seasonalquests[quest->GetEventIdForQuest()][quest_id] = GameTime::GetGameTime(); m_SeasonalQuestChanged = true; } @@ -23172,14 +23175,29 @@ void Player::ResetWeeklyQuestStatus() m_WeeklyQuestChanged = false; } -void Player::ResetSeasonalQuestStatus(uint16 event_id) +void Player::ResetSeasonalQuestStatus(uint16 event_id, time_t eventStartTime) { - if (m_seasonalquests.empty() || m_seasonalquests[event_id].empty()) - return; - - m_seasonalquests.erase(event_id); // DB data deleted in caller m_SeasonalQuestChanged = false; + + auto eventItr = m_seasonalquests.find(event_id); + if (eventItr == m_seasonalquests.end()) + return; + + if (eventItr->second.empty()) + return; + + for (auto questItr = eventItr->second.begin(); questItr != eventItr->second.end(); ) + { + if (questItr->second < eventStartTime) + questItr = eventItr->second.erase(questItr); + else + ++questItr; + } + + if (eventItr->second.empty()) + m_seasonalquests.erase(eventItr); + } void Player::ResetMonthlyQuestStatus() diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index efe5e6231dc..bd1ece31ed6 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1299,7 +1299,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void ResetDailyQuestStatus(); void ResetWeeklyQuestStatus(); void ResetMonthlyQuestStatus(); - void ResetSeasonalQuestStatus(uint16 event_id); + void ResetSeasonalQuestStatus(uint16 event_id, time_t eventStartTime); uint16 FindQuestSlot(uint32 quest_id) const; uint32 GetQuestSlotQuestId(uint16 slot) const; @@ -2276,12 +2276,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> //We allow only one timed quest active at the same time. Below can then be simple value instead of set. typedef std::set<uint32> QuestSet; - typedef std::set<uint32> SeasonalQuestSet; - typedef std::unordered_map<uint32, SeasonalQuestSet> SeasonalEventQuestMap; + typedef std::unordered_map<uint32, time_t> SeasonalQuestMapByQuest; + typedef std::unordered_map<uint32, SeasonalQuestMapByQuest> SeasonalQuestMapByEvent; QuestSet m_timedquests; QuestSet m_weeklyquests; QuestSet m_monthlyquests; - SeasonalEventQuestMap m_seasonalquests; + SeasonalQuestMapByEvent m_seasonalquests; ObjectGuid m_playerSharingQuest; uint32 m_sharedQuestId; diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index b7c240805ea..53b16101b08 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -1184,7 +1184,7 @@ void GameEventMgr::ApplyNewEvent(uint16 event_id) RunSmartAIScripts(event_id, true); // check for seasonal quest reset. - sWorld->ResetEventSeasonalQuests(event_id); + sWorld->ResetEventSeasonalQuests(event_id, GetLastStartTime(event_id)); } void GameEventMgr::UpdateEventNPCFlags(uint16 event_id) @@ -1818,6 +1818,21 @@ void GameEventMgr::SetHolidayEventTime(GameEventData& event) } } +time_t GameEventMgr::GetLastStartTime(uint16 event_id) const +{ + if (event_id >= mGameEvent.size()) + return 0; + + if (mGameEvent[event_id].state != GAMEEVENT_NORMAL) + return 0; + + SystemTimePoint now = GameTime::GetSystemTime(); + SystemTimePoint eventInitialStart = std::chrono::system_clock::from_time_t(mGameEvent[event_id].start); + Minutes occurence(mGameEvent[event_id].occurence); + SystemTimePoint::duration durationSinceLastStart = (now - eventInitialStart) % occurence; + return std::chrono::system_clock::to_time_t(now - durationSinceLastStart); +} + bool IsHolidayActive(HolidayIds id) { if (id == HOLIDAY_NONE) diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h index 4d51ae1ec9b..8417651689d 100644 --- a/src/server/game/Events/GameEventMgr.h +++ b/src/server/game/Events/GameEventMgr.h @@ -148,6 +148,7 @@ class TC_GAME_API GameEventMgr bool hasCreatureActiveEventExcept(ObjectGuid::LowType creature_guid, uint16 event_id); bool hasGameObjectActiveEventExcept(ObjectGuid::LowType go_guid, uint16 event_id); void SetHolidayEventTime(GameEventData& event); + time_t GetLastStartTime(uint16 event_id) const; typedef std::list<ObjectGuid::LowType> GuidList; typedef std::list<uint32> IdList; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 9ec3ed3b51f..f24237bc643 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -3417,17 +3417,18 @@ void World::InitGuildResetTime() sWorld->setWorldState(WS_GUILD_DAILY_RESET_TIME, uint64(m_NextGuildReset)); } -void World::ResetEventSeasonalQuests(uint16 event_id) +void World::ResetEventSeasonalQuests(uint16 event_id, time_t eventStartTime) { TC_LOG_INFO("misc", "Seasonal quests reset for all characters."); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT); stmt->setUInt16(0, event_id); + stmt->setInt64(1, eventStartTime); CharacterDatabase.Execute(stmt); for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) if (itr->second->GetPlayer()) - itr->second->GetPlayer()->ResetSeasonalQuestStatus(event_id); + itr->second->GetPlayer()->ResetSeasonalQuestStatus(event_id, eventStartTime); } void World::ResetRandomBG() diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index e245c419b43..f489a304c0d 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -761,7 +761,7 @@ class TC_GAME_API World uint32 GetCleaningFlags() const { return m_CleaningFlags; } void SetCleaningFlags(uint32 flags) { m_CleaningFlags = flags; } - void ResetEventSeasonalQuests(uint16 event_id); + void ResetEventSeasonalQuests(uint16 event_id, time_t eventStartTime); void ReloadRBAC(); |