diff --git a/sql/characters.sql b/sql/characters.sql index fae2d9d36..4a5c0b26c 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -23,7 +23,7 @@ DROP TABLE IF EXISTS `character_db_version`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `character_db_version` ( - `required_z1142_s0531_01_characters_bugreport` bit(1) default NULL + `required_z1217_s0636_02_characters_game_event_status` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; /*!40101 SET character_set_client = @saved_cs_client */; @@ -746,6 +746,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 1053abacf..0a0a78721 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, - `required_z1205_s0624_01_mangos_mangos_string` bit(1) default NULL + `required_z1217_s0636_03_mangos_game_event_mail` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; /*!40101 SET character_set_client = @saved_cs_client */; @@ -651,8 +651,11 @@ INSERT INTO `command` VALUES ('revive',3,'Syntax: .revive\r\n\r\nRevive the selected player. If no player is selected, it will revive you.'), ('save',0,'Syntax: .save\r\n\r\nSaves your character.'), ('saveall',1,'Syntax: .saveall\r\n\r\nSave all characters in game.'), -('send items',3,'Syntax: .send items #playername \"#subject\" \"#text\" itemid1[:count1] itemid2[:count2] ... itemidN[:countN]\r\n\r\nSend a mail to a player. Subject and mail text must be in \"\". If for itemid not provided related count values then expected 1, if count > max items in stack then items will be send in required amount stacks. All stacks amount in mail limited to 12.'), -('send mail',1,'Syntax: .send mail #playername \"#subject\" \"#text\"\r\n\r\nSend a mail to a player. Subject and mail text must be in \"\".'), +('send items',3,'Syntax: .send items #playername "#subject" "#text" itemid1[:count1] itemid2[:count2] ... itemidN[:countN]\r\n\r\nSend a mail to a player. Subject and mail text must be in "". If for itemid not provided related count values then expected 1, if count > max items in stack then items will be send in required amount stacks. All stacks amount in mail limited to 12.'), +('send mail',1,'Syntax: .send mail #playername "#subject" "#text"\r\n\r\nSend a mail to a player. Subject and mail text must be in "".'), +('send mass items',3,'Syntax: .send mass items #racemask|$racename|alliance|horde|all "#subject" "#text" itemid1[:count1] itemid2[:count2] ... itemidN[:countN]\r\n\r\nSend a mail to players. Subject and mail text must be in "". If for itemid not provided related count values then expected 1, if count > max items in stack then items will be send in required amount stacks. All stacks amount in mail limited to 12.'), +('send mass mail',1,'Syntax: .send mass mail #racemask|$racename|alliance|horde|all "#subject" "#text"\r\n\r\nSend a mail to players. Subject and mail text must be in "".'), +('send mass money','3','Syntax: .send mass money #racemask|$racename|alliance|horde|all "#subject" "#text" #money\r\n\r\nSend mail with money to players. Subject and mail text must be in "".'), ('send message',3,'Syntax: .send message $playername $message\r\n\r\nSend screen message to player from ADMINISTRATOR.'), ('send money',3,'Syntax: .send money #playername \"#subject\" \"#text\" #money\r\n\r\nSend mail with money to a player. Subject and mail text must be in \"\".'), ('server corpses',2,'Syntax: .server corpses\r\n\r\nTriggering corpses expire check in world.'), @@ -1627,6 +1630,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/Makefile.am b/sql/updates/Makefile.am index dfd32cb79..6b230f6a6 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -194,7 +194,10 @@ pkgdata_DATA = \ z1192_s0612_02_mangos_spell_chain.sql \ z1194_s0611_01_mangos_mangos_string.sql \ z1202_s0620_01_mangos_mangos_string.sql \ - z1205_s0624_01_mangos_mangos_string.sql + z1205_s0624_01_mangos_mangos_string.sql \ + z1217_s0636_01_mangos_command.sql \ + z1217_s0636_02_characters_game_event_status.sql \ + z1217_s0636_03_mangos_game_event_mail.sql ## Additional files to include when running 'make dist' # SQL update files, to upgrade database schema from older revisions @@ -367,4 +370,7 @@ EXTRA_DIST = \ z1192_s0612_02_mangos_spell_chain.sql \ z1194_s0611_01_mangos_mangos_string.sql \ z1202_s0620_01_mangos_mangos_string.sql \ - z1205_s0624_01_mangos_mangos_string.sql + z1205_s0624_01_mangos_mangos_string.sql \ + z1217_s0636_01_mangos_command.sql \ + z1217_s0636_02_characters_game_event_status.sql \ + z1217_s0636_03_mangos_game_event_mail.sql diff --git a/sql/updates/z1217_s0636_01_mangos_command.sql b/sql/updates/z1217_s0636_01_mangos_command.sql new file mode 100644 index 000000000..396444b58 --- /dev/null +++ b/sql/updates/z1217_s0636_01_mangos_command.sql @@ -0,0 +1,9 @@ +ALTER TABLE db_version CHANGE COLUMN required_z1205_s0624_01_mangos_mangos_string required_z1217_s0636_01_mangos_command bit; + +DELETE FROM command WHERE name IN ('send mass items','send mass mail','send mass money'); + +INSERT INTO command (name, security, help) VALUES +('send mass items',3,'Syntax: .send mass items #racemask|$racename|alliance|horde|all "#subject" "#text" itemid1[:count1] itemid2[:count2] ... itemidN[:countN]\r\n\r\nSend a mail to players. Subject and mail text must be in "". If for itemid not provided related count values then expected 1, if count > max items in stack then items will be send in required amount stacks. All stacks amount in mail limited to 12.'), +('send mass mail',1,'Syntax: .send mass mail #racemask|$racename|alliance|horde|all "#subject" "#text"\r\n\r\nSend a mail to players. Subject and mail text must be in "".'), +('send mass money','3','Syntax: .send mass money #racemask|$racename|alliance|horde|all "#subject" "#text" #money\r\n\r\nSend mail with money to players. Subject and mail text must be in "".'); + diff --git a/sql/updates/z1217_s0636_02_characters_game_event_status.sql b/sql/updates/z1217_s0636_02_characters_game_event_status.sql new file mode 100644 index 000000000..7d08cbd48 --- /dev/null +++ b/sql/updates/z1217_s0636_02_characters_game_event_status.sql @@ -0,0 +1,7 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_z1142_s0531_01_characters_bugreport required_z1217_s0636_02_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/z1217_s0636_03_mangos_game_event_mail.sql b/sql/updates/z1217_s0636_03_mangos_game_event_mail.sql new file mode 100644 index 000000000..6f2127b97 --- /dev/null +++ b/sql/updates/z1217_s0636_03_mangos_game_event_mail.sql @@ -0,0 +1,11 @@ +ALTER TABLE db_version CHANGE COLUMN required_z1217_s0636_01_mangos_command required_z1217_s0636_03_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/src/game/Chat.cpp b/src/game/Chat.cpp index 53ecb2469..c4a838569 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -496,8 +496,18 @@ ChatCommand * ChatHandler::getCommandTable() { NULL, 0, false, NULL, "", NULL } }; + static ChatCommand sendMassCommandTable[] = + { + { "items", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMassItemsCommand, "", NULL }, + { "mail", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMassMailCommand, "", NULL }, + { "money", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMassMoneyCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand sendCommandTable[] = { + { "mass", SEC_ADMINISTRATOR, true, NULL, "", sendMassCommandTable }, + { "items", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendItemsCommand, "", NULL }, { "mail", SEC_MODERATOR, true, &ChatHandler::HandleSendMailCommand, "", NULL }, { "message", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMessageCommand, "", NULL }, @@ -3082,6 +3092,61 @@ uint32 ChatHandler::ExtractAccountId(char** args, std::string* accountName /*= N return account_id; } +struct RaceMaskName +{ + char const* literal; + uint32 raceMask; +}; + +static RaceMaskName const raceMaskNames[] = +{ + // races + { "human", (1<<(RACE_HUMAN-1)) }, + { "orc", (1<<(RACE_ORC-1)) }, + { "dwarf", (1<<(RACE_DWARF-1)) }, + { "nightelf", (1<<(RACE_NIGHTELF-1))}, + { "undead", (1<<(RACE_UNDEAD-1)) }, + { "tauren", (1<<(RACE_TAUREN-1)) }, + { "gnome", (1<<(RACE_GNOME-1)) }, + { "troll", (1<<(RACE_TROLL-1)) }, + + // masks + { "alliance", RACEMASK_ALLIANCE }, + { "horde", RACEMASK_HORDE }, + { "all", RACEMASK_ALL_PLAYABLE }, + + // terminator + { NULL, 0 } +}; + +bool ChatHandler::ExtractRaceMask(char** text, uint32& raceMask, char const** maskName /*=NULL*/) +{ + if (ExtractUInt32(text, raceMask)) + { + if (maskName) + *maskName = "custom mask"; + } + else + { + for (RaceMaskName const* itr = raceMaskNames; itr->literal; ++itr) + { + if (ExtractLiteralArg(text, itr->literal)) + { + raceMask = itr->raceMask; + + if (maskName) + *maskName = itr->literal; + break; + } + } + + if (!raceMask) + return false; + } + + return true; +} + std::string ChatHandler::GetNameLink(Player* chr) const { return playerLink(chr->GetName()); diff --git a/src/game/Chat.h b/src/game/Chat.h index 49e521cd6..560402914 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -36,6 +36,7 @@ class ChatHandler; class WorldSession; class WorldPacket; class GMTicket; +class MailDraft; class Object; class GameObject; class Creature; @@ -428,6 +429,10 @@ class ChatHandler bool HandleSendMessageCommand(char* args); bool HandleSendMoneyCommand(char* args); + bool HandleSendMassItemsCommand(char* args); + bool HandleSendMassMailCommand(char* args); + bool HandleSendMassMoneyCommand(char* args); + bool HandleServerCorpsesCommand(char* args); bool HandleServerExitCommand(char* args); bool HandleServerIdleRestartCommand(char* args); @@ -561,6 +566,7 @@ class ChatHandler ObjectGuid ExtractGuidFromLink(char** text); GameTele const* ExtractGameTeleFromLink(char** text); bool ExtractLocationFromLink(char** text, uint32& mapid, float& x, float& y, float& z); + bool ExtractRaceMask(char** text, uint32& raceMask, char const** maskName = NULL); std::string ExtractPlayerNameFromLink(char** text); bool ExtractPlayerTarget(char** args, Player** player, ObjectGuid* player_guid = NULL, std::string* player_name = NULL); // select by arg (name/link) or in-game selection online/offline player @@ -591,6 +597,11 @@ class ChatHandler bool HandleGetValueHelper(Object* target, uint32 field, char* typeStr); bool HandlerDebugModValueHelper(Object* target, uint32 field, char* typeStr, char* valStr); bool HandleSetValueHelper(Object* target, uint32 field, char* typeStr, char* valStr); + + bool HandleSendItemsHelper(MailDraft& draft, char* args); + bool HandleSendMailHelper(MailDraft& draft, char* args); + bool HandleSendMoneyHelper(MailDraft& draft, char* args); + template void ShowNpcOrGoSpawnInformation(uint32 guid); template diff --git a/src/game/GameEventMgr.cpp b/src/game/GameEventMgr.cpp index f8cd68760..b1a2fbb7a 100644 --- a/src/game/GameEventMgr.cpp +++ b/src/game/GameEventMgr.cpp @@ -27,15 +27,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; @@ -69,10 +69,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); @@ -83,7 +82,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) { @@ -473,29 +471,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 { @@ -522,6 +622,9 @@ uint32 GameEventMgr::Update() // return the next e void GameEventMgr::UnApplyEvent(uint16 event_id) { + m_ActiveEvents.erase(event_id); + CharacterDatabase.PExecute("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); @@ -532,10 +635,14 @@ void GameEventMgr::UnApplyEvent(uint16 event_id) UpdateCreatureData(event_id, false); // Remove quests that are events only to non event npc UpdateEventQuests(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.PExecute("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()); @@ -549,6 +656,10 @@ void GameEventMgr::ApplyNewEvent(uint16 event_id) UpdateCreatureData(event_id, true); // Add quests that are events only to non event npc UpdateEventQuests(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) @@ -798,6 +909,31 @@ void GameEventMgr::UpdateEventQuests(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 4ae483fd7..94dfe0cfe 100644 --- a/src/game/GameEventMgr.h +++ b/src/game/GameEventMgr.h @@ -52,6 +52,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 @@ -63,27 +75,26 @@ 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 UpdateEventQuests(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/game/Level1.cpp b/src/game/Level1.cpp index 3e7892f4d..3edcecf2e 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -1620,19 +1620,16 @@ bool ChatHandler::HandleSendMailCommand(char* args) if (!ExtractPlayerTarget(&args, &target, &target_guid, &target_name)) return false; - char* msgSubject = ExtractQuotedArg(&args); - if (!msgSubject) - return false; + MailDraft draft; - char* msgText = ExtractQuotedArg(&args); - if (!msgText) + // fill draft + if (!HandleSendMailHelper(draft, args)) return false; // from console show nonexistent sender MailSender sender(MAIL_NORMAL, m_session ? m_session->GetPlayer()->GetObjectGuid().GetCounter() : 0, MAIL_STATIONERY_GM); - MailDraft(msgSubject, msgText) - .SendMailTo(MailReceiver(target, target_guid),sender); + draft.SendMailTo(MailReceiver(target, target_guid),sender); std::string nameLink = playerLink(target_name); PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 172cc4701..80c3d5ba4 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -34,6 +34,7 @@ #include "Guild.h" #include "ObjectAccessor.h" #include "MapManager.h" +#include "MassMailMgr.h" #include "ScriptMgr.h" #include "Language.h" #include "GridNotifiersImpl.h" @@ -5891,16 +5892,56 @@ bool ChatHandler::HandleAccountSetAddonCommand(char* args) return true; } -//Send items by mail -bool ChatHandler::HandleSendItemsCommand(char* args) +bool ChatHandler::HandleSendMailHelper(MailDraft& draft, char* args) { - // format: name "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12] - Player* receiver; - ObjectGuid receiver_guid; - std::string receiver_name; - if (!ExtractPlayerTarget(&args, &receiver, &receiver_guid, &receiver_name)) + // format: "subject text" "mail text" + char* msgSubject = ExtractQuotedArg(&args); + if (!msgSubject) + return false; + + char* msgText = ExtractQuotedArg(&args); + if (!msgText) + return false; + + // msgSubject, msgText isn't NUL after prev. check + draft.SetSubjectAndBody(msgSubject, msgText); + + return true; +} + +bool ChatHandler::HandleSendMassMailCommand(char* args) +{ + // format: raceMask "subject text" "mail text" + uint32 raceMask = 0; + char const* name = NULL; + + if (!ExtractRaceMask(&args, raceMask, &name)) return false; + // need dynamic object because it trasfered to mass mailer + MailDraft* draft = new MailDraft; + + // fill mail + if (!HandleSendMailHelper(*draft, args)) + { + delete draft; + return false; + } + + // from console show nonexistent sender + MailSender sender(MAIL_NORMAL, m_session ? m_session->GetPlayer()->GetObjectGuid().GetCounter() : 0, MAIL_STATIONERY_GM); + + sMassMailMgr.AddMassMailTask(draft, sender, raceMask); + + PSendSysMessage(LANG_MAIL_SENT, name); + return true; +} + + + +bool ChatHandler::HandleSendItemsHelper(MailDraft& draft, char* args) +{ + // format: "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12] char* msgSubject = ExtractQuotedArg(&args); if (!msgSubject) return false; @@ -5962,11 +6003,8 @@ bool ChatHandler::HandleSendItemsCommand(char* args) } } - // from console show nonexistent sender - MailSender sender(MAIL_NORMAL, m_session ? m_session->GetPlayer()->GetObjectGuid().GetCounter() : 0, MAIL_STATIONERY_GM); - // fill mail - MailDraft draft(msgSubject, msgText); + draft.SetSubjectAndBody(msgSubject, msgText); for(ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr) { @@ -5977,6 +6015,27 @@ bool ChatHandler::HandleSendItemsCommand(char* args) } } + return true; +} + +bool ChatHandler::HandleSendItemsCommand(char* args) +{ + // format: name "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12] + Player* receiver; + ObjectGuid receiver_guid; + std::string receiver_name; + if (!ExtractPlayerTarget(&args, &receiver, &receiver_guid, &receiver_name)) + return false; + + MailDraft draft; + + // fill mail + if (!HandleSendItemsHelper(draft, args)) + return false; + + // from console show nonexistent sender + MailSender sender(MAIL_NORMAL, m_session ? m_session->GetPlayer()->GetObjectGuid().GetCounter() : 0, MAIL_STATIONERY_GM); + draft.SendMailTo(MailReceiver(receiver, receiver_guid), sender); std::string nameLink = playerLink(receiver_name); @@ -5984,17 +6043,40 @@ bool ChatHandler::HandleSendItemsCommand(char* args) return true; } -///Send money by mail -bool ChatHandler::HandleSendMoneyCommand(char* args) +bool ChatHandler::HandleSendMassItemsCommand(char* args) { - /// format: name "subject text" "mail text" money + // format: racemask "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12] - Player* receiver; - ObjectGuid receiver_guid; - std::string receiver_name; - if (!ExtractPlayerTarget(&args, &receiver, &receiver_guid, &receiver_name)) + uint32 raceMask = 0; + char const* name = NULL; + + if (!ExtractRaceMask(&args, raceMask, &name)) return false; + // need dynamic object because it trasfered to mass mailer + MailDraft* draft = new MailDraft; + + + // fill mail + if (!HandleSendItemsHelper(*draft, args)) + { + delete draft; + return false; + } + + // from console show nonexistent sender + MailSender sender(MAIL_NORMAL, m_session ? m_session->GetPlayer()->GetObjectGuid().GetCounter() : 0, MAIL_STATIONERY_GM); + + sMassMailMgr.AddMassMailTask(draft, sender, raceMask); + + PSendSysMessage(LANG_MAIL_SENT, name); + return true; +} + +bool ChatHandler::HandleSendMoneyHelper(MailDraft& draft, char* args) +{ + /// format: "subject text" "mail text" money + char* msgSubject = ExtractQuotedArg(&args); if (!msgSubject) return false; @@ -6010,18 +6092,67 @@ bool ChatHandler::HandleSendMoneyCommand(char* args) if (money <= 0) return false; + // msgSubject, msgText isn't NUL after prev. check + draft.SetSubjectAndBody(msgSubject, msgText).SetMoney(money); + + return true; +} + +bool ChatHandler::HandleSendMoneyCommand(char* args) +{ + /// format: name "subject text" "mail text" money + + Player* receiver; + ObjectGuid receiver_guid; + std::string receiver_name; + if (!ExtractPlayerTarget(&args, &receiver, &receiver_guid, &receiver_name)) + return false; + + MailDraft draft; + + // fill mail + if (!HandleSendMoneyHelper(draft, args)) + return false; + // from console show nonexistent sender MailSender sender(MAIL_NORMAL, m_session ? m_session->GetPlayer()->GetObjectGuid().GetCounter() : 0, MAIL_STATIONERY_GM); - MailDraft(msgSubject, msgText) - .SetMoney(money) - .SendMailTo(MailReceiver(receiver, receiver_guid),sender); + draft.SendMailTo(MailReceiver(receiver, receiver_guid),sender); std::string nameLink = playerLink(receiver_name); PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); return true; } +bool ChatHandler::HandleSendMassMoneyCommand(char* args) +{ + /// format: raceMask "subject text" "mail text" money + + uint32 raceMask = 0; + char const* name = NULL; + + if (!ExtractRaceMask(&args, raceMask, &name)) + return false; + + // need dynamic object because it trasfered to mass mailer + MailDraft* draft = new MailDraft; + + // fill mail + if (!HandleSendMoneyHelper(*draft, args)) + { + delete draft; + return false; + } + + // from console show nonexistent sender + MailSender sender(MAIL_NORMAL, m_session ? m_session->GetPlayer()->GetObjectGuid().GetCounter() : 0, MAIL_STATIONERY_GM); + + sMassMailMgr.AddMassMailTask(draft, sender, raceMask); + + PSendSysMessage(LANG_MAIL_SENT, name); + return true; +} + /// Send a message to a player in game bool ChatHandler::HandleSendMessageCommand(char* args) { diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index c5d65f4f7..884e1ad39 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -820,6 +820,16 @@ m_bodyId(!text.empty() ? sObjectMgr.CreateItemText(text) : 0), m_money(0), m_COD } +MailDraft& MailDraft::SetSubjectAndBody(std::string subject, std::string text) +{ + m_subject = subject; + + MANGOS_ASSERT(!m_bodyId); + m_bodyId = !text.empty() ? sObjectMgr.CreateItemText(text) : 0; + + return *this; +} + /** * Adds an item to the MailDraft. * @@ -878,6 +888,40 @@ void MailDraft::deleteIncludedItems( bool inDB /**= false*/ ) m_items.clear(); } +/** + * Clone MailDraft from another MailDraft. + * + * @param draft Point to source for draft cloning. + */ +void MailDraft::CloneFrom(MailDraft const& draft) +{ + m_mailTemplateId = draft.GetMailTemplateId(); + m_mailTemplateItemsNeed = draft.m_mailTemplateItemsNeed; + + m_subject = draft.GetSubject(); + + MANGOS_ASSERT(!m_bodyId); + if (uint32 bodyId = draft.GetBodyId()) + { + std::string text = sObjectMgr.GetItemText(bodyId); + m_bodyId = sObjectMgr.CreateItemText(text); + } + + m_money = draft.GetMoney(); + m_COD = draft.GetCOD(); + + for(MailItemMap::const_iterator mailItemIter = draft.m_items.begin(); mailItemIter != draft.m_items.end(); ++mailItemIter) + { + Item* item = mailItemIter->second; + + if(Item* newitem = item->CloneItem(item->GetCount())) + { + newitem->SaveToDB(); + AddItem(newitem); + } + } +} + /* * Returns a mail to its sender. * @param sender_acc The id of the account of the sender. diff --git a/src/game/Mail.h b/src/game/Mail.h index e51e99d09..a36d46fe6 100644 --- a/src/game/Mail.h +++ b/src/game/Mail.h @@ -19,9 +19,11 @@ /** * @addtogroup mailing The mail system - * The mailing system in MaNGOS consists of mostly two files: + * The mailing system in MaNGOS consists of mostly 4 files: * - Mail.h * - Mail.cpp + * - MassMailMgr.h + * - MassMailMgr.cpp * * @{ * @@ -228,7 +230,8 @@ class MailDraft public: // modifiers // this two modifiers expected to be applied in normal case to blank draft and exclusively, It DON'T must overwrite already set itemTextId, in other cases it will work and with mixed cases but this will be not normal way use. - MailDraft& SetSubjectAndBodyId(std::string subject, uint32 itemTextId) { m_subject = subject; MANGOS_ASSERT(!m_bodyId); m_bodyId = m_bodyId; return *this; } + MailDraft& SetSubjectAndBodyId(std::string subject, uint32 itemTextId) { m_subject = subject; MANGOS_ASSERT(!m_bodyId); m_bodyId = itemTextId; return *this; } + MailDraft& SetSubjectAndBody(std::string subject, std::string text); MailDraft& SetMailTemplate(uint16 mailTemplateId, bool need_items = true) { m_mailTemplateId = mailTemplateId, m_mailTemplateItemsNeed = need_items; return *this; } MailDraft& AddItem(Item* item); @@ -244,6 +247,8 @@ class MailDraft * @param COD the amount to which the cod should be set. */ MailDraft& SetCOD(uint32 COD) { m_COD = COD; return *this; } + + void CloneFrom(MailDraft const& draft); public: // finishers void SendReturnToSender(uint32 sender_acc, ObjectGuid sender_guid, ObjectGuid receiver_guid); void SendMailTo(MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0); diff --git a/src/game/Makefile.am b/src/game/Makefile.am index 80b5c68b3..6fa558257 100644 --- a/src/game/Makefile.am +++ b/src/game/Makefile.am @@ -159,6 +159,8 @@ libmangosgame_a_SOURCES = \ MapManager.h \ MapReference.h \ MapRefManager.h \ + MassMailMgr.cpp \ + MassMailMgr.h \ MiscHandler.cpp \ MotionMaster.cpp \ MotionMaster.h \ diff --git a/src/game/MassMailMgr.cpp b/src/game/MassMailMgr.cpp new file mode 100644 index 000000000..3e320b801 --- /dev/null +++ b/src/game/MassMailMgr.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2011 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @addtogroup mailing + * @{ + * + * @file MassMailMgr.cpp + * This file contains the the code needed for MaNGOS to handle mass mails send in safe and perfomence not affecting way. + * + */ + +#include "MassMailMgr.h" +#include "Policies/SingletonImp.h" +#include "Database/DatabaseEnv.h" +#include "Database/DatabaseImpl.h" +#include "SharedDefines.h" +#include "World.h" +#include "ObjectMgr.h" + +INSTANTIATE_SINGLETON_1(MassMailMgr); + +void MassMailMgr::AddMassMailTask(MailDraft* mailProto, MailSender sender, uint32 raceMask) +{ + if (RACEMASK_ALL_PLAYABLE & ~raceMask) // have races not included in mask + { + std::ostringstream ss; + ss << "SELECT guid FROM characters WHERE (1 << (race - 1)) & " << raceMask << " AND deleteDate IS NULL"; + AddMassMailTask(mailProto, sender, ss.str().c_str()); + } + else + AddMassMailTask(mailProto, sender, "SELECT guid FROM characters WHERE deleteDate IS NULL"); +} + +struct MassMailerQueryHandler +{ + void HandleQueryCallback(QueryResult * result, MailDraft* mailProto, MailSender sender) + { + if (!result) + return; + + MassMailMgr::ReceiversList& recievers = sMassMailMgr.AddMassMailTask(mailProto, sender); + + do + { + Field *fields = result->Fetch(); + recievers.insert(fields[0].GetUInt32()); + + } while (result->NextRow()); + delete result; + } +} massMailerQueryHandler; + +void MassMailMgr::AddMassMailTask(MailDraft* mailProto, MailSender sender, char const* query) +{ + CharacterDatabase.AsyncPQuery(&massMailerQueryHandler, &MassMailerQueryHandler::HandleQueryCallback, mailProto, sender, query); +} + +void MassMailMgr::Update(bool sendall /*= false*/) +{ + if (m_massMails.empty()) + return; + + uint32 maxcount = sWorld.getConfig(CONFIG_UINT32_MASS_MAILER_SEND_PER_TICK); + + do + { + MassMail& task = m_massMails.front(); + + while (!task.m_recivers.empty() && (sendall || maxcount > 0)) + { + uint32 receiver_lowguid = *task.m_recivers.begin(); + task.m_recivers.erase(task.m_recivers.begin()); + + ObjectGuid receiver_guid = ObjectGuid(HIGHGUID_PLAYER, receiver_lowguid); + Player *receiver = sObjectMgr.GetPlayer(receiver_guid); + + // last case. can be just send + if (task.m_recivers.empty()) + { + // prevent mail return + task.m_protoMail->SendMailTo(MailReceiver(receiver, receiver_guid), task.m_sender, MAIL_CHECK_MASK_RETURNED); + + if (!sendall) + --maxcount; + break; + } + + // need clone draft + MailDraft draft; + draft.CloneFrom(*task.m_protoMail); + + // prevent mail return + draft.SendMailTo(MailReceiver(receiver, receiver_guid), task.m_sender, MAIL_CHECK_MASK_RETURNED); + + if (!sendall) + --maxcount; + } + + if (task.m_recivers.empty()) + m_massMails.pop_front(); + } + while(!m_massMails.empty() && (sendall || maxcount > 0)); +} + +void MassMailMgr::GetStatistic(uint32& tasks, uint32& mails, uint32& needTime) const +{ + tasks = m_massMails.size(); + + uint32 mailsCount = 0; + for (MassMailList::const_iterator mailItr = m_massMails.begin(); mailItr != m_massMails.end(); ++mailItr) + mailsCount += mailItr->m_recivers.size(); + + mails = mailsCount; + + // 50 msecs is tick length + needTime = 50 * mailsCount / sWorld.getConfig(CONFIG_UINT32_MASS_MAILER_SEND_PER_TICK) / IN_MILLISECONDS; +} + + +/*! @} */ diff --git a/src/game/MassMailMgr.h b/src/game/MassMailMgr.h new file mode 100644 index 000000000..26e54bb8e --- /dev/null +++ b/src/game/MassMailMgr.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2011 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @addtogroup mailing The mail system + * The mailing system in MaNGOS consists of mostly 4 files: + * - Mail.h + * - Mail.cpp + * - MassMailMgr.h + * - MassMailMgr.cpp + * + * @{ + * + * @file MassMailMgr.h + * This file contains the the headers needed for MaNGOS to handle mass mails send in safe and perfomence not affecting way. + * + */ + +#ifndef MANGOS_MASS_MAIL_MGR_H +#define MANGOS_MASS_MAIL_MGR_H + +#include "Common.h" +#include "Mail.h" +#include "Policies/Singleton.h" + +#include + +/** + * A class to represent the mail send factory to multiply (often all existed) characters. + * + * Note: implementation not persistence for server shutdowns + */ +class MassMailMgr +{ + public: // Constructors + MassMailMgr() {} + + public: // Accessors + void GetStatistic(uint32& tasks, uint32& mails, uint32& needTime) const; + + public: // modifiers + typedef UNORDERED_SET ReceiversList; + + /** + * And new mass mail task for raceMask filter applied to characters list. + * + * @param mailProto prepared mail for clone and send to characters, will deleted in result call. + * @param raceMask mask of races that must receive mail. + * + * Note: this function safe to be called from Map::Update content/etc, real data add will executed in next tick after query resultrs ready + */ + void AddMassMailTask(MailDraft* mailProto, MailSender sender, uint32 raceMask); + + /** + * And new mass mail task with SQL query text for fill receivers list. + * + * @param mailProto prepared mail for clone and send to characters, will deleted in result call + * @param queryStr SQL query for get guid list of receivers, first field in query result must be uint32 low guids list. + * + * Note: this function safe to be called from Map::Update content/etc, real data add will executed in next tick after query resultrs ready + */ + void AddMassMailTask(MailDraft* mailProto, MailSender sender, char const* queryStr); + + /** + * And new mass mail task and let fill receivers list returned as result. + * + * @param mailProto prepared mail for clone and send to characters, will deleted in result call + * @returns reference to receivers list for it fill in caller code. + * + * Note: this function NOT SAFE for call from Map::Update content/etc + */ + ReceiversList& AddMassMailTask(MailDraft* mailProto, MailSender sender) + { + m_massMails.push_back(MassMail(mailProto, sender)); + return m_massMails.rbegin()->m_recivers; + } + + /** + * Next step in mass mail activity, send some amount mails from queued tasks + */ + void Update(bool sendall = false); + + private: + + /// Mass mail task store mail prototype and receivers list who not get mail yet + struct MassMail + { + explicit MassMail(MailDraft* mailProto, MailSender sender) + : m_protoMail(mailProto), m_sender(sender) + { + MANGOS_ASSERT(mailProto); + } + + explicit MassMail(MassMail const& massmail) + : m_protoMail(const_cast(massmail).m_protoMail), m_sender(massmail.m_sender) + { + } + + /// m_protoMail is owned by MassMail, so at copy original MassMail field set to NULL + std::auto_ptr m_protoMail; + + MailSender m_sender; + ReceiversList m_recivers; + }; + + typedef std::list MassMailList; + + /// List of current queued mass mail tasks + MassMailList m_massMails; +}; + +#define sMassMailMgr MaNGOS::Singleton::Instance() + +#endif +/*! @} */ diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index c74f8261e..26ad1c7dd 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -52,6 +52,15 @@ enum Races (1<<(RACE_NIGHTELF-1)) |(1<<(RACE_UNDEAD-1)) |(1<<(RACE_TAUREN-1)) | \ (1<<(RACE_GNOME-1)) |(1<<(RACE_TROLL-1))) +// for most cases batter use ChrRace data for team check as more safe, but when need full mask of team can be use this defines. +#define RACEMASK_ALLIANCE \ + ((1<<(RACE_HUMAN-1)) |(1<<(RACE_DWARF-1)) |(1<<(RACE_NIGHTELF-1))| \ + (1<<(RACE_GNOME-1))) + +#define RACEMASK_HORDE \ + ((1<<(RACE_ORC-1)) |(1<<(RACE_UNDEAD-1)) |(1<<(RACE_TAUREN-1)) | \ + (1<<(RACE_TROLL-1))) + // Class value is index in ChrClasses.dbc enum Classes { diff --git a/src/game/World.cpp b/src/game/World.cpp index 84aec7d65..c54befe2a 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -41,6 +41,7 @@ #include "SpellMgr.h" #include "Chat.h" #include "DBCStores.h" +#include "MassMailMgr.h" #include "LootMgr.h" #include "ItemEnchantmentMgr.h" #include "MapManager.h" @@ -594,6 +595,8 @@ void World::LoadConfigSettings(bool reload) setConfig(CONFIG_UINT32_MAIL_DELIVERY_DELAY, "MailDeliveryDelay", HOUR); + setConfigMin(CONFIG_UINT32_MASS_MAILER_SEND_PER_TICK, "MassMailer.SendPerTick", 10, 1); + setConfigPos(CONFIG_UINT32_UPTIME_UPDATE, "UpdateUptimeInterval", 10); if (reload) { @@ -1309,6 +1312,9 @@ void World::Update(uint32 diff) ///- Update the game time and check for shutdown time _UpdateGameTime(); + ///-Update mass mailer tasks if any + sMassMailMgr.Update(); + ///
  • Handle auctions when the timer has passed if (m_timers[WUPDATE_AUCTIONS].Passed()) { diff --git a/src/game/World.h b/src/game/World.h index d5b320e84..eed96ea95 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -124,6 +124,7 @@ enum eConfigUInt32Values CONFIG_UINT32_START_GM_LEVEL, CONFIG_UINT32_GROUP_VISIBILITY, CONFIG_UINT32_MAIL_DELIVERY_DELAY, + CONFIG_UINT32_MASS_MAILER_SEND_PER_TICK, CONFIG_UINT32_UPTIME_UPDATE, CONFIG_UINT32_AUCTION_DEPOSIT_MIN, CONFIG_UINT32_SKILL_CHANCE_ORANGE, diff --git a/src/mangosd/Master.cpp b/src/mangosd/Master.cpp index 889ceddc6..71c8a1cfd 100644 --- a/src/mangosd/Master.cpp +++ b/src/mangosd/Master.cpp @@ -38,6 +38,7 @@ #include "Util.h" #include "revision_sql.h" #include "MaNGOSsoap.h" +#include "MassMailMgr.h" #include "DBCStores.h" #include @@ -343,6 +344,9 @@ int Master::Run() ///- Clean account database before leaving clearOnlineAccounts(); + // send all still queued mass mails (before DB connections shutdown) + sMassMailMgr.Update(true); + ///- Wait for DB delay threads to end CharacterDatabase.HaltDelayThread(); WorldDatabase.HaltDelayThread(); diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index f5d4d8df7..402cd5eb0 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -611,6 +611,11 @@ LogColors = "" # Mail delivery delay time for item sending # Default: 3600 sec (1 hour) # +# MassMailer.SendPerTick +# Max amount mail send each tick from mails list scheduled for mass mailer proccesing. +# More mails increase server load but speedup mass mail proccess. Normal tick length: 50 msecs, so 20 ticks in sec and 200 mails in sec by default. +# Default: 10 +# # PetUnsummonAtMount # Persmanent pet will unsummoned at player mount # Default: 0 - not unsummon @@ -686,6 +691,7 @@ MaxPrimaryTradeSkill = 2 MinPetitionSigns = 9 MaxGroupXPDistance = 74 MailDeliveryDelay = 3600 +MassMailer.SendPerTick = 10 PetUnsummonAtMount = 0 Event.Announce = 0 BeepAtStart = 1 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index d9a5b039c..1401c58b0 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 "1216" + #define REVISION_NR "1217" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index f62102499..bbdb40d83 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_z1142_s0531_01_characters_bugreport" - #define REVISION_DB_MANGOS "required_z1205_s0624_01_mangos_mangos_string" + #define REVISION_DB_CHARACTERS "required_z1217_s0636_02_characters_game_event_status" + #define REVISION_DB_MANGOS "required_z1217_s0636_03_mangos_game_event_mail" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__ diff --git a/win/VC100/game.vcxproj b/win/VC100/game.vcxproj index 4b23b2a66..cf34ec740 100644 --- a/win/VC100/game.vcxproj +++ b/win/VC100/game.vcxproj @@ -411,6 +411,7 @@ + @@ -547,6 +548,7 @@ + diff --git a/win/VC100/game.vcxproj.filters b/win/VC100/game.vcxproj.filters index a8246262a..92d7b24ad 100644 --- a/win/VC100/game.vcxproj.filters +++ b/win/VC100/game.vcxproj.filters @@ -123,6 +123,9 @@ World/Handlers + + World/Handlers + World/Handlers @@ -477,6 +480,9 @@ World/Handlers + + World/Handlers + World/Handlers diff --git a/win/VC80/game.vcproj b/win/VC80/game.vcproj index aef7f5047..2f4f711ae 100644 --- a/win/VC80/game.vcproj +++ b/win/VC80/game.vcproj @@ -757,6 +757,14 @@ RelativePath="..\..\src\game\MapManager.h" > + + + + diff --git a/win/VC90/game.vcproj b/win/VC90/game.vcproj index 7ef8ec4df..73fd0d365 100644 --- a/win/VC90/game.vcproj +++ b/win/VC90/game.vcproj @@ -750,6 +750,14 @@ RelativePath="..\..\src\game\MapManager.h" > + + + +