Skip to content

Commit

Permalink
feat (core\db): Server Mail Reward System (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
acidmanifesto authored Aug 30, 2023
1 parent 22fd8b8 commit 6303bcc
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 2 deletions.
23 changes: 23 additions & 0 deletions data/sql/updates/pending_db_characters/rev_1693343760497114600.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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;
3 changes: 3 additions & 0 deletions data/sql/updates/pending_db_world/rev_1693344185076263500.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DELETE FROM `command` WHERE `name`='reload mail_server_template';
INSERT INTO `command` (`name`, `help`) VALUES
('reload mail_server_template', 'Syntax: .reload mail_server_template\nReload server_mail_template table.');
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,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_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ enum CharacterDatabaseStatements : uint32
CHAR_SEL_CHARACTER_ACTIONS,
CHAR_SEL_CHARACTER_ACTIONS_SPEC,
CHAR_SEL_MAIL_COUNT,
CHAR_SEL_MAIL_SERVER_CHARACTER,
CHAR_REP_MAIL_SERVER_CHARACTER,
CHAR_SEL_CHARACTER_SOCIALLIST,
CHAR_SEL_CHARACTER_HOMEBIND,
CHAR_SEL_CHARACTER_SPELLCOOLDOWNS,
Expand Down
115 changes: 115 additions & 0 deletions src/server/game/Globals/ObjectMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3164,6 +3164,121 @@ void ObjectMgr::LoadVehicleAccessories()
LOG_INFO("server.loading", ">> Loaded %u Vehicle Accessories in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}

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 ? rewardMoneyA : rewardMoneyH);
if (Item* mailItem = Item::CreateItem(player->GetTeamId() == TEAM_ALLIANCE ? rewardItemA : rewardItemH, player->GetTeamId() == TEAM_ALLIANCE ? rewardItemCountA : rewardItemCountH))
{
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->setUInt32(0, player->GetGUID().GetCounter());
stmt->setUInt32(1, id);
CharacterDatabase.Execute(stmt);

LOG_DEBUG("entities.player", "ObjectMgr::SendServerMail() Sent mail id %u to %s", id, player->GetGUID().ToString().c_str());
}
}

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.");
return;
}

_serverMailStore.rehash(result->GetRowCount());

do
{
Field* fields = result->Fetch();

uint32 id = fields[0].GetUInt32();

ServerMail& servMail = _serverMailStore[id];

servMail.id = id;
servMail.reqLevel = fields[1].GetUInt8();
servMail.reqPlayTime = fields[2].GetUInt32();
servMail.moneyA = fields[3].GetUInt32();
servMail.moneyH = fields[4].GetUInt32();
servMail.itemA = fields[5].GetUInt32();
servMail.itemCountA = fields[6].GetUInt32();
servMail.itemH = fields[7].GetUInt32();
servMail.itemCountH = fields[8].GetUInt32();
servMail.subject = fields[9].GetString();
servMail.body = fields[10].GetString();
servMail.active = fields[11].GetUInt8();

if (servMail.reqLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
LOG_ERROR(
"sql.sql", "Table `mail_server_template` has reqLevel %u but max level is %u for id %u, 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 %u or moneyH %u larger than MAX_MONEY_AMOUNT %u for id %u, 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 %u for id %u, 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 %u for id %u, skipped.", servMail.itemH, servMail.id);
return;
}

if (!servMail.itemA && servMail.itemCountA)
{
LOG_ERROR("sql.sql", "Table `mail_server_template` has itemCountA %u 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 %u with no ItemH, set to 0", servMail.itemCountH);
servMail.itemCountH = 0;
}
} while (result->NextRow());

LOG_INFO("server.loading", ">> Loaded %lu Mail Server Template in %u ms", _serverMailStore.size(), GetMSTimeDiffToNow(oldMSTime));
}

void ObjectMgr::LoadVehicleSeatAddon()
{
uint32 oldMSTime = getMSTime();
Expand Down
31 changes: 30 additions & 1 deletion src/server/game/Globals/ObjectMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,26 @@ struct QuestGreeting

typedef std::unordered_map<uint8, std::unordered_map<uint32, QuestGreeting>> QuestGreetingContainer;

struct GraveyardData
struct ServerMail
{
ServerMail() = default;
uint32 id{};
uint8 reqLevel{};
uint32 reqPlayTime{};
uint32 moneyA{};
uint32 moneyH{};
uint32 itemA{};
uint32 itemCountA{};
uint32 itemH{};
uint32 itemCountH{};
std::string subject;
std::string body;
uint8 active{};
};

typedef std::unordered_map<uint32, ServerMail> ServerMailContainer;

struct GraveyardData
{
uint32 safeLocId;
uint32 team;
Expand Down Expand Up @@ -1217,6 +1236,7 @@ class FC_GAME_API ObjectMgr
void LoadInstanceTemplate();
void LoadInstanceEncounters();
void LoadMailLevelRewards();
void LoadMailServerTemplates();
void LoadVehicleTemplateAccessories();
void LoadVehicleAccessories();
void LoadVehicleSeatAddon();
Expand Down Expand Up @@ -1350,6 +1370,11 @@ class FC_GAME_API ObjectMgr
return nullptr;
}

ServerMailContainer const& GetAllServerMailStore() const
{
return _serverMailStore;
}

BroadcastText const* GetBroadcastText(uint32 id) const
{
BroadcastTextContainer::const_iterator itr = _broadcastTextStore.find(id);
Expand Down Expand Up @@ -1603,6 +1628,9 @@ class FC_GAME_API ObjectMgr

bool IsTransportMap(uint32 mapId) const { return _transportMaps.count(mapId) != 0; }

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;

VehicleSeatAddon const* GetVehicleSeatAddon(uint32 seatId) const
{
VehicleSeatAddonContainer::const_iterator itr = _vehicleSeatAddonStore.find(seatId);
Expand Down Expand Up @@ -1774,6 +1802,7 @@ class FC_GAME_API ObjectMgr
FirelandsStringContainer _firelandsStringStore;

CacheVendorItemContainer _cacheVendorItemStore;
ServerMailContainer _serverMailStore;
std::unordered_map<uint32, Trainer::Trainer> _trainers;
std::map<std::tuple<uint32, uint32, uint32>, uint32> _creatureDefaultTrainers;

Expand Down
4 changes: 4 additions & 0 deletions src/server/game/World/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2064,7 +2064,11 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Player level dependent mail rewards...");
sObjectMgr->LoadMailLevelRewards();

LOG_INFO("server.loading", "Loading Mail Server Template..."); // must be after load LoadMailLevelRewards
sObjectMgr->LoadMailServerTemplates();

// Loot tables
LOG_INFO("server.loading", "Loading Loot Tables...");
LoadLootTables();

LOG_INFO("server.loading", "Loading Skill Discovery Table...");
Expand Down
9 changes: 9 additions & 0 deletions src/server/scripts/Commands/cs_reload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class reload_commandscript : public CommandScript
{ "quest_template_locale", rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_TEMPLATE_LOCALE, true, &HandleReloadLocalesQuestCommand, "" },
{ "mail_level_reward", rbac::RBAC_PERM_COMMAND_RELOAD_MAIL_LEVEL_REWARD, true, &HandleReloadMailLevelRewardCommand, "" },
{ "mail_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_MAIL_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesMailCommand, "" },
{ "mail_server_template", rbac::RBAC_PERM_COMMAND_RELOAD_MAIL_LOOT_TEMPLATE, true, &HandleReloadMailServerTemplateCommand, "" },
{ "milling_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_MILLING_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesMillingCommand, "" },
{ "npc_spellclick_spells", rbac::RBAC_PERM_COMMAND_RELOAD_NPC_SPELLCLICK_SPELLS, true, &HandleReloadSpellClickSpellsCommand, "" },
{ "npc_vendor", rbac::RBAC_PERM_COMMAND_RELOAD_NPC_VENDOR, true, &HandleReloadNpcVendorCommand, "" },
Expand Down Expand Up @@ -652,6 +653,14 @@ class reload_commandscript : public CommandScript
return true;
}

static bool HandleReloadMailServerTemplateCommand(ChatHandler* handler, char const* /*args*/)
{
LOG_INFO("server.loading", "Re-Loading `server_mail_template` table");
sObjectMgr->LoadMailServerTemplates();
handler->SendGlobalGMSysMessage("DB table `server_mail_template` reloaded.");
return true;
}

static bool HandleReloadLootTemplatesReferenceCommand(ChatHandler* handler, char const* /*args*/)
{
LOG_INFO("misc", "Re-Loading Loot Tables LootType 10 (Reference)...");
Expand Down
57 changes: 57 additions & 0 deletions src/server/scripts/World/server_mail.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* This file is part of the FirelandsCore 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 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 Affero General Public License for
* more details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "DatabaseEnv.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "QueryResult.h"
#include "ScriptMgr.h"
#include "WorldSession.h"

class ServerMailReward : public PlayerScript
{
public:
ServerMailReward() : PlayerScript("ServerMailReward") {}

// CHARACTER_LOGIN = 8
void OnLogin(Player* player, bool /*firstLogin*/)
{
for (auto const& servMail : sObjectMgr->GetAllServerMailStore())
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_SERVER_CHARACTER);
stmt->setUInt32(0, player->GetGUID().GetCounter());
stmt->setUInt32(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();
}
3 changes: 2 additions & 1 deletion src/server/scripts/World/world_script_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ void AddSC_npc_innkeeper();
void AddSC_npcs_special();
void AddSC_achievement_scripts();
void AddSC_action_ip_logger();
void AddSC_server_mail();
void AddSC_duel_reset();
void AddSC_world_map_scripts();
void AddSC_quest_scripts();
// player
void AddSC_chat_log();
void AddSC_action_ip_logger();

// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
Expand All @@ -52,6 +52,7 @@ void AddWorldScripts()
AddSC_world_map_scripts();
AddSC_quest_scripts();
AddSC_chat_log(); // location: scripts\World\chat_log.cpp
AddSC_server_mail(); // location: scripts\World\server_mail.cpp

// FIXME: This should be moved in a script validation hook.
// To avoid duplicate code, we check once /*ONLY*/ if logging is permitted or not.
Expand Down

0 comments on commit 6303bcc

Please sign in to comment.