diff --git a/data/sql/updates/pending_db_world/rev_1504044222560696700.sql b/data/sql/updates/pending_db_world/rev_1504044222560696700.sql new file mode 100644 index 000000000..da4672ea7 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1504044222560696700.sql @@ -0,0 +1,154 @@ +INSERT INTO version_db_world (`sql_rev`) VALUES ('1504044222560696700'); + +-- fix NO_ZERO_DATE error on mysql 5.7 (for latest linux platforms) +-- please use 5.6 on windows +SET SESSION sql_mode = "ONLY_FULL_GROUP_BY,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES"; + +DROP TABLE IF EXISTS holiday_dates; +CREATE TABLE holiday_dates ( + id INT UNSIGNED NOT NULL, + date_id TINYINT UNSIGNED NOT NULL, + date_value INT UNSIGNED NOT NULL, + PRIMARY KEY (id, date_id) +); + +CREATE FUNCTION packDate (yy TINYINT UNSIGNED, mm TINYINT UNSIGNED, dd TINYINT UNSIGNED) +RETURNS INT UNSIGNED DETERMINISTIC +RETURN (yy << 24) | ((mm - 1) << 20) | ((dd - 1) << 14); + +INSERT INTO holiday_dates VALUES +(181, 6, 220692480 + (1 << 14)), -- rescheduled +(181, 7, 238338048 + (1 << 14)), +(181, 8, 254869504 + (1 << 14)), +(181, 9, 270958592 + (1 << 14)), +(181, 10, 288635520), -- 7.2 (PTR) dbc +(181, 11, 305166976), +(181, 12, 322271872), +(201, 9, 221723264), +(201, 10, 238484096), +(201, 11, 255244928), +(201, 12, 272661120), +(201, 13, 289421952), +(201, 14, 305625728), +(201, 15, 322386560), +(321, 7, 210106368), +(321, 8, 226704000), +(321, 9, 243300992), +(321, 10, 260389504), +(321, 11, 276970112), +(321, 12, 294075008), +(321, 13, 310672000), +(327, 7, 218429440 + (7 << 14)), +(327, 8, 235207296), +(327, 9, 252967552), +(327, 10, 269499008), +(327, 11, 285555328), +(327, 12, 303184512), +(327, 13, 319224448), +(404, 4, 228997760), +(404, 5, 245758592), +(404, 6, 262519424), +(404, 7, 279263872), +(404, 8, 296024704), +(404, 9, 312785536), +(404, 10, 329661056), +(423, 3, 219185152 + (7 << 14)), +(423, 4, 236092032), +(423, 5, 252738176), +(423, 6, 269728384), +(423, 7, 286374528), +(423, 8, 303184512), +(423, 9, 319881856), + +(374, 0, packDate(16, 12, 02)), +(375, 0, packDate(16, 12, 30)), +(376, 0, packDate(17, 02, 03)), +(374, 1, packDate(17, 03, 03)), +(375, 1, packDate(17, 03, 31)), +(376, 1, packDate(17, 04, 28)), +(374, 2, packDate(17, 06, 02)), +(375, 2, packDate(17, 06, 30)), +(376, 2, packDate(17, 08, 04)), +(374, 3, packDate(17, 09, 01)), +(375, 3, packDate(17, 09, 29)), +(376, 3, packDate(17, 11, 03)), +(374, 4, packDate(17, 12, 01)), +(375, 4, packDate(17, 12, 29)), +(376, 4, packDate(18, 02, 02)), +(374, 5, packDate(18, 03, 02)), +(375, 5, packDate(18, 03, 30)), +(376, 5, packDate(18, 05, 04)), +(374, 6, packDate(18, 06, 01)), +(375, 6, packDate(18, 06, 29)), +(376, 6, packDate(18, 08, 03)), +(374, 7, packDate(18, 08, 31)), +(375, 7, packDate(18, 09, 28)), +(376, 7, packDate(18, 11, 02)), +(374, 8, packDate(18, 11, 30)), +(375, 8, packDate(19, 01, 04)), +(376, 8, packDate(19, 02, 01)), +(374, 9, packDate(19, 03, 01)), +(375, 9, packDate(19, 03, 29)), +(376, 9, packDate(19, 05, 03)), +(374, 10, packDate(19, 05, 31)), +(375, 10, packDate(19, 06, 28)), +(376, 10, packDate(19, 08, 02)), +(374, 11, packDate(19, 08, 30)), +(375, 11, packDate(19, 10, 04)), +(376, 11, packDate(19, 11, 01)), +(374, 12, packDate(19, 11, 29)), +(375, 12, packDate(20, 01, 03)), +(376, 12, packDate(20, 01, 31)), +(374, 13, packDate(20, 02, 28)), +(375, 13, packDate(20, 04, 03)), +(376, 13, packDate(20, 05, 01)), +(374, 14, packDate(20, 05, 29)), +(375, 14, packDate(20, 07, 03)), +(376, 14, packDate(20, 07, 31)), +(374, 15, packDate(20, 09, 04)), +(375, 15, packDate(20, 10, 02)), +(376, 15, packDate(20, 10, 30)), +(374, 16, packDate(20, 12, 04)), +(375, 16, packDate(21, 01, 01)), +(376, 16, packDate(21, 01, 29)), +(374, 17, packDate(21, 02, 26)), +(375, 17, packDate(21, 04, 02)), +(376, 17, packDate(21, 04, 30)), +(374, 18, packDate(21, 06, 04)), +(375, 18, packDate(21, 07, 02)), +(376, 18, packDate(21, 07, 30)), +(374, 19, packDate(21, 09, 03)), +(375, 19, packDate(21, 10, 01)), +(376, 19, packDate(21, 10, 29)), +(374, 20, packDate(21, 12, 03)), +(375, 20, packDate(21, 12, 31)), +(376, 20, packDate(22, 02, 04)), +(374, 21, packDate(22, 03, 04)), +(375, 21, packDate(22, 04, 01)), +(376, 21, packDate(22, 04, 29)), +(374, 22, packDate(22, 06, 03)), +(375, 22, packDate(22, 07, 01)), +(376, 22, packDate(22, 07, 29)), +(374, 23, packDate(22, 09, 02)), +(375, 23, packDate(22, 09, 30)), +(376, 23, packDate(22, 11, 04)), +(374, 24, packDate(22, 12, 02)), +(375, 24, packDate(22, 12, 30)), +(376, 24, packDate(23, 02, 03)), +(374, 25, packDate(23, 03, 03)), +(375, 25, packDate(23, 03, 31)), +(376, 25, packDate(23, 04, 28)); + +UPDATE holiday_dates SET date_value = date_value & ~0x3FFF; + +DROP FUNCTION packDate; + +ALTER TABLE game_event ADD COLUMN holidayStage TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER holiday; + +UPDATE game_event SET holiday = 424 WHERE eventEntry = 64; +UPDATE game_event SET holiday = 0 WHERE eventEntry = 63; +UPDATE game_event SET holiday = 374 WHERE eventEntry = 23; +UPDATE game_event SET holiday = 375 WHERE eventEntry = 110; +UPDATE game_event SET holiday = 376 WHERE eventEntry = 62; +UPDATE game_event SET holidayStage = 1 WHERE eventEntry IN (1, 2, 7, 8, 9, 10, 11, 12, 18, 19, 20, 21, 23, 24, 26, 50, 51, 53, 54, 62, 110); +UPDATE game_event SET holidayStage = 2 WHERE eventEntry IN (3, 4, 5); diff --git a/src/game/Events/GameEventMgr.cpp b/src/game/Events/GameEventMgr.cpp index 096825803..26670a333 100644 --- a/src/game/Events/GameEventMgr.cpp +++ b/src/game/Events/GameEventMgr.cpp @@ -19,6 +19,8 @@ #include "GameObjectAI.h" #include "Transport.h" +#include + bool GameEventMgr::CheckOneGameEvent(uint16 entry) const { switch (mGameEvent[entry].state) @@ -194,8 +196,8 @@ void GameEventMgr::LoadFromDB() { { uint32 oldMSTime = getMSTime(); - // 1 2 3 4 5 6 7 8 - QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, description, world_event FROM game_event"); + // 1 2 3 4 5 6 7 8 9 + QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event FROM game_event"); if (!result) { mGameEvent.clear(); @@ -225,9 +227,13 @@ void GameEventMgr::LoadFromDB() pGameEvent.length = fields[4].GetUInt64(); pGameEvent.holiday_id = HolidayIds(fields[5].GetUInt32()); - pGameEvent.state = (GameEventState)(fields[7].GetUInt8()); + pGameEvent.holidayStage = fields[6].GetUInt8(); + pGameEvent.description = fields[7].GetString(); + pGameEvent.state = (GameEventState)(fields[8].GetUInt8()); pGameEvent.nextstart = 0; + ++count; + if (pGameEvent.length == 0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check { sLog->outErrorDb("`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.", event_id); @@ -241,11 +247,10 @@ void GameEventMgr::LoadFromDB() sLog->outErrorDb("`game_event` game event id (%i) have not existed holiday id %u.", event_id, pGameEvent.holiday_id); pGameEvent.holiday_id = HOLIDAY_NONE; } + + SetHolidayEventTime(pGameEvent); } - pGameEvent.description = fields[6].GetString(); - - ++count; } while (result->NextRow()); @@ -951,6 +956,45 @@ void GameEventMgr::LoadFromDB() } } +void GameEventMgr::LoadHolidayDates() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT id, date_id, date_value FROM holiday_dates"); + + if (!result) + { + sLog->outString(">> Loaded 0 holiday dates. DB table `holiday_dates` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + uint32 holidayId = fields[0].GetUInt32(); + HolidaysEntry* entry = const_cast(sHolidaysStore.LookupEntry(holidayId)); + if (!entry) + { + sLog->outErrorDb("holiday_dates entry has invalid holiday id %u.", holidayId); + continue; + } + uint8 dateId = fields[1].GetUInt8(); + if (dateId >= MAX_HOLIDAY_DATES) + { + sLog->outErrorDb("holiday_dates entry has out of range date_id %u.", dateId); + continue; + } + entry->Date[dateId] = fields[2].GetUInt32(); + modifiedHolidays.insert(entry->Id); + ++count; + + } while (result->NextRow()); + + sLog->outString(">> Loaded %u holiday dates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + uint32 GameEventMgr::GetNPCFlag(Creature* cr) { uint32 mask = 0; @@ -1669,6 +1713,83 @@ void GameEventMgr::RunSmartAIScripts(uint16 event_id, bool activate) } } +void GameEventMgr::SetHolidayEventTime(GameEventData& event) +{ + if (!event.holidayStage) // Ignore holiday + return; + + const HolidaysEntry* holiday = sHolidaysStore.LookupEntry(event.holiday_id); + + if (!holiday->Date[0] || !holiday->Duration[0]) // Invalid definitions + { + sLog->outErrorDb("Missing date or duration for holiday %u.", event.holiday_id); + return; + } + + uint8 stageIndex = event.holidayStage - 1; + event.length = holiday->Duration[stageIndex] * HOUR / MINUTE; + + time_t stageOffset = 0; + for (int i = 0; i < stageIndex; ++i) + stageOffset += holiday->Duration[i] * HOUR; + + switch (holiday->CalendarFilterType) + { + case -1: // Yearly + event.occurence = YEAR / MINUTE; // Not all too useful + break; + case 0: // Weekly + event.occurence = WEEK / MINUTE; + break; + case 1: // Defined dates only (Darkmoon Faire) + break; + case 2: // Only used for looping events (Call to Arms) + break; + } + + if (holiday->Looping) + { + event.occurence = 0; + for (int i = 0; i < MAX_HOLIDAY_DURATIONS && holiday->Duration[i]; ++i) + event.occurence += holiday->Duration[i] * HOUR / MINUTE; + } + + bool singleDate = ((holiday->Date[0] >> 24) & 0x1F) == 31; // Events with fixed date within year have - 1 + + time_t curTime = time(NULL); + for (int i = 0; i < MAX_HOLIDAY_DATES && holiday->Date[i]; ++i) + { + uint32 date = holiday->Date[i]; + + tm timeInfo; + if (singleDate) + timeInfo.tm_year = localtime(&curTime)->tm_year - 1; // First try last year (event active through New Year) + else + timeInfo.tm_year = ((date >> 24) & 0x1F) + 100; + timeInfo.tm_mon = (date >> 20) & 0xF; + timeInfo.tm_mday = ((date >> 14) & 0x3F) + 1; + timeInfo.tm_hour = (date >> 6) & 0x1F; + timeInfo.tm_min = date & 0x3F; + timeInfo.tm_sec = 0; + timeInfo.tm_isdst = -1; + tm tmCopy = timeInfo; + + time_t startTime = mktime(&timeInfo); + if (curTime < startTime + event.length * MINUTE) + { + event.start = startTime + stageOffset; + return; + } + else if (singleDate) + { + tmCopy.tm_year = localtime(&curTime)->tm_year; // This year + event.start = mktime(&tmCopy) + stageOffset; + return; + } + } + sLog->outString("No suitable start date found for holiday %u.", event.holiday_id); +} + bool IsHolidayActive(HolidayIds id) { if (id == HOLIDAY_NONE) diff --git a/src/game/Events/GameEventMgr.h b/src/game/Events/GameEventMgr.h index 91eb98110..64f165988 100644 --- a/src/game/Events/GameEventMgr.h +++ b/src/game/Events/GameEventMgr.h @@ -50,6 +50,7 @@ struct GameEventData uint32 occurence; // time between end and start uint32 length; // length of the event (minutes) after finishing all conditions HolidayIds holiday_id; + uint8 holidayStage; GameEventState state; // state of the game event, these are saved into the game_event table on change! GameEventConditionMap conditions; // conditions to finish std::set prerequisite_events; // events that must be completed before starting this event @@ -95,6 +96,7 @@ class GameEventMgr bool CheckOneGameEvent(uint16 entry) const; uint32 NextCheck(uint16 entry) const; void LoadFromDB(); + void LoadHolidayDates(); uint32 Update(); bool IsActiveEvent(uint16 event_id) { return (m_ActiveEvents.find(event_id) != m_ActiveEvents.end()); } uint32 StartSystem(); @@ -128,6 +130,7 @@ class GameEventMgr bool hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id); bool hasCreatureActiveEventExcept(uint32 creature_guid, uint16 event_id); bool hasGameObjectActiveEventExcept(uint32 go_guid, uint16 event_id); + void SetHolidayEventTime(GameEventData& event); typedef std::list GuidList; typedef std::list IdList; @@ -162,6 +165,7 @@ class GameEventMgr public: GameEventGuidMap mGameEventCreatureGuids; GameEventGuidMap mGameEventGameobjectGuids; + std::set modifiedHolidays; }; #define sGameEventMgr ACE_Singleton::instance() diff --git a/src/game/Handlers/CalendarHandler.cpp b/src/game/Handlers/CalendarHandler.cpp index 888bf00d7..c09ce07a1 100644 --- a/src/game/Handlers/CalendarHandler.cpp +++ b/src/game/Handlers/CalendarHandler.cpp @@ -34,6 +34,7 @@ Copied events should probably have a new owner #include "GuildMgr.h" #include "ArenaTeamMgr.h" #include "WorldSession.h" +#include "GameEventMgr.h" void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/) { @@ -139,11 +140,10 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/) data.append(dataBuffer); // TODO: Fix this, how we do know how many and what holidays to send? - uint32 holidayCount = 0; - data << uint32(holidayCount); - for (uint32 i = 0; i < holidayCount; ++i) + data << uint32(sGameEventMgr->modifiedHolidays.size()); + for (uint32 entry : sGameEventMgr->modifiedHolidays) { - HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(666); + HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(entry); data << uint32(holiday->Id); // m_ID data << uint32(holiday->Region); // m_region, might be looping diff --git a/src/game/World/World.cpp b/src/game/World/World.cpp index e7cdd9aa3..32da6e747 100644 --- a/src/game/World/World.cpp +++ b/src/game/World/World.cpp @@ -1520,7 +1520,8 @@ void World::SetInitialWorldSettings() sPoolMgr->LoadFromDB(); sLog->outString("Loading Game Event Data..."); // must be after loading pools fully - sGameEventMgr->LoadFromDB(); + sGameEventMgr->LoadHolidayDates(); // Must be after loading DBC + sGameEventMgr->LoadFromDB(); // Must be after loading holiday dates sLog->outString("Loading UNIT_NPC_FLAG_SPELLCLICK Data..."); // must be after LoadQuests sObjectMgr->LoadNPCSpellClickSpells();