aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/server/game/Entities/Player/Player.cpp24
-rwxr-xr-xsrc/server/game/Entities/Player/Player.h2
-rwxr-xr-xsrc/server/game/Server/Protocol/Handlers/CharacterHandler.cpp383
-rwxr-xr-xsrc/server/game/Server/Protocol/Handlers/MiscHandler.cpp6
-rwxr-xr-xsrc/server/game/Server/Protocol/Handlers/NPCHandler.cpp14
-rwxr-xr-xsrc/server/game/Server/Protocol/Handlers/QueryHandler.cpp2
-rwxr-xr-xsrc/server/game/Server/WorldSession.cpp75
-rwxr-xr-xsrc/server/game/Server/WorldSession.h61
-rwxr-xr-xsrc/server/shared/Database/Implementation/CharacterDatabase.cpp5
-rwxr-xr-xsrc/server/shared/Database/Implementation/CharacterDatabase.h3
-rwxr-xr-xsrc/server/shared/Database/Implementation/LoginDatabase.cpp1
-rwxr-xr-xsrc/server/shared/Database/Implementation/LoginDatabase.h1
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,
};