aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/base/characters_database.sql4
-rw-r--r--sql/updates/characters/3.3.5/2025_07_20_00_characters_2022_07_03_00_characters.sql5
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp6
-rw-r--r--src/server/game/Entities/Player/Player.cpp38
-rw-r--r--src/server/game/Entities/Player/Player.h8
-rw-r--r--src/server/game/Events/GameEventMgr.cpp17
-rw-r--r--src/server/game/Events/GameEventMgr.h1
-rw-r--r--src/server/game/World/World.cpp5
-rw-r--r--src/server/game/World/World.h2
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();