diff options
author | arks <ariel.silva305@gmail.com> | 2015-01-07 01:09:38 -0300 |
---|---|---|
committer | arks <ariel.silva305@gmail.com> | 2015-01-27 21:37:00 -0300 |
commit | 6d4c672fb1b799e1888881282e40992a9e15a006 (patch) | |
tree | e183aa96486ce99c672833f16ce69df4dd773d55 | |
parent | ccdc100d10143285bc058d968f0472ea4d073120 (diff) |
Implement CharSections.dbc and Serverside Checks.
-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 | 27 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCfmt.h | 1 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 97 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 1 | ||||
-rw-r--r-- | src/server/game/Handlers/CharacterHandler.cpp | 30 |
7 files changed, 172 insertions, 7 deletions
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 4dd38face72..3060c690cfc 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -48,6 +48,7 @@ struct WMOAreaTableTripple }; typedef std::map<WMOAreaTableTripple, WMOAreaTableEntry const*> WMOAreaInfoByTripple; +typedef std::multimap<uint32, CharSectionsEntry const*> CharSectionsMap; DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt); DBCStorage <AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt); @@ -67,6 +68,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); @@ -305,6 +308,12 @@ void LoadDBCStores(const std::string& dataPath) if (CharStartOutfitEntry const* outfit = sCharStartOutfitStore.LookupEntry(i)) sCharStartOutfitMap[outfit->Race | (outfit->Class << 8) | (outfit->Gender << 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"); LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc"); @@ -945,6 +954,18 @@ CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, ui return itr->second; } +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 655de86b7e1..d955e9581ab 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -69,6 +69,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); LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty); @@ -91,6 +92,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 1e6c8218489..252a6ba06cc 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -647,6 +647,33 @@ struct CharStartOutfitEntry //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 53-76 not required at server side }; +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 44db2a35d73..5c24e6f938a 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -33,6 +33,7 @@ char const BannedAddOnsfmt[] = "nxxxxxxxxxx"; char const BarberShopStyleEntryfmt[] = "nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; char const BattlemasterListEntryfmt[] = "niiiiiiiiixssssssssssssssssxiixx"; char const CharStartOutfitEntryfmt[] = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const CharSectionsEntryfmt[] = "diiixxxiii"; char const CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi"; char const ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; char const ChrClassesEntryfmt[] = "nxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 161fca432b4..5c2349fae21 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -966,8 +966,6 @@ bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo) { //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(guidlow, 0, HIGHGUID_PLAYER); @@ -1009,6 +1007,13 @@ bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo) return false; } + if (!ValidateAppearance(createInfo->Race, createInfo->Class, createInfo->Gender, createInfo->HairStyle, createInfo->HairColor, createInfo->Face, createInfo->FacialHair, 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; + } + uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16); SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24))); @@ -1981,12 +1986,29 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) *data << uint8(gender); // gender uint32 playerBytes = fields[5].GetUInt32(); + uint32 playerBytes2 = fields[6].GetUInt32(); + + uint16 atLoginFlags = fields[15].GetUInt16(); + + if (!ValidateAppearance(uint8(plrRace), uint8(plrClass), gender, uint8(playerBytes >> 16), uint8(playerBytes >> 24), uint8(playerBytes >> 8), uint8(playerBytes2), uint8(playerBytes))) + { + TC_LOG_ERROR("entities.player.loading", "Player %u has wrong Appearance values (Hair/Skin/Color), forcing recustomize", guid); + + 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; + } + } + *data << uint8(playerBytes); // skin *data << uint8(playerBytes >> 8); // face *data << uint8(playerBytes >> 16); // hair style *data << uint8(playerBytes >> 24); // hair color - uint32 playerBytes2 = fields[6].GetUInt32(); *data << uint8(playerBytes2 & 0xFF); // facial hair *data << uint8(fields[7].GetUInt8()); // level @@ -2001,7 +2023,6 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) uint32 charFlags = 0; uint32 playerFlags = fields[14].GetUInt32(); - uint16 atLoginFlags = fields[15].GetUInt16(); if (playerFlags & PLAYER_FLAGS_HIDE_HELM) charFlags |= CHARACTER_FLAG_HIDE_HELM; if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK) @@ -17280,6 +17301,20 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32()); SetByteValue(PLAYER_BYTES_3, 0, fields[5].GetUInt8()); SetByteValue(PLAYER_BYTES_3, 1, fields[49].GetUInt8()); + + 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; + } + SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32()); SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[48].GetUInt32()); @@ -26919,3 +26954,57 @@ void Player::SendSupercededSpell(uint32 oldSpell, uint32 newSpell) GetSession()->SendPacket(&data); } +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; +} diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 61141192cd1..870fa6160af 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1499,6 +1499,7 @@ class Player : public Unit, public GridObject<Player> static bool LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight, ObjectGuid guid); static bool IsValidGender(uint8 Gender) { return Gender <= GENDER_FEMALE; } + 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 ee8e051ecd9..23600e4aa68 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1254,6 +1254,9 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData) if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->getGender())) return; + if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->getGender(), bs_hair->hair_id, Color, uint8(_player->GetUInt32Value(PLAYER_FLAGS) >> 8), bs_facialHair->hair_id, bs_skinColor ? bs_skinColor->hair_id : 0)) + return; + GameObject* go = _player->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_BARBER_CHAIR, 5.0f); if (!go) { @@ -1338,10 +1341,8 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) >> customizeInfo.FacialHair >> customizeInfo.Face; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_AT_LOGIN); - + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME_DATA); stmt->setUInt32(0, customizeInfo.Guid.GetCounter()); - // TODO: Make async with callback PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) @@ -1351,6 +1352,29 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) } Field* fields = result->Fetch(); + uint8 plrRace = fields[0].GetUInt8(); + uint8 plrClass = fields[1].GetUInt8(); + uint8 plrGender = fields[2].GetUInt8(); + + if (!Player::ValidateAppearance(plrRace, plrClass, plrGender, customizeInfo.HairStyle, customizeInfo.HairColor, customizeInfo.Face, customizeInfo.FacialHair, customizeInfo.Skin, true)) + { + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo); + return; + } + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_AT_LOGIN); + + stmt->setUInt32(0, customizeInfo.Guid.GetCounter()); + // TODO: Make async with callback + result = CharacterDatabase.Query(stmt); + + if (!result) + { + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo); + return; + } + + fields = result->Fetch(); uint32 at_loginFlags = fields[0].GetUInt16(); if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE)) |