aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarbenium <carbenium@outlook.com>2015-04-19 23:37:29 +0200
committerCarbenium <carbenium@outlook.com>2015-04-21 00:52:21 +0200
commitbba6eb8d3dfe73a02063a7cefe6f465dae69334b (patch)
tree16dbbcaab1bca43b281828fd20b12f2ce6441540
parent4208c0d8396e10dc806939e1d17885d16ff7b84e (diff)
Core/Player: Added character templates
* Characters with predefined levels can be created * Avaiable factions and classes can be configured * Valid values for `factionGroup` in table `character_template_class` are 3 (Alliance) or 5 (Horde) * Added new permission RBAC_PERM_USE_CHARACTER_TEMPLATES - has to be set, to allow the usage of the templates Closes #13952
-rw-r--r--sql/updates/auth/2015_04_21_00_auth.sql9
-rw-r--r--sql/updates/characters/2015_04_21_00_characters.sql16
-rw-r--r--src/server/game/Accounts/RBAC.h4
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp75
-rw-r--r--src/server/game/Globals/ObjectMgr.h33
-rw-r--r--src/server/game/Handlers/AuthHandler.cpp4
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp19
-rw-r--r--src/server/game/Server/Packets/AuthenticationPackets.cpp8
-rw-r--r--src/server/game/Server/Packets/AuthenticationPackets.h16
-rw-r--r--src/server/game/World/World.cpp3
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp10
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.cpp2
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.h2
13 files changed, 180 insertions, 21 deletions
diff --git a/sql/updates/auth/2015_04_21_00_auth.sql b/sql/updates/auth/2015_04_21_00_auth.sql
new file mode 100644
index 00000000000..1a9ac877fbd
--- /dev/null
+++ b/sql/updates/auth/2015_04_21_00_auth.sql
@@ -0,0 +1,9 @@
+DELETE FROM `rbac_permissions` WHERE `id` IN (10, 662);
+INSERT INTO `rbac_permissions` (`id`, `name`) VALUES
+(10, 'Use character templates'),
+(662, 'Command: reload character_template');
+
+DELETE FROM `rbac_linked_permissions` WHERE `linkedId` IN (10, 662);
+INSERT INTO `rbac_linked_permissions` (`id`, `linkedId`) VALUES
+(196, 10),
+(196, 662);
diff --git a/sql/updates/characters/2015_04_21_00_characters.sql b/sql/updates/characters/2015_04_21_00_characters.sql
new file mode 100644
index 00000000000..b5fc1bd2ac8
--- /dev/null
+++ b/sql/updates/characters/2015_04_21_00_characters.sql
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS `character_template`;
+CREATE TABLE IF NOT EXISTS `character_template` (
+ `id` int(10) unsigned NOT NULL,
+ `name` varchar(70) NOT NULL,
+ `description` varchar(100) NOT NULL,
+ `level` tinyint(3) unsigned NOT NULL DEFAULT '1',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `character_template_class`;
+CREATE TABLE IF NOT EXISTS `character_template_class` (
+ `templateId` int(10) unsigned NOT NULL,
+ `factionGroup` tinyint(3) unsigned NOT NULL COMMENT '3 - Alliance, 5 - Horde',
+ `class` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`templateId`,`factionGroup`,`class`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index e913880ac77..2a499db8fdb 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -59,7 +59,7 @@ enum RBACPermissions
// 7 - reuse
// 8 - reuse
// 9 - reuse
- // 10 - reuse
+ RBAC_PERM_USE_CHARACTER_TEMPLATES = 10,
RBAC_PERM_LOG_GM_TRADE = 11,
// 12 - reuse
RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES = 13,
@@ -566,7 +566,7 @@ enum RBACPermissions
RBAC_PERM_COMMAND_RELOAD_LOCALES_CRETURE_TEXT = 659,
RBAC_PERM_COMMAND_RELOAD_LOCALES_GAMEOBJECT = 660,
RBAC_PERM_COMMAND_RELOAD_LOCALES_GOSSIP_MENU_OPTION = 661,
- // UNUSED
+ RBAC_PERM_COMMAND_RELOAD_CHARACTER_TEMPLATE = 662,
RBAC_PERM_COMMAND_RELOAD_LOCALES_ITEM_SET_NAME = 663,
RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 664,
RBAC_PERM_COMMAND_RELOAD_LOCALES_PAGE_TEXT = 665,
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index fda6779c30f..eafcf16383c 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -9133,6 +9133,81 @@ void ObjectMgr::LoadRaceAndClassExpansionRequirements()
TC_LOG_INFO("server.loading", ">> Loaded 0 class expansion requirements. DB table `class_expansion_requirement` is empty.");
}
+void ObjectMgr::LoadCharacterTemplates()
+{
+ uint32 oldMSTime = getMSTime();
+ _characterTemplateStore.clear();
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TEMPLATES);
+ PreparedQueryResult templates = CharacterDatabase.Query(stmt);
+
+ if (!templates)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 character templates. DB table `character_template` is empty.");
+ return;
+ }
+
+ PreparedQueryResult classes;
+ uint32 count = 0;
+
+ do
+ {
+ Field* fields = templates->Fetch();
+
+ uint32 templateSetId = fields[0].GetUInt32();
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TEMPLATE_CLASSES);
+ stmt->setUInt32(0, templateSetId);
+ classes = CharacterDatabase.Query(stmt);
+
+ if (classes)
+ {
+ CharacterTemplate templ;
+ templ.TemplateSetId = templateSetId;
+ templ.Name = fields[1].GetString();
+ templ.Description = fields[2].GetString();
+ templ.Level = fields[3].GetUInt8();
+
+ do
+ {
+ fields = classes->Fetch();
+
+ uint8 factionGroup = fields[0].GetUInt8();
+ uint8 classID = fields[1].GetUInt8();
+
+ if (!((factionGroup & (FACTION_MASK_PLAYER | FACTION_MASK_ALLIANCE)) == (FACTION_MASK_PLAYER | FACTION_MASK_ALLIANCE)) &&
+ !((factionGroup & (FACTION_MASK_PLAYER | FACTION_MASK_HORDE)) == (FACTION_MASK_PLAYER | FACTION_MASK_HORDE)))
+ {
+ TC_LOG_ERROR("sql.sql", "Faction group %u defined for character template %u in `character_template_class` is invalid. Skipped.", factionGroup, templateSetId);
+ continue;
+ }
+
+ ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(classID);
+ if (!classEntry)
+ {
+ TC_LOG_ERROR("sql.sql", "Class %u defined for character template %u in `character_template_class` does not exists, skipped.", classID, templateSetId);
+ continue;
+ }
+
+ templ.Classes.emplace_back(factionGroup, classID);
+
+ } while (classes->NextRow());
+
+ if (!templ.Classes.empty())
+ {
+ _characterTemplateStore[templateSetId] = templ;
+ ++count;
+ }
+ }
+ else
+ {
+ TC_LOG_ERROR("sql.sql", "Character template %u does not have any classes defined in `character_template_class`. Skipped.", templateSetId);
+ continue;
+ }
+ } while (templates->NextRow());
+ TC_LOG_INFO("server.loading", ">> Loaded %u character templates in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
void ObjectMgr::LoadRealmNames()
{
uint32 oldMSTime = getMSTime();
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index b8e16ec1c4d..bde5aab51e1 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -596,6 +596,26 @@ typedef std::unordered_map<uint32, TrainerSpellData> CacheTrainerSpellContainer;
typedef std::unordered_map<uint8, uint8> ExpansionRequirementContainer;
typedef std::unordered_map<uint32, std::string> RealmNameContainer;
+struct CharcterTemplateClass
+{
+ CharcterTemplateClass(uint8 factionGroup, uint8 classID) :
+ FactionGroup(factionGroup), ClassID(classID) { }
+
+ uint8 FactionGroup;
+ uint8 ClassID;
+};
+
+struct CharacterTemplate
+{
+ uint32 TemplateSetId;
+ std::list<CharcterTemplateClass> Classes;
+ std::string Name;
+ std::string Description;
+ uint8 Level;
+};
+
+typedef std::unordered_map<uint32, CharacterTemplate> CharacterTemplateContainer;
+
enum SkillRangeType
{
SKILL_RANGE_LANGUAGE, // 300..300
@@ -1287,6 +1307,7 @@ class ObjectMgr
bool IsTransportMap(uint32 mapId) const { return _transportMaps.count(mapId) != 0; }
void LoadRaceAndClassExpansionRequirements();
+ void LoadCharacterTemplates();
void LoadRealmNames();
std::string GetRealmName(uint32 realm) const;
@@ -1309,6 +1330,16 @@ class ObjectMgr
return EXPANSION_CLASSIC;
}
+ CharacterTemplateContainer const& GetCharacterTemplates() const { return _characterTemplateStore; }
+ CharacterTemplate const* GetCharacterTemplate(uint32 id) const
+ {
+ auto itr = _characterTemplateStore.find(id);
+ if (itr != _characterTemplateStore.end())
+ return &itr->second;
+
+ return nullptr;
+ }
+
private:
// first free id for selected id type
uint32 _auctionId;
@@ -1457,6 +1488,8 @@ class ObjectMgr
ExpansionRequirementContainer _classExpansionRequirementStore;
RealmNameContainer _realmNameStore;
+ CharacterTemplateContainer _characterTemplateStore;
+
enum CreatureLinkedRespawnType
{
CREATURE_TO_CREATURE,
diff --git a/src/server/game/Handlers/AuthHandler.cpp b/src/server/game/Handlers/AuthHandler.cpp
index e353279ad57..10db667c549 100644
--- a/src/server/game/Handlers/AuthHandler.cpp
+++ b/src/server/game/Handlers/AuthHandler.cpp
@@ -39,6 +39,10 @@ void WorldSession::SendAuthResponse(uint8 code, bool queued, uint32 queuePos)
// Send current home realm. Also there is no need to send it later in realm queries.
response.SuccessInfo.Value.VirtualRealms.emplace_back(GetVirtualRealmAddress(), true, false, realmName, realmName);
+ if (HasPermission(rbac::RBAC_PERM_USE_CHARACTER_TEMPLATES))
+ for (auto& templ : sObjectMgr->GetCharacterTemplates())
+ response.SuccessInfo.Value.Templates.emplace_back(templ.second);
+
response.SuccessInfo.Value.AvailableClasses = &sObjectMgr->GetClassExpansionRequirements();
response.SuccessInfo.Value.AvailableRaces = &sObjectMgr->GetRaceExpansionRequirements();
}
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 6b6bd1a1f7d..c7960772507 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -705,6 +705,25 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, WorldPac
LoginDatabase.CommitTransaction(trans);
+ if (createInfo->TemplateSet.HasValue)
+ {
+ if (HasPermission(rbac::RBAC_PERM_USE_CHARACTER_TEMPLATES))
+ {
+ if (CharacterTemplate const* charTemplate = sObjectMgr->GetCharacterTemplate(createInfo->TemplateSet.Value))
+ {
+ if (charTemplate->Level != 1)
+ {
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_LEVEL);
+ stmt->setUInt8(0, uint8(charTemplate->Level));
+ stmt->setUInt64(1, newChar.GetGUID().GetCounter());
+ CharacterDatabase.Execute(stmt);
+ }
+ }
+ }
+ else
+ TC_LOG_WARN("cheat", "Account: %u (IP: %s) tried to use a character template without given permission. Possible cheating attempt.", GetAccountId(), GetRemoteAddress().c_str());
+ }
+
SendCharCreate(CHAR_CREATE_SUCCESS);
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Create Character: %s %s", GetAccountId(), GetRemoteAddress().c_str(), createInfo->Name.c_str(), newChar.GetGUID().ToString().c_str());
diff --git a/src/server/game/Server/Packets/AuthenticationPackets.cpp b/src/server/game/Server/Packets/AuthenticationPackets.cpp
index b6d2ff21630..8dfb05e69f7 100644
--- a/src/server/game/Server/Packets/AuthenticationPackets.cpp
+++ b/src/server/game/Server/Packets/AuthenticationPackets.cpp
@@ -104,11 +104,11 @@ WorldPacket const* WorldPackets::Auth::AuthResponse::Write()
for (auto& templat : SuccessInfo.Value.Templates)
{
_worldPacket << uint32(templat.TemplateSetId);
- _worldPacket << uint32(templat.TemplateClasses.size());
- for (auto& templatClass : templat.TemplateClasses)
+ _worldPacket << uint32(templat.Classes.size());
+ for (auto& templateClass : templat.Classes)
{
- _worldPacket << uint8(templatClass.Class);
- _worldPacket << uint8(templatClass.FactionGroup);
+ _worldPacket << uint8(templateClass.ClassID);
+ _worldPacket << uint8(templateClass.FactionGroup);
}
_worldPacket.WriteBits(templat.Name.length(), 7);
diff --git a/src/server/game/Server/Packets/AuthenticationPackets.h b/src/server/game/Server/Packets/AuthenticationPackets.h
index f914e18f6bc..719993e4ee4 100644
--- a/src/server/game/Server/Packets/AuthenticationPackets.h
+++ b/src/server/game/Server/Packets/AuthenticationPackets.h
@@ -83,20 +83,6 @@ namespace WorldPackets
std::string RealmNameNormalized; ///< the name of the realm without spaces
};
- struct CharacterTemplate
- {
- struct TemplateClass
- {
- uint8 Class;
- uint8 FactionGroup; ///< @todo research
- };
-
- uint32 TemplateSetId; ///< @todo research
- std::list<TemplateClass> TemplateClasses;
- std::string Name;
- std::string Description;
- };
-
struct AuthSuccessInfo
{
uint32 TimeRemain = 0; ///< the remaining game time that the account has in seconds. It is not currently implemented and probably won't ever be.
@@ -111,7 +97,7 @@ namespace WorldPackets
uint32 CurrencyID = 0; ///< this is probably used for the ingame shop. @todo implement
std::list<RealmInfo> VirtualRealms; ///< list of realms connected to this one (inclusive) @todo implement
- std::list<CharacterTemplate> Templates; ///< list of pre-made character templates. @todo implement
+ std::list<CharacterTemplate> Templates; ///< list of pre-made character templates.
ExpansionRequirementContainer const* AvailableClasses = nullptr; ///< the minimum AccountExpansion required to select the classes
ExpansionRequirementContainer const* AvailableRaces = nullptr; ///< the minimum AccountExpansion required to select the races
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 7010982dc2b..05f75358a7b 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -2028,6 +2028,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading race and class expansion requirements...");
sObjectMgr->LoadRaceAndClassExpansionRequirements();
+ TC_LOG_INFO("server.loading", "Loading character templates...");
+ sObjectMgr->LoadCharacterTemplates();
+
TC_LOG_INFO("server.loading", "Loading realm names...");
sObjectMgr->LoadRealmNames();
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index c8066ee92e9..556589e281d 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -76,6 +76,7 @@ public:
{ "areatrigger_teleport", rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TELEPORT, true, &HandleReloadAreaTriggerTeleportCommand, "", NULL },
{ "autobroadcast", rbac::RBAC_PERM_COMMAND_RELOAD_AUTOBROADCAST, true, &HandleReloadAutobroadcastCommand, "", NULL },
{ "battleground_template", rbac::RBAC_PERM_COMMAND_RELOAD_BATTLEGROUND_TEMPLATE, true, &HandleReloadBattlegroundTemplate, "", NULL },
+ { "character_template", rbac::RBAC_PERM_COMMAND_RELOAD_CHARACTER_TEMPLATE, true, &HandleReloadCharacterTemplate, "", NULL },
{ "command", rbac::RBAC_PERM_COMMAND_RELOAD_COMMAND, true, &HandleReloadCommandCommand, "", NULL },
{ "conditions", rbac::RBAC_PERM_COMMAND_RELOAD_CONDITIONS, true, &HandleReloadConditions, "", NULL },
{ "config", rbac::RBAC_PERM_COMMAND_RELOAD_CONFIG, true, &HandleReloadConfigCommand, "", NULL },
@@ -202,6 +203,7 @@ public:
HandleReloadAutobroadcastCommand(handler, "");
HandleReloadBattlegroundTemplate(handler, "");
+ HandleReloadCharacterTemplate(handler, "");
return true;
}
@@ -383,6 +385,14 @@ public:
return true;
}
+ static bool HandleReloadCharacterTemplate(ChatHandler* handler, char const* /*args*/)
+ {
+ TC_LOG_INFO("misc", "Re-Loading Character Templates...");
+ sObjectMgr->LoadCharacterTemplates();
+ handler->SendGlobalGMSysMessage("DB table `character_template` and `character_template_class` reloaded.");
+ return true;
+ }
+
static bool HandleReloadCommandCommand(ChatHandler* handler, const char* /*args*/)
{
handler->SetLoadCommandTable(true);
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
index cf871ab79a4..c7a63911a4c 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
@@ -68,6 +68,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARACTER_TEMPLATES, "SELECT id, name, description, level FROM character_template", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARACTER_TEMPLATE_CLASSES, "SELECT factionGroup, class FROM character_template_class WHERE templateId = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM_ALL, "DELETE FROM character_battleground_random", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM, "DELETE FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC);
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h
index e6c0a0db48c..b6897204b6a 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.h
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.h
@@ -71,6 +71,8 @@ enum CharacterDatabaseStatements
CHAR_SEL_CHAR_ZONE,
CHAR_SEL_CHAR_POSITION_XYZ,
CHAR_SEL_CHAR_POSITION,
+ CHAR_SEL_CHARACTER_TEMPLATES,
+ CHAR_SEL_CHARACTER_TEMPLATE_CLASSES,
CHAR_DEL_BATTLEGROUND_RANDOM_ALL,
CHAR_DEL_BATTLEGROUND_RANDOM,