diff options
author | Carbenium <keresztesschmidt@gmail.com> | 2015-04-05 00:30:02 +0200 |
---|---|---|
committer | Carbenium <keresztesschmidt@gmail.com> | 2015-04-05 01:48:45 +0200 |
commit | d1902b40957c4ce6eb0f20a48d6bb86822d96bd0 (patch) | |
tree | 227e36b7f16fde19d3cb8a576464a578a3a268fa /src | |
parent | 0878a42b7e57c62505b4cd62d9b79b9f767345c6 (diff) |
Core/Player: Implement CharSections.dbc and serverside checks
Ported from 6d4c672fb1b799e1888881282e40992a9e15a006
Original work by @ariel-
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/DataStores/DBCStores.cpp | 21 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.h | 2 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStructure.h | 28 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCfmt.h | 1 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 77 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 1 | ||||
-rw-r--r-- | src/server/game/Handlers/CharacterHandler.cpp | 39 | ||||
-rw-r--r-- | src/server/shared/Database/Implementation/CharacterDatabase.cpp | 2 |
8 files changed, 166 insertions, 5 deletions
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 4794120d188..e83e9d2f5d4 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -51,6 +51,7 @@ struct WMOAreaTableTripple }; typedef std::map<WMOAreaTableTripple, WMOAreaTableEntry const*> WMOAreaInfoByTripple; +typedef std::multimap<uint32, CharSectionsEntry const*> CharSectionsMap; DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt); static AreaFlagByAreaID sAreaFlagByAreaID; @@ -68,6 +69,8 @@ DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryf DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore(BarberShopStyleEntryfmt); DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore(CharStartOutfitEntryfmt); std::map<uint32, CharStartOutfitEntry const*> sCharStartOutfitMap; +DBCStorage <CharSectionsEntry> sCharSectionsStore(CharSectionsEntryfmt); +CharSectionsMap sCharSectionMap; DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt); DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt); DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt); @@ -375,6 +378,12 @@ void LoadDBCStores(const std::string& dataPath) if (CharStartOutfitEntry const* outfit = sCharStartOutfitStore.LookupEntry(i)) sCharStartOutfitMap[outfit->RaceID | (outfit->ClassID << 8) | (outfit->GenderID << 16)] = outfit; + LoadDBC(availableDbcLocales, bad_dbc_files, sCharSectionsStore, dbcPath, "CharSections.dbc"); + for (uint32 i = 0; i < sCharSectionsStore.GetNumRows(); ++i) + if (CharSectionsEntry const* entry = sCharSectionsStore.LookupEntry(i)) + if (entry->Race && ((1 << (entry->Race - 1)) & RACEMASK_ALL_PLAYABLE) != 0) //ignore Nonplayable races + sCharSectionMap.emplace(uint8(entry->GenType) | (uint8(entry->Gender) << 8) | (uint8(entry->Race) << 16), entry); + LoadDBC(availableDbcLocales, bad_dbc_files, sCharTitlesStore, dbcPath, "CharTitles.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc");//19116 @@ -979,6 +988,18 @@ uint32 GetPowerIndexByClass(uint32 powerType, uint32 classId) return PowersByClass[classId][powerType]; } +CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color) +{ + std::pair<CharSectionsMap::const_iterator, CharSectionsMap::const_iterator> eqr = sCharSectionMap.equal_range(uint32(genType) | uint32(gender << 8) | uint32(race << 16)); + for (CharSectionsMap::const_iterator itr = eqr.first; itr != eqr.second; ++itr) + { + if (itr->second->Type == type && itr->second->Color == color) + return itr->second; + } + + return NULL; +} + /// Returns LFGDungeonEntry for a specific map and difficulty. Will return first found entry if multiple dungeons use the same map (such as Scarlet Monastery) LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty) { diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index fcb7e0ccc68..f16b56609dd 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -77,6 +77,7 @@ PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 lev PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id); CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender); +CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color); uint32 GetPowerIndexByClass(uint32 powerType, uint32 classId); LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty); @@ -133,6 +134,7 @@ extern DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore; extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore; extern DBCStorage <ChatChannelsEntry> sChatChannelsStore; extern DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore; +extern DBCStorage <CharSectionsEntry> sCharSectionsStore; extern DBCStorage <CharTitlesEntry> sCharTitlesStore; extern DBCStorage <ChrClassesEntry> sChrClassesStore; extern DBCStorage <ChrRacesEntry> sChrRacesStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index f997aaf776f..0d4ada1ab39 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -199,6 +199,34 @@ struct CharStartOutfitEntry uint32 PetFamilyID; // 30 Pet Family Entry for starting pet }; +enum CharSectionFlags +{ + SECTION_FLAG_PLAYER = 0x01, + SECTION_FLAG_DEATH_KNIGHT = 0x04 +}; + +enum CharSectionType +{ + SECTION_TYPE_SKIN = 0, + SECTION_TYPE_FACE = 1, + SECTION_TYPE_FACIAL_HAIR = 2, + SECTION_TYPE_HAIR = 3, + SECTION_TYPE_UNDERWEAR = 4 +}; + +struct CharSectionsEntry +{ + //uint32 Id; + uint32 Race; + uint32 Gender; + uint32 GenType; + //char* TexturePath[3]; + uint32 Flags; + uint32 Type; + uint32 Color; +}; + + struct CharTitlesEntry { uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId() diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 338a06cb3f8..98677746c3c 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -34,6 +34,7 @@ char const BannedAddOnsfmt[] = "nxxxxxxxxxx"; char const BarberShopStyleEntryfmt[] = "nixxxiii"; char const BattlemasterListEntryfmt[] = "niiiiiiiiiiiiiiiiixsiiiixxxxxxx"; char const CharStartOutfitEntryfmt[] = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiiii"; +char const CharSectionsEntryfmt[] = "diiixxxiii"; char const CharTitlesEntryfmt[] = "nxssix"; char const ChatChannelsEntryfmt[] = "nixsx"; char const ChrClassesEntryfmt[] = "nixsxxxixiiiixxxxxx"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 80990c1ed01..6e3f0841e77 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -948,8 +948,6 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac { //FIXME: outfitId not used in player creating /// @todo need more checks against packet modifications - // should check that skin, face, hair* are valid via DBC per race/class - // also do it in Player::BuildEnumData, Player::LoadFromDB Object::_Create(ObjectGuid::Create<HighGuid::Player>(guidlow)); @@ -976,6 +974,13 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac return false; } + if (!ValidateAppearance(createInfo->Race, createInfo->Class, createInfo->Sex, createInfo->HairStyle, createInfo->HairColor, createInfo->Face, createInfo->FacialHairStyle, createInfo->Skin, true)) + { + TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with invalid appearance attributes - refusing to do so", + GetSession()->GetAccountId(), m_name.c_str()); + return false; + } + SetMap(sMapMgr->CreateMap(info->mapId, this)); uint8 powertype = cEntry->PowerType; @@ -16649,6 +16654,19 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32()); SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[44].GetUInt32()); + if (!ValidateAppearance( + fields[3].GetUInt8(), // race + fields[4].GetUInt8(), // class + gender, GetByteValue(PLAYER_BYTES, 2), // hair type + GetByteValue(PLAYER_BYTES, 3), //hair color + uint8(fields[9].GetUInt32() >> 8), // face + GetByteValue(PLAYER_BYTES_2, 0), // facial hair + GetByteValue(PLAYER_BYTES, 0))) // skin color + { + TC_LOG_ERROR("entities.player", "Player %s has wrong Appearance values (Hair/Skin/Color), can't be loaded.", guid.ToString().c_str()); + return false; + } + // set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise) SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, fields[58].GetUInt8()); @@ -26525,3 +26543,58 @@ void Player::RemoveSocial() sSocialMgr->RemovePlayerSocial(GetGUID()); m_social = nullptr; } + +bool Player::ValidateAppearance(uint8 race, uint8 class_, uint8 gender, uint8 hairID, uint8 hairColor, uint8 faceID, uint8 facialHair, uint8 skinColor, bool create /*=false*/) +{ + // Check skin color + // For Skin type is always 0 + if (CharSectionsEntry const* entry = GetCharSectionEntry(race, SECTION_TYPE_SKIN, gender, 0, skinColor)) + { // Skin Color defined as Face color, too, we check skin & face in one pass + if (CharSectionsEntry const* entry2 = GetCharSectionEntry(race, SECTION_TYPE_FACE, gender, faceID, skinColor)) + { + // Check DeathKnight exclusive + if (((entry->Flags & SECTION_FLAG_DEATH_KNIGHT) || (entry2->Flags & SECTION_FLAG_DEATH_KNIGHT)) && class_ != CLASS_DEATH_KNIGHT) + return false; + if (create && !((entry->Flags & SECTION_FLAG_PLAYER) && (entry2->Flags & SECTION_FLAG_PLAYER))) + return false; + } + else + return false; + } + else + return false; + + // These combinations don't have an entry of Type SECTION_TYPE_FACIAL_HAIR, exclude them from that check + bool excludeCheck = (race == RACE_TAUREN) || (race == RACE_DRAENEI) || (gender == GENDER_FEMALE && race != RACE_NIGHTELF && race != RACE_UNDEAD_PLAYER); + + // Check Hair + if (CharSectionsEntry const* entry = GetCharSectionEntry(race, SECTION_TYPE_HAIR, gender, hairID, hairColor)) + { + if ((entry->Flags & SECTION_FLAG_DEATH_KNIGHT) && class_ != CLASS_DEATH_KNIGHT) + return false; + if (create && !(entry->Flags & SECTION_FLAG_PLAYER)) + return false; + + if (!excludeCheck) + { + if (CharSectionsEntry const* entry2 = GetCharSectionEntry(race, SECTION_TYPE_FACIAL_HAIR, gender, facialHair, hairColor)) + { + if ((entry2->Flags & SECTION_FLAG_DEATH_KNIGHT) && class_ != CLASS_DEATH_KNIGHT) + return false; + if (create && !(entry2->Flags & SECTION_FLAG_PLAYER)) + return false; + } + else + return false; + } + else + { + // @TODO: Bound checking for facialHair ID (used clientside for markings, tauren beard, etc.) + // Not present in DBC + } + } + else + return false; + + return true; +}
\ No newline at end of file diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 098b653574b..1a650c509d6 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1757,6 +1757,7 @@ class Player : public Unit, public GridObject<Player> static bool IsValidGender(uint8 Gender) { return Gender <= GENDER_FEMALE; } static bool IsValidClass(uint8 Class) { return ((1 << (Class - 1)) & CLASSMASK_ALL_PLAYABLE) != 0; } static bool IsValidRace(uint8 Race) { return ((1 << (Race - 1)) & RACEMASK_ALL_PLAYABLE) != 0; } + static bool ValidateAppearance(uint8 race, uint8 class_, uint8 gender, uint8 hairID, uint8 hairColor, uint8 faceID, uint8 facialHair, uint8 skinColor, bool create = false); /*********************************************************/ /*** SAVE SYSTEM ***/ diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 881326f3b19..5fe6be24c51 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -257,6 +257,29 @@ void WorldSession::HandleCharEnum(PreparedQueryResult result) TC_LOG_INFO("network", "Loading char guid %s from account %u.", charInfo.Guid.ToString().c_str(), GetAccountId()); + uint8 plrRace = fields[2].GetUInt8(); + uint8 plrClass = fields[3].GetUInt8(); + uint8 plrGender = fields[4].GetUInt8(); + uint8 plrSkin = uint8(fields[5].GetUInt32() & 0xFF); + uint8 plrFace = uint8((fields[5].GetUInt32() >> 8) & 0xFF); + uint8 plrHairStyle = uint8((fields[5].GetUInt32() >> 16) & 0xFF); + uint8 plrHairColor = uint8((fields[5].GetUInt32() >> 24) & 0xFF); + uint8 plrFacialHair = uint8(fields[6].GetUInt32() & 0xFF); + + if (!Player::ValidateAppearance(charInfo.Race, charInfo.Class, charInfo.Sex, charInfo.HairStyle, charInfo.HairColor, charInfo.Face, charInfo.FacialHair, charInfo.Skin)) + { + TC_LOG_ERROR("entities.player.loading", "Player %s has wrong Appearance values (Hair/Skin/Color), forcing recustomize", charInfo.Guid.ToString().c_str()); + + if (!(charInfo.CustomizationFlag == CHAR_CUSTOMIZE_FLAG_CUSTOMIZE)) + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + stmt->setUInt16(0, uint16(AT_LOGIN_CUSTOMIZE)); + stmt->setUInt64(1, charInfo.Guid.GetCounter()); + CharacterDatabase.Execute(stmt); + charInfo.CustomizationFlag = 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); @@ -1385,6 +1408,9 @@ void WorldSession::HandleAlterAppearance(WorldPackets::Character::AlterApperance if (bs_skinColor && (bs_skinColor->Type != 3 || bs_skinColor->Race != _player->getRace() || bs_skinColor->Sex != _player->getGender())) return; + if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->getGender(), bs_hair->ID, packet.NewHairColor, uint8(_player->GetUInt32Value(PLAYER_FLAGS) >> 8), bs_facialHair->ID, bs_skinColor ? bs_skinColor->ID : 0)) + return; + GameObject* go = _player->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_BARBER_CHAIR, 5.0f); if (!go) { @@ -1477,8 +1503,17 @@ void WorldSession::HandleCharCustomizeCallback(PreparedQueryResult result, World Field* fields = result->Fetch(); std::string oldName = fields[0].GetString(); - uint16 atLoginFlags = fields[1].GetUInt16(); - uint32 playerBytes2 = fields[2].GetUInt32(); + uint8 plrRace = fields[1].GetUInt8(); + uint8 plrClass = fields[2].GetUInt8(); + uint8 plrGender = fields[3].GetUInt8(); + uint16 atLoginFlags = fields[4].GetUInt16(); + uint32 playerBytes2 = fields[5].GetUInt32(); + + if (!Player::ValidateAppearance(plrRace, plrClass, plrGender, customizeInfo->HairStyleID, customizeInfo->HairColorID, customizeInfo->FaceID, customizeInfo->FacialHairStyleID, customizeInfo->SkinID, true)) + { + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo); + return; + } if (!(atLoginFlags & AT_LOGIN_CUSTOMIZE)) { diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 352854636dc..cf871ab79a4 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -477,7 +477,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_GUID_NAME_BY_ACC, "SELECT guid, name FROM characters WHERE account = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO, "SELECT name, at_login, playerBytes2 FROM characters WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login, playerBytes2 FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, "SELECT at_login, knownTitles, playerBytes, playerBytes2 FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH); |