diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index fefb729e4cf..f34eb8411ec 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -105,51 +105,6 @@ #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS) -enum CharacterFlags -{ - CHARACTER_FLAG_NONE = 0x00000000, - CHARACTER_FLAG_UNK1 = 0x00000001, - CHARACTER_FLAG_UNK2 = 0x00000002, - CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004, - CHARACTER_FLAG_UNK4 = 0x00000008, - CHARACTER_FLAG_UNK5 = 0x00000010, - CHARACTER_FLAG_UNK6 = 0x00000020, - CHARACTER_FLAG_UNK7 = 0x00000040, - CHARACTER_FLAG_UNK8 = 0x00000080, - CHARACTER_FLAG_UNK9 = 0x00000100, - CHARACTER_FLAG_UNK10 = 0x00000200, - CHARACTER_FLAG_HIDE_HELM = 0x00000400, - CHARACTER_FLAG_HIDE_CLOAK = 0x00000800, - CHARACTER_FLAG_UNK13 = 0x00001000, - CHARACTER_FLAG_GHOST = 0x00002000, - CHARACTER_FLAG_RENAME = 0x00004000, - CHARACTER_FLAG_UNK16 = 0x00008000, - CHARACTER_FLAG_UNK17 = 0x00010000, - CHARACTER_FLAG_UNK18 = 0x00020000, - CHARACTER_FLAG_UNK19 = 0x00040000, - CHARACTER_FLAG_UNK20 = 0x00080000, - CHARACTER_FLAG_UNK21 = 0x00100000, - CHARACTER_FLAG_UNK22 = 0x00200000, - CHARACTER_FLAG_UNK23 = 0x00400000, - CHARACTER_FLAG_UNK24 = 0x00800000, - CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000, - CHARACTER_FLAG_DECLINED = 0x02000000, - CHARACTER_FLAG_UNK27 = 0x04000000, - CHARACTER_FLAG_UNK28 = 0x08000000, - CHARACTER_FLAG_UNK29 = 0x10000000, - CHARACTER_FLAG_UNK30 = 0x20000000, - CHARACTER_FLAG_UNK31 = 0x40000000, - CHARACTER_FLAG_UNK32 = 0x80000000 -}; - -enum CharacterCustomizeFlags -{ - CHAR_CUSTOMIZE_FLAG_NONE = 0x00000000, - CHAR_CUSTOMIZE_FLAG_CUSTOMIZE = 0x00000001, // name, gender, etc... - CHAR_CUSTOMIZE_FLAG_FACTION = 0x00010000, // name, gender, faction, etc... - CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc... -}; - // corpse reclaim times #define DEATH_EXPIRE_STEP (5*MINUTE) #define MAX_DEATH_COUNT 3 @@ -1452,200 +1407,6 @@ void Player::setDeathState(DeathState s) SetUInt32Value(PLAYER_SELF_RES_SPELL, 0); } -bool Player::BuildEnumData(PreparedQueryResult result, ByteBuffer* dataBuffer, ByteBuffer* bitBuffer) -{ - // 0 1 2 3 4 5 6 7 - // SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.skin, characters.face, characters.hairStyle, - // 8 9 10 11 12 13 14 15 - // characters.hairColor, characters.facialStyle, characters.level, characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, - // 16 17 18 19 20 21 22 - // guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, - // 23 24 25 - // character_banned.guid, characters.slot, character_declinedname.genitive - - Field* fields = result->Fetch(); - - ObjectGuid guid(HighGuid::Player, fields[0].GetUInt32()); - std::string name = fields[1].GetString(); - uint8 plrRace = fields[2].GetUInt8(); - uint8 plrClass = fields[3].GetUInt8(); - uint8 gender = fields[4].GetUInt8(); - uint8 skin = fields[5].GetUInt8(); - uint8 face = fields[6].GetUInt8(); - uint8 hairStyle = fields[7].GetUInt8(); - uint8 hairColor = fields[8].GetUInt8(); - uint8 facialStyle = fields[9].GetUInt8(); - uint8 level = fields[10].GetUInt8(); - uint32 zone = fields[11].GetUInt16(); - uint32 mapId = uint32(fields[12].GetUInt16()); - float x = fields[13].GetFloat(); - float y = fields[14].GetFloat(); - float z = fields[15].GetFloat(); - uint32 guildId = fields[16].GetUInt32(); - ObjectGuid guildGuid; - if (guildId) - guildGuid = ObjectGuid(HighGuid::Guild, guildId); - uint32 playerFlags = fields[17].GetUInt32(); - uint32 atLoginFlags = fields[18].GetUInt16(); - Tokenizer equipment(fields[22].GetString(), ' '); - uint8 slot = fields[24].GetUInt8(); - - if (!ValidateAppearance(uint8(plrRace), uint8(plrClass), gender, hairStyle, hairColor, face, facialStyle, skin)) - { - TC_LOG_ERROR("entities.player.loading", "Player %s has wrong Appearance values (Hair/Skin/Color), forcing recustomize", guid.ToString().c_str()); - - // Make sure customization always works properly - send all zeroes instead - skin = 0, face = 0, hairStyle = 0, hairColor = 0, facialStyle = 0; - - if (!(atLoginFlags & AT_LOGIN_CUSTOMIZE)) - { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); - stmt->setUInt16(0, uint16(AT_LOGIN_CUSTOMIZE)); - stmt->setUInt32(1, guid); - CharacterDatabase.Execute(stmt); - atLoginFlags |= AT_LOGIN_CUSTOMIZE; - } - } - - - uint32 charFlags = 0; - if (atLoginFlags & AT_LOGIN_RESURRECT) - playerFlags &= ~PLAYER_FLAGS_GHOST; - if (playerFlags & PLAYER_FLAGS_HIDE_HELM) - charFlags |= CHARACTER_FLAG_HIDE_HELM; - - if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK) - charFlags |= CHARACTER_FLAG_HIDE_CLOAK; - - if (playerFlags & PLAYER_FLAGS_GHOST) - charFlags |= CHARACTER_FLAG_GHOST; - - if (atLoginFlags & AT_LOGIN_RENAME) - charFlags |= CHARACTER_FLAG_RENAME; - - if (fields[23].GetUInt32()) - charFlags |= CHARACTER_FLAG_LOCKED_BY_BILLING; - - if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED) && !fields[22].GetString().empty()) - charFlags |= CHARACTER_FLAG_DECLINED; - - uint32 customizationFlag = 0; - if (atLoginFlags & AT_LOGIN_CUSTOMIZE) - customizationFlag = CHAR_CUSTOMIZE_FLAG_CUSTOMIZE; - else if (atLoginFlags & AT_LOGIN_CHANGE_FACTION) - customizationFlag = CHAR_CUSTOMIZE_FLAG_FACTION; - else if (atLoginFlags & AT_LOGIN_CHANGE_RACE) - customizationFlag = CHAR_CUSTOMIZE_FLAG_RACE; - - uint32 petDisplayId = 0; - uint32 petLevel = 0; - CreatureFamily petFamily = CREATURE_FAMILY_NONE; - // show pet at selection character in character list only for non-ghost character - if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (plrClass == CLASS_WARLOCK || plrClass == CLASS_HUNTER || plrClass == CLASS_DEATH_KNIGHT)) - { - uint32 entry = fields[19].GetUInt32(); - CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(entry); - if (creatureInfo) - { - petDisplayId = fields[20].GetUInt32(); - petLevel = fields[21].GetUInt16(); - petFamily = creatureInfo->family; - } - } - - // Packet content flags - bitBuffer->WriteBit(guid[3]); - bitBuffer->WriteBit(guildGuid[1]); - bitBuffer->WriteBit(guildGuid[7]); - bitBuffer->WriteBit(guildGuid[2]); - bitBuffer->WriteBits(uint32(name.length()), 7); - bitBuffer->WriteBit(guid[4]); - bitBuffer->WriteBit(guid[7]); - bitBuffer->WriteBit(guildGuid[3]); - bitBuffer->WriteBit(guid[5]); - bitBuffer->WriteBit(guildGuid[6]); - bitBuffer->WriteBit(guid[1]); - bitBuffer->WriteBit(guildGuid[5]); - bitBuffer->WriteBit(guildGuid[4]); - bitBuffer->WriteBit(atLoginFlags & AT_LOGIN_FIRST); - bitBuffer->WriteBit(guid[0]); - bitBuffer->WriteBit(guid[2]); - bitBuffer->WriteBit(guid[6]); - bitBuffer->WriteBit(guildGuid[0]); - - // Character data - *dataBuffer << uint8(plrClass); // Class - for (uint8 slot = 0; slot < INVENTORY_SLOT_BAG_END; ++slot) - { - uint32 visualbase = slot * 2; - uint32 itemId = GetUInt32ValueFromArray(equipment, visualbase); - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); - if (!proto) - { - *dataBuffer << uint8(0); - *dataBuffer << uint32(0); - *dataBuffer << uint32(0); - continue; - } - - SpellItemEnchantmentEntry const* enchant = nullptr; - uint32 enchants = GetUInt32ValueFromArray(equipment, visualbase + 1); - for (uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot) - { - // values stored in 2 uint16 - uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot * 16); - if (!enchantId) - continue; - - enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); - if (enchant) - break; - } - - *dataBuffer << uint8(proto->InventoryType); - *dataBuffer << uint32(proto->DisplayInfoID); - *dataBuffer << uint32(enchant ? enchant->aura_id : 0); - } - - *dataBuffer << uint32(petFamily); // Pet family - dataBuffer->WriteByteSeq(guildGuid[2]); - *dataBuffer << uint8(slot); // List order - *dataBuffer << uint8(hairStyle); // Hair style - dataBuffer->WriteByteSeq(guildGuid[3]); - *dataBuffer << uint32(petDisplayId); // Pet DisplayID - *dataBuffer << uint32(charFlags); // Character flags - *dataBuffer << uint8(hairColor); // Hair color - dataBuffer->WriteByteSeq(guid[4]); - *dataBuffer << uint32(mapId); // Map Id - dataBuffer->WriteByteSeq(guildGuid[5]); - *dataBuffer << float(z); // Z - dataBuffer->WriteByteSeq(guildGuid[6]); - *dataBuffer << uint32(petLevel); // Pet level - dataBuffer->WriteByteSeq(guid[3]); - *dataBuffer << float(y); // Y - *dataBuffer << uint32(customizationFlag); // Character customization flags - *dataBuffer << uint8(facialStyle); // Facial hair - dataBuffer->WriteByteSeq(guid[7]); - *dataBuffer << uint8(gender); // Gender - dataBuffer->append(name.c_str(), name.length()); // Name - *dataBuffer << uint8(face); // Face - dataBuffer->WriteByteSeq(guid[0]); - dataBuffer->WriteByteSeq(guid[2]); - dataBuffer->WriteByteSeq(guildGuid[1]); - dataBuffer->WriteByteSeq(guildGuid[7]); - *dataBuffer << float(x); // X - *dataBuffer << uint8(skin); // Skin - *dataBuffer << uint8(plrRace); // Race - *dataBuffer << uint8(level); // Level - dataBuffer->WriteByteSeq(guid[6]); - dataBuffer->WriteByteSeq(guildGuid[4]); - dataBuffer->WriteByteSeq(guildGuid[0]); - dataBuffer->WriteByteSeq(guid[5]); - dataBuffer->WriteByteSeq(guid[1]); - *dataBuffer << uint32(zone); // Zone id - return true; -} - void Player::ToggleAFK() { ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index c81ce85f63d..65226ec1a1c 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1047,8 +1047,6 @@ class TC_GAME_API Player : public Unit, public GridObject void Update(uint32 time) override; - static bool BuildEnumData(PreparedQueryResult result, ByteBuffer* dataBuffer, ByteBuffer* bitBuffer); - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override; void SetInWater(bool apply); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 67b3c104b93..fc8742b83f4 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -23,6 +23,7 @@ #include "BattlenetServerManager.h" #include "CalendarMgr.h" #include "CharacterCache.h" +#include "CharacterPackets.h" #include "Chat.h" #include "DatabaseEnv.h" #include "DBCStores.h" @@ -242,53 +243,56 @@ bool LoginQueryHolder::Initialize() void WorldSession::HandleCharEnum(PreparedQueryResult result) { - uint32 charCount = 0; - ByteBuffer bitBuffer; - ByteBuffer dataBuffer; + WorldPackets::Character::EnumCharactersResult charEnum; + charEnum.Success = true; + + _legitCharacters.clear(); - bitBuffer.WriteBits(0, 23); - bitBuffer.WriteBit(1); if (result) { - _legitCharacters.clear(); - - charCount = uint32(result->GetRowCount()); - bitBuffer.reserve(24 * charCount / 8); - dataBuffer.reserve(charCount * 381); - - bitBuffer.WriteBits(charCount, 17); - do { - ObjectGuid guid(HighGuid::Player, (*result)[0].GetUInt32()); + charEnum.Characters.emplace_back(result->Fetch()); - TC_LOG_INFO("network", "Loading char guid %s from account %u.", guid.ToString().c_str(), GetAccountId()); + WorldPackets::Character::EnumCharactersResult::CharacterInfo& charInfo = charEnum.Characters.back(); - Player::BuildEnumData(result, &dataBuffer, &bitBuffer); + TC_LOG_INFO("network", "Loading char guid %s from account %u.", charInfo.Guid.ToString().c_str(), GetAccountId()); - // Do not allow banned characters to log in - if (!(*result)[23].GetUInt32()) - _legitCharacters.insert(guid); + if (!Player::ValidateAppearance(charInfo.RaceID, charInfo.ClassID, charInfo.SexID, charInfo.HairStyle, charInfo.HairColor, charInfo.FaceID, charInfo.FacialHair, charInfo.SkinID)) + { + TC_LOG_ERROR("entities.player.loading", "Player %s has wrong Appearance values (Hair/Skin/Color), forcing recustomize", charInfo.Guid.ToString().c_str()); - if (!sCharacterCache->HasCharacterCacheEntry(guid)) // This can happen if characters are inserted into the database manually. Core hasn't loaded name data yet. - sCharacterCache->AddCharacterCacheEntry(guid, GetAccountId(), (*result)[1].GetString(), (*result)[4].GetUInt8(), (*result)[2].GetUInt8(), (*result)[3].GetUInt8(), (*result)[10].GetUInt8()); + // Make sure customization always works properly - send all zeroes instead + charInfo.SkinID = 0; + charInfo.FaceID = 0; + charInfo.HairStyle = 0; + charInfo.HairColor = 0; + charInfo.FacialHair = 0; - } while (result->NextRow()); + if (!(charInfo.Flags2 == CHAR_CUSTOMIZE_FLAG_CUSTOMIZE)) + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + stmt->setUInt16(0, uint16(AT_LOGIN_CUSTOMIZE)); + stmt->setUInt32(1, charInfo.Guid.GetCounter()); + CharacterDatabase.Execute(stmt); + charInfo.Flags2 = CHAR_CUSTOMIZE_FLAG_CUSTOMIZE; + } + } + + // Do not allow locked characters to login + if (!(charInfo.Flags & (CHARACTER_FLAG_LOCKED_FOR_TRANSFER | CHARACTER_FLAG_LOCKED_BY_BILLING))) + _legitCharacters.insert(charInfo.Guid); + + if (!sCharacterCache->HasCharacterCacheEntry(charInfo.Guid)) // This can happen if characters are inserted into the database manually. Core hasn't loaded name data yet. + sCharacterCache->AddCharacterCacheEntry(charInfo.Guid, GetAccountId(), charInfo.Name, charInfo.SexID, charInfo.RaceID, charInfo.ClassID, charInfo.ExperienceLevel); + } + while (result->NextRow()); } - else - bitBuffer.WriteBits(0, 17); - bitBuffer.FlushBits(); - - WorldPacket data(SMSG_CHAR_ENUM, 7 + bitBuffer.size() + dataBuffer.size()); - data.append(bitBuffer); - if (charCount) - data.append(dataBuffer); - - SendPacket(&data); + SendPacket(charEnum.Write()); } -void WorldSession::HandleCharEnumOpcode(WorldPacket& /*recvData*/) +void WorldSession::HandleCharEnumOpcode(WorldPackets::Character::EnumCharacters& /*enumCharacters*/) { // remove expired bans PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_BANS); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 1e0aff0e40f..c41b5c71ac0 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -780,6 +780,51 @@ enum SheathTypes #define MAX_SHEATHETYPE 8 +enum CharacterFlags +{ + CHARACTER_FLAG_NONE = 0x00000000, + CHARACTER_FLAG_UNK1 = 0x00000001, + CHARACTER_FLAG_UNK2 = 0x00000002, + CHARACTER_FLAG_LOCKED_FOR_TRANSFER = 0x00000004, + CHARACTER_FLAG_UNK4 = 0x00000008, + CHARACTER_FLAG_UNK5 = 0x00000010, + CHARACTER_FLAG_UNK6 = 0x00000020, + CHARACTER_FLAG_UNK7 = 0x00000040, + CHARACTER_FLAG_UNK8 = 0x00000080, + CHARACTER_FLAG_UNK9 = 0x00000100, + CHARACTER_FLAG_UNK10 = 0x00000200, + CHARACTER_FLAG_HIDE_HELM = 0x00000400, + CHARACTER_FLAG_HIDE_CLOAK = 0x00000800, + CHARACTER_FLAG_UNK13 = 0x00001000, + CHARACTER_FLAG_GHOST = 0x00002000, + CHARACTER_FLAG_RENAME = 0x00004000, + CHARACTER_FLAG_UNK16 = 0x00008000, + CHARACTER_FLAG_UNK17 = 0x00010000, + CHARACTER_FLAG_UNK18 = 0x00020000, + CHARACTER_FLAG_UNK19 = 0x00040000, + CHARACTER_FLAG_UNK20 = 0x00080000, + CHARACTER_FLAG_UNK21 = 0x00100000, + CHARACTER_FLAG_UNK22 = 0x00200000, + CHARACTER_FLAG_UNK23 = 0x00400000, + CHARACTER_FLAG_UNK24 = 0x00800000, + CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000, + CHARACTER_FLAG_DECLINED = 0x02000000, + CHARACTER_FLAG_UNK27 = 0x04000000, + CHARACTER_FLAG_UNK28 = 0x08000000, + CHARACTER_FLAG_UNK29 = 0x10000000, + CHARACTER_FLAG_UNK30 = 0x20000000, + CHARACTER_FLAG_UNK31 = 0x40000000, + CHARACTER_FLAG_UNK32 = 0x80000000 +}; + +enum CharacterCustomizeFlags +{ + CHAR_CUSTOMIZE_FLAG_NONE = 0x00000000, + CHAR_CUSTOMIZE_FLAG_CUSTOMIZE = 0x00000001, // name, gender, etc... + CHAR_CUSTOMIZE_FLAG_FACTION = 0x00010000, // name, gender, faction, etc... + CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc... +}; + enum CharacterSlot { SLOT_HEAD = 0, diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 5cb643d10fd..9f401f73948 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -19,6 +19,7 @@ #define AllPackets_h__ #include "AuthenticationPackets.h" +#include "CharacterPackets.h" #include "GuildPackets.h" #include "LFGPackets.h" #include "NPCPackets.h" diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp new file mode 100644 index 00000000000..6ee8f6b9a93 --- /dev/null +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -0,0 +1,211 @@ +/* + * This file is part of the TrinityCore 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 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, see . + */ + +#include "CharacterPackets.h" +#include "DBCStores.h" +#include "Field.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "World.h" + +WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Field* fields) +{ + // 0 1 2 3 4 5 6 7 + // SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.skin, characters.face, characters.hairStyle, + // 8 9 10 11 12 13 14 15 + // characters.hairColor, characters.facialStyle, characters.level, characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, + // 16 17 18 19 20 21 22 + // guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, + // 23 24 25 + // character_banned.guid, characters.slot, character_declinedname.genitive + + Guid = ObjectGuid::Create(fields[0].GetUInt32()); + Name = fields[1].GetString(); + RaceID = fields[2].GetUInt8(); + ClassID = fields[3].GetUInt8(); + SexID = fields[4].GetUInt8(); + SkinID = fields[5].GetUInt8(); + FaceID = fields[6].GetUInt8(); + HairStyle = fields[7].GetUInt8(); + HairColor = fields[8].GetUInt8(); + FacialHair = fields[9].GetUInt8(); + ExperienceLevel = fields[10].GetUInt8(); + ZoneID = int32(fields[11].GetUInt16()); + MapID = int32(fields[12].GetUInt16()); + PreloadPos = Position(fields[13].GetFloat(), fields[14].GetFloat(), fields[15].GetFloat()); + + if (ObjectGuid::LowType guildId = fields[16].GetUInt32()) + GuildGUID = ObjectGuid::Create(guildId); + + uint32 playerFlags = fields[17].GetUInt32(); + uint32 atLoginFlags = fields[18].GetUInt16(); + + if (atLoginFlags & AT_LOGIN_RESURRECT) + playerFlags &= ~PLAYER_FLAGS_GHOST; + + if (playerFlags & PLAYER_FLAGS_GHOST) + Flags |= CHARACTER_FLAG_GHOST; + + if (atLoginFlags & AT_LOGIN_RENAME) + Flags |= CHARACTER_FLAG_RENAME; + + if (fields[23].GetUInt32()) + Flags |= CHARACTER_FLAG_LOCKED_BY_BILLING; + + if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED) && !fields[22].GetString().empty()) + Flags |= CHARACTER_FLAG_DECLINED; + + if (atLoginFlags & AT_LOGIN_CUSTOMIZE) + Flags2 = CHAR_CUSTOMIZE_FLAG_CUSTOMIZE; + else if (atLoginFlags & AT_LOGIN_CHANGE_FACTION) + Flags2 = CHAR_CUSTOMIZE_FLAG_FACTION; + else if (atLoginFlags & AT_LOGIN_CHANGE_RACE) + Flags2 = CHAR_CUSTOMIZE_FLAG_RACE; + + FirstLogin = (atLoginFlags & AT_LOGIN_FIRST) != 0; + + // show pet at selection character in character list only for non-ghost character + if (!(playerFlags & PLAYER_FLAGS_GHOST) && (ClassID == CLASS_WARLOCK || ClassID == CLASS_HUNTER || ClassID == CLASS_DEATH_KNIGHT)) + { + if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(fields[19].GetUInt32())) + { + PetCreatureDisplayID = fields[20].GetUInt32(); + PetExperienceLevel = fields[21].GetUInt16(); + PetCreatureFamilyID = creatureInfo->family; + } + } + + Tokenizer equipment(fields[22].GetString(), ' '); + ListPosition = fields[24].GetUInt8(); + + for (uint8 slot = 0; slot < INVENTORY_SLOT_BAG_END; ++slot) + { + uint32 visualBase = slot * 2; + uint32 itemId = Player::GetUInt32ValueFromArray(equipment, visualBase); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (!proto) + continue; + + SpellItemEnchantmentEntry const* enchant = nullptr; + uint32 enchants = Player::GetUInt32ValueFromArray(equipment, visualBase + 1); + for (uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot) + { + // values stored in 2 uint16 + uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot * 16); + if (!enchantId) + continue; + + enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); + if (enchant) + break; + } + + VisualItems[slot].InvType = proto->InventoryType; + VisualItems[slot].DisplayID = proto->DisplayInfoID; + VisualItems[slot].DisplayEnchantID = enchant ? enchant->aura_id : 0; + } +} + +WorldPacket const* WorldPackets::Character::EnumCharactersResult::Write() +{ + _worldPacket.reserve(6 + Characters.size() * sizeof(CharacterInfo) + FactionChangeRestrictions.size() * sizeof(RestrictedFactionChangeRuleInfo)); + + _worldPacket.WriteBits(FactionChangeRestrictions.size(), 23); + _worldPacket.WriteBit(Success); + _worldPacket.WriteBits(Characters.size(), 17); + + for (CharacterInfo const& charInfo : Characters) + { + _worldPacket.WriteBit(charInfo.Guid[3]); + _worldPacket.WriteBit(charInfo.GuildGUID[1]); + _worldPacket.WriteBit(charInfo.GuildGUID[7]); + _worldPacket.WriteBit(charInfo.GuildGUID[2]); + _worldPacket.WriteBits(charInfo.Name.length(), 7); + _worldPacket.WriteBit(charInfo.Guid[4]); + _worldPacket.WriteBit(charInfo.Guid[7]); + _worldPacket.WriteBit(charInfo.GuildGUID[3]); + _worldPacket.WriteBit(charInfo.Guid[5]); + _worldPacket.WriteBit(charInfo.GuildGUID[6]); + _worldPacket.WriteBit(charInfo.Guid[1]); + _worldPacket.WriteBit(charInfo.GuildGUID[5]); + _worldPacket.WriteBit(charInfo.GuildGUID[4]); + _worldPacket.WriteBit(charInfo.FirstLogin); + _worldPacket.WriteBit(charInfo.Guid[0]); + _worldPacket.WriteBit(charInfo.Guid[2]); + _worldPacket.WriteBit(charInfo.Guid[6]); + _worldPacket.WriteBit(charInfo.GuildGUID[0]); + } + + _worldPacket.FlushBits(); + + for (CharacterInfo const& charInfo : Characters) + { + _worldPacket << uint8(charInfo.ClassID); + + for (WorldPackets::Character::EnumCharactersResult::CharacterInfo::VisualItemInfo const& visualItem : charInfo.VisualItems) + { + _worldPacket << uint8(visualItem.InvType); + _worldPacket << uint32(visualItem.DisplayID); + _worldPacket << uint32(visualItem.DisplayEnchantID); + } + + _worldPacket << uint32(charInfo.PetCreatureFamilyID); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[2]); + _worldPacket << uint8(charInfo.ListPosition); + _worldPacket << uint8(charInfo.HairStyle); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[3]); + _worldPacket << uint32(charInfo.PetCreatureDisplayID); + _worldPacket << uint32(charInfo.Flags); + _worldPacket << uint8(charInfo.HairColor); + _worldPacket.WriteByteSeq(charInfo.Guid[4]); + _worldPacket << int32(charInfo.MapID); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[5]); + _worldPacket << float(charInfo.PreloadPos.GetPositionZ()); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[6]); + _worldPacket << uint32(charInfo.PetExperienceLevel); + _worldPacket.WriteByteSeq(charInfo.Guid[3]); + _worldPacket << float(charInfo.PreloadPos.GetPositionY()); + _worldPacket << uint32(charInfo.Flags2); + _worldPacket << uint8(charInfo.FacialHair); + _worldPacket.WriteByteSeq(charInfo.Guid[7]); + _worldPacket << uint8(charInfo.SexID); + _worldPacket.WriteString(charInfo.Name); + _worldPacket << uint8(charInfo.FaceID); + _worldPacket.WriteByteSeq(charInfo.Guid[0]); + _worldPacket.WriteByteSeq(charInfo.Guid[2]); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[1]); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[7]); + _worldPacket << float(charInfo.PreloadPos.GetPositionX()); + _worldPacket << uint8(charInfo.SkinID); + _worldPacket << uint8(charInfo.RaceID); + _worldPacket << uint8(charInfo.ExperienceLevel); + _worldPacket.WriteByteSeq(charInfo.Guid[6]); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[4]); + _worldPacket.WriteByteSeq(charInfo.GuildGUID[0]); + _worldPacket.WriteByteSeq(charInfo.Guid[5]); + _worldPacket.WriteByteSeq(charInfo.Guid[1]); + _worldPacket << int32(charInfo.ZoneID); + } + + for (RestrictedFactionChangeRuleInfo const& rule : FactionChangeRestrictions) + { + _worldPacket << int32(rule.Mask); + _worldPacket << uint8(rule.Race); + } + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/CharacterPackets.h b/src/server/game/Server/Packets/CharacterPackets.h new file mode 100644 index 00000000000..eddf37df324 --- /dev/null +++ b/src/server/game/Server/Packets/CharacterPackets.h @@ -0,0 +1,107 @@ +/* + * This file is part of the TrinityCore 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 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, see . + */ + +#ifndef MiscPackets_h__ +#define MiscPackets_h__ + +#include "Packet.h" +#include "ObjectGuid.h" +#include "Position.h" +#include + +class Field; + +namespace WorldPackets +{ + namespace Character + { + class EnumCharacters final : public ClientPacket + { + public: + EnumCharacters(WorldPacket&& packet) : ClientPacket(CMSG_ENUM_CHARACTERS, std::move(packet)) { } + + void Read() override { } + }; + + class EnumCharactersResult final : public ServerPacket + { + public: + struct CharacterInfo + { + /** + * @fn void WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Field* fields); + * + * @brief Initialize the struct with values from QueryResult + * + * @param fields Field set of CharacterDatabaseStatements::CHAR_SEL_ENUM + */ + CharacterInfo(Field* fields); + + struct VisualItemInfo + { + uint32 DisplayID = 0; + uint32 DisplayEnchantID = 0; + uint8 InvType = 0; + }; + + Position PreloadPos; + ObjectGuid Guid; + ObjectGuid GuildGUID; + uint32 Flags = 0; ///< Character flag @see enum CharacterFlags + uint32 Flags2 = 0; ///< Character customization flags @see enum CharacterCustomizeFlags + int32 MapID = 0; + uint32 PetCreatureDisplayID = 0; + uint32 PetCreatureFamilyID = 0; + uint32 PetExperienceLevel = 0; + int32 ZoneID = 0; + uint8 ClassID = 0; + uint8 ExperienceLevel = 0; + uint8 FaceID = 0; + uint8 FacialHair = 0; + uint8 HairColor = 0; + uint8 HairStyle = 0; + uint8 ListPosition = 0; ///< Order of the characters in list + uint8 RaceID = 0; + uint8 SexID = 0; + uint8 SkinID = 0; + bool FirstLogin = false; + std::string Name; + std::array VisualItems = { }; + }; + + struct RestrictedFactionChangeRuleInfo + { + RestrictedFactionChangeRuleInfo(int32 mask, uint8 race) + : Mask(mask), Race(race) { } + + int32 Mask = 0; + uint8 Race = 0; + }; + + EnumCharactersResult() : ServerPacket(SMSG_ENUM_CHARACTERS_RESULT) { } + + WorldPacket const* Write() override; + + bool Success = false; ///< + + std::vector Characters; ///< all characters on the list + std::vector FactionChangeRestrictions; ///< @todo: research + }; + } +} + +#endif // CharacterPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 9940833dd23..5fea98ee1ee 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -256,7 +256,6 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_CHAR_CREATE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharCreateOpcode ); DEFINE_HANDLER(CMSG_CHAR_CUSTOMIZE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharCustomize ); DEFINE_HANDLER(CMSG_CHAR_DELETE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharDeleteOpcode ); - DEFINE_HANDLER(CMSG_CHAR_ENUM, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharEnumOpcode ); DEFINE_HANDLER(CMSG_CHAR_FACTION_CHANGE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharFactionOrRaceChange ); DEFINE_HANDLER(CMSG_CHAR_RACE_CHANGE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharFactionOrRaceChange ); DEFINE_HANDLER(CMSG_CHAR_RENAME, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharRenameOpcode ); @@ -294,6 +293,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_EMOTE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleEmoteOpcode ); DEFINE_HANDLER(CMSG_ENABLETAXI, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTaxiQueryAvailableNodes ); DEFINE_HANDLER(CMSG_ENABLE_NAGLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_EarlyProccess ); + DEFINE_HANDLER(CMSG_ENUM_CHARACTERS, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharEnumOpcode ); DEFINE_HANDLER(CMSG_EQUIPMENT_SET_DELETE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetDelete ); DEFINE_HANDLER(CMSG_EQUIPMENT_SET_SAVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetSave ); DEFINE_HANDLER(CMSG_EQUIPMENT_SET_USE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleEquipmentSetUse ); @@ -757,7 +757,6 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_CREATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_CUSTOMIZE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_DELETE, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_ENUM, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_FACTION_CHANGE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_RENAME, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAT_IGNORED_ACCOUNT_MUTED, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -829,6 +828,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_EMOTE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENABLE_BARBER_SHOP, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENCHANTMENTLOG, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENUM_CHARACTERS_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENVIRONMENTALDAMAGELOG, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_EQUIPMENT_SET_LIST, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_EQUIPMENT_SET_SAVED, STATUS_NEVER, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index 2cf21ec3079..a12fc58083e 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -149,7 +149,6 @@ enum OpcodeClient : uint16 CMSG_CHAR_CREATE = 0x4A36, CMSG_CHAR_CUSTOMIZE = 0x2C34, CMSG_CHAR_DELETE = 0x6425, - CMSG_CHAR_ENUM = 0x0502, CMSG_CHAR_FACTION_CHANGE = 0x2735, CMSG_CHAR_RACE_CHANGE = 0x0D24, CMSG_CHAR_RENAME = 0x2327, @@ -188,6 +187,7 @@ enum OpcodeClient : uint16 CMSG_EMOTE = 0x4C26, CMSG_ENABLETAXI = 0x0C16, CMSG_ENABLE_NAGLE = 0x4449, + CMSG_ENUM_CHARACTERS = 0x0502, CMSG_EQUIPMENT_SET_DELETE = 0x4D07, CMSG_EQUIPMENT_SET_SAVE = 0x4F27, CMSG_EQUIPMENT_SET_USE = 0x0417, @@ -691,7 +691,6 @@ enum OpcodeServer SMSG_CHAR_CREATE = 0x2D05, SMSG_CHAR_CUSTOMIZE = 0x4F16, SMSG_CHAR_DELETE = 0x0304, - SMSG_CHAR_ENUM = 0x10B0, SMSG_CHAR_FACTION_CHANGE = 0x4C06, SMSG_CHAR_RENAME = 0x2024, SMSG_CHAT_IGNORED_ACCOUNT_MUTED = 0x15A4, @@ -768,6 +767,7 @@ enum OpcodeServer SMSG_EMOTE = 0x0A34, SMSG_ENABLE_BARBER_SHOP = 0x2D16, SMSG_ENCHANTMENTLOG = 0x6035, + SMSG_ENUM_CHARACTERS_RESULT = 0x10B0, SMSG_ENVIRONMENTALDAMAGELOG = 0x6C05, SMSG_EQUIPMENT_SET_LIST = 0x2E04, SMSG_EQUIPMENT_SET_SAVED = 0x2216, @@ -1431,9 +1431,6 @@ enum OpcodeMisc : uint16 COMPRESSED_OPCODE_MASK = 0x8000 }; -typedef OpcodeClient OpcodeClient; -typedef OpcodeServer OpcodeServer; - /// Player state enum SessionStatus { @@ -1452,7 +1449,7 @@ enum PacketProcessing PROCESS_THREADSAFE //packet is thread-safe - process it in Map::Update() }; -class WorldSession; +class WorldPacket; class WorldSession; class OpcodeHandler diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index eb3b160240b..8785646266e 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -411,8 +411,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) } // some auth opcodes can be recieved before STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes - // however when we recieve CMSG_CHAR_ENUM we are surely no longer during the logout process. - if (packet->GetOpcode() == CMSG_CHAR_ENUM) + // however when we recieve CMSG_ENUM_CHARACTERS we are surely no longer during the logout process. + if (packet->GetOpcode() == CMSG_ENUM_CHARACTERS) m_playerRecentlyLogout = false; if (AntiDOS.EvaluateOpcode(*packet, currentTime)) @@ -1521,7 +1521,7 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co } case CMSG_CHAR_CREATE: // 7 5 3 async db queries - case CMSG_CHAR_ENUM: // 22 3 2 async db queries + case CMSG_ENUM_CHARACTERS: // 22 3 2 async db queries case CMSG_GMTICKET_CREATE: // 1 25 1 async db query case CMSG_GMTICKET_UPDATETEXT: // 0 15 1 async db query case CMSG_GMTICKET_DELETETICKET: // 1 25 1 async db query diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index c652b90779a..4078cc6499b 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -80,6 +80,11 @@ namespace WorldPackets enum class ConnectToSerial : uint32; } + namespace Character + { + class EnumCharacters; + } + namespace LFG { class LFGJoin; @@ -550,13 +555,13 @@ class TC_GAME_API WorldSession void Handle_ServerSide(WorldPacket& recvPacket); // sever side only, can't be accepted from client void Handle_Deprecated(WorldPacket& recvPacket); // never used anymore by client - void HandleCharEnumOpcode(WorldPacket& recvPacket); + void HandleCharEnum(PreparedQueryResult result); + void HandleCharEnumOpcode(WorldPackets::Character::EnumCharacters& /*enumCharacters*/); void HandleCharDeleteOpcode(WorldPacket& recvPacket); void HandleCharCreateOpcode(WorldPacket& recvPacket); void HandlePlayerLoginOpcode(WorldPacket& recvPacket); void HandleContinuePlayerLogin(); void HandleLoadScreenOpcode(WorldPacket& recvPacket); - void HandleCharEnum(PreparedQueryResult result); void HandlePlayerLogin(LoginQueryHolder* holder); void HandleCharFactionOrRaceChange(WorldPacket& recvData); void HandleCharFactionOrRaceChangeCallback(std::shared_ptr factionChangeInfo, PreparedQueryResult result);