diff --git a/sql/characters.sql b/sql/characters.sql index 27eacb825..c1c050440 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `character_db_version`; CREATE TABLE `character_db_version` ( - `required_10862_01_characters_mail` bit(1) default NULL + `required_10973_01_characters_game_event_status` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- @@ -1060,6 +1060,25 @@ LOCK TABLES `creature_respawn` WRITE; /*!40000 ALTER TABLE `creature_respawn` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `game_event_status` +-- + +DROP TABLE IF EXISTS `game_event_status`; +CREATE TABLE `game_event_status` ( + `event` smallint(6) unsigned NOT NULL default '0', + PRIMARY KEY (`event`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Game event system'; + +-- +-- Dumping data for table `game_event_status` +-- + +LOCK TABLES `game_event_status` WRITE; +/*!40000 ALTER TABLE `game_event_status` DISABLE KEYS */; +/*!40000 ALTER TABLE `game_event_status` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `gameobject_respawn` -- diff --git a/sql/mangos.sql b/sql/mangos.sql index 2bd318691..39ea61dc5 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_10972_01_mangos_command` bit(1) default NULL + `required_10973_01_mangos_game_event_mail` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -1697,6 +1697,29 @@ LOCK TABLES `game_event_gameobject` WRITE; /*!40000 ALTER TABLE `game_event_gameobject` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `game_event_mail` +-- + +DROP TABLE IF EXISTS `game_event_mail`; +CREATE TABLE `game_event_mail` ( + `event` smallint(6) NOT NULL default '0' COMMENT 'Negatives value to send at event stop, positive value for send at event start.', + `raceMask` mediumint(8) unsigned NOT NULL default '0', + `quest` mediumint(8) unsigned NOT NULL default '0', + `mailTemplateId` mediumint(8) unsigned NOT NULL default '0', + `senderEntry` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`event`,`raceMask`,`quest`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Game event system'; + +-- +-- Dumping data for table `game_event_mail` +-- + +LOCK TABLES `game_event_mail` WRITE; +/*!40000 ALTER TABLE `game_event_mail` DISABLE KEYS */; +/*!40000 ALTER TABLE `game_event_mail` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `game_event_quest` -- diff --git a/sql/updates/10973_01_characters_game_event_status.sql b/sql/updates/10973_01_characters_game_event_status.sql new file mode 100644 index 000000000..5b16872b6 --- /dev/null +++ b/sql/updates/10973_01_characters_game_event_status.sql @@ -0,0 +1,7 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_10862_01_characters_mail required_10973_01_characters_game_event_status bit; + +DROP TABLE IF EXISTS `game_event_status`; +CREATE TABLE `game_event_status` ( + `event` smallint(6) unsigned NOT NULL default '0', + PRIMARY KEY (`event`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Game event system'; diff --git a/sql/updates/10973_01_mangos_game_event_mail.sql b/sql/updates/10973_01_mangos_game_event_mail.sql new file mode 100644 index 000000000..b3e637aec --- /dev/null +++ b/sql/updates/10973_01_mangos_game_event_mail.sql @@ -0,0 +1,11 @@ +ALTER TABLE db_version CHANGE COLUMN required_10972_01_mangos_command required_10973_01_mangos_game_event_mail bit; + +DROP TABLE IF EXISTS `game_event_mail`; +CREATE TABLE `game_event_mail` ( + `event` smallint(6) NOT NULL default '0' COMMENT 'Negatives value to send at event stop, positive value for send at event start.', + `raceMask` mediumint(8) unsigned NOT NULL default '0', + `quest` mediumint(8) unsigned NOT NULL default '0', + `mailTemplateId` mediumint(8) unsigned NOT NULL default '0', + `senderEntry` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`event`,`raceMask`,`quest`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Game event system'; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 5251ceb62..5634b00f0 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -145,6 +145,8 @@ pkgdata_DATA = \ 10950_01_mangos_mangos_string.sql \ 10951_01_mangos_spell_proc_event.sql \ 10972_01_mangos_command.sql \ + 10973_01_characters_game_event_status.sql \ + 10973_01_mangos_game_event_mail.sql \ README ## Additional files to include when running 'make dist' @@ -270,4 +272,6 @@ EXTRA_DIST = \ 10950_01_mangos_mangos_string.sql \ 10951_01_mangos_spell_proc_event.sql \ 10972_01_mangos_command.sql \ + 10973_01_characters_game_event_status.sql \ + 10973_01_mangos_game_event_mail.sql \ README diff --git a/src/game/GameEventMgr.cpp b/src/game/GameEventMgr.cpp index 223395ae1..7757f69b0 100644 --- a/src/game/GameEventMgr.cpp +++ b/src/game/GameEventMgr.cpp @@ -26,15 +26,15 @@ #include "Log.h" #include "MapManager.h" #include "BattleGroundMgr.h" +#include "MassMailMgr.h" #include "SpellMgr.h" #include "Policies/SingletonImp.h" INSTANTIATE_SINGLETON_1(GameEventMgr); -bool GameEventMgr::CheckOneGameEvent(uint16 entry) const +bool GameEventMgr::CheckOneGameEvent(uint16 entry, time_t currenttime) const { // Get the event information - time_t currenttime = time(NULL); if( mGameEvent[entry].start < currenttime && currenttime < mGameEvent[entry].end && ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE)) < (mGameEvent[entry].length * MINUTE) ) return true; @@ -68,10 +68,9 @@ uint32 GameEventMgr::NextCheck(uint16 entry) const return delay; } -void GameEventMgr::StartEvent( uint16 event_id, bool overwrite ) +void GameEventMgr::StartEvent( uint16 event_id, bool overwrite /*=false*/, bool resume /*=false*/) { - AddActiveEvent(event_id); - ApplyNewEvent(event_id); + ApplyNewEvent(event_id, resume); if(overwrite) { mGameEvent[event_id].start = time(NULL); @@ -82,7 +81,6 @@ void GameEventMgr::StartEvent( uint16 event_id, bool overwrite ) void GameEventMgr::StopEvent( uint16 event_id, bool overwrite ) { - RemoveActiveEvent(event_id); UnApplyEvent(event_id); if(overwrite) { @@ -482,29 +480,131 @@ void GameEventMgr::LoadFromDB() sLog.outString(); sLog.outString( ">> Loaded %u quest additions in game events", count ); } + + mGameEventMails.resize(mGameEvent.size()*2-1); + + result = WorldDatabase.Query("SELECT event, raceMask, quest, mailTemplateId, senderEntry FROM game_event_mail"); + + count = 0; + if (!result) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u start/end game event mails", count ); + } + else + { + + barGoLink bar((int)result->GetRowCount()); + do + { + Field *fields = result->Fetch(); + + bar.step(); + uint16 event_id = fields[0].GetUInt16(); + + GameEventMail mail; + mail.raceMask = fields[1].GetUInt32(); + mail.questId = fields[2].GetUInt32(); + mail.mailTemplateId = fields[3].GetUInt32(); + mail.senderEntry = fields[4].GetUInt32(); + + if (event_id == 0) + { + sLog.outErrorDb("`game_event_mail` game event id (%i) not allowed", event_id); + continue; + } + + int32 internal_event_id = mGameEvent.size() + event_id - 1; + + if (internal_event_id < 0 || (size_t)internal_event_id >= mGameEventMails.size()) + { + sLog.outErrorDb("`game_event_mail` game event id (%i) is out of range compared to max event id in `game_event`", event_id); + continue; + } + + if (!(mail.raceMask & RACEMASK_ALL_PLAYABLE)) + { + sLog.outErrorDb("Table `game_event_mail` have raceMask (%u) requirement for game event %i that not include any player races, ignoring.", mail.raceMask, event_id); + continue; + } + + if (mail.questId && !sObjectMgr.GetQuestTemplate(mail.questId)) + { + sLog.outErrorDb("Table `game_event_mail` have nonexistent quest (%u) requirement for game event %i, ignoring.", mail.questId, event_id); + continue; + } + + if (!sMailTemplateStore.LookupEntry(mail.mailTemplateId)) + { + sLog.outErrorDb("Table `game_event_mail` have invalid mailTemplateId (%u) for game event %i that invalid not include any player races, ignoring.", mail.mailTemplateId, event_id); + continue; + } + + if (!ObjectMgr::GetCreatureTemplate(mail.senderEntry)) + { + sLog.outErrorDb("Table `game_event_mail` have nonexistent sender creature entry (%u) for game event %i that invalid not include any player races, ignoring.", mail.senderEntry, event_id); + continue; + } + + ++count; + + MailList& maillist = mGameEventMails[internal_event_id]; + maillist.push_back(mail); + + } while( result->NextRow() ); + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u start/end game event mails", count ); + } } uint32 GameEventMgr::Initialize() // return the next event delay in ms { m_ActiveEvents.clear(); - uint32 delay = Update(); + + ActiveEvents activeAtShutdown; + + if (QueryResult *result = CharacterDatabase.Query("SELECT event FROM game_event_status")) + { + do + { + Field *fields = result->Fetch(); + uint16 event_id = fields[0].GetUInt16(); + activeAtShutdown.insert(event_id); + } while( result->NextRow() ); + delete result; + + CharacterDatabase.Execute("TRUNCATE game_event_status"); + } + + uint32 delay = Update(&activeAtShutdown); BASIC_LOG("Game Event system initialized." ); m_IsGameEventsInit = true; return delay; } -uint32 GameEventMgr::Update() // return the next event delay in ms +// return the next event delay in ms +uint32 GameEventMgr::Update(ActiveEvents const* activeAtShutdown /*= NULL*/) { + time_t currenttime = time(NULL); + uint32 nextEventDelay = max_ge_check_delay; // 1 day uint32 calcDelay; for (uint16 itr = 1; itr < mGameEvent.size(); ++itr) { //sLog.outErrorDb("Checking event %u",itr); - if (CheckOneGameEvent(itr)) + if (CheckOneGameEvent(itr, currenttime)) { //DEBUG_LOG("GameEvent %u is active",itr->first); if (!IsActiveEvent(itr)) - StartEvent(itr); + { + bool resume = activeAtShutdown && (activeAtShutdown->find(itr) != activeAtShutdown->end()); + StartEvent(itr, false, resume); + } } else { @@ -532,6 +632,9 @@ uint32 GameEventMgr::Update() // return the next e void GameEventMgr::UnApplyEvent(uint16 event_id) { + m_ActiveEvents.erase(event_id); + CharacterDatabase.PQuery("DELETE FROM game_event_status WHERE event = %u", event_id); + sLog.outString("GameEvent %u \"%s\" removed.", event_id, mGameEvent[event_id].description.c_str()); // un-spawn positive event tagged objects GameEventUnspawn(event_id); @@ -543,10 +646,14 @@ void GameEventMgr::UnApplyEvent(uint16 event_id) // Remove quests that are events only to non event npc UpdateEventQuests(event_id, false); UpdateWorldStates(event_id, false); + SendEventMails(event_nid); } -void GameEventMgr::ApplyNewEvent(uint16 event_id) +void GameEventMgr::ApplyNewEvent(uint16 event_id, bool resume) { + m_ActiveEvents.insert(event_id); + CharacterDatabase.PQuery("INSERT INTO game_event_status (event) VALUES (%u)", event_id); + if (sWorld.getConfig(CONFIG_BOOL_EVENT_ANNOUNCE)) sWorld.SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str()); @@ -561,6 +668,10 @@ void GameEventMgr::ApplyNewEvent(uint16 event_id) // Add quests that are events only to non event npc UpdateEventQuests(event_id, true); UpdateWorldStates(event_id, true); + + // Not send mails at game event startup, if game event just resume after server shutdown (has been active at server before shutdown) + if (!resume) + SendEventMails(event_id); } void GameEventMgr::GameEventSpawn(int16 event_id) @@ -833,6 +944,31 @@ void GameEventMgr::UpdateWorldStates(uint16 event_id, bool Activate) } } +void GameEventMgr::SendEventMails(int16 event_id) +{ + int32 internal_event_id = mGameEvent.size() + event_id - 1; + + MailList const& mails = mGameEventMails[internal_event_id]; + + for (MailList::const_iterator itr = mails.begin(); itr != mails.end(); ++itr) + { + if (itr->questId) + { + // need special query + std::ostringstream ss; + ss << "SELECT characters.guid FROM characters, character_queststatus " + "WHERE (1 << (characters.race - 1)) & " + << itr->raceMask + << " AND characters.deleteDate IS NULL AND character_queststatus.guid = characters.guid AND character_queststatus.quest = " + << itr->questId + << " AND character_queststatus.rewarded <> 0"; + sMassMailMgr.AddMassMailTask(new MailDraft(itr->mailTemplateId), MailSender(MAIL_CREATURE, itr->senderEntry), ss.str().c_str()); + } + else + sMassMailMgr.AddMassMailTask(new MailDraft(itr->mailTemplateId), MailSender(MAIL_CREATURE, itr->senderEntry), itr->raceMask); + } +} + // Get the Game Event ID for Creature by guid template <> int16 GameEventMgr::GetGameEventId(uint32 guid_or_poolid) diff --git a/src/game/GameEventMgr.h b/src/game/GameEventMgr.h index a2936b465..42ee3425f 100644 --- a/src/game/GameEventMgr.h +++ b/src/game/GameEventMgr.h @@ -51,6 +51,18 @@ struct GameEventCreatureData uint32 spell_id_end; }; +struct GameEventMail +{ + GameEventMail() : raceMask(0), questId(0), mailTemplateId(0), senderEntry(0) {} + GameEventMail(uint32 _raceMask, uint32 _quest, uint32 _mailTemplateId, uint32 _senderEntry) + : raceMask(_raceMask), questId(_quest), mailTemplateId(_mailTemplateId), senderEntry(_senderEntry) {} + + uint32 raceMask; + uint32 questId; // quest must be rewarded if set + uint32 mailTemplateId; + uint32 senderEntry; +}; + typedef std::pair GameEventCreatureDataPair; class GameEventMgr @@ -62,28 +74,27 @@ class GameEventMgr typedef std::vector GameEventDataMap; ActiveEvents const& GetActiveEventList() const { return m_ActiveEvents; } GameEventDataMap const& GetEventMap() const { return mGameEvent; } - bool CheckOneGameEvent(uint16 entry) const; + bool CheckOneGameEvent(uint16 entry, time_t currenttime) const; uint32 NextCheck(uint16 entry) const; void LoadFromDB(); - uint32 Update(); + uint32 Update(ActiveEvents const* activeAtShutdown = NULL); bool IsActiveEvent(uint16 event_id) const { return ( m_ActiveEvents.find(event_id)!=m_ActiveEvents.end()); } uint32 Initialize(); - void StartEvent(uint16 event_id, bool overwrite = false); + void StartEvent(uint16 event_id, bool overwrite = false, bool resume = false); void StopEvent(uint16 event_id, bool overwrite = false); template int16 GetGameEventId(uint32 guid_or_poolid); GameEventCreatureData const* GetCreatureUpdateDataForActiveEvent(uint32 lowguid) const; private: - void AddActiveEvent(uint16 event_id) { m_ActiveEvents.insert(event_id); } - void RemoveActiveEvent(uint16 event_id) { m_ActiveEvents.erase(event_id); } - void ApplyNewEvent(uint16 event_id); + void ApplyNewEvent(uint16 event_id, bool resume); void UnApplyEvent(uint16 event_id); void GameEventSpawn(int16 event_id); void GameEventUnspawn(int16 event_id); void UpdateCreatureData(int16 event_id, bool activate); - void UpdateEventQuests(uint16 event_id, bool Activate); - void UpdateWorldStates(uint16 event_id, bool Activate); + void UpdateEventQuests(uint16 event_id, bool activate); + void UpdateWorldStates(uint16 event_id, bool activate); + void SendEventMails(int16 event_id); protected: typedef std::list GuidList; typedef std::list IdList; @@ -96,11 +107,15 @@ class GameEventMgr typedef std::list QuestList; typedef std::vector GameEventQuestMap; - GameEventQuestMap mGameEventQuests; // events*2-1 + GameEventQuestMap mGameEventQuests; // events size, only positive event case - GameEventCreatureDataMap mGameEventCreatureData; // events*2-1 + GameEventCreatureDataMap mGameEventCreatureData; // events size, only positive event case GameEventCreatureDataPerGuidMap mGameEventCreatureDataPerGuid; + typedef std::list MailList; + typedef std::vector GameEventMailMap; + GameEventMailMap mGameEventMails; // events*2-1 + GameEventGuidMap mGameEventCreatureGuids; // events*2-1 GameEventGuidMap mGameEventGameobjectGuids; // events*2-1 GameEventIdMap mGameEventSpawnPoolIds; // events size, only positive event case diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 371723cbb..36d7c0b06 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10972" + #define REVISION_NR "10973" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 53e6e956a..eda27ffef 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ - #define REVISION_DB_CHARACTERS "required_10862_01_characters_mail" - #define REVISION_DB_MANGOS "required_10972_01_mangos_command" + #define REVISION_DB_CHARACTERS "required_10973_01_characters_game_event_status" + #define REVISION_DB_MANGOS "required_10973_01_mangos_game_event_mail" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__