diff options
Diffstat (limited to 'src')
12 files changed, 364 insertions, 213 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index bf5d03cf7c1..81ab2ac0f13 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -904,7 +904,7 @@ void Player::CleanupsBeforeDelete(bool finalCleanup) itr->second.save->RemovePlayer(this); } -bool Player::Create(uint32 guidlow, const std::string& name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 /*outfitId*/) +bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo) { //FIXME: outfitId not used in player creating // TODO: need more checks against packet modifications @@ -913,9 +913,9 @@ bool Player::Create(uint32 guidlow, const std::string& name, uint8 race, uint8 c Object::_Create(guidlow, 0, HIGHGUID_PLAYER); - m_name = name; + m_name = createInfo->Name; - PlayerInfo const* info = sObjectMgr->GetPlayerInfo(race, class_); + PlayerInfo const* info = sObjectMgr->GetPlayerInfo(createInfo->Race, createInfo->Class); if (!info) { sLog->outError("Player have incorrect race/class pair. Can't be loaded."); @@ -927,10 +927,10 @@ bool Player::Create(uint32 guidlow, const std::string& name, uint8 race, uint8 c Relocate(info->positionX, info->positionY, info->positionZ, info->orientation); - ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_); + ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class); if (!cEntry) { - sLog->outError("Class %u not found in DBC (Wrong DBC files?)", class_); + sLog->outError("Class %u not found in DBC (Wrong DBC files?)", createInfo->Class); return false; } @@ -941,15 +941,15 @@ bool Player::Create(uint32 guidlow, const std::string& name, uint8 race, uint8 c SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); - setFactionForRace(race); + setFactionForRace(createInfo->Race); - if (!IsValidGender(gender)) + if (!IsValidGender(createInfo->Gender)) { - sLog->outError("Player has invalid gender (%hu), can't be loaded.", gender); + sLog->outError("Player has invalid gender (%hu), can't be loaded.", createInfo->Gender); return false; } - uint32 RaceClassGender = (race) | (class_ << 8) | (gender << 16); + uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16); SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24))); InitDisplayIds(); @@ -965,9 +965,9 @@ bool Player::Create(uint32 guidlow, const std::string& name, uint8 race, uint8 c // -1 is default value SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); - SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24))); - SetUInt32Value(PLAYER_BYTES_2, (facialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24))); - SetByteValue(PLAYER_BYTES_3, 0, gender); + SetUInt32Value(PLAYER_BYTES, (createInfo->Skin | (createInfo->Face << 8) | (createInfo->HairStyle << 16) | (createInfo->HairColor << 24))); + SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24))); + SetByteValue(PLAYER_BYTES_3, 0, createInfo->Gender); SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1) SetUInt32Value(PLAYER_GUILDID, 0); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index a84af725be4..725c3fbbc37 100755 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1064,7 +1064,7 @@ class Player : public Unit, public GridObject<Player> } void SummonIfPossible(bool agree); - bool Create(uint32 guidlow, const std::string& name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId); + bool Create(uint32 guidlow, CharacterCreateInfo* createInfo); void Update(uint32 time); diff --git a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp index 0c3b0c24e1b..1dd18d9c2f1 100755 --- a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp @@ -231,7 +231,7 @@ void WorldSession::HandleCharEnumOpcode(WorldPacket & /*recv_data*/) CharacterDatabase.Execute(stmt); /// get all the data necessary for loading all characters (along with their pets) on the account - m_charEnumCallback = + _charEnumCallback = CharacterDatabase.AsyncPQuery( !sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED) ? // ------- Query Without Declined Names -------- @@ -381,42 +381,6 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket & recv_data) return; } - if (sObjectMgr->GetPlayerGUIDByName(name)) - { - data << (uint8)CHAR_CREATE_NAME_IN_USE; - SendPacket(&data); - return; - } - - QueryResult resultacct = LoginDatabase.PQuery("SELECT IFNULL(SUM(numchars), 0) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); - if (resultacct) - { - Field *fields = resultacct->Fetch(); - uint32 acctcharcount = fields[0].GetUInt32(); - - if (acctcharcount >= sWorld->getIntConfig(CONFIG_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(); - - if (charcount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) - { - data << (uint8)CHAR_CREATE_SERVER_LIMIT; - SendPacket(&data); - return; - } - } - // speedup check for heroic class disabled case uint32 heroic_free_slots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); if (heroic_free_slots == 0 && GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) @@ -435,155 +399,290 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket & recv_data) return; } - bool AllowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; - uint32 skipCinematics = sWorld->getIntConfig(CONFIG_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); + delete _charCreateCallback.GetParam(); // Delete existing if any, to make the callback chain reset to stage 0 + _charCreateCallback.SetParam(new CharacterCreateInfo(name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId, data)); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_GET_CHECK_NAME); + stmt->setString(0, name); + _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); +} - if (!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) +void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, CharacterCreateInfo* createInfo) +{ + /** This is a series of callbacks executed consecutively as a result from the database becomes available. + This is much more efficient than synchronous requests on packet handler, and much less DoS prone. + It also prevents data syncrhonisation errors. + */ + switch (createInfo->Stage) { - QueryResult result2 = CharacterDatabase.PQuery("SELECT level, race, class FROM characters WHERE account = '%u' %s", - GetAccountId(), (skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1"); - if (result2) + case 0: { - uint32 team_= Player::TeamForRace(race_); + if (result) + { + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_NAME_IN_USE); + SendPacket(&data); + delete createInfo; + _charCreateCallback.FreeResult(); + return; + } - Field* field = result2->Fetch(); - uint8 acc_race = field[1].GetUInt32(); + ASSERT(_charCreateCallback.GetParam() == createInfo); - 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; + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_SUM_REALMCHARS); + stmt->setUInt32(0, GetAccountId()); - if (heroic_free_slots == 0) - { - data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; - SendPacket(&data); - return; - } - } + _charCreateCallback.FreeResult(); + _charCreateCallback.SetFutureResult(LoginDatabase.AsyncQuery(stmt)); - 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; - } + createInfo->Stage++; + } + break; + case 1: + { + uint16 acctCharCount = 0; + if (result) + { + Field *fields = result->Fetch(); + // SELECT SUM(x) is MYSQL_TYPE_NEWDECIMAL - needs to be read as string + const char* ch = fields[0].GetCString(); + acctCharCount = atoi(ch); } - // need to check team only for first character - // TODO: what to if account already has characters of both races? - if (!AllowTwoSideAccounts) + if (acctCharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) { - uint32 acc_team=0; - if (acc_race > 0) - acc_team = Player::TeamForRace(acc_race); + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_ACCOUNT_LIMIT); + SendPacket(&data); + delete createInfo; + _charCreateCallback.FreeResult(); + return; + } + + + ASSERT(_charCreateCallback.GetParam() == createInfo); + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_GET_SUM_CHARS); + stmt->setUInt32(0, GetAccountId()); + + _charCreateCallback.FreeResult(); + _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); - if (acc_team != team_) + createInfo->Stage++; + } + break; + case 2: + { + if (result) + { + Field *fields = result->Fetch(); + createInfo->CharCount = fields[0].GetUInt8(); + + if (createInfo->CharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) { - data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_SERVER_LIMIT); SendPacket(&data); + delete createInfo; + _charCreateCallback.FreeResult(); return; } } - // search same race for cinematic or same class if need - // TODO: check if cinematic already shown? (already logged in?; cinematic field) - while ((skipCinematics == 1 && !have_same_race) || class_ == CLASS_DEATH_KNIGHT) + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; + uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); + uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); + + // if 0 then allowed creating without any characters + bool hasHeroicReqLevel = (heroicReqLevel == 0); + if (GetSecurity() == SEC_PLAYER && createInfo->Class == CLASS_DEATH_KNIGHT && !hasHeroicReqLevel) + { + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_LEVEL_REQUIREMENT); + SendPacket(&data); + delete createInfo; + _charCreateCallback.FreeResult(); + return; + } + + _charCreateCallback.FreeResult(); + + if (!allowTwoSideAccounts || skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT) { - if (!result2->NextRow()) - break; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_GET_CHAR_CREATE_INFO); + stmt->setUInt32(0, GetAccountId()); + stmt->setUInt32(1, (skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT) ? 10 : 1); + _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + createInfo->Stage++; + return; + } + + createInfo->Stage++; + HandleCharCreateCallback(PreparedQueryResult(NULL), createInfo); // Will jump to case 3 + } + break; + case 3: + { + bool haveSameRace = false; + uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); + bool hasHeroicReqLevel = (heroicReqLevel == 0); + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; + uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); - field = result2->Fetch(); - acc_race = field[1].GetUInt32(); + if (result) + { + uint32 team = Player::TeamForRace(createInfo->Race); + uint32 freeHeroicSlots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); - if (!have_same_race) - have_same_race = race_ == acc_race; + Field* field = result->Fetch(); + uint8 accRace = field[1].GetUInt32(); - if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) + if (GetSecurity() == SEC_PLAYER && createInfo->Class == CLASS_DEATH_KNIGHT) { - uint8 acc_class = field[2].GetUInt32(); - if (acc_class == CLASS_DEATH_KNIGHT) + uint8 accClass = field[2].GetUInt32(); + if (accClass == CLASS_DEATH_KNIGHT) { - if (heroic_free_slots > 0) - --heroic_free_slots; + if (freeHeroicSlots > 0) + --freeHeroicSlots; - if (heroic_free_slots == 0) + if (freeHeroicSlots == 0) { - data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_UNIQUE_CLASS_LIMIT); SendPacket(&data); + delete createInfo; return; } } - if (!have_req_level_for_heroic) + if (!hasHeroicReqLevel) + { + uint32 accLevel = field[0].GetUInt32(); + if (accLevel >= heroicReqLevel) + hasHeroicReqLevel = true; + } + } + + // need to check team only for first character + // TODO: what to if account already has characters of both races? + if (!allowTwoSideAccounts) + { + uint32 accTeam = 0; + if (accRace > 0) + accTeam = Player::TeamForRace(accRace); + + if (accTeam != team) + { + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_PVP_TEAMS_VIOLATION); + SendPacket(&data); + delete createInfo; + return; + } + } + + // search same race for cinematic or same class if need + // TODO: check if cinematic already shown? (already logged in?; cinematic field) + while ((skipCinematics == 1 && !haveSameRace) || createInfo->Class == CLASS_DEATH_KNIGHT) + { + if (!result->NextRow()) + break; + + field = result->Fetch(); + accRace = field[1].GetUInt32(); + + if (!haveSameRace) + haveSameRace = createInfo->Race == accRace; + + if (GetSecurity() == SEC_PLAYER && createInfo->Class == CLASS_DEATH_KNIGHT) { - uint32 acc_level = field[0].GetUInt32(); - if (acc_level >= req_level_for_heroic) - have_req_level_for_heroic = true; + uint8 acc_class = field[2].GetUInt32(); + if (acc_class == CLASS_DEATH_KNIGHT) + { + if (freeHeroicSlots > 0) + --freeHeroicSlots; + + if (freeHeroicSlots == 0) + { + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_UNIQUE_CLASS_LIMIT); + SendPacket(&data); + delete createInfo; + return; + } + } + + if (!hasHeroicReqLevel) + { + uint32 acc_level = field[0].GetUInt32(); + if (acc_level >= heroicReqLevel) + hasHeroicReqLevel = true; + } } } } - } - } - if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic) - { - data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; - SendPacket(&data); - return; - } + if (createInfo->Data.rpos() < createInfo->Data.wpos()) + { + uint8 unk; + createInfo->Data >> unk; + sLog->outDebug(LOG_FILTER_NETWORKIO, "Character creation %s (account %u) has unhandled tail data: [%u]", createInfo->Name.c_str(), GetAccountId(), unk); + } - if (recv_data.rpos() < recv_data.wpos()) - { - uint8 unk; - recv_data >> unk; - sLog->outDebug(LOG_FILTER_NETWORKIO, "Character creation %s (account %u) has unhandled tail data: [%u]", name.c_str(), GetAccountId(), unk); - } + Player* pNewChar = new Player(this); + if (!pNewChar->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_PLAYER), createInfo)) + { + // Player not create (race/class/etc problem?) + pNewChar->CleanupsBeforeDelete(); + delete pNewChar; - 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/etc problem?) - pNewChar->CleanupsBeforeDelete(); - delete pNewChar; + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_ERROR); + SendPacket(&data); + delete createInfo; + return; + } - data << (uint8)CHAR_CREATE_ERROR; - SendPacket(&data); + if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2) + pNewChar->setCinematic(1); // not show intro - return; - } + pNewChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login - if ((have_same_race && skipCinematics == 1) || skipCinematics == 2) - pNewChar->setCinematic(1); // not show intro + // Player created, save it now + pNewChar->SaveToDB(); + createInfo->CharCount += 1; - pNewChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login + SQLTransaction trans = LoginDatabase.BeginTransaction(); - // Player created, save it now - pNewChar->SaveToDB(); - charcount+=1; + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALMCHARACTERS); + stmt->setUInt32(0, GetAccountId()); + stmt->setUInt32(1, realmID); + trans->Append(stmt); - 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); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_ADD_REALMCHARACTERS); + stmt->setUInt32(0, createInfo->CharCount); + stmt->setUInt32(1, GetAccountId()); + stmt->setUInt32(2, realmID); + trans->Append(stmt); - pNewChar->CleanupsBeforeDelete(); + LoginDatabase.CommitTransaction(trans); - data << (uint8)CHAR_CREATE_SUCCESS; - SendPacket(&data); + pNewChar->CleanupsBeforeDelete(); - std::string IP_str = GetRemoteAddress(); - sLog->outDetail("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()); - sScriptMgr->OnPlayerCreate(pNewChar); - delete pNewChar; // created only to call SaveToDB() + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(CHAR_CREATE_SUCCESS); + SendPacket(&data); + std::string IP_str = GetRemoteAddress(); + sLog->outDetail("Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), IP_str.c_str(), createInfo->Name.c_str(), pNewChar->GetGUIDLow()); + sLog->outChar("Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), IP_str.c_str(), createInfo->Name.c_str(), pNewChar->GetGUIDLow()); + sScriptMgr->OnPlayerCreate(pNewChar); + + delete pNewChar; // created only to call SaveToDB() + delete createInfo; + _charCreateCallback.FreeResult(); + } + break; + } } void WorldSession::HandleCharDeleteOpcode(WorldPacket & recv_data) @@ -677,7 +776,7 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket & recv_data) return; } - m_charLoginCallback = CharacterDatabase.DelayQueryHolder((SQLQueryHolder*)holder); + _charLoginCallback = CharacterDatabase.DelayQueryHolder((SQLQueryHolder*)holder); } void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) @@ -1052,8 +1151,8 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) // make sure that the character belongs to the current account, that rename at login is enabled // and that there is no character with the desired new name - m_charRenameCallback.SetParam(newname); - m_charRenameCallback.SetFutureResult( + _charRenameCallback.SetParam(newname); + _charRenameCallback.SetFutureResult( CharacterDatabase.AsyncPQuery( "SELECT guid, name FROM characters WHERE guid = %d AND account = %d AND (at_login & %d) = %d AND NOT EXISTS (SELECT NULL FROM characters WHERE name = '%s')", GUID_LOPART(guid), GetAccountId(), AT_LOGIN_RENAME, AT_LOGIN_RENAME, escaped_newname.c_str() diff --git a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp index 20955902820..81f771fdada 100755 --- a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp @@ -558,8 +558,8 @@ void WorldSession::HandleAddFriendOpcode(WorldPacket & recv_data) sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: %s asked to add friend : '%s'", GetPlayer()->GetName(), friendName.c_str()); - m_addFriendCallback.SetParam(friendNote); - m_addFriendCallback.SetFutureResult( + _addFriendCallback.SetParam(friendNote); + _addFriendCallback.SetFutureResult( CharacterDatabase.AsyncPQuery("SELECT guid, race, account FROM characters WHERE name = '%s'", friendName.c_str()) ); } @@ -647,7 +647,7 @@ void WorldSession::HandleAddIgnoreOpcode(WorldPacket & recv_data) sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: %s asked to Ignore: '%s'", GetPlayer()->GetName(), IgnoreName.c_str()); - m_addIgnoreCallback = CharacterDatabase.AsyncPQuery("SELECT guid FROM characters WHERE name = '%s'", IgnoreName.c_str()); + _addIgnoreCallback = CharacterDatabase.AsyncPQuery("SELECT guid FROM characters WHERE name = '%s'", IgnoreName.c_str()); } void WorldSession::HandleAddIgnoreOpcodeCallBack(QueryResult result) diff --git a/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp b/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp index d46efb1ea56..cbe9896065a 100755 --- a/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp @@ -523,8 +523,8 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket & recv_data) void WorldSession::SendStablePet(uint64 guid) { - m_sendStabledPetCallback.SetParam(guid); - m_sendStabledPetCallback.SetFutureResult( + _sendStabledPetCallback.SetParam(guid); + _sendStabledPetCallback.SetFutureResult( CharacterDatabase.AsyncPQuery("SELECT owner, id, entry, level, name FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot", _player->GetGUIDLow(), PET_SAVE_FIRST_STABLE_SLOT, PET_SAVE_LAST_STABLE_SLOT) ); @@ -622,7 +622,7 @@ void WorldSession::HandleStablePet(WorldPacket & recv_data) return; } - m_stablePetCallback = CharacterDatabase.AsyncPQuery("SELECT owner, slot, id FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot ", + _stablePetCallback = CharacterDatabase.AsyncPQuery("SELECT owner, slot, id FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot ", _player->GetGUIDLow(), PET_SAVE_FIRST_STABLE_SLOT, PET_SAVE_LAST_STABLE_SLOT); } @@ -679,8 +679,8 @@ void WorldSession::HandleUnstablePet(WorldPacket & recv_data) if (GetPlayer()->HasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - m_unstablePetCallback.SetParam(petnumber); - m_unstablePetCallback.SetFutureResult( + _unstablePetCallback.SetParam(petnumber); + _unstablePetCallback.SetFutureResult( CharacterDatabase.AsyncPQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot >='%u' AND slot <= '%u'", _player->GetGUIDLow(), petnumber, PET_SAVE_FIRST_STABLE_SLOT, PET_SAVE_LAST_STABLE_SLOT) ); @@ -803,8 +803,8 @@ void WorldSession::HandleStableSwapPet(WorldPacket & recv_data) } // find swapped pet slot in stable - m_stableSwapCallback.SetParam(pet_number); - m_stableSwapCallback.SetFutureResult( + _stableSwapCallback.SetParam(pet_number); + _stableSwapCallback.SetFutureResult( CharacterDatabase.PQuery("SELECT slot, entry FROM character_pet WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet_number) ); diff --git a/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp b/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp index ea3b7a804ed..6f766057cce 100755 --- a/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp @@ -75,7 +75,7 @@ void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid) GUID_LOPART(guid) ); - m_nameQueryCallbacks.insert(lFutureResult); + _nameQueryCallbacks.insert(lFutureResult); // CharacterDatabase.AsyncPQuery(&WorldSession::SendNameQueryOpcodeFromDBCallBack, GetAccountId(), } diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 9883699e086..f689e0c3266 100755 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -945,11 +945,11 @@ void WorldSession::ProcessQueryCallbacks() QueryResult result; //! HandleNameQueryOpcode - while (!m_nameQueryCallbacks.is_empty()) + while (!_nameQueryCallbacks.is_empty()) { QueryResultFuture lResult; ACE_Time_Value timeout = ACE_Time_Value::zero; - if (m_nameQueryCallbacks.next_readable(lResult, &timeout) != 1) + if (_nameQueryCallbacks.next_readable(lResult, &timeout) != 1) break; if (lResult.ready()) @@ -961,80 +961,87 @@ void WorldSession::ProcessQueryCallbacks() } //! HandleCharEnumOpcode - if (m_charEnumCallback.ready()) + if (_charEnumCallback.ready()) { - m_charEnumCallback.get(result); + _charEnumCallback.get(result); HandleCharEnum(result); - m_charEnumCallback.cancel(); + _charEnumCallback.cancel(); } + if (_charCreateCallback.IsReady()) + { + PreparedQueryResult pResult; + _charCreateCallback.GetResult(pResult); + HandleCharCreateCallback(pResult, _charCreateCallback.GetParam()); + // Don't call FreeResult() here, the callback handler will do that depending on the events in the callback chain + } //! HandlePlayerLoginOpcode - if (m_charLoginCallback.ready()) + if (_charLoginCallback.ready()) { SQLQueryHolder* param; - m_charLoginCallback.get(param); + _charLoginCallback.get(param); HandlePlayerLogin((LoginQueryHolder*)param); - m_charLoginCallback.cancel(); + _charLoginCallback.cancel(); } //! HandleAddFriendOpcode - if (m_addFriendCallback.IsReady()) + if (_addFriendCallback.IsReady()) { - std::string param = m_addFriendCallback.GetParam(); - m_addFriendCallback.GetResult(result); + std::string param = _addFriendCallback.GetParam(); + _addFriendCallback.GetResult(result); HandleAddFriendOpcodeCallBack(result, param); - m_addFriendCallback.FreeResult(); + _addFriendCallback.FreeResult(); } //- HandleCharRenameOpcode - if (m_charRenameCallback.IsReady()) + if (_charRenameCallback.IsReady()) { - std::string param = m_charRenameCallback.GetParam(); - m_charRenameCallback.GetResult(result); + std::string param = _charRenameCallback.GetParam(); + _charRenameCallback.GetResult(result); HandleChangePlayerNameOpcodeCallBack(result, param); - m_charRenameCallback.FreeResult(); + _charRenameCallback.FreeResult(); } //- HandleCharAddIgnoreOpcode - if (m_addIgnoreCallback.ready()) + if (_addIgnoreCallback.ready()) { - m_addIgnoreCallback.get(result); + _addIgnoreCallback.get(result); HandleAddIgnoreOpcodeCallBack(result); - m_addIgnoreCallback.cancel(); + _addIgnoreCallback.cancel(); } //- SendStabledPet - if (m_sendStabledPetCallback.IsReady()) + if (_sendStabledPetCallback.IsReady()) { - uint64 param = m_sendStabledPetCallback.GetParam(); - m_sendStabledPetCallback.GetResult(result); + uint64 param = _sendStabledPetCallback.GetParam(); + _sendStabledPetCallback.GetResult(result); SendStablePetCallback(result, param); - m_sendStabledPetCallback.FreeResult(); + _sendStabledPetCallback.FreeResult(); } //- HandleStablePet - if (m_stablePetCallback.ready()) + if (_stablePetCallback.ready()) { - m_stablePetCallback.get(result); + _stablePetCallback.get(result); HandleStablePetCallback(result); - m_stablePetCallback.cancel(); + _stablePetCallback.cancel(); } //- HandleUnstablePet - if (m_unstablePetCallback.IsReady()) + if (_unstablePetCallback.IsReady()) { - uint32 param = m_unstablePetCallback.GetParam(); - m_unstablePetCallback.GetResult(result); + uint32 param = _unstablePetCallback.GetParam(); + _unstablePetCallback.GetResult(result); HandleUnstablePetCallback(result, param); - m_unstablePetCallback.FreeResult(); + _unstablePetCallback.FreeResult(); } //- HandleStableSwapPet - if (m_stableSwapCallback.IsReady()) + if (_stableSwapCallback.IsReady()) { - uint32 param = m_stableSwapCallback.GetParam(); - m_stableSwapCallback.GetResult(result); + uint32 param = _stableSwapCallback.GetParam(); + _stableSwapCallback.GetResult(result); HandleStableSwapPetCallback(result, param); - m_stableSwapCallback.FreeResult(); + _stableSwapCallback.FreeResult(); } } diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 97217786fe2..7e443d299c2 100755 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -44,7 +44,6 @@ class Quest; class WorldPacket; class WorldSocket; class LoginQueryHolder; -class CharacterHandler; class SpellCastTargets; struct AreaTableEntry; struct LfgJoinResultData; @@ -174,10 +173,45 @@ public: virtual bool Process(WorldPacket* packet); }; +// Proxy structure to contain data passed to callback function, +// only to prevent bloating the parameter list +class CharacterCreateInfo +{ + friend class WorldSession; + friend class Player; + + protected: + CharacterCreateInfo(std::string name, uint8 race, uint8 cclass, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId, + WorldPacket& data) : Name(name), Race(race), Class(cclass), Gender(gender), Skin(skin), Face(face), HairStyle(hairStyle), HairColor(hairColor), FacialHair(facialHair), + OutfitId(outfitId), Data(data), CharCount(0), Stage(0) + {} + + /// User specified variables + std::string Name; + uint8 Race; + uint8 Class; + uint8 Gender; + uint8 Skin; + uint8 Face; + uint8 HairStyle; + uint8 HairColor; + uint8 FacialHair; + uint8 OutfitId; + WorldPacket& Data; + + /// Server side data + uint8 CharCount; + + /// Internal + uint8 Stage; // Stage of the callback chain + + private: + virtual ~CharacterCreateInfo(){}; +}; + /// Player session in the World class WorldSession { - friend class CharacterHandler; public: WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter); ~WorldSession(); @@ -366,6 +400,7 @@ class WorldSession void HandleCharEnumOpcode(WorldPacket& recvPacket); void HandleCharDeleteOpcode(WorldPacket& recvPacket); void HandleCharCreateOpcode(WorldPacket& recvPacket); + void HandleCharCreateCallback(PreparedQueryResult result, CharacterCreateInfo* createInfo); void HandlePlayerLoginOpcode(WorldPacket& recvPacket); void HandleCharEnum(QueryResult result); void HandlePlayerLogin(LoginQueryHolder * holder); @@ -860,16 +895,17 @@ class WorldSession private: void ProcessQueryCallbacks(); - ACE_Future_Set<QueryResult> m_nameQueryCallbacks; - QueryResultFuture m_charEnumCallback; - QueryResultFuture m_addIgnoreCallback; - QueryResultFuture m_stablePetCallback; - QueryCallback<QueryResult, std::string> m_charRenameCallback; - QueryCallback<QueryResult, std::string> m_addFriendCallback; - QueryCallback<QueryResult, uint32> m_unstablePetCallback; - QueryCallback<QueryResult, uint32> m_stableSwapCallback; - QueryCallback<QueryResult, uint64> m_sendStabledPetCallback; - QueryResultHolderFuture m_charLoginCallback; + ACE_Future_Set<QueryResult> _nameQueryCallbacks; + QueryResultFuture _charEnumCallback; + QueryResultFuture _addIgnoreCallback; + QueryResultFuture _stablePetCallback; + QueryCallback<QueryResult, std::string> _charRenameCallback; + QueryCallback<QueryResult, std::string> _addFriendCallback; + QueryCallback<QueryResult, uint32> _unstablePetCallback; + QueryCallback<QueryResult, uint32> _stableSwapCallback; + QueryCallback<QueryResult, uint64> _sendStabledPetCallback; + QueryCallback<PreparedQueryResult, CharacterCreateInfo*> _charCreateCallback; + QueryResultHolderFuture _charLoginCallback; private: // private trade methods @@ -884,6 +920,7 @@ class WorldSession { return _allowedCharsToLogin.find(lowGUID) != _allowedCharsToLogin.end(); } + // this stores the GUIDs of the characters who can login // characters who failed on Player::BuildEnumData shouldn't login std::set<uint32> _allowedCharsToLogin; diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index af6cd018025..dddfbd293e2 100755 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -26,7 +26,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_ADD_QUEST_POOL_SAVE, "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES (?, ?)", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM, "DELETE FROM guild_bank_item WHERE guildid = ? AND TabId = ? AND SlotId = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_DEL_EXPIRED_BANS, "UPDATE character_banned SET active = 0 WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate <> bandate", CONNECTION_ASYNC) - PREPARE_STATEMENT(CHAR_GET_GUID_BY_NAME, "SELECT guid FROM characters WHERE name = ?", CONNECTION_SYNCH) + PREPARE_STATEMENT(CHAR_GET_GUID_BY_NAME, "SELECT guid FROM characters WHERE name = ?", CONNECTION_SYNCH); + PREPARE_STATEMENT(CHAR_GET_CHECK_NAME, "SELECT 1 FROM characters WHERE name = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_GET_SUM_CHARS, "SELECT COUNT(guid) FROM characters WHERE account = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_GET_CHAR_CREATE_INFO, "SELECT level, race, class FROM characters WHERE account = ? LIMIT 0,?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_ADD_BAN, "INSERT INTO character_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?, 1)", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_SET_NOT_BANNED, "UPDATE character_banned SET active = 0 WHERE guid = ? AND active != 0", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_GET_BANINFO, "SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate, banreason, bannedby FROM character_banned WHERE guid = ? ORDER BY bandate ASC", CONNECTION_SYNCH) diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 7a967f8bd11..dd752914138 100755 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -47,6 +47,9 @@ enum CharacterDatabaseStatements CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM, CHAR_DEL_EXPIRED_BANS, CHAR_GET_GUID_BY_NAME, + CHAR_GET_CHECK_NAME, + CHAR_GET_SUM_CHARS, + CHAR_GET_CHAR_CREATE_INFO, CHAR_ADD_BAN, CHAR_SET_NOT_BANNED, CHAR_GET_BANINFO, diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp index 5014535be1c..13cfe09914d 100755 --- a/src/server/shared/Database/Implementation/LoginDatabase.cpp +++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp @@ -44,4 +44,5 @@ void LoginDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(LOGIN_SET_ACCOUNT_NOT_BANNED, "UPDATE account_banned SET active = 0 WHERE id = ? AND active != 0", CONNECTION_ASYNC) PREPARE_STATEMENT(LOGIN_DEL_REALMCHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ? AND realmid = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(LOGIN_ADD_REALMCHARACTERS, "INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC) + PREPARE_STATEMENT(LOGIN_GET_SUM_REALMCHARS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); } diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h index cb7cb2b83d1..b5f927ffe28 100755 --- a/src/server/shared/Database/Implementation/LoginDatabase.h +++ b/src/server/shared/Database/Implementation/LoginDatabase.h @@ -64,6 +64,7 @@ enum LoginDatabaseStatements LOGIN_SET_ACCOUNT_NOT_BANNED, LOGIN_DEL_REALMCHARACTERS, LOGIN_ADD_REALMCHARACTERS, + LOGIN_GET_SUM_REALMCHARS, MAX_LOGINDATABASE_STATEMENTS, }; |