Core/DBLayer: Make database interaction after create character packet completely asynchronous. This is more performant and fixes a DoS loophole and possible data desynchronisation caused by spamming this packet.
Closes #584 Closes #2089
This commit is contained in:
@@ -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);
|
||||
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));
|
||||
}
|
||||
|
||||
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 == 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_);
|
||||
|
||||
Field* field = result2->Fetch();
|
||||
uint8 acc_race = field[1].GetUInt32();
|
||||
|
||||
if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
|
||||
if (result)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
WorldPacket data(SMSG_CHAR_CREATE, 1);
|
||||
data << uint8(CHAR_CREATE_NAME_IN_USE);
|
||||
SendPacket(&data);
|
||||
delete createInfo;
|
||||
_charCreateCallback.FreeResult();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
ASSERT(_charCreateCallback.GetParam() == createInfo);
|
||||
|
||||
if (acc_team != team_)
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_SUM_REALMCHARS);
|
||||
stmt->setUInt32(0, GetAccountId());
|
||||
|
||||
_charCreateCallback.FreeResult();
|
||||
_charCreateCallback.SetFutureResult(LoginDatabase.AsyncQuery(stmt));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (acctCharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT))
|
||||
{
|
||||
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));
|
||||
|
||||
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)
|
||||
{
|
||||
if (!result2->NextRow())
|
||||
break;
|
||||
WorldPacket data(SMSG_CHAR_CREATE, 1);
|
||||
data << uint8(CHAR_CREATE_LEVEL_REQUIREMENT);
|
||||
SendPacket(&data);
|
||||
delete createInfo;
|
||||
_charCreateCallback.FreeResult();
|
||||
return;
|
||||
}
|
||||
|
||||
field = result2->Fetch();
|
||||
acc_race = field[1].GetUInt32();
|
||||
_charCreateCallback.FreeResult();
|
||||
|
||||
if (!have_same_race)
|
||||
have_same_race = race_ == acc_race;
|
||||
if (!allowTwoSideAccounts || skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
|
||||
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);
|
||||
|
||||
if (result)
|
||||
{
|
||||
uint32 team = Player::TeamForRace(createInfo->Race);
|
||||
uint32 freeHeroicSlots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM);
|
||||
|
||||
Field* field = result->Fetch();
|
||||
uint8 accRace = field[1].GetUInt32();
|
||||
|
||||
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 acc_level = field[0].GetUInt32();
|
||||
if (acc_level >= req_level_for_heroic)
|
||||
have_req_level_for_heroic = true;
|
||||
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)
|
||||
{
|
||||
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 (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);
|
||||
}
|
||||
|
||||
Player* pNewChar = new Player(this);
|
||||
if (!pNewChar->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_PLAYER), createInfo))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2)
|
||||
pNewChar->setCinematic(1); // not show intro
|
||||
|
||||
pNewChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login
|
||||
|
||||
// Player created, save it now
|
||||
pNewChar->SaveToDB();
|
||||
createInfo->CharCount += 1;
|
||||
|
||||
SQLTransaction trans = LoginDatabase.BeginTransaction();
|
||||
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALMCHARACTERS);
|
||||
stmt->setUInt32(0, GetAccountId());
|
||||
stmt->setUInt32(1, realmID);
|
||||
trans->Append(stmt);
|
||||
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_ADD_REALMCHARACTERS);
|
||||
stmt->setUInt32(0, createInfo->CharCount);
|
||||
stmt->setUInt32(1, GetAccountId());
|
||||
stmt->setUInt32(2, realmID);
|
||||
trans->Append(stmt);
|
||||
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
|
||||
pNewChar->CleanupsBeforeDelete();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic)
|
||||
{
|
||||
data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT;
|
||||
SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
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), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId))
|
||||
{
|
||||
// Player not create (race/class/etc problem?)
|
||||
pNewChar->CleanupsBeforeDelete();
|
||||
delete pNewChar;
|
||||
|
||||
data << (uint8)CHAR_CREATE_ERROR;
|
||||
SendPacket(&data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((have_same_race && skipCinematics == 1) || skipCinematics == 2)
|
||||
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);
|
||||
|
||||
pNewChar->CleanupsBeforeDelete();
|
||||
|
||||
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(), 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()
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -75,7 +75,7 @@ void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid)
|
||||
GUID_LOPART(guid)
|
||||
);
|
||||
|
||||
m_nameQueryCallbacks.insert(lFutureResult);
|
||||
_nameQueryCallbacks.insert(lFutureResult);
|
||||
|
||||
// CharacterDatabase.AsyncPQuery(&WorldSession::SendNameQueryOpcodeFromDBCallBack, GetAccountId(),
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user