void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) { std::string name; uint8 race_, class_; recv_data >> name; recv_data >> race_; recv_data >> class_; WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases if(GetSecurity() == SEC_PLAYER) { if(uint32 mask = sWorld.getConfig(CONFIG_UINT32_CHARACTERS_CREATING_DISABLED)) { bool disabled = false; uint32 team = Player::TeamForRace(race_); switch(team) { case ALLIANCE: disabled = mask & (1 << 0); break; case HORDE: disabled = mask & (1 << 1); break; } if(disabled) { data << (uint8)CHAR_CREATE_DISABLED; SendPacket( &data ); return; } } } ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_); ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_); if( !classEntry || !raceEntry ) { data << (uint8)CHAR_CREATE_FAILED; SendPacket( &data ); sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_); return; } // prevent character creating Expansion race without Expansion account if (raceEntry->expansion > Expansion()) { data << (uint8)CHAR_CREATE_EXPANSION; sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, race_); SendPacket( &data ); return; } // prevent character creating Expansion class without Expansion account if (classEntry->expansion > Expansion()) { data << (uint8)CHAR_CREATE_EXPANSION_CLASS; sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, class_); SendPacket( &data ); return; } // prevent character creating with invalid name if (!normalizePlayerName(name)) { data << (uint8)CHAR_NAME_NO_NAME; SendPacket( &data ); sLog.outError("Account:[%d] but tried to Create character with empty [name]", GetAccountId()); return; } // check name limitations uint8 res = ObjectMgr::CheckPlayerName(name, true); if (res != CHAR_NAME_SUCCESS) { data << uint8(res); SendPacket( &data ); return; } if (GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(name)) { data << (uint8)CHAR_NAME_RESERVED; SendPacket( &data ); return; } if (sObjectMgr.GetPlayerGUIDByName(name)) { data << (uint8)CHAR_CREATE_NAME_IN_USE; SendPacket( &data ); return; } QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); if (resultacct) { Field *fields=resultacct->Fetch(); uint32 acctcharcount = fields[0].GetUInt32(); delete resultacct; if (acctcharcount >= sWorld.getConfig(CONFIG_UINT32_CHARACTERS_PER_ACCOUNT)) { data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT; SendPacket( &data ); return; } } QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId()); uint8 charcount = 0; if ( result ) { Field *fields = result->Fetch(); charcount = fields[0].GetUInt8(); delete result; if (charcount >= sWorld.getConfig(CONFIG_UINT32_CHARACTERS_PER_REALM)) { data << (uint8)CHAR_CREATE_SERVER_LIMIT; SendPacket( &data ); return; } } // speedup check for heroic class disabled case uint32 heroic_free_slots = sWorld.getConfig(CONFIG_UINT32_HEROIC_CHARACTERS_PER_REALM); if(heroic_free_slots == 0 && GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) { data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; SendPacket( &data ); return; } // speedup check for heroic class disabled case uint32 req_level_for_heroic = sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING); if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) { data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; SendPacket( &data ); return; } bool AllowTwoSideAccounts = sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; CinematicsSkipMode skipCinematics = CinematicsSkipMode(sWorld.getConfig(CONFIG_UINT32_SKIP_CINEMATICS)); bool have_same_race = false; // if 0 then allowed creating without any characters bool have_req_level_for_heroic = (req_level_for_heroic==0); if(!AllowTwoSideAccounts || skipCinematics == CINEMATICS_SKIP_SAME_RACE || class_ == CLASS_DEATH_KNIGHT) { QueryResult *result2 = CharacterDatabase.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s", GetAccountId(), (skipCinematics == CINEMATICS_SKIP_SAME_RACE || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1"); if(result2) { uint32 team_= Player::TeamForRace(race_); Field* field = result2->Fetch(); uint8 acc_race = field[1].GetUInt32(); if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) { uint8 acc_class = field[2].GetUInt32(); if(acc_class == CLASS_DEATH_KNIGHT) { if(heroic_free_slots > 0) --heroic_free_slots; if(heroic_free_slots == 0) { data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; SendPacket( &data ); delete result2; return; } } if(!have_req_level_for_heroic) { uint32 acc_level = field[0].GetUInt32(); if(acc_level >= req_level_for_heroic) have_req_level_for_heroic = true; } } // need to check team only for first character // TODO: what to if account already has characters of both races? if (!AllowTwoSideAccounts) { uint32 acc_team = 0; if(acc_race > 0) acc_team = Player::TeamForRace(acc_race); if(acc_team != team_) { data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; SendPacket( &data ); delete result2; return; } } // search same race for cinematic or same class if need // TODO: check if cinematic already shown? (already logged in?; cinematic field) while ((skipCinematics == CINEMATICS_SKIP_SAME_RACE && !have_same_race) || class_ == CLASS_DEATH_KNIGHT) { if(!result2->NextRow()) break; field = result2->Fetch(); acc_race = field[1].GetUInt32(); if(!have_same_race) have_same_race = race_ == acc_race; if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) { uint8 acc_class = field[2].GetUInt32(); if(acc_class == CLASS_DEATH_KNIGHT) { if(heroic_free_slots > 0) --heroic_free_slots; if(heroic_free_slots == 0) { data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; SendPacket( &data ); delete result2; return; } } if(!have_req_level_for_heroic) { uint32 acc_level = field[0].GetUInt32(); if(acc_level >= req_level_for_heroic) have_req_level_for_heroic = true; } } } delete result2; } } if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic) { data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; SendPacket( &data ); return; } // extract other data required for player creating uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; recv_data >> gender >> skin >> face; recv_data >> hairStyle >> hairColor >> facialHair >> outfitId; Player *pNewChar = new Player(this); if(!pNewChar->Create( sObjectMgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId )) { // Player not create (race/class problem?) delete pNewChar; data << (uint8)CHAR_CREATE_ERROR; SendPacket( &data ); return; } if ((have_same_race && skipCinematics == CINEMATICS_SKIP_SAME_RACE) || skipCinematics == CINEMATICS_SKIP_ALL) pNewChar->setCinematic(1); // not show intro pNewChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login // Player created, save it now pNewChar->SaveToDB(); charcount += 1; loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); delete pNewChar; // created only to call SaveToDB() data << (uint8)CHAR_CREATE_SUCCESS; SendPacket( &data ); std::string IP_str = GetRemoteAddress(); sLog.outBasic("Account: %d (IP: %s) Create Character:[%s]", GetAccountId(), IP_str.c_str(), name.c_str()); sLog.outChar("Account: %d (IP: %s) Create Character:[%s]", GetAccountId(), IP_str.c_str(), name.c_str()); }
void WorldSession::HandleCharCreateOpcode(WorldPacket& recv_data) { std::string name; uint8 race_, class_; recv_data >> name; recv_data >> race_; recv_data >> class_; // extract other data required for player creating uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; recv_data >> gender >> skin >> face; recv_data >> hairStyle >> hairColor >> facialHair >> outfitId; WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases if (GetSecurity() == SEC_PLAYER) { if (uint32 mask = sWorld.getConfig(CONFIG_UINT32_CHARACTERS_CREATING_DISABLED)) { bool disabled = false; Team team = Player::TeamForRace(race_); switch (team) { case ALLIANCE: disabled = (mask & (1 << 0)) != 0; break; case HORDE: disabled = (mask & (1 << 1)) != 0; break; default: break; } if (disabled) { data << (uint8)CHAR_CREATE_DISABLED; SendPacket(data, true); return; } } } ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_); ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_); if (!classEntry || !raceEntry) { data << (uint8)CHAR_CREATE_FAILED; SendPacket(data, true); sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_); return; } // prevent character creating with invalid name if (!normalizePlayerName(name)) { data << (uint8)CHAR_NAME_NO_NAME; SendPacket(data, true); sLog.outError("Account:[%d] but tried to Create character with empty [name]", GetAccountId()); return; } // check name limitations uint8 res = ObjectMgr::CheckPlayerName(name, true); if (res != CHAR_NAME_SUCCESS) { data << uint8(res); SendPacket(data, true); return; } if (GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(name)) { data << (uint8)CHAR_NAME_RESERVED; SendPacket(data, true); return; } if (sObjectMgr.GetPlayerGuidByName(name)) { data << (uint8)CHAR_CREATE_NAME_IN_USE; SendPacket(data, true); return; } QueryResult* resultacct = LoginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%u'", GetAccountId()); if (resultacct) { Field* fields = resultacct->Fetch(); uint32 acctcharcount = fields[0].GetUInt32(); delete resultacct; if (acctcharcount >= sWorld.getConfig(CONFIG_UINT32_CHARACTERS_PER_ACCOUNT)) { data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT; SendPacket(data, true); return; } } QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%u'", GetAccountId()); uint8 charcount = 0; if (result) { Field* fields = result->Fetch(); charcount = fields[0].GetUInt8(); delete result; if (charcount >= sWorld.getConfig(CONFIG_UINT32_CHARACTERS_PER_REALM)) { data << (uint8)CHAR_CREATE_SERVER_LIMIT; SendPacket(data, true); return; } } bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; CinematicsSkipMode skipCinematics = CinematicsSkipMode(sWorld.getConfig(CONFIG_UINT32_SKIP_CINEMATICS)); bool have_same_race = false; if (!AllowTwoSideAccounts || skipCinematics == CINEMATICS_SKIP_SAME_RACE) { QueryResult* result2 = CharacterDatabase.PQuery("SELECT race FROM characters WHERE account = '%u' %s", GetAccountId(), (skipCinematics == CINEMATICS_SKIP_SAME_RACE) ? "" : "LIMIT 1"); if (result2) { Team team_ = Player::TeamForRace(race_); Field* field = result2->Fetch(); uint8 acc_race = field[0].GetUInt32(); // need to check team only for first character // TODO: what to if account already has characters of both races? if (!AllowTwoSideAccounts) { if (acc_race == 0 || Player::TeamForRace(acc_race) != team_) { data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; SendPacket(data, true); delete result2; return; } } // search same race for cinematic or same class if need // TODO: check if cinematic already shown? (already logged in?; cinematic field) while (skipCinematics == CINEMATICS_SKIP_SAME_RACE && !have_same_race) { if (!result2->NextRow()) break; field = result2->Fetch(); acc_race = field[0].GetUInt32(); have_same_race = race_ == acc_race; } delete result2; } } Player* pNewChar = new Player(this); if (!pNewChar->Create(sObjectMgr.GeneratePlayerLowGuid(), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId)) { // Player not create (race/class problem?) delete pNewChar; data << (uint8)CHAR_CREATE_ERROR; SendPacket(data, true); return; } if ((have_same_race && skipCinematics == CINEMATICS_SKIP_SAME_RACE) || skipCinematics == CINEMATICS_SKIP_ALL) pNewChar->setCinematic(1); // not show intro pNewChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login // Player created, save it now pNewChar->SaveToDB(); charcount += 1; LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%u' AND realmid = '%u'", GetAccountId(), realmID); LoginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); data << (uint8)CHAR_CREATE_SUCCESS; SendPacket(data, true); const std::string& IP_str = GetRemoteAddress(); BASIC_LOG("Account: %d (IP: %s) Create Character:[%s] (guid: %u)", GetAccountId(), IP_str.c_str(), name.c_str(), pNewChar->GetGUIDLow()); sLog.outChar("Account: %d (IP: %s) Create Character:[%s] (guid: %u)", GetAccountId(), IP_str.c_str(), name.c_str(), pNewChar->GetGUIDLow()); delete pNewChar; // created only to call SaveToDB() }