Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
ZhengPeiRu21
2022-04-20 09:18:17 -06:00
committed by GitHub
14 changed files with 296 additions and 6 deletions

View File

@@ -0,0 +1,51 @@
-- DB update 2022_03_01_00 -> 2022_04_19_00
DROP PROCEDURE IF EXISTS `updateDb`;
DELIMITER //
CREATE PROCEDURE updateDb ()
proc:BEGIN DECLARE OK VARCHAR(100) DEFAULT 'FALSE';
SELECT COUNT(*) INTO @COLEXISTS
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'version_db_characters' AND COLUMN_NAME = '2022_03_01_00';
IF @COLEXISTS = 0 THEN LEAVE proc; END IF;
START TRANSACTION;
ALTER TABLE version_db_characters CHANGE COLUMN 2022_03_01_00 2022_04_19_00 bit;
SELECT sql_rev INTO OK FROM version_db_characters WHERE sql_rev = '1644531230030579700'; IF OK <> 'FALSE' THEN LEAVE proc; END IF;
--
-- START UPDATING QUERIES
--
INSERT INTO `version_db_characters` (`sql_rev`) VALUES ('1644531230030579700');
DROP TABLE IF EXISTS `mail_server_character`;
CREATE TABLE IF NOT EXISTS `mail_server_character` (
`guid` INT UNSIGNED NOT NULL,
`mailId` INT UNSIGNED NOT NULL,
PRIMARY KEY (`guid`, `mailId`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4;
DROP TABLE IF EXISTS `mail_server_template`;
CREATE TABLE IF NOT EXISTS `mail_server_template` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`reqLevel` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`reqPlayTime` INT UNSIGNED NOT NULL DEFAULT '0',
`moneyA` INT UNSIGNED NOT NULL DEFAULT '0',
`moneyH` INT UNSIGNED NOT NULL DEFAULT '0',
`itemA` INT UNSIGNED NOT NULL DEFAULT '0',
`itemCountA` INT UNSIGNED NOT NULL DEFAULT '0',
`itemH` INT UNSIGNED NOT NULL DEFAULT '0',
`itemCountH` INT UNSIGNED NOT NULL DEFAULT '0',
`subject` TEXT NOT NULL,
`body` TEXT NOT NULL,
`active` TINYINT UNSIGNED NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4;
--
-- END UPDATING QUERIES
--
UPDATE version_db_characters SET date = '2022_04_19_00' WHERE sql_rev = '1644531230030579700';
COMMIT;
END //
DELIMITER ;
CALL updateDb();
DROP PROCEDURE IF EXISTS `updateDb`;

View File

@@ -0,0 +1,31 @@
-- DB update 2022_04_18_04 -> 2022_04_19_00
DROP PROCEDURE IF EXISTS `updateDb`;
DELIMITER //
CREATE PROCEDURE updateDb ()
proc:BEGIN DECLARE OK VARCHAR(100) DEFAULT 'FALSE';
SELECT COUNT(*) INTO @COLEXISTS
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'version_db_world' AND COLUMN_NAME = '2022_04_18_04';
IF @COLEXISTS = 0 THEN LEAVE proc; END IF;
START TRANSACTION;
ALTER TABLE version_db_world CHANGE COLUMN 2022_04_18_04 2022_04_19_00 bit;
SELECT sql_rev INTO OK FROM version_db_world WHERE sql_rev = '1644531208243387100'; IF OK <> 'FALSE' THEN LEAVE proc; END IF;
--
-- START UPDATING QUERIES
--
INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1644531208243387100');
DELETE FROM `command` WHERE `name`='reload mail_server_template';
INSERT INTO `command` (`name`, `security`, `help`) VALUES
('reload mail_server_template', 3, 'Syntax: .reload mail_server_template\nReload server_mail_template table.');
--
-- END UPDATING QUERIES
--
UPDATE version_db_world SET date = '2022_04_19_00' WHERE sql_rev = '1644531208243387100';
COMMIT;
END //
DELIMITER ;
CALL updateDb();
DROP PROCEDURE IF EXISTS `updateDb`;

View File

@@ -92,6 +92,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAIL_SERVER_CHARACTER, "SELECT mailId from mail_server_character WHERE guid = ? and mailId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_MAIL_SERVER_CHARACTER, "REPLACE INTO mail_server_character (guid, mailId) values (?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT friend, flags, note FROM character_social JOIN characters ON characters.guid = character_social.friend WHERE character_social.guid = ? AND deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, category, item, time, needSend FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);

View File

@@ -81,6 +81,8 @@ enum CharacterDatabaseStatements : uint32
CHAR_SEL_CHARACTER_ACTIONS_SPEC,
CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD,
CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH,
CHAR_SEL_MAIL_SERVER_CHARACTER,
CHAR_REP_MAIL_SERVER_CHARACTER,
CHAR_SEL_CHARACTER_SOCIALLIST,
CHAR_SEL_CHARACTER_HOMEBIND,
CHAR_SEL_CHARACTER_SPELLCOOLDOWNS,

View File

@@ -9761,3 +9761,116 @@ uint32 ObjectMgr::GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty)
return 0;
}
void ObjectMgr::SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const
{
if (active)
{
if (player->getLevel() < reqLevel)
return;
if (player->GetTotalPlayedTime() < reqPlayTime)
return;
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
MailSender sender(MAIL_NORMAL, player->GetGUID().GetCounter(), MAIL_STATIONERY_GM);
MailDraft draft(subject, body);
draft.AddMoney(player->GetTeamId() == TEAM_ALLIANCE ? rewardMoneyH : rewardMoneyA);
if (Item* mailItem = Item::CreateItem(player->GetTeamId() == TEAM_ALLIANCE ? rewardItemH : rewardItemA, player->GetTeamId() == TEAM_ALLIANCE ? rewardItemCountH : rewardItemCountA))
{
mailItem->SaveToDB(trans);
draft.AddItem(mailItem);
}
draft.SendMailTo(trans, MailReceiver(player), sender);
CharacterDatabase.CommitTransaction(trans);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_MAIL_SERVER_CHARACTER);
stmt->SetData(0, player->GetGUID().GetCounter());
stmt->SetData(1, id);
CharacterDatabase.Execute(stmt);
LOG_DEBUG("entities.player", "ObjectMgr::SendServerMail() Sent mail id {} to {}", id, player->GetGUID().ToString());
}
}
void ObjectMgr::LoadMailServerTemplates()
{
uint32 oldMSTime = getMSTime();
_serverMailStore.clear(); // for reload case
// 0 1 2 3 4 5 6 7 8 9 10 11
QueryResult result = CharacterDatabase.Query("SELECT `id`, `reqLevel`, `reqPlayTime`, `moneyA`, `moneyH`, `itemA`, `itemCountA`, `itemH`,`itemCountH`, `subject`, `body`, `active` FROM `mail_server_template`");
if (!result)
{
LOG_INFO("sql.sql", ">> Loaded 0 server mail rewards. DB table `mail_server_template` is empty.");
LOG_INFO("server.loading", " ");
return;
}
_serverMailStore.rehash(result->GetRowCount());
do
{
Field* fields = result->Fetch();
uint32 id = fields[0].Get<uint32>();
ServerMail& servMail = _serverMailStore[id];
servMail.id = id;
servMail.reqLevel = fields[1].Get<uint8>();
servMail.reqPlayTime = fields[2].Get<uint32>();
servMail.moneyA = fields[3].Get<uint32>();
servMail.moneyH = fields[4].Get<uint32>();
servMail.itemA = fields[5].Get<uint32>();
servMail.itemCountA = fields[6].Get<uint32>();
servMail.itemH = fields[7].Get<uint32>();
servMail.itemCountH = fields[8].Get<uint32>();
servMail.subject = fields[9].Get<std::string>();
servMail.body = fields[10].Get<std::string>();
servMail.active = fields[11].Get<uint8>();
if (servMail.reqLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
LOG_ERROR("sql.sql", "Table `mail_server_template` has reqLevel {} but max level is {} for id {}, skipped.", servMail.reqLevel, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL), servMail.id);
return;
}
if (servMail.moneyA > MAX_MONEY_AMOUNT || servMail.moneyH > MAX_MONEY_AMOUNT)
{
LOG_ERROR("sql.sql", "Table `mail_server_template` has moneyA {} or moneyH {} larger than MAX_MONEY_AMOUNT {} for id {}, skipped.", servMail.moneyA, servMail.moneyH, MAX_MONEY_AMOUNT, servMail.id);
return;
}
ItemTemplate const* itemTemplateA = sObjectMgr->GetItemTemplate(servMail.itemA);
if (!itemTemplateA && servMail.itemA)
{
LOG_ERROR("sql.sql", "Table `mail_server_template` has invalid item in itemA {} for id {}, skipped.", servMail.itemA, servMail.id);
return;
}
ItemTemplate const* itemTemplateH = sObjectMgr->GetItemTemplate(servMail.itemH);
if (!itemTemplateH && servMail.itemH)
{
LOG_ERROR("sql.sql", "Table `mail_server_template` has invalid item in itemH {} for id {}, skipped.", servMail.itemH, servMail.id);
return;
}
if (!servMail.itemA && servMail.itemCountA)
{
LOG_ERROR("sql.sql", "Table `mail_server_template` has itemCountA {} with no ItemA, set to 0", servMail.itemCountA);
servMail.itemCountA = 0;
}
if (!servMail.itemH && servMail.itemCountH)
{
LOG_ERROR("sql.sql", "Table `mail_server_template` has itemCountH {} with no ItemH, set to 0", servMail.itemCountH);
servMail.itemCountH = 0;
}
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} Mail Server Template in {} ms", _serverMailStore.size(), GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}

View File

@@ -667,6 +667,7 @@ typedef std::array<std::unordered_map<uint32, QuestGreeting>, 2> QuestGreetingCo
typedef std::unordered_map<uint32, VendorItemData> CacheVendorItemContainer;
typedef std::unordered_map<uint32, TrainerSpellData> CacheTrainerSpellContainer;
typedef std::unordered_map<uint32, ServerMail> ServerMailContainer;
enum SkillRangeType
{
@@ -1041,6 +1042,7 @@ public:
void LoadInstanceTemplate();
void LoadInstanceEncounters();
void LoadMailLevelRewards();
void LoadMailServerTemplates();
void LoadVehicleTemplateAccessories();
void LoadVehicleAccessories();
@@ -1170,6 +1172,8 @@ public:
return nullptr;
}
[[nodiscard]] ServerMailContainer const& GetAllServerMailStore() const { return _serverMailStore; }
[[nodiscard]] BroadcastText const* GetBroadcastText(uint32 id) const
{
BroadcastTextContainer::const_iterator itr = _broadcastTextStore.find(id);
@@ -1405,6 +1409,7 @@ public:
[[nodiscard]] bool IsTransportMap(uint32 mapId) const { return _transportMaps.count(mapId) != 0; }
[[nodiscard]] uint32 GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty) const;
void SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const;
private:
// first free id for selected id type
@@ -1559,6 +1564,8 @@ private:
CacheVendorItemContainer _cacheVendorItemStore;
CacheTrainerSpellContainer _cacheTrainerSpellStore;
ServerMailContainer _serverMailStore;
std::set<uint32> _difficultyEntries[MAX_DIFFICULTY - 1]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate
std::set<uint32> _hasDifficultyEntries[MAX_DIFFICULTY - 1]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate

View File

@@ -211,4 +211,21 @@ struct Mail
[[nodiscard]] bool IsReturnedMail() const { return checked & MAIL_CHECK_MASK_RETURNED; }
};
struct ServerMail
{
ServerMail() = default;
uint32 id{ 0 };
uint8 reqLevel{ 0 };
uint32 reqPlayTime{ 0 };
uint32 moneyA{ 0 };
uint32 moneyH{ 0 };
uint32 itemA{ 0 };
uint32 itemCountA{ 0 };
uint32 itemH{ 0 };
uint32 itemCountH{ 0 };
std::string subject;
std::string body;
uint8 active{ 0 };
};
#endif

View File

@@ -1835,6 +1835,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Player level dependent mail rewards...");
sObjectMgr->LoadMailLevelRewards();
LOG_INFO("server.loading", "Load Mail Server Template...");
sObjectMgr->LoadMailServerTemplates();
// Loot tables
LoadLootTables();

View File

@@ -125,6 +125,7 @@ public:
{ "quest_request_item_locale", HandleReloadLocalesQuestRequestItemsCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "mail_level_reward", HandleReloadMailLevelRewardCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "mail_loot_template", HandleReloadLootTemplatesMailCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "mail_server_template", HandleReloadMailServerTemplateCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "milling_loot_template", HandleReloadLootTemplatesMillingCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "npc_spellclick_spells", HandleReloadSpellClickSpellsCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "npc_trainer", HandleReloadNpcTrainerCommand, SEC_ADMINISTRATOR, Console::Yes },
@@ -197,6 +198,7 @@ public:
HandleReloadDungeonAccessCommand(handler);
HandleReloadMailLevelRewardCommand(handler);
HandleReloadMailServerTemplateCommand(handler);
HandleReloadCommandCommand(handler);
HandleReloadReservedNameCommand(handler);
HandleReloadAcoreStringCommand(handler);
@@ -1148,6 +1150,14 @@ public:
return true;
}
static bool HandleReloadMailServerTemplateCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Re-Loading `server_mail_template` table");
sObjectMgr->LoadMailServerTemplates();
handler->SendGlobalGMSysMessage("DB table `server_mail_template` reloaded.");
return true;
}
static bool HandleReloadAuctionsCommand(ChatHandler* handler)
{
///- Reload dynamic data tables from the database

View File

@@ -61,7 +61,7 @@ public:
{
BossAI::EnterCombat(who);
events.ScheduleEvent(EVENT_SHADOWFLAME, 10000, 20000);
events.ScheduleEvent(EVENT_SHADOWFLAME, 18000);
events.ScheduleEvent(EVENT_WINGBUFFET, 30000);
events.ScheduleEvent(EVENT_SHADOWOFEBONROC, 8000, 10000);
}
@@ -82,7 +82,7 @@ public:
{
case EVENT_SHADOWFLAME:
DoCastVictim(SPELL_SHADOWFLAME);
events.ScheduleEvent(EVENT_SHADOWFLAME, 10000, 20000);
events.ScheduleEvent(EVENT_SHADOWFLAME, urand(15000, 25000));
break;
case EVENT_WINGBUFFET:
DoCastVictim(SPELL_WINGBUFFET);

View File

@@ -46,7 +46,7 @@ public:
{
BossAI::EnterCombat(who);
events.ScheduleEvent(EVENT_SHADOWFLAME, 10000, 20000);
events.ScheduleEvent(EVENT_SHADOWFLAME, 18000);
events.ScheduleEvent(EVENT_WINGBUFFET, 30000);
events.ScheduleEvent(EVENT_FLAMEBUFFET, 5000);
}
@@ -67,7 +67,7 @@ public:
{
case EVENT_SHADOWFLAME:
DoCastVictim(SPELL_SHADOWFLAME);
events.ScheduleEvent(EVENT_SHADOWFLAME, 10000, 20000);
events.ScheduleEvent(EVENT_SHADOWFLAME, urand(15000, 25000));
break;
case EVENT_WINGBUFFET:
DoCastVictim(SPELL_WINGBUFFET);

View File

@@ -51,7 +51,7 @@ public:
{
BossAI::EnterCombat(victim);
events.ScheduleEvent(EVENT_SHADOWFLAME, 10000, 20000);
events.ScheduleEvent(EVENT_SHADOWFLAME, 18000);
events.ScheduleEvent(EVENT_WINGBUFFET, 30000);
events.ScheduleEvent(EVENT_FRENZY, 10000);
}
@@ -72,7 +72,7 @@ public:
{
case EVENT_SHADOWFLAME:
DoCastVictim(SPELL_SHADOWFLAME);
events.ScheduleEvent(EVENT_SHADOWFLAME, 10000, 20000);
events.ScheduleEvent(EVENT_SHADOWFLAME, urand(15000, 25000));
break;
case EVENT_WINGBUFFET:
DoCastVictim(SPELL_WINGBUFFET);

View File

@@ -0,0 +1,52 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "Mail.h"
#include "ObjectMgr.h"
#include "QueryResult.h"
class ServerMailReward : public PlayerScript
{
public:
ServerMailReward() : PlayerScript("ServerMailReward") { }
// CHARACTER_LOGIN = 8
void OnLogin(Player* player) override
{
for (auto const& servMail : sObjectMgr->GetAllServerMailStore())
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_SERVER_CHARACTER);
stmt->SetData(0, player->GetGUID().GetCounter());
stmt->SetData(1, servMail.second.id);
WorldSession* mySess = player->GetSession();
mySess->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(stmt)
.WithPreparedCallback([mySess, servMail](PreparedQueryResult result)
{
if (!result)
sObjectMgr->SendServerMail(mySess->GetPlayer(), servMail.second.id, servMail.second.reqLevel, servMail.second.reqPlayTime, servMail.second.moneyA, servMail.second.moneyH, servMail.second.itemA, servMail.second.itemCountA, servMail.second.itemH, servMail.second.itemCountH, servMail.second.subject, servMail.second.body, servMail.second.active);
}));
}
}
};
void AddSC_server_mail()
{
new ServerMailReward();
}

View File

@@ -31,6 +31,7 @@ void AddSC_chat_log(); // location: scripts\World\chat_log.cpp
void AddSC_action_ip_logger(); // location: scripts\World\action_ip_logger.cpp
void AddSC_player_scripts();
void AddSC_npc_stave_of_ancients();
void AddSC_server_mail();
// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
@@ -51,4 +52,5 @@ void AddWorldScripts()
AddSC_action_ip_logger(); // location: scripts\World\action_ip_logger.cpp
AddSC_player_scripts();
AddSC_npc_stave_of_ancients();
AddSC_server_mail();
}