diff options
author | Skjalf <47818697+Nyeriah@users.noreply.github.com> | 2021-12-26 08:39:15 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-26 08:39:15 -0300 |
commit | 58302e41969e16dfe1ba93792622d990c03f2b2f (patch) | |
tree | a8c90ecf9375ac3695e8e3b911e5d05d4227b134 | |
parent | d2950b21b9135b507d044051f9689d4eadb64dec (diff) |
feat(Core/Player): Implement player specific settings (#9483)
19 files changed, 381 insertions, 8 deletions
diff --git a/data/sql/updates/pending_db_characters/rev_1638643807174948000.sql b/data/sql/updates/pending_db_characters/rev_1638643807174948000.sql new file mode 100644 index 0000000000..0444c1864e --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1638643807174948000.sql @@ -0,0 +1,9 @@ +INSERT INTO `version_db_characters` (`sql_rev`) VALUES ('1638643807174948000'); + +DROP TABLE IF EXISTS `character_settings`; +CREATE TABLE IF NOT EXISTS `character_settings` ( + `guid` INT UNSIGNED NOT NULL, + `source` VARCHAR(40) NOT NULL, + `data` TEXT NULL, + PRIMARY KEY (`guid`, `source`) +) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COMMENT='Player Settings'; diff --git a/data/sql/updates/pending_db_world/rev_1638641366613580300.sql b/data/sql/updates/pending_db_world/rev_1638641366613580300.sql new file mode 100644 index 0000000000..43b31e15a3 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1638641366613580300.sql @@ -0,0 +1,12 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1638641366613580300'); + +DELETE FROM `command` WHERE `name` IN ('settings', 'settings announcer'); +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('settings', 1, 'Syntax: .settings $subcommand\nType .setting to see the list of all available commands.'), +('settings announcer', 1, 'Syntax: .settings announcer <type> <on/off>.\nDisables receiving announcements. Valid announcement types are: \'autobroadcast\', \'arena\' and \'bg\''); + +DELETE FROM `acore_string` WHERE `entry` IN (5079, 5080, 5081); +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(5079, 'You must be at least level %u to disable autobroadcast messages.'), +(5080, 'You are now receiving global %s messages.'), +(5081, 'You will no longer receive global %s messages.'); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index f81862717f..75720a7c28 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -603,6 +603,11 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Character names PrepareStatement(CHAR_INS_RESERVED_PLAYER_NAME, "INSERT IGNORE INTO reserved_name (name) VALUES (?)", CONNECTION_ASYNC); + + // Character settings + PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC); } CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 5149373fe0..ca39783c96 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -519,6 +519,10 @@ enum CharacterDatabaseStatements : uint32 CHAR_INS_RESERVED_PLAYER_NAME, + CHAR_SEL_CHAR_SETTINGS, + CHAR_REP_CHAR_SETTINGS, + CHAR_DEL_CHAR_SETTINGS, + MAX_CHARACTERDATABASE_STATEMENTS }; diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index 27f84445c8..168c0497fe 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -984,7 +984,7 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundBracket uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32) 80); uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32) 80); - sWorld->SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, bgName, q_min_level, q_max_level, qPlayers, MaxPlayers); + sWorld->SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName, q_min_level, q_max_level, qPlayers, MaxPlayers); } else { @@ -1068,7 +1068,7 @@ void BattlegroundQueue::SendMessageBGQueue(Player* leader, Battleground* bg, PvP return; } - sWorld->SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, bgName, q_min_level, q_max_level, qAlliance + qHorde, MaxPlayers); + sWorld->SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName, q_min_level, q_max_level, qAlliance + qHorde, MaxPlayers); } } } @@ -1119,7 +1119,7 @@ void BattlegroundQueue::SendJoinMessageArenaQueue(Player* leader, GroupQueueInfo return; } - sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD, bgName, arenatype.c_str(), q_min_level, q_max_level, qPlayers, playersNeed); + sWorld->SendWorldTextOptional(LANG_ARENA_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE, bgName, arenatype.c_str(), q_min_level, q_max_level, qPlayers, playersNeed); } } else @@ -1134,7 +1134,7 @@ void BattlegroundQueue::SendJoinMessageArenaQueue(Player* leader, GroupQueueInfo uint32 ArenaTeamRating = ginfo->ArenaTeamRating; std::string TeamName = team->GetName(); - sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, TeamName.c_str(), ArenaType, ArenaType, ArenaTeamRating); + sWorld->SendWorldTextOptional(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE, TeamName.c_str(), ArenaType, ArenaType, ArenaTeamRating); } } @@ -1158,7 +1158,9 @@ void BattlegroundQueue::SendExitMessageArenaQueue(GroupQueueInfo* ginfo) std::string TeamName = team->GetName(); if (ArenaType && ginfo->Players.empty()) - sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, TeamName.c_str(), ArenaType, ArenaType, ArenaTeamRating); + { + sWorld->SendWorldTextOptional(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE, TeamName.c_str(), ArenaType, ArenaType, ArenaTeamRating); + } } void BattlegroundQueue::SetQueueAnnouncementTimer(uint32 bracketId, int32 timer, bool isCrossFactionBG /*= true*/) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index fb56b26102..c699ea0d8d 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4168,6 +4168,10 @@ void Player::DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool up stmt->setUInt32(0, lowGuid); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SETTINGS); + stmt->setUInt32(0, lowGuid); + trans->Append(stmt); + Corpse::DeleteFromDB(playerGuid, trans); sScriptMgr->OnDeleteFromDB(trans, lowGuid); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 9469bbab06..ff2f17ab79 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -31,6 +31,7 @@ #include "ObjectMgr.h" #include "Optional.h" #include "PetDefines.h" +#include "PlayerSettings.h" #include "PlayerTaxi.h" #include "QuestDef.h" #include "SpellMgr.h" @@ -875,6 +876,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS = 32, PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH = 34, PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 35, + PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS = 36, MAX_PLAYER_LOGIN_QUERY }; @@ -2598,6 +2600,10 @@ public: std::string GetPlayerName(); + // Settings + [[nodiscard]] PlayerSetting GetPlayerSetting(std::string source, uint8 index); + void UpdatePlayerSetting(std::string source, uint8 index, uint32 value); + protected: // Gamemaster whisper whitelist WhisperListContainer WhisperList; @@ -2680,6 +2686,7 @@ public: void _LoadTalents(PreparedQueryResult result); void _LoadInstanceTimeRestrictions(PreparedQueryResult result); void _LoadBrewOfTheMonth(PreparedQueryResult result); + void _LoadCharacterSettings(PreparedQueryResult result); /*********************************************************/ /*** SAVE SYSTEM ***/ @@ -2703,6 +2710,7 @@ public: void _SaveStats(CharacterDatabaseTransaction trans); void _SaveCharacter(bool create, CharacterDatabaseTransaction trans); void _SaveInstanceTimeRestrictions(CharacterDatabaseTransaction trans); + void _SavePlayerSettings(CharacterDatabaseTransaction trans); /*********************************************************/ /*** ENVIRONMENTAL SYSTEM ***/ @@ -2955,6 +2963,8 @@ private: WorldLocation _corpseLocation; Optional<float> _farSightDistance = { }; + + PlayerSettingMap m_charSettingsMap; }; void AddItemsSetItem(Player* player, Item* item); diff --git a/src/server/game/Entities/Player/PlayerSettings.cpp b/src/server/game/Entities/Player/PlayerSettings.cpp new file mode 100644 index 0000000000..30a539ed42 --- /dev/null +++ b/src/server/game/Entities/Player/PlayerSettings.cpp @@ -0,0 +1,128 @@ +/* + * 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 "Tokenize.h" +#include "StringConvert.h" +#include "Player.h" + +/*********************************************************/ +/*** PLAYER SETTINGS SYSTEM ***/ +/*********************************************************/ + +void Player::_LoadCharacterSettings(PreparedQueryResult result) +{ + m_charSettingsMap.clear(); + + if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED)) + { + return; + } + + if (result) + { + do + { + Field* fields = result->Fetch(); + + std::string source = fields[0].GetString();; + std::string data = fields[1].GetString(); + + std::vector<std::string_view> tokens = Acore::Tokenize(data, ' ', true); + + PlayerSettingVector setting; + setting.resize(tokens.size()); + + uint32 count = 0; + + for (auto token : tokens) + { + PlayerSetting set; + set.value = Acore::StringTo<uint32>(token).value(); + setting[++count] = set; + } + + m_charSettingsMap[source] = setting; + + } while (result->NextRow()); + } +} + +PlayerSetting Player::GetPlayerSetting(std::string source, uint8 index) +{ + auto itr = m_charSettingsMap.find(source); + + if (itr == m_charSettingsMap.end()) + { + // Settings not found, this will initialize a new entry. + UpdatePlayerSetting(source, index, 0); + return GetPlayerSetting(source, index); + } + + return itr->second[index]; +} + +void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans) +{ + if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED)) + { + return; + } + + for (auto itr : m_charSettingsMap) + { + std::ostringstream data; + + for (auto setting : itr.second) + { + data << setting.value << ' '; + } + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS); + stmt->setUInt32(0, GetGUID().GetCounter()); + stmt->setString(1, itr.first); + stmt->setString(2, data.str()); + trans->Append(stmt); + } +} + +void Player::UpdatePlayerSetting(std::string source, uint8 index, uint32 value) +{ + auto itr = m_charSettingsMap.find(source); + + if (itr == m_charSettingsMap.end()) + { + // Settings not found, initialize a new entry. + uint8 size = index ? index : index + 1; + + PlayerSettingVector setting; + setting.resize(size); + + for (uint32 itr = 0; itr <= index; ++itr) + { + PlayerSetting set; + set.value = itr == index ? value : 0; + + setting[itr] = set; + } + + m_charSettingsMap[source] = setting; + } + else + { + itr->second[index].value = value; + } +} diff --git a/src/server/game/Entities/Player/PlayerSettings.h b/src/server/game/Entities/Player/PlayerSettings.h new file mode 100644 index 0000000000..24d9439bd9 --- /dev/null +++ b/src/server/game/Entities/Player/PlayerSettings.h @@ -0,0 +1,51 @@ +/* + * 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/>. + */ + +#ifndef _PLAYER_SETTINGS_H +#define _PLAYER_SETTINGS_H + +class Player; + +const std::string AzerothcorePSSource = "ac_default"; + +enum CharacterSettingIndexes : uint8 +{ + SETTING_ANNOUNCER_FLAGS, + MAX_CHAR_SETTINGS +}; + +enum AnnouncerFlags : uint8 +{ + ANNOUNCER_FLAG_DISABLE_BG_QUEUE = 1, + ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE = 2, + ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST = 4 +}; + +struct PlayerSetting +{ + uint32 value; + + [[nodiscard]] bool HasFlag(uint32 flag) { return (value & flag) != 0; } + [[nodiscard]] bool IsEnabled(uint32 equals = 1) { return value == equals; } + void AddFlag(uint32 flag) { value = value | flag; } + void RemoveFlag(uint32 flag) { value = value &~ flag; } +}; + +typedef std::vector<PlayerSetting> PlayerSettingVector; +typedef std::map<std::string, PlayerSettingVector> PlayerSettingMap; + +#endif diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 7f64681ed3..2e62c84d8b 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -5622,6 +5622,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons _LoadBrewOfTheMonth(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH)); + _LoadCharacterSettings(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS)); + // Players are immune to taunt ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); @@ -7166,6 +7168,7 @@ void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create, bool logo GetSession()->SaveTutorialsData(trans); // changed only while character in game _SaveGlyphs(trans); _SaveInstanceTimeRestrictions(trans); + _SavePlayerSettings(trans); // check if stats should only be saved on logout // save stats can be out of transaction diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 297c15b4dc..65c49c9849 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -205,6 +205,10 @@ bool LoginQueryHolder::Initialize() stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS, stmt); + return res; } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index ea5c74cb84..1517ab0bfd 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1244,7 +1244,11 @@ enum AcoreStrings LANG_CMD_ITEM_REFUNDED_AP = 5077, LANG_CMD_ITEM_REFUND_NOT_FOUND = 5078, - // Room for more strings 5078-9999 + LANG_CMD_AUTOBROADCAST_LVL_ERROR = 5079, + LANG_CMD_SETTINGS_ANNOUNCER_ON = 5080, + LANG_CMD_SETTINGS_ANNOUNCER_OFF = 5081, + + // Room for more strings 5082-9999 // Level requirement notifications LANG_SAY_REQ = 6604, diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 924ba75e50..ffad84b57e 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -171,6 +171,7 @@ enum WorldBoolConfigs CONFIG_SET_BOP_ITEM_TRADEABLE, CONFIG_ALLOW_LOGGING_IP_ADDRESSES_IN_DATABASE, CONFIG_REALM_LOGIN_ENABLED, + CONFIG_PLAYER_SETTINGS_ENABLED, BOOL_CONFIG_VALUE_COUNT }; @@ -334,6 +335,7 @@ enum WorldIntConfigs CONFIG_CHARDELETE_MIN_LEVEL, CONFIG_AUTOBROADCAST_CENTER, CONFIG_AUTOBROADCAST_INTERVAL, + CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE, CONFIG_MAX_RESULTS_LOOKUP_COMMANDS, CONFIG_DB_PING_INTERVAL, CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, @@ -537,6 +539,7 @@ public: virtual void SetInitialWorldSettings() = 0; virtual void LoadConfigSettings(bool reload = false) = 0; virtual void SendWorldText(uint32 string_id, ...) = 0; + virtual void SendWorldTextOptional(uint32 string_id, uint32 flag, ...) = 0; virtual void SendGlobalText(const char* text, WorldSession* self) = 0; virtual void SendGMText(uint32 string_id, ...) = 0; virtual void SendGlobalMessage(WorldPacket* packet, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) = 0; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 80a24c9519..d043903d07 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1243,6 +1243,8 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_LOOT_NEED_BEFORE_GREED_ILVL_RESTRICTION] = sConfigMgr->GetOption<int32>("LootNeedBeforeGreedILvlRestriction", 70); + m_bool_configs[CONFIG_PLAYER_SETTINGS_ENABLED] = sConfigMgr->GetOption<bool>("EnablePlayerSettings", 0); + ///- Read the "Data" directory from the config file std::string dataPath = sConfigMgr->GetOption<std::string>("DataDir", "./"); if (dataPath.empty() || (dataPath.at(dataPath.length() - 1) != '/' && dataPath.at(dataPath.length() - 1) != '\\')) @@ -1324,6 +1326,7 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_AUTOBROADCAST] = sConfigMgr->GetOption<bool>("AutoBroadcast.On", false); m_int_configs[CONFIG_AUTOBROADCAST_CENTER] = sConfigMgr->GetOption<int32>("AutoBroadcast.Center", 0); m_int_configs[CONFIG_AUTOBROADCAST_INTERVAL] = sConfigMgr->GetOption<int32>("AutoBroadcast.Timer", 60000); + m_int_configs[CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE] = sConfigMgr->GetOption<int32>("AutoBroadcast.MinDisableLevel", 0); if (reload) { m_timers[WUPDATE_AUTOBROADCAST].SetInterval(m_int_configs[CONFIG_AUTOBROADCAST_INTERVAL]); @@ -2576,6 +2579,34 @@ void World::SendWorldText(uint32 string_id, ...) va_end(ap); } +void World::SendWorldTextOptional(uint32 string_id, uint32 flag, ...) +{ + va_list ap; + va_start(ap, flag); + + Acore::WorldWorldTextBuilder wt_builder(string_id, &ap); + Acore::LocalizedPacketListDo<Acore::WorldWorldTextBuilder> wt_do(wt_builder); + for (auto const& itr : m_sessions) + { + if (!itr.second || !itr.second->GetPlayer() || !itr.second->GetPlayer()->IsInWorld()) + { + continue; + } + + if (sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED)) + { + if (itr.second->GetPlayer()->GetPlayerSetting(AzerothcorePSSource, SETTING_ANNOUNCER_FLAGS).HasFlag(flag)) + { + continue; + } + } + + wt_do(itr.second->GetPlayer()); + } + + va_end(ap); +} + /// Send a System Message to all GMs (except self if mentioned) void World::SendGMText(uint32 string_id, ...) { @@ -2953,7 +2984,9 @@ void World::SendAutoBroadcast() uint32 abcenter = sWorld->getIntConfig(CONFIG_AUTOBROADCAST_CENTER); if (abcenter == 0) - sWorld->SendWorldText(LANG_AUTO_BROADCAST, msg.c_str()); + { + sWorld->SendWorldTextOptional(LANG_AUTO_BROADCAST, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST, msg.c_str()); + } else if (abcenter == 1) { @@ -2964,7 +2997,7 @@ void World::SendAutoBroadcast() else if (abcenter == 2) { - sWorld->SendWorldText(LANG_AUTO_BROADCAST, msg.c_str()); + sWorld->SendWorldTextOptional(LANG_AUTO_BROADCAST, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST, msg.c_str()); WorldPacket data(SMSG_NOTIFICATION, (msg.size() + 1)); data << msg; diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index b2f2658654..6c5713c254 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -262,6 +262,8 @@ public: void SendZoneText(uint32 zone, const char* text, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL); void SendServerMessage(ServerMessageType type, const char* text = "", Player* player = nullptr); + void SendWorldTextOptional(uint32 string_id, uint32 flag, ...); + /// Are we in the middle of a shutdown? bool IsShuttingDown() const { return m_ShutdownTimer > 0; } uint32 GetShutDownTimeLeft() const { return m_ShutdownTimer; } diff --git a/src/server/scripts/Commands/cs_player_settings.cpp b/src/server/scripts/Commands/cs_player_settings.cpp new file mode 100644 index 0000000000..2b7381e4fd --- /dev/null +++ b/src/server/scripts/Commands/cs_player_settings.cpp @@ -0,0 +1,81 @@ +/* + * 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 "Chat.h" +#include "Player.h" +#include "PlayerSettings.h" +#include "ScriptMgr.h" + +using namespace Acore::ChatCommands; + +class player_settings_commandscript : public CommandScript +{ +public: + player_settings_commandscript() : CommandScript("player_settings_commandscript") {} + + ChatCommandTable GetCommands() const override + { + static ChatCommandTable playerSettingsCommandTable = + { + { "announcer", HandleSettingsAnnouncerFlags, SEC_MODERATOR, Console::No }, + }; + static ChatCommandTable commandTable = + { + { "settings", playerSettingsCommandTable }, + }; + return commandTable; + } + + static bool HandleSettingsAnnouncerFlags(ChatHandler* handler, std::string type, bool on) + { + Player* player = handler->GetPlayer(); + + PlayerSetting setting; + setting = player->GetPlayerSetting(AzerothcorePSSource, SETTING_ANNOUNCER_FLAGS); + + if (type == "bg") + { + on ? setting.RemoveFlag(ANNOUNCER_FLAG_DISABLE_BG_QUEUE) : setting.AddFlag(ANNOUNCER_FLAG_DISABLE_BG_QUEUE); + player->UpdatePlayerSetting(AzerothcorePSSource, SETTING_ANNOUNCER_FLAGS, setting.value); + } + else if (type == "arena") + { + on ? setting.RemoveFlag(ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE) : setting.AddFlag(ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE); + player->UpdatePlayerSetting(AzerothcorePSSource, SETTING_ANNOUNCER_FLAGS, setting.value); + } + else if (type == "autobroadcast") + { + if (player->getLevel() < sWorld->getIntConfig(CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE)) + { + handler->SetSentErrorMessage(true); + handler->PSendSysMessage(LANG_CMD_AUTOBROADCAST_LVL_ERROR, sWorld->getIntConfig(CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE)); + } + + on ? setting.RemoveFlag(ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST) : setting.AddFlag(ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST); + player->UpdatePlayerSetting(AzerothcorePSSource, SETTING_ANNOUNCER_FLAGS, setting.value); + } + + handler->SetSentErrorMessage(false); + handler->PSendSysMessage(on ? LANG_CMD_SETTINGS_ANNOUNCER_ON : LANG_CMD_SETTINGS_ANNOUNCER_OFF, type); + return true; + } +}; + +void AddSC_player_settings_commandscript() +{ + new player_settings_commandscript(); +} diff --git a/src/server/scripts/Commands/cs_script_loader.cpp b/src/server/scripts/Commands/cs_script_loader.cpp index 1d40846ef5..4c7433b6b1 100644 --- a/src/server/scripts/Commands/cs_script_loader.cpp +++ b/src/server/scripts/Commands/cs_script_loader.cpp @@ -61,6 +61,7 @@ void AddSC_titles_commandscript(); void AddSC_wp_commandscript(); void AddSC_cache_commandscript(); void AddSC_item_commandscript(); +void AddSC_player_settings_commandscript(); // The name of this function should match: // void Add${NameOfDirectory}Scripts() @@ -111,4 +112,5 @@ void AddCommandsScripts() AddSC_wp_commandscript(); AddSC_cache_commandscript(); AddSC_item_commandscript(); + AddSC_player_settings_commandscript(); } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 6766a477bf..1e7fe0b39f 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -2545,6 +2545,13 @@ AutoBroadcast.Center = 0 AutoBroadcast.Timer = 60000 # +# AutoBroadcast.MinDisableLevel +# Description: Minimum level required to disable autobroadcast announcements if EnablePlayerSettings option is enabled. +# Default: 0 - (Not allowed to disable it) + +AutoBroadcast.MinDisableLevel = 0 + +# ################################################################################################### ################################################################################################### @@ -3600,6 +3607,14 @@ LFG.MaxKickCount = 2 LFG.KickPreventionTimer = 900 # +# EnablePlayerSettings +# Description: Enables the usage of character specific settings. +# Default: 0 - Disabled +# 1 - Enabled + +EnablePlayerSettings = 0 + +# ################################################################################################### ################################################################################################### diff --git a/src/test/mocks/WorldMock.h b/src/test/mocks/WorldMock.h index 6d85da9191..82a024a867 100644 --- a/src/test/mocks/WorldMock.h +++ b/src/test/mocks/WorldMock.h @@ -77,6 +77,7 @@ public: MOCK_METHOD(void, SetInitialWorldSettings, ()); MOCK_METHOD(void, LoadConfigSettings, (bool reload), ()); void SendWorldText(uint32 string_id, ...) override {} + void SendWorldTextOptional(uint32 string_id, uint32 flag, ...) override {} MOCK_METHOD(void, SendGlobalText, (const char* text, WorldSession* self), ()); void SendGMText(uint32 string_id, ...) override {} MOCK_METHOD(void, SendGlobalMessage, (WorldPacket* packet, WorldSession* self, TeamId teamId), ()); |