aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2022-07-03 23:44:48 +0200
committerShauren <shauren.trinity@gmail.com>2022-07-03 23:44:48 +0200
commit49ad0d2d495a9f817ad2820f8e616f3a6564b480 (patch)
tree93d6744a952dd4dabba7ef9cfe7f8c3421d2f76a
parent306022054bec6010a63e80407c8d2f8d672b935b (diff)
Core/Quests: Reset seasonal quests based on saved completion time and intended holiday start time
-rw-r--r--sql/base/characters_database.sql4
-rw-r--r--sql/updates/characters/master/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.cpp39
-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, 63 insertions, 24 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql
index 8333e27a4f5..109c9f9e2ee 100644
--- a/sql/base/characters_database.sql
+++ b/sql/base/characters_database.sql
@@ -1426,6 +1426,7 @@ CREATE TABLE `character_queststatus_seasonal` (
`guid` bigint 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';
@@ -3670,7 +3671,8 @@ INSERT INTO `updates` VALUES
('2022_02_28_00_characters_2020_09_27_00_characters.sql','2292A1ED0E7F46DEC41384F75FA6D9461464EEB8','ARCHIVED','2022-02-28 12:43:58',0),
('2022_03_06_00_characters.sql','474AAF9D03E6A56017899C968DC9875368301934','ARCHIVED','2022-03-06 15:12:24',0),
('2022_03_11_00_characters_2021_07_18_00_characters.sql','0BA579ED21F4E75AC2B4797421B5029568B3F6E2','RELEASED','2022-03-11 18:56:07',0),
-('2022_06_01_00_characters.sql','582AC6E256F8365F83AB70BA165CCC8B218E19FF','RELEASED','2022-06-01 21:16:56',0);
+('2022_06_01_00_characters.sql','582AC6E256F8365F83AB70BA165CCC8B218E19FF','RELEASED','2022-06-01 21:16:56',0),
+('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/master/2022_07_03_00_characters.sql b/sql/updates/characters/master/2022_07_03_00_characters.sql
new file mode 100644
index 00000000000..11ed0f7e42c
--- /dev/null
+++ b/sql/updates/characters/master/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 dc39e147e52..26d6648d15e 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -104,7 +104,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);
@@ -112,11 +112,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 " SelectItemInstanceContent ", bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid LEFT JOIN item_instance_modifiers im ON ii.guid = im.itemGuid WHERE ci.guid = ? ORDER BY (ii.flags & 0x80000) ASC, bag ASC, slot ASC", CONNECTION_ASYNC);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index e0420958d71..00750ba6501 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -19635,11 +19635,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;
if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(quest_id))
SetQuestCompletedBit(questBit, true);
@@ -21300,18 +21301,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->setUInt64(0, GetGUID().GetCounter());
stmt->setUInt32(1, questId);
stmt->setUInt32(2, eventId);
+ stmt->setInt64(3, completedTime);
trans->Append(stmt);
}
}
@@ -25043,7 +25046,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;
}
@@ -25089,8 +25092,11 @@ void Player::ResetWeeklyQuestStatus()
m_WeeklyQuestChanged = false;
}
-void Player::ResetSeasonalQuestStatus(uint16 event_id)
+void Player::ResetSeasonalQuestStatus(uint16 event_id, time_t eventStartTime)
{
+ // DB data deleted in caller
+ m_SeasonalQuestChanged = false;
+
auto eventItr = m_seasonalquests.find(event_id);
if (eventItr == m_seasonalquests.end())
return;
@@ -25098,13 +25104,22 @@ void Player::ResetSeasonalQuestStatus(uint16 event_id)
if (eventItr->second.empty())
return;
- for (uint32 questId : eventItr->second)
- if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(questId))
- SetQuestCompletedBit(questBit, false);
+ for (auto questItr = eventItr->second.begin(); questItr != eventItr->second.end(); )
+ {
+ if (questItr->second < eventStartTime)
+ {
+ if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(questItr->first))
+ SetQuestCompletedBit(questBit, false);
+
+ questItr = eventItr->second.erase(questItr);
+ }
+ else
+ ++questItr;
+ }
+
+ if (eventItr->second.empty())
+ m_seasonalquests.erase(eventItr);
- m_seasonalquests.erase(eventItr);
- // DB data deleted in caller
- m_SeasonalQuestChanged = false;
}
void Player::ResetMonthlyQuestStatus()
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index ae34c91f697..efa7e7ef760 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1612,7 +1612,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void DailyReset();
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;
@@ -2866,12 +2866,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 78c6741a8e8..5dc3630af91 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -1147,7 +1147,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)
@@ -1805,6 +1805,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 ba13beb0b59..b0654119567 100644
--- a/src/server/game/Events/GameEventMgr.h
+++ b/src/server/game/Events/GameEventMgr.h
@@ -139,6 +139,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 17247bde9d7..0639b31e678 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -3716,17 +3716,18 @@ void World::ResetCurrencyWeekCap()
sWorld->setWorldState(WS_CURRENCY_RESET_TIME, uint32(m_NextCurrencyReset));
}
-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 70634f51a42..214e9cbd6c0 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -794,7 +794,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();