diff options
Diffstat (limited to 'src')
28 files changed, 956 insertions, 296 deletions
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index ac3625ca3f4..8572d23bf64 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -359,17 +359,17 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const bool EnablePortals = false; bool PlayHoverAnim = false; bool IsSuppressingGreetings = false; - bool HasMovementUpdate = flags & UPDATEFLAG_LIVING ? true : false; - bool HasMovementTransport = flags & UPDATEFLAG_TRANSPORT_POSITION ? true : false; - bool Stationary = flags & UPDATEFLAG_STATIONARY_POSITION ? true : false; - bool CombatVictim = flags & UPDATEFLAG_HAS_TARGET ? true : false; - bool ServerTime = flags & UPDATEFLAG_TRANSPORT ? true : false; - bool VehicleCreate = flags & UPDATEFLAG_VEHICLE ? true : false; - bool AnimKitCreate = flags & UPDATEFLAG_ANIMKITS ? true : false; - bool Rotation = flags & UPDATEFLAG_ROTATION ? true : false; + bool HasMovementUpdate = (flags & UPDATEFLAG_LIVING) != 0; + bool HasMovementTransport = (flags & UPDATEFLAG_TRANSPORT_POSITION) != 0; + bool Stationary = (flags & UPDATEFLAG_STATIONARY_POSITION) != 0; + bool CombatVictim = (flags & UPDATEFLAG_HAS_TARGET) != 0; + bool ServerTime = (flags & UPDATEFLAG_TRANSPORT) != 0; + bool VehicleCreate = (flags & UPDATEFLAG_VEHICLE) != 0; + bool AnimKitCreate = (flags & UPDATEFLAG_ANIMKITS) != 0; + bool Rotation = (flags & UPDATEFLAG_ROTATION) != 0; bool HasAreaTrigger = false; bool HasGameObject = false; - bool ThisIsYou = flags & UPDATEFLAG_SELF ? true : false; + bool ThisIsYou = (flags & UPDATEFLAG_SELF) != 0; bool ReplaceActive = false; bool SceneObjCreate = false; bool ScenePendingInstances = false; @@ -379,7 +379,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const PauseTimesCount = go->GetGOValue()->Transport.StopFrames->size(); data->WriteBit(NoBirthAnim); - data->WriteBit(EnablePortals); + data->WriteBit(EnablePortals); data->WriteBit(PlayHoverAnim); data->WriteBit(IsSuppressingGreetings); data->WriteBit(HasMovementUpdate); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index e45f7c896b9..be2563ea0ee 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -31,6 +31,7 @@ #include "Channel.h" #include "ChannelMgr.h" #include "CharacterDatabaseCleaner.h" +#include "CharacterPackets.h" #include "Chat.h" #include "Common.h" #include "ConditionMgr.h" @@ -930,7 +931,7 @@ void Player::CleanupsBeforeDelete(bool finalCleanup) itr->second.save->RemovePlayer(this); } -bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo) +bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::CharacterCreateInfo const* createInfo) { //FIXME: outfitId not used in player creating /// @todo need more checks against packet modifications @@ -970,14 +971,14 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo setFactionForRace(createInfo->Race); - if (!IsValidGender(createInfo->Gender)) + if (!IsValidGender(createInfo->Sex)) { TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid gender (%u) - refusing to do so", - GetSession()->GetAccountId(), m_name.c_str(), createInfo->Gender); + GetSession()->GetAccountId(), m_name.c_str(), createInfo->Sex); return false; } - uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16); + uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Sex << 16); SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24))); InitDisplayIds(); @@ -993,11 +994,11 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); // -1 is default value SetUInt32Value(PLAYER_BYTES, (createInfo->Skin | (createInfo->Face << 8) | (createInfo->HairStyle << 16) | (createInfo->HairColor << 24))); - SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHair | + SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHairStyle | (0x00 << 8) | (0x00 << 16) | (((GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED) << 24))); - SetByteValue(PLAYER_BYTES_3, 0, createInfo->Gender); + SetByteValue(PLAYER_BYTES_3, 0, createInfo->Sex); SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1) SetGuidValue(OBJECT_FIELD_DATA, ObjectGuid::Empty); @@ -1121,7 +1122,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo addActionButton(action_itr->button, action_itr->action, action_itr->type); // original items - if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(createInfo->Race, createInfo->Class, createInfo->Gender)) + if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(createInfo->Race, createInfo->Class, createInfo->Sex)) { for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j) { @@ -20242,7 +20243,7 @@ void Player::SetUInt32ValueInArray(Tokenizer& Tokenizer, uint16 index, uint32 va Tokenizer[index] = buf; } -void Player::Customize(CharacterCustomizeInfo const* customizeInfo, SQLTransaction& trans) +void Player::Customize(WorldPackets::Character::CharacterCustomizeInfo const* customizeInfo, SQLTransaction& trans) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PLAYERBYTES2); stmt->setUInt64(0, customizeInfo->Guid.GetCounter()); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 74222bd162f..8509ec2bcf1 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -44,7 +44,6 @@ struct VendorItem; template<class T> class AchievementMgr; class ReputationMgr; class Channel; -class CharacterCreateInfo; class Creature; class DynamicObject; class Group; @@ -56,8 +55,6 @@ class PlayerSocial; class SpellCastTargets; class UpdateMask; -struct CharacterCustomizeInfo; - typedef std::deque<Mail*> PlayerMails; #define PLAYER_MAX_SKILLS 128 @@ -1289,7 +1286,7 @@ class Player : public Unit, public GridObject<Player> void SetSummonPoint(uint32 mapid, float x, float y, float z); void SummonIfPossible(bool agree); - bool Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo); + bool Create(ObjectGuid::LowType guidlow, WorldPackets::Character::CharacterCreateInfo const* createInfo); void Update(uint32 time) override; @@ -1711,7 +1708,7 @@ class Player : public Unit, public GridObject<Player> static void SetUInt32ValueInArray(Tokenizer& data, uint16 index, uint32 value); static void SetFloatValueInArray(Tokenizer& data, uint16 index, float value); - static void Customize(CharacterCustomizeInfo const* customizeInfo, SQLTransaction& trans); + static void Customize(WorldPackets::Character::CharacterCustomizeInfo const* customizeInfo, SQLTransaction& trans); static void SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, SQLTransaction& trans); static void DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRealmChars = true, bool deleteFinally = false); diff --git a/src/server/game/Handlers/AuthHandler.cpp b/src/server/game/Handlers/AuthHandler.cpp index 9b14a8eef81..8cde824c26e 100644 --- a/src/server/game/Handlers/AuthHandler.cpp +++ b/src/server/game/Handlers/AuthHandler.cpp @@ -20,6 +20,7 @@ #include "WorldSession.h" #include "WorldPacket.h" #include "AuthenticationPackets.h" +#include "SystemPackets.h" void WorldSession::SendAuthResponse(uint8 code, bool queued, uint32 queuePos) { @@ -64,7 +65,7 @@ void WorldSession::SendAuthWaitQue(uint32 position) response.WaitInfo.value.WaitCount = position; response.Result = AUTH_WAIT_QUEUE; } - + response.Write(); SendPacket(&response.GetWorldPacket()); } @@ -75,3 +76,26 @@ void WorldSession::SendClientCacheVersion(uint32 version) data << uint32(version); SendPacket(&data); } + +void WorldSession::SendSetTimeZoneInformation() +{ + /// @todo: replace dummy values + WorldPackets::System::SetTimeZoneInformation packet; + packet.ServerTimeTZ = "Europe/Paris"; + packet.GameTimeTZ = "Europe/Paris"; + + packet.Write(); + SendPacket(&packet.GetWorldPacket()); +} + +void WorldSession::SendFeatureSystemStatusGlueScreen() +{ + WorldPackets::System::FeatureSystemStatusGlueScreen features; + features.BpayStoreAvailable = false; + features.BpayStoreDisabledByParentalControls = false; + features.CharUndeleteEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED); + features.BpayStoreEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_BPAY_STORE_ENABLED); + + features.Write(); + SendPacket(&features.GetWorldPacket()); +} diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index e5f537426cf..5104be43e59 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -149,7 +149,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) { WorldPacket data; sBattlegroundMgr->BuildStatusFailedPacket(&data, bg, _player, 0, ERR_LFG_CANT_USE_BATTLEGROUND); - GetPlayer()->GetSession()->SendPacket(&data); + SendPacket(&data); return; } @@ -158,7 +158,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) { WorldPacket data; sBattlegroundMgr->BuildStatusFailedPacket(&data, bg, _player, 0, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); - _player->GetSession()->SendPacket(&data); + SendPacket(&data); return; } @@ -167,7 +167,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) // player is already in random queue WorldPacket data; sBattlegroundMgr->BuildStatusFailedPacket(&data, bg, _player, 0, ERR_IN_RANDOM_BG); - _player->GetSession()->SendPacket(&data); + SendPacket(&data); return; } @@ -176,7 +176,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) // player is already in queue, can't start random queue WorldPacket data; sBattlegroundMgr->BuildStatusFailedPacket(&data, bg, _player, 0, ERR_IN_NON_RANDOM_BG); - _player->GetSession()->SendPacket(&data); + SendPacket(&data); return; } @@ -190,7 +190,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) { WorldPacket data; sBattlegroundMgr->BuildStatusFailedPacket(&data, bg, _player, 0, ERR_BATTLEGROUND_TOO_MANY_QUEUES); - _player->GetSession()->SendPacket(&data); + SendPacket(&data); return; } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index c5d5f5540d5..8593b4352c3 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -227,10 +227,10 @@ void WorldSession::HandleCharEnum(PreparedQueryResult result) charEnum.Success = true; charEnum.IsDeletedCharacters = false; + _legitCharacters.clear(); + if (result) { - _legitCharacters.clear(); - do { Field* fields = result->Fetch(); @@ -270,39 +270,59 @@ void WorldSession::HandleCharEnumOpcode(WorldPacket& /*recvData*/) stmt->setUInt8(0, PET_SAVE_AS_CURRENT); stmt->setUInt32(1, GetAccountId()); - _charEnumCallback = CharacterDatabase.AsyncQuery(stmt); + _charEnumCallback.SetParam(false); + _charEnumCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); } -void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) +void WorldSession::HandleCharUndeleteEnum(PreparedQueryResult result) { - CharacterCreateInfo createInfo; + WorldPackets::Character::CharEnumResult charEnum; + charEnum.Success = true; + charEnum.IsDeletedCharacters = true; - uint32 nameLength = recvData.ReadBits(6); - bool hasTempalte = recvData.ReadBit(); + if (result) + { + do + { + Field* fields = result->Fetch(); + WorldPackets::Character::CharEnumResult::CharacterInfo charInfo(fields); - recvData >> createInfo.Race - >> createInfo.Class - >> createInfo.Gender - >> createInfo.Skin - >> createInfo.Face - >> createInfo.HairStyle - >> createInfo.HairColor - >> createInfo.FacialHair - >> createInfo.OutfitId; + TC_LOG_INFO("network", "Loading undeleted char guid %s from account %u.", charInfo.Guid.ToString().c_str(), GetAccountId()); - createInfo.Name = recvData.ReadString(nameLength); + charEnum.Characters.emplace_back(charInfo); + } + while (result->NextRow()); + } - if (hasTempalte) - recvData.read_skip<uint32>(); // Template from SMSG_AUTH_RESPONSE + charEnum.Write(); + SendPacket(&charEnum.GetWorldPacket()); +} +void WorldSession::HandleCharUndeleteEnumOpcode(WorldPacket& /*recvData*/) +{ + /// get all the data necessary for loading all undeleted characters (along with their pets) on the account + PreparedStatement* stmt = nullptr; + if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_UNDELETE_ENUM_DECLINED_NAME); + else + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_UNDELETE_ENUM); + stmt->setUInt8(0, PET_SAVE_AS_CURRENT); + stmt->setUInt32(1, GetAccountId()); + + _charEnumCallback.SetParam(true); + _charEnumCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); +} + +void WorldSession::HandleCharCreateOpcode(WorldPackets::Character::CharacterCreate& charCreate) +{ if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_TEAMMASK)) { if (uint32 mask = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED)) { bool disabled = false; - switch (Player::TeamForRace(createInfo.Race)) + switch (Player::TeamForRace(charCreate.CreateInfo->Race)) { case ALLIANCE: disabled = (mask & (1 << 0)) != 0; @@ -320,18 +340,18 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } } - ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(createInfo.Class); + ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(charCreate.CreateInfo->Class); if (!classEntry) { - TC_LOG_ERROR("network", "Class (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo.Class, GetAccountId()); + TC_LOG_ERROR("network", "Class (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", charCreate.CreateInfo->Class, GetAccountId()); SendCharCreate(CHAR_CREATE_FAILED); return; } - ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(createInfo.Race); + ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(charCreate.CreateInfo->Race); if (!raceEntry) { - TC_LOG_ERROR("network", "Race (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo.Race, GetAccountId()); + TC_LOG_ERROR("network", "Race (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", charCreate.CreateInfo->Race, GetAccountId()); SendCharCreate(CHAR_CREATE_FAILED); return; } @@ -339,7 +359,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) // prevent character creating Expansion race without Expansion account if (raceEntry->expansion > Expansion()) { - TC_LOG_ERROR("network", "Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, createInfo.Race); + TC_LOG_ERROR("network", "Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, charCreate.CreateInfo->Race); SendCharCreate(CHAR_CREATE_EXPANSION); return; } @@ -347,7 +367,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) // prevent character creating Expansion class without Expansion account if (classEntry->expansion > Expansion()) { - TC_LOG_ERROR("network", "Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, createInfo.Class); + TC_LOG_ERROR("network", "Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, charCreate.CreateInfo->Class); SendCharCreate(CHAR_CREATE_EXPANSION_CLASS); return; } @@ -355,7 +375,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RACEMASK)) { uint32 raceMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK); - if ((1 << (createInfo.Race - 1)) & raceMaskDisabled) + if ((1 << (charCreate.CreateInfo->Race - 1)) & raceMaskDisabled) { SendCharCreate(CHAR_CREATE_DISABLED); return; @@ -365,7 +385,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_CLASSMASK)) { uint32 classMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK); - if ((1 << (createInfo.Class - 1)) & classMaskDisabled) + if ((1 << (charCreate.CreateInfo->Class - 1)) & classMaskDisabled) { SendCharCreate(CHAR_CREATE_DISABLED); return; @@ -373,7 +393,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } // prevent character creating with invalid name - if (!normalizePlayerName(createInfo.Name)) + if (!normalizePlayerName(charCreate.CreateInfo->Name)) { TC_LOG_ERROR("network", "Account:[%d] but tried to Create character with empty [name] ", GetAccountId()); SendCharCreate(CHAR_NAME_NO_NAME); @@ -381,32 +401,30 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } // check name limitations - ResponseCodes res = ObjectMgr::CheckPlayerName(createInfo.Name, true); + ResponseCodes res = ObjectMgr::CheckPlayerName(charCreate.CreateInfo->Name, true); if (res != CHAR_NAME_SUCCESS) { SendCharCreate(res); return; } - if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(createInfo.Name)) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(charCreate.CreateInfo->Name)) { SendCharCreate(CHAR_NAME_RESERVED); return; } - if (createInfo.Class == CLASS_DEATH_KNIGHT && !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_HEROIC_CHARACTER)) + if (charCreate.CreateInfo->Class == CLASS_DEATH_KNIGHT && !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_HEROIC_CHARACTER)) { // speedup check for heroic class disabled case - uint32 heroic_free_slots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); - if (heroic_free_slots == 0) + if (!sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM)) { SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); return; } // speedup check for heroic class disabled case - uint32 req_level_for_heroic = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); - if (req_level_for_heroic > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) + if (sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER) > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT); return; @@ -414,18 +432,17 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); - stmt->setString(0, createInfo.Name); + stmt->setString(0, charCreate.CreateInfo->Name); - delete _charCreateCallback.GetParam(); // Delete existing if any, to make the callback chain reset to stage 0 - _charCreateCallback.SetParam(new CharacterCreateInfo(std::move(createInfo))); + _charCreateCallback.SetParam(charCreate.CreateInfo); _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); } -void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, CharacterCreateInfo* createInfo) +void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, WorldPackets::Character::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. + It also prevents data synchronisation errors. */ switch (_charCreateCallback.GetStage()) { @@ -434,12 +451,11 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (result) { SendCharCreate(CHAR_CREATE_NAME_IN_USE); - delete createInfo; _charCreateCallback.Reset(); return; } - ASSERT(_charCreateCallback.GetParam() == createInfo); + ASSERT(_charCreateCallback.GetParam().get() == createInfo); PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SUM_REALM_CHARACTERS); stmt->setUInt32(0, GetAccountId()); @@ -464,12 +480,11 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (acctCharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) { SendCharCreate(CHAR_CREATE_ACCOUNT_LIMIT); - delete createInfo; _charCreateCallback.Reset(); return; } - ASSERT(_charCreateCallback.GetParam() == createInfo); + ASSERT(_charCreateCallback.GetParam().get() == createInfo); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); stmt->setUInt32(0, GetAccountId()); @@ -489,7 +504,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (createInfo->CharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) { SendCharCreate(CHAR_CREATE_SERVER_LIMIT); - delete createInfo; _charCreateCallback.Reset(); return; } @@ -542,7 +556,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (freeHeroicSlots == 0) { SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); - delete createInfo; _charCreateCallback.Reset(); return; } @@ -567,7 +580,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (accTeam != team) { SendCharCreate(CHAR_CREATE_PVP_TEAMS_VIOLATION); - delete createInfo; _charCreateCallback.Reset(); return; } @@ -597,7 +609,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (freeHeroicSlots == 0) { SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); - delete createInfo; _charCreateCallback.Reset(); return; } @@ -616,7 +627,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (checkHeroicReqs && !hasHeroicReqLevel) { SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT); - delete createInfo; _charCreateCallback.Reset(); return; } @@ -629,7 +639,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte newChar.CleanupsBeforeDelete(); SendCharCreate(CHAR_CREATE_ERROR); - delete createInfo; _charCreateCallback.Reset(); return; } @@ -665,50 +674,46 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte sWorld->AddCharacterNameData(newChar.GetGUID(), newChar.GetName(), newChar.getGender(), newChar.getRace(), newChar.getClass(), newChar.getLevel()); newChar.CleanupsBeforeDelete(); - delete createInfo; _charCreateCallback.Reset(); break; } } } -void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) +void WorldSession::HandleCharDeleteOpcode(WorldPackets::Character::CharacterDelete& charDelete) { - ObjectGuid guid; - recvData >> guid; - // Initiating uint32 initAccountId = GetAccountId(); // can't delete loaded character - if (ObjectAccessor::FindPlayer(guid)) + if (ObjectAccessor::FindPlayer(charDelete.Guid)) { - sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); + sScriptMgr->OnPlayerFailedDelete(charDelete.Guid, initAccountId); return; } - uint32 accountId = 0; - uint8 level = 0; - std::string name; - // is guild leader - if (sGuildMgr->GetGuildByLeader(guid)) + if (sGuildMgr->GetGuildByLeader(charDelete.Guid)) { - sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); + sScriptMgr->OnPlayerFailedDelete(charDelete.Guid, initAccountId); SendCharDelete(CHAR_DELETE_FAILED_GUILD_LEADER); return; } // is arena team captain - if (sArenaTeamMgr->GetArenaTeamByCaptain(guid)) + if (sArenaTeamMgr->GetArenaTeamByCaptain(charDelete.Guid)) { - sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); + sScriptMgr->OnPlayerFailedDelete(charDelete.Guid, initAccountId); SendCharDelete(CHAR_DELETE_FAILED_ARENA_CAPTAIN); return; } + uint32 accountId = 0; + uint8 level = 0; + std::string name; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DATA_BY_GUID); - stmt->setUInt64(0, guid.GetCounter()); + stmt->setUInt64(0, charDelete.Guid.GetCounter()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { @@ -721,26 +726,26 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) // prevent deleting other players' characters using cheating tools if (accountId != initAccountId) { - sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); + sScriptMgr->OnPlayerFailedDelete(charDelete.Guid, initAccountId); return; } - TC_LOG_INFO("entities.player.character", "Account: %u, IP: %s deleted character: %s, %s, Level: %u", accountId, GetRemoteAddress().c_str(), name.c_str(), guid.ToString().c_str(), level); + TC_LOG_INFO("entities.player.character", "Account: %u, IP: %s deleted character: %s, %s, Level: %u", accountId, GetRemoteAddress().c_str(), name.c_str(), charDelete.Guid.ToString().c_str(), level); // To prevent hook failure, place hook before removing reference from DB - sScriptMgr->OnPlayerDelete(guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete. + sScriptMgr->OnPlayerDelete(charDelete.Guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete. // Shouldn't interfere with character deletion though if (sLog->ShouldLog("entities.player.dump", LOG_LEVEL_INFO)) // optimize GetPlayerDump call { std::string dump; - if (PlayerDumpWriter().GetDump(guid.GetCounter(), dump)) - sLog->outCharDump(dump.c_str(), accountId, guid.GetCounter(), name.c_str()); + if (PlayerDumpWriter().GetDump(charDelete.Guid.GetCounter(), dump)) + sLog->outCharDump(dump.c_str(), accountId, charDelete.Guid.GetCounter(), name.c_str()); } - sGuildFinderMgr->RemoveAllMembershipRequestsFromPlayer(guid); - sCalendarMgr->RemoveAllPlayerEventsAndInvites(guid); - Player::DeleteFromDB(guid, accountId); + sGuildFinderMgr->RemoveAllMembershipRequestsFromPlayer(charDelete.Guid); + sCalendarMgr->RemoveAllPlayerEventsAndInvites(charDelete.Guid); + Player::DeleteFromDB(charDelete.Guid, accountId); SendCharDelete(CHAR_DELETE_SUCCESS); } @@ -1156,7 +1161,7 @@ void WorldSession::HandleShowingCloakOpcode(WorldPacket& recvData) void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) { - CharacterRenameInfo renameInfo; + WorldPackets::Character::CharacterRenameInfo renameInfo; recvData >> renameInfo.Guid >> renameInfo.Name; @@ -1193,11 +1198,11 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) stmt->setString(4, renameInfo.Name); delete _charRenameCallback.GetParam(); - _charRenameCallback.SetParam(new CharacterRenameInfo(std::move(renameInfo))); + _charRenameCallback.SetParam(new WorldPackets::Character::CharacterRenameInfo(std::move(renameInfo))); _charRenameCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); } -void WorldSession::HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult result, CharacterRenameInfo const* renameInfo) +void WorldSession::HandleCharRenameCallBack(PreparedQueryResult result, WorldPackets::Character::CharacterRenameInfo const* renameInfo) { if (!result) { @@ -1394,7 +1399,7 @@ void WorldSession::HandleRemoveGlyph(WorldPacket& recvData) void WorldSession::HandleCharCustomize(WorldPacket& recvData) { - CharacterCustomizeInfo customizeInfo; + WorldPackets::Character::CharacterCustomizeInfo customizeInfo; recvData >> customizeInfo.Guid; if (!IsLegitCharacterForAccount(customizeInfo.Guid)) @@ -1625,7 +1630,7 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData) void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) { - CharacterFactionChangeInfo factionChangeInfo; + WorldPackets::Character::CharacterFactionChangeInfo factionChangeInfo; recvData >> factionChangeInfo.Guid; if (!IsLegitCharacterForAccount(factionChangeInfo.Guid)) @@ -2240,21 +2245,216 @@ void WorldSession::HandleOpeningCinematic(WorldPacket& /*recvData*/) } } +void WorldSession::HandleUndeleteCooldownStatusQuery(WorldPacket& /*recvData*/) +{ + /// empty result to force wait + PreparedQueryResultPromise result; + result.set_value(PreparedQueryResult(nullptr)); + _undeleteCooldownStatusCallback.SetFutureResult(result.get_future()); +} + +void WorldSession::HandleUndeleteCooldownStatusCallback(PreparedQueryResult result) +{ + switch (_undeleteCooldownStatusCallback.GetStage()) + { + case 0: + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LAST_CHAR_UNDELETE); + stmt->setUInt32(0, GetBattlenetAccountId()); + + _undeleteCooldownStatusCallback.FreeResult(); + _undeleteCooldownStatusCallback.SetFutureResult(LoginDatabase.AsyncQuery(stmt)); + _undeleteCooldownStatusCallback.NextStage(); + break; + } + case 1: + { + uint32 cooldown = 0; + uint32 maxCooldown = sWorld->getIntConfig(CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_COOLDOWN); + if (result) + { + uint32 lastUndelete = result->Fetch()[0].GetUInt32(); + if (lastUndelete) + cooldown = uint32(std::max<int32>(0, int32(lastUndelete) + maxCooldown - time(nullptr))); + } + + SendUndeleteCooldownStatusResponse(cooldown, maxCooldown); + _undeleteCooldownStatusCallback.Reset(); + break; + } + } + +} + +void WorldSession::HandleCharUndeleteOpcode(WorldPackets::Character::UndeleteCharacter& undeleteInfo) +{ + if (!sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED)) + { + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_ERROR_DISABLED, undeleteInfo.UndeleteInfo.get()); + return; + } + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LAST_CHAR_UNDELETE); + stmt->setUInt32(0, GetBattlenetAccountId()); + + _charUndeleteCallback.SetParam(undeleteInfo.UndeleteInfo); + _charUndeleteCallback.SetFutureResult(LoginDatabase.AsyncQuery(stmt)); + _charUndeleteCallback.NextStage(); +} + +void WorldSession::HandleCharUndeleteCallback(PreparedQueryResult result, WorldPackets::Character::CharacterUndeleteInfo* undeleteInfo) +{ + /** 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. + * It also prevents data synchronisation errors. + */ + + ASSERT(_charUndeleteCallback.GetParam().get() == undeleteInfo); + + switch (_charUndeleteCallback.GetStage()) + { + case 1: + { + if (result) + { + uint32 lastUndelete = result->Fetch()[0].GetUInt32(); + uint32 maxCooldown = sWorld->getIntConfig(CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_COOLDOWN); + if (lastUndelete && (lastUndelete + maxCooldown > time(nullptr))) + { + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_ERROR_COOLDOWN, undeleteInfo); + _charUndeleteCallback.Reset(); + return; + } + } + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID); + stmt->setUInt64(0, undeleteInfo->CharacterGuid.GetCounter()); + + _charUndeleteCallback.FreeResult(); + _charUndeleteCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _charUndeleteCallback.NextStage(); + break; + } + case 2: + { + if (!result) + { + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_ERROR_CHAR_CREATE, undeleteInfo); + _charUndeleteCallback.Reset(); + return; + } + + Field* fields = result->Fetch(); + undeleteInfo->Name = fields[1].GetString(); + uint32 account = fields[2].GetUInt32(); + + if (account != GetAccountId()) + { + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_ERROR_UNKNOWN, undeleteInfo); + _charUndeleteCallback.Reset(); + return; + } + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); + stmt->setString(0, undeleteInfo->Name); + + _charUndeleteCallback.FreeResult(); + _charUndeleteCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _charUndeleteCallback.NextStage(); + break; + } + case 3: + { + if (result) + { + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_ERROR_NAME_TAKEN_BY_THIS_ACCOUNT, undeleteInfo); + _charUndeleteCallback.Reset(); + return; + } + + /// @todo: add more safety checks + /// * max char count per account + /// * max heroic char count + /// * team violation + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); + stmt->setUInt32(0, GetAccountId()); + + _charUndeleteCallback.FreeResult(); + _charUndeleteCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _charUndeleteCallback.NextStage(); + break; + } + case 4: + { + if (result) + { + Field* fields = result->Fetch(); + + if (fields[0].GetUInt64() >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) // SQL's COUNT() returns uint64 but it will always be less than uint8.Max + { + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_ERROR_CHAR_CREATE, undeleteInfo); + _charUndeleteCallback.Reset(); + return; + } + } + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_RESTORE_DELETE_INFO); + stmt->setString(0, undeleteInfo->Name); + stmt->setUInt32(1, GetAccountId()); + stmt->setUInt64(2, undeleteInfo->CharacterGuid.GetCounter()); + CharacterDatabase.Execute(stmt); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_CHAR_UNDELETE); + stmt->setUInt32(0, GetBattlenetAccountId()); + LoginDatabase.Execute(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME_DATA); + stmt->setUInt64(0, undeleteInfo->CharacterGuid.GetCounter()); + + _charUndeleteCallback.FreeResult(); + _charUndeleteCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _charUndeleteCallback.NextStage(); + break; + } + case 5: + { + if (!result) + { + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_ERROR_UNKNOWN, undeleteInfo); + _charUndeleteCallback.Reset(); + return; + } + + Field* fields = result->Fetch(); + sWorld->AddCharacterNameData(undeleteInfo->CharacterGuid, undeleteInfo->Name, fields[2].GetUInt8(), fields[0].GetUInt8(), fields[1].GetUInt8(), fields[3].GetUInt8()); + + SendUndeleteCharacterResponse(CHARACTER_UNDELETE_RESULT_OK, undeleteInfo); + _charUndeleteCallback.Reset(); + break; + } + } +} + void WorldSession::SendCharCreate(ResponseCodes result) { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(result); - SendPacket(&data); + WorldPackets::Character::CharacterCreateResponse response; + response.Code = result; + + response.Write(); + SendPacket(&response.GetWorldPacket()); } void WorldSession::SendCharDelete(ResponseCodes result) { - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << uint8(result); - SendPacket(&data); + WorldPackets::Character::CharacterDeleteResponse response; + response.Code = result; + + response.Write(); + SendPacket(&response.GetWorldPacket()); } -void WorldSession::SendCharRename(ResponseCodes result, CharacterRenameInfo const& renameInfo) +void WorldSession::SendCharRename(ResponseCodes result, WorldPackets::Character::CharacterRenameInfo const& renameInfo) { WorldPacket data(SMSG_CHAR_RENAME, 1 + 8 + renameInfo.Name.size() + 1); data << uint8(result); @@ -2266,7 +2466,7 @@ void WorldSession::SendCharRename(ResponseCodes result, CharacterRenameInfo cons SendPacket(&data); } -void WorldSession::SendCharCustomize(ResponseCodes result, CharacterCustomizeInfo const& customizeInfo) +void WorldSession::SendCharCustomize(ResponseCodes result, WorldPackets::Character::CharacterCustomizeInfo const& customizeInfo) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1 + 8 + customizeInfo.Name.size() + 1 + 6); data << uint8(result); @@ -2284,7 +2484,7 @@ void WorldSession::SendCharCustomize(ResponseCodes result, CharacterCustomizeInf SendPacket(&data); } -void WorldSession::SendCharFactionChange(ResponseCodes result, CharacterFactionChangeInfo const& factionChangeInfo) +void WorldSession::SendCharFactionChange(ResponseCodes result, WorldPackets::Character::CharacterFactionChangeInfo const& factionChangeInfo) { WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1 + 8 + factionChangeInfo.Name.size() + 1 + 7); data << uint8(result); @@ -2318,3 +2518,24 @@ void WorldSession::SendBarberShopResult(BarberShopResult result) data << uint32(result); SendPacket(&data); } + +void WorldSession::SendUndeleteCooldownStatusResponse(uint32 currentCooldown, uint32 maxCooldown) +{ + WorldPackets::Character::UndeleteCooldownStatusResponse response; + response.OnCooldown = (currentCooldown > 0); + response.MaxCooldown = maxCooldown; + response.CurrentCooldown = currentCooldown; + + response.Write(); + SendPacket(&response.GetWorldPacket()); +} + +void WorldSession::SendUndeleteCharacterResponse(CharacterUndeleteResult result, WorldPackets::Character::CharacterUndeleteInfo const* undeleteInfo) +{ + WorldPackets::Character::UndeleteCharacterResponse response; + response.UndeleteInfo = undeleteInfo; + response.Result = result; + + response.Write(); + SendPacket(&response.GetWorldPacket()); +} diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 097ac61ec8a..e2c82763161 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -4324,45 +4324,58 @@ enum ResponseCodes CHAR_CREATE_CHARACTER_RACE_ONLY = 67, CHAR_CREATE_CHARACTER_GOLD_LIMIT = 68, CHAR_CREATE_FORCE_LOGIN = 69, + CHAR_CREATE_TRIAL = 70, + + CHAR_DELETE_IN_PROGRESS = 71, + CHAR_DELETE_SUCCESS = 72, + CHAR_DELETE_FAILED = 73, + CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 74, + CHAR_DELETE_FAILED_GUILD_LEADER = 75, + CHAR_DELETE_FAILED_ARENA_CAPTAIN = 76, + CHAR_DELETE_FAILED_HAS_HEIRLOOM_OR_MAIL = 77, + + CHAR_LOGIN_IN_PROGRESS = 78, + CHAR_LOGIN_SUCCESS = 79, + CHAR_LOGIN_NO_WORLD = 80, + CHAR_LOGIN_DUPLICATE_CHARACTER = 81, + CHAR_LOGIN_NO_INSTANCES = 82, + CHAR_LOGIN_FAILED = 83, + CHAR_LOGIN_DISABLED = 84, + CHAR_LOGIN_NO_CHARACTER = 85, + CHAR_LOGIN_LOCKED_FOR_TRANSFER = 86, + CHAR_LOGIN_LOCKED_BY_BILLING = 87, + CHAR_LOGIN_LOCKED_BY_MOBILE_AH = 88, + CHAR_LOGIN_TEMPORARY_GM_LOCK = 89, + CHAR_LOGIN_LOCKED_BY_CHARACTER_UPGRADE = 90, + CHAR_LOGIN_LOCKED_BY_REVOKED_CHARACTER_UPGRADE = 91, + + CHAR_NAME_SUCCESS = 92, + CHAR_NAME_FAILURE = 93, + CHAR_NAME_NO_NAME = 94, + CHAR_NAME_TOO_SHORT = 95, + CHAR_NAME_TOO_LONG = 96, + CHAR_NAME_INVALID_CHARACTER = 97, + CHAR_NAME_MIXED_LANGUAGES = 98, + CHAR_NAME_PROFANE = 99, + CHAR_NAME_RESERVED = 100, + CHAR_NAME_INVALID_APOSTROPHE = 101, + CHAR_NAME_MULTIPLE_APOSTROPHES = 102, + CHAR_NAME_THREE_CONSECUTIVE = 103, + CHAR_NAME_INVALID_SPACE = 104, + CHAR_NAME_CONSECUTIVE_SPACES = 105, + CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 106, + CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 107, + CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 108 +}; - CHAR_DELETE_IN_PROGRESS = 70, - CHAR_DELETE_SUCCESS = 71, - CHAR_DELETE_FAILED = 72, - CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 73, - CHAR_DELETE_FAILED_GUILD_LEADER = 74, - CHAR_DELETE_FAILED_ARENA_CAPTAIN = 75, - CHAR_DELETE_FAILED_HAS_HEIRLOOM_OR_MAIL = 76, - - CHAR_LOGIN_IN_PROGRESS = 77, - CHAR_LOGIN_SUCCESS = 78, - CHAR_LOGIN_NO_WORLD = 79, - CHAR_LOGIN_DUPLICATE_CHARACTER = 80, - CHAR_LOGIN_NO_INSTANCES = 81, - CHAR_LOGIN_FAILED = 82, - CHAR_LOGIN_DISABLED = 83, - CHAR_LOGIN_NO_CHARACTER = 84, - CHAR_LOGIN_LOCKED_FOR_TRANSFER = 85, - CHAR_LOGIN_LOCKED_BY_BILLING = 86, - CHAR_LOGIN_LOCKED_BY_MOBILE_AH = 87, - CHAR_LOGIN_TEMPORARY_GM_LOCK = 88, - - CHAR_NAME_SUCCESS = 89, - CHAR_NAME_FAILURE = 90, - CHAR_NAME_NO_NAME = 91, - CHAR_NAME_TOO_SHORT = 92, - CHAR_NAME_TOO_LONG = 93, - CHAR_NAME_INVALID_CHARACTER = 94, - CHAR_NAME_MIXED_LANGUAGES = 95, - CHAR_NAME_PROFANE = 96, - CHAR_NAME_RESERVED = 97, - CHAR_NAME_INVALID_APOSTROPHE = 98, - CHAR_NAME_MULTIPLE_APOSTROPHES = 99, - CHAR_NAME_THREE_CONSECUTIVE = 100, - CHAR_NAME_INVALID_SPACE = 101, - CHAR_NAME_CONSECUTIVE_SPACES = 102, - CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 103, - CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 104, - CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 105, +enum CharacterUndeleteResult +{ + CHARACTER_UNDELETE_RESULT_OK = 0, + CHARACTER_UNDELETE_RESULT_ERROR_COOLDOWN = 1, + CHARACTER_UNDELETE_RESULT_ERROR_CHAR_CREATE = 2, + CHARACTER_UNDELETE_RESULT_ERROR_DISABLED = 3, + CHARACTER_UNDELETE_RESULT_ERROR_NAME_TAKEN_BY_THIS_ACCOUNT = 4, + CHARACTER_UNDELETE_RESULT_ERROR_UNKNOWN = 5 }; /// Ban function modes diff --git a/src/server/game/Server/Packets/AuthenticationPackets.h b/src/server/game/Server/Packets/AuthenticationPackets.h index 305a841d4e7..209300e023e 100644 --- a/src/server/game/Server/Packets/AuthenticationPackets.h +++ b/src/server/game/Server/Packets/AuthenticationPackets.h @@ -29,7 +29,7 @@ namespace WorldPackets class AuthChallenge final : public ServerPacket { public: - AuthChallenge() : ServerPacket(SMSG_AUTH_CHALLENGE, 8 + 32 + 1), Challenge(0) { } + AuthChallenge() : ServerPacket(SMSG_AUTH_CHALLENGE, 4 + 32 + 1), Challenge(0) { } void Write() override; diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index ff44777ac79..095eb452e2b 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -37,8 +37,8 @@ WorldPackets::Character::CharEnumResult::CharacterInfo::CharacterInfo(Field* fie HairColor = uint8((fields[5].GetUInt32() >> 24) & 0xFF); FacialHair = uint8(fields[6].GetUInt32() & 0xFF); Level = fields[7].GetUInt8(); - ZoneId = fields[8].GetUInt16(); - MapId = fields[9].GetUInt16(); + ZoneId = int32(fields[8].GetUInt16()); + MapId = int32(fields[9].GetUInt16()); PreLoadPosition.x = fields[10].GetFloat(); PreLoadPosition.y = fields[11].GetFloat(); PreLoadPosition.z = fields[12].GetFloat(); @@ -147,8 +147,8 @@ void WorldPackets::Character::CharEnumResult::Write() _worldPacket << uint8(charInfo.HairColor); _worldPacket << uint8(charInfo.FacialHair); _worldPacket << uint8(charInfo.Level); - _worldPacket << uint32(charInfo.ZoneId); - _worldPacket << uint32(charInfo.MapId); + _worldPacket << int32(charInfo.ZoneId); + _worldPacket << int32(charInfo.MapId); _worldPacket << float(charInfo.PreLoadPosition.x); _worldPacket << float(charInfo.PreLoadPosition.y); _worldPacket << float(charInfo.PreLoadPosition.z); @@ -178,11 +178,97 @@ void WorldPackets::Character::CharEnumResult::Write() for (RestrictedFactionChangeRuleInfo const& rule : FactionChangeRestrictions) { - _worldPacket << uint32(rule.Mask); + _worldPacket << int32(rule.Mask); _worldPacket << uint8(rule.Race); } } +WorldPackets::Character::CharacterCreate::CharacterCreate(WorldPacket&& packet) + : ClientPacket(std::move(packet)) +{ + ASSERT(_worldPacket.GetOpcode() == CMSG_CHAR_CREATE); +} + +void WorldPackets::Character::CharacterCreate::Read() +{ + CreateInfo.reset(new CharacterCreateInfo()); + uint32 nameLength = _worldPacket.ReadBits(6); + CreateInfo->TemplateSet.HasValue = _worldPacket.ReadBit(); + _worldPacket >> CreateInfo->Race; + _worldPacket >> CreateInfo->Class; + _worldPacket >> CreateInfo->Sex; + _worldPacket >> CreateInfo->Skin; + _worldPacket >> CreateInfo->Face; + _worldPacket >> CreateInfo->HairStyle; + _worldPacket >> CreateInfo->HairColor; + _worldPacket >> CreateInfo->FacialHairStyle; + _worldPacket >> CreateInfo->OutfitId; + CreateInfo->Name = _worldPacket.ReadString(nameLength); + if (CreateInfo->TemplateSet.HasValue) + _worldPacket >> CreateInfo->TemplateSet.value; +} + +WorldPackets::Character::CharacterCreateResponse::CharacterCreateResponse() + : ServerPacket(SMSG_CHAR_CREATE, 1) { } + +void WorldPackets::Character::CharacterCreateResponse::Write() +{ + _worldPacket << uint8(Code); +} + +WorldPackets::Character::CharacterDelete::CharacterDelete(WorldPacket&& packet) + : ClientPacket(std::move(packet)) +{ + ASSERT(_worldPacket.GetOpcode() == CMSG_CHAR_DELETE); +} + +void WorldPackets::Character::CharacterDelete::Read() +{ + _worldPacket >> Guid; +} + +WorldPackets::Character::CharacterDeleteResponse::CharacterDeleteResponse() + : ServerPacket(SMSG_CHAR_DELETE, 1) { } + +void WorldPackets::Character::CharacterDeleteResponse::Write() +{ + _worldPacket << uint8(Code); +} + +WorldPackets::Character::UndeleteCharacter::UndeleteCharacter(WorldPacket&& packet) + : ClientPacket(std::move(packet)) +{ + ASSERT(_worldPacket.GetOpcode() == CMSG_UNDELETE_CHARACTER); +} + +void WorldPackets::Character::UndeleteCharacter::Read() +{ + UndeleteInfo.reset(new CharacterUndeleteInfo()); + _worldPacket >> UndeleteInfo->ClientToken; + _worldPacket >> UndeleteInfo->CharacterGuid; +} + +WorldPackets::Character::UndeleteCharacterResponse::UndeleteCharacterResponse() + : ServerPacket(SMSG_UNDELETE_CHARACTER_RESPONSE, 26) { } + +void WorldPackets::Character::UndeleteCharacterResponse::Write() +{ + ASSERT(UndeleteInfo); + _worldPacket << int32(UndeleteInfo->ClientToken); + _worldPacket << uint32(Result); + _worldPacket << UndeleteInfo->CharacterGuid; +} + +WorldPackets::Character::UndeleteCooldownStatusResponse::UndeleteCooldownStatusResponse() + : ServerPacket(SMSG_UNDELETE_COOLDOWN_STATUS_RESPONSE, 9) { } + +void WorldPackets::Character::UndeleteCooldownStatusResponse::Write() +{ + _worldPacket.WriteBit(OnCooldown); + _worldPacket << uint32(MaxCooldown); + _worldPacket << uint32(CurrentCooldown); +} + WorldPackets::Character::PlayerLogin::PlayerLogin(WorldPacket&& packet) : ClientPacket(std::move(packet)) { diff --git a/src/server/game/Server/Packets/CharacterPackets.h b/src/server/game/Server/Packets/CharacterPackets.h index 2f8b069164a..135d53f8e8c 100644 --- a/src/server/game/Server/Packets/CharacterPackets.h +++ b/src/server/game/Server/Packets/CharacterPackets.h @@ -25,6 +25,77 @@ namespace WorldPackets { namespace Character { + struct CharacterCreateInfo + { + friend class CharacterCreate; + friend class Player; + friend class WorldSession; + + protected: + /// User specified variables + uint8 Race = 0; + uint8 Class = 0; + uint8 Sex = GENDER_NONE; + uint8 Skin = 0; + uint8 Face = 0; + uint8 HairStyle = 0; + uint8 HairColor = 0; + uint8 FacialHairStyle = 0; + uint8 OutfitId = 0; + Optional<int32> TemplateSet; + std::string Name; + + /// Server side data + uint8 CharCount = 0; + }; + + struct CharacterRenameInfo + { + friend class WorldSession; + + protected: + ObjectGuid Guid; + std::string Name; + }; + + struct CharacterCustomizeInfo : public CharacterRenameInfo + { + friend class Player; + friend class WorldSession; + + protected: + uint8 Gender = GENDER_NONE; + uint8 Skin = 0; + uint8 Face = 0; + uint8 HairStyle = 0; + uint8 HairColor = 0; + uint8 FacialHair = 0; + }; + + struct CharacterFactionChangeInfo : public CharacterCustomizeInfo + { + friend class Player; + friend class WorldSession; + + protected: + uint8 Race = 0; + }; + + struct CharacterUndeleteInfo + { + friend class UndeleteCharacter; + friend class UndeleteCharacterResponse; + friend class WorldSession; + + protected: + /// User specified variables + ObjectGuid CharacterGuid; ///< Guid of the character to restore + int32 ClientToken = 0; ///< @todo: research + + /// Server side data + std::string Name; + }; + class CharEnumResult final : public ServerPacket { public: @@ -51,8 +122,8 @@ namespace WorldPackets uint8 HairColor = 0; uint8 FacialHair = 0; uint8 Level = 0; - uint32 ZoneId = 0; - uint32 MapId = 0; + int32 ZoneId = 0; + int32 MapId = 0; G3D::Vector3 PreLoadPosition; ObjectGuid GuildGuid; uint32 Flags = 0; ///< Character flag @see enum CharacterFlags @@ -68,7 +139,7 @@ namespace WorldPackets } Pet; bool BoostInProgress = false; ///< @todo - uint32 ProfessionIds[2]; ///< @todo + int32 ProfessionIds[2]; ///< @todo struct VisualItemInfo { @@ -82,11 +153,11 @@ namespace WorldPackets struct RestrictedFactionChangeRuleInfo { - RestrictedFactionChangeRuleInfo(uint32 mask, uint8 race) + RestrictedFactionChangeRuleInfo(int32 mask, uint8 race) : Mask(mask), Race(race) { } - uint32 Mask = 0; - uint8 Race = 0; + int32 Mask = 0; + uint8 Race = 0; }; CharEnumResult(); @@ -100,6 +171,100 @@ namespace WorldPackets std::list<RestrictedFactionChangeRuleInfo> FactionChangeRestrictions; ///< @todo: research }; + class CharacterCreate final : public ClientPacket + { + public: + CharacterCreate(WorldPacket&& packet); + + void Read() override; + + /** + * @var uint8 Race + * @var uint8 Class + * @var uint8 Sex + * @var uint8 Skin + * @var uint8 Face + * @var uint8 HairStyle + * @var uint8 HairColor + * @var uint8 FacialHairStyle + * @var uint8 OutfitId + * @var Optional<int32> TemplateSet + * @var std::string Name + */ + std::shared_ptr<CharacterCreateInfo> CreateInfo; + }; + + class CharacterCreateResponse final : public ServerPacket + { + public: + CharacterCreateResponse(); + + void Write() override; + + uint8 Code = 0; ///< Result code @see enum ResponseCodes + }; + + class CharacterDelete final : public ClientPacket + { + public: + CharacterDelete(WorldPacket&& packet); + + void Read() override; + + ObjectGuid Guid; ///< Guid of the character to delete + }; + + class CharacterDeleteResponse final : public ServerPacket + { + public: + CharacterDeleteResponse(); + + void Write() override; + + uint8 Code = 0; ///< Result code @see enum ResponseCodes + }; + + class UndeleteCharacter final : public ClientPacket + { + public: + UndeleteCharacter(WorldPacket&& packet); + + void Read() override; + + /** + * @var ObjectGuid CharacterGuid + * @var int32 ClientToken + */ + std::shared_ptr<CharacterUndeleteInfo> UndeleteInfo; + }; + + class UndeleteCharacterResponse final : public ServerPacket + { + public: + UndeleteCharacterResponse(); + + void Write() override; + + /** + * @var ObjectGuid CharacterGuid + * @var int32 ClientToken + */ + CharacterUndeleteInfo const* UndeleteInfo = nullptr; + uint32 Result = 0; ///< @see enum CharacterUndeleteResult + }; + + class UndeleteCooldownStatusResponse final : public ServerPacket + { + public: + UndeleteCooldownStatusResponse(); + + void Write() override; + + bool OnCooldown = false; ///< + uint32 MaxCooldown = 0; ///< Max. cooldown until next free character restoration. Displayed in undelete confirm message. (in sec) + uint32 CurrentCooldown = 0; ///< Current cooldown until next free character restoration. (in sec) + }; + class PlayerLogin final : public ClientPacket { public: diff --git a/src/server/game/Server/Packets/SystemPackets.cpp b/src/server/game/Server/Packets/SystemPackets.cpp new file mode 100644 index 00000000000..c1d11db9ef9 --- /dev/null +++ b/src/server/game/Server/Packets/SystemPackets.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "SystemPackets.h" + +WorldPackets::System::FeatureSystemStatusGlueScreen::FeatureSystemStatusGlueScreen() + : ServerPacket(SMSG_FEATURE_SYSTEM_STATUS_GLUE_SCREEN, 1) { } + +void WorldPackets::System::FeatureSystemStatusGlueScreen::Write() +{ + _worldPacket.WriteBit(BpayStoreEnabled); + _worldPacket.WriteBit(BpayStoreAvailable); + _worldPacket.WriteBit(BpayStoreDisabledByParentalControls); + _worldPacket.WriteBit(CharUndeleteEnabled); + _worldPacket.FlushBits(); +} + +WorldPackets::System::SetTimeZoneInformation::SetTimeZoneInformation() + : ServerPacket(SMSG_SET_TIME_ZONE_INFORMATION) { } + +void WorldPackets::System::SetTimeZoneInformation::Write() +{ + _worldPacket.reserve(1 + ServerTimeTZ.length() + GameTimeTZ.length()); + + _worldPacket.WriteBits(ServerTimeTZ.length(), 7); + _worldPacket.WriteBits(GameTimeTZ.length(), 7); + _worldPacket.WriteString(ServerTimeTZ); + _worldPacket.WriteString(GameTimeTZ); +} diff --git a/src/server/game/Server/Packets/SystemPackets.h b/src/server/game/Server/Packets/SystemPackets.h new file mode 100644 index 00000000000..cb5a22a9b98 --- /dev/null +++ b/src/server/game/Server/Packets/SystemPackets.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SystemPackets_h__ +#define SystemPackets_h__ + +#include "Packet.h" + +namespace WorldPackets +{ + namespace System + { + class FeatureSystemStatusGlueScreen final : public ServerPacket + { + public: + FeatureSystemStatusGlueScreen(); + + void Write() override; + + bool BpayStoreAvailable = false; // NYI + bool BpayStoreDisabledByParentalControls = false; // NYI + bool CharUndeleteEnabled = false; // NYI + bool BpayStoreEnabled = false; // NYI + }; + + class SetTimeZoneInformation final : public ServerPacket + { + public: + SetTimeZoneInformation(); + + void Write() override; + + std::string ServerTimeTZ; + std::string GameTimeTZ; + }; + } +} + +#endif // SystemPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 4d62b83552f..12dc1fd096c 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -214,13 +214,14 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_CHANNEL_UNSILENCE_VOICE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHANNEL_VOICE_OFF, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelVoiceOnOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHANNEL_VOICE_ON, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelVoiceOnOpcode ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_CREATE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharCreateOpcode ); + DEFINE_HANDLER(CMSG_CHAR_CREATE, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::CharacterCreate, &WorldSession::HandleCharCreateOpcode); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_CUSTOMIZE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharCustomize ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_DELETE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharDeleteOpcode ); + DEFINE_HANDLER(CMSG_CHAR_DELETE, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::CharacterDelete, &WorldSession::HandleCharDeleteOpcode); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_ENUM, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharEnumOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_FACTION_CHANGE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharFactionOrRaceChange ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_RACE_CHANGE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharFactionOrRaceChange ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_RENAME, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharRenameOpcode ); + DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAR_UNDELETE_ENUM, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleCharUndeleteEnumOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAT_FILTERED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CHAT_IGNORED, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleChatIgnoredOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_CLEAR_CHANNEL_WATCH, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); @@ -259,6 +260,7 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_EQUIPMENT_SET_DELETE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetDelete ); DEFINE_OPCODE_HANDLER_OLD(CMSG_EQUIPMENT_SET_SAVE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetSave ); DEFINE_OPCODE_HANDLER_OLD(CMSG_EQUIPMENT_SET_USE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleEquipmentSetUse ); + DEFINE_OPCODE_HANDLER_OLD(CMSG_FACTION_BONUS_INFO, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_FAR_SIGHT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleFarSightOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_FORCE_MOVE_ROOT_ACK, STATUS_UNHANDLED, PROCESS_THREADSAFE, &WorldSession::HandleMoveRootAck ); DEFINE_OPCODE_HANDLER_OLD(CMSG_FORCE_MOVE_UNROOT_ACK, STATUS_UNHANDLED, PROCESS_THREADSAFE, &WorldSession::HandleMoveUnRootAck ); @@ -585,6 +587,8 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_TUTORIAL_FLAG, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialFlag ); DEFINE_OPCODE_HANDLER_OLD(CMSG_TUTORIAL_RESET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialReset ); DEFINE_OPCODE_HANDLER_OLD(CMSG_UNACCEPT_TRADE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleUnacceptTradeOpcode ); + DEFINE_HANDLER(CMSG_UNDELETE_CHARACTER, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::UndeleteCharacter, &WorldSession::HandleCharUndeleteOpcode); + DEFINE_OPCODE_HANDLER_OLD(CMSG_UNDELETE_COOLDOWN_STATUS_QUERY, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleUndeleteCooldownStatusQuery); DEFINE_OPCODE_HANDLER_OLD(CMSG_UNLEARN_SKILL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleUnlearnSkillOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_UNLEARN_SPECIALIZATION, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_UNREGISTER_ALL_ADDON_PREFIXES, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleUnregisterAddonPrefixesOpcode); @@ -736,6 +740,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLEGROUND_INFO_THROTTLED, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLEGROUND_PLAYER_JOINED, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLEGROUND_PLAYER_LEFT, STATUS_UNHANDLED); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_BATTLE_PET_JOURNAL, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BINDER_CONFIRM, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BINDPOINTUPDATE, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BINDZONEREPLY, STATUS_UNHANDLED); @@ -774,7 +779,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHARACTER_LOGIN_FAILED, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_CREATE, STATUS_NEVER); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_CUSTOMIZE, STATUS_UNHANDLED); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_DELETE, STATUS_UNHANDLED); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_DELETE, STATUS_NEVER); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_ENUM, STATUS_NEVER); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_FACTION_CHANGE, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHAR_RENAME, STATUS_UNHANDLED); @@ -856,7 +861,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_EXPLORATION_EXPERIENCE, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FAILED_PLAYER_CONDITION, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FEATURE_SYSTEM_STATUS, STATUS_UNHANDLED); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_FEATURE_SYSTEM_STATUS_GLUE_SCREEN, STATUS_UNHANDLED); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_FEATURE_SYSTEM_STATUS_GLUE_SCREEN, STATUS_NEVER); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FEIGN_DEATH_RESISTED, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FISH_ESCAPED, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_FISH_NOT_HOOKED, STATUS_UNHANDLED); @@ -954,6 +959,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_HOTFIX_INFO, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_HOTFIX_NOTIFY, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_INITIALIZE_FACTIONS, STATUS_UNHANDLED); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_INITIAL_SETUP, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_INITIAL_SPELLS, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_INIT_CURRENCY, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_INIT_WORLD_STATES, STATUS_UNHANDLED); @@ -1231,6 +1237,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAY_HOVER_ANIM, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PROFICIENCY, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PROJECTILE_POSITION, STATUS_UNHANDLED); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_TIME_ZONE_INFORMATION, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SHOWTAXINODES, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SHOW_BANK, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SHOW_MAILBOX, STATUS_UNHANDLED); @@ -1324,6 +1331,8 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_MOVIE, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TURN_IN_PETITION_RESULTS, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_TUTORIAL_FLAGS, STATUS_UNHANDLED); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_UNDELETE_CHARACTER_RESPONSE, STATUS_NEVER); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_UNDELETE_COOLDOWN_STATUS_RESPONSE, STATUS_NEVER); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UNIT_HEALTH_FREQUENT, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UNIT_SPELLCAST_START, STATUS_UNHANDLED); DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_ACCOUNT_DATA, STATUS_UNHANDLED); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index c08a9409016..7155bd7970b 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -46,7 +46,7 @@ enum OpcodeClient : uint32 CMSG_ADD_IGNORE = 0xBADD, CMSG_ADD_VOICE_IGNORE = 0xBADD, CMSG_ALTER_APPEARANCE = 0xBADD, - CMSG_AREATRIGGER = 0xBADD, + CMSG_AREATRIGGER = 0x01B4, CMSG_AREA_SPIRIT_HEALER_QUERY = 0xBADD, CMSG_AREA_SPIRIT_HEALER_QUEUE = 0xBADD, CMSG_ARENA_TEAM_ACCEPT = 0xBADD, @@ -162,6 +162,7 @@ enum OpcodeClient : uint32 CMSG_CHAR_FACTION_CHANGE = 0xBADD, CMSG_CHAR_RACE_CHANGE = 0xBADD, CMSG_CHAR_RENAME = 0xBADD, + CMSG_CHAR_UNDELETE_ENUM = 0x0F2D, CMSG_CHAT_FILTERED = 0xBADD, CMSG_CHAT_IGNORED = 0xBADD, CMSG_CLEAR_CHANNEL_WATCH = 0xBADD, @@ -201,6 +202,7 @@ enum OpcodeClient : uint32 CMSG_EQUIPMENT_SET_DELETE = 0xBADD, CMSG_EQUIPMENT_SET_SAVE = 0x1B54, CMSG_EQUIPMENT_SET_USE = 0xBADD, + CMSG_FACTION_BONUS_INFO = 0x0928, CMSG_FAR_SIGHT = 0xBADD, CMSG_FORCE_MOVE_ROOT_ACK = 0xBADD, CMSG_FORCE_MOVE_UNROOT_ACK = 0xBADD, @@ -278,7 +280,7 @@ enum OpcodeClient : uint32 CMSG_GUILD_REPLACE_GUILD_MASTER = 0xBADD, CMSG_GUILD_REQUEST_CHALLENGE_UPDATE = 0xBADD, CMSG_GUILD_REQUEST_MAX_DAILY_XP = 0xBADD, - CMSG_GUILD_REQUEST_PARTY_STATE = 0xBADD, + CMSG_GUILD_REQUEST_PARTY_STATE = 0x0A8E, CMSG_GUILD_ROSTER = 0xBADD, CMSG_GUILD_SET_ACHIEVEMENT_TRACKING = 0xBADD, CMSG_GUILD_SET_GUILD_MASTER = 0xBADD, @@ -443,8 +445,8 @@ enum OpcodeClient : uint32 CMSG_QUESTGIVER_QUERY_QUEST = 0x1924, CMSG_QUESTGIVER_QUEST_AUTOLAUNCH = 0xBADD, CMSG_QUESTGIVER_REQUEST_REWARD = 0xBADD, - CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0xBADD, - CMSG_QUESTGIVER_STATUS_QUERY = 0xBADD, + CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0x0131, + CMSG_QUESTGIVER_STATUS_QUERY = 0x0704, CMSG_QUESTLOG_REMOVE_QUEST = 0xBADD, CMSG_QUESTLOG_SWAP_QUEST = 0xBADD, CMSG_QUEST_CONFIRM_ACCEPT = 0xBADD, @@ -458,7 +460,7 @@ enum OpcodeClient : uint32 CMSG_RECLAIM_CORPSE = 0xBADD, CMSG_REDIRECTION_AUTH_PROOF = 0x0485, CMSG_REFORGE_ITEM = 0xBADD, - CMSG_REORDER_CHARACTERS = 0xBADD, + CMSG_REORDER_CHARACTERS = 0x0DAA, CMSG_REPAIR_ITEM = 0xBADD, CMSG_REPLACE_ACCOUNT_DATA = 0xBADD, CMSG_REPOP_REQUEST = 0xBADD, @@ -567,6 +569,8 @@ enum OpcodeClient : uint32 CMSG_TUTORIAL_FLAG = 0xBADD, CMSG_TUTORIAL_RESET = 0xBADD, CMSG_UNACCEPT_TRADE = 0xBADD, + CMSG_UNDELETE_CHARACTER = 0x0D99, + CMSG_UNDELETE_COOLDOWN_STATUS_QUERY = 0x19A9, CMSG_UNLEARN_SKILL = 0xBADD, CMSG_UNLEARN_SPECIALIZATION = 0xBADD, CMSG_UNREGISTER_ALL_ADDON_PREFIXES = 0xBADD, @@ -738,6 +742,7 @@ enum OpcodeServer : uint32 SMSG_BATTLEGROUND_INFO_THROTTLED = 0xBADD, SMSG_BATTLEGROUND_PLAYER_JOINED = 0xBADD, SMSG_BATTLEGROUND_PLAYER_LEFT = 0xBADD, + SMSG_BATTLE_PET_JOURNAL = 0x19A2, SMSG_BINDER_CONFIRM = 0xBADD, SMSG_BINDPOINTUPDATE = 0x0A30, SMSG_BINDZONEREPLY = 0xBADD, @@ -916,7 +921,7 @@ enum OpcodeServer : uint32 SMSG_GROUP_SET_LEADER = 0xBADD, SMSG_GROUP_SET_ROLE = 0xBADD, SMSG_GROUP_UNINVITE = 0xBADD, - SMSG_GUILD_ACHIEVEMENT_DATA = 0xBADD, + SMSG_GUILD_ACHIEVEMENT_DATA = 0x1866, SMSG_GUILD_ACHIEVEMENT_DELETED = 0xBADD, SMSG_GUILD_ACHIEVEMENT_EARNED = 0xBADD, SMSG_GUILD_ACHIEVEMENT_MEMBERS = 0xBADD, @@ -933,7 +938,7 @@ enum OpcodeServer : uint32 SMSG_GUILD_CRITERIA_DATA = 0xBADD, SMSG_GUILD_CRITERIA_DELETED = 0xBADD, SMSG_GUILD_DECLINE = 0xBADD, - SMSG_GUILD_EVENT = 0xBADD, + SMSG_GUILD_EVENT = 0x1027, SMSG_GUILD_EVENT_LOG_QUERY_RESULT = 0xBADD, SMSG_GUILD_EVENT_PRESENCE_CHANGE = 0x1228, SMSG_GUILD_FLAGGED_FOR_RENAME = 0xBADD, @@ -950,12 +955,12 @@ enum OpcodeServer : uint32 SMSG_GUILD_MOVE_STARTING = 0xBADD, SMSG_GUILD_NEWS_DELETED = 0xBADD, SMSG_GUILD_NEWS_UPDATE = 0xBADD, - SMSG_GUILD_PARTY_STATE_RESPONSE = 0xBADD, + SMSG_GUILD_PARTY_STATE_RESPONSE = 0x1225, SMSG_GUILD_PERMISSIONS_QUERY_RESULTS = 0xBADD, SMSG_GUILD_QUERY_RESPONSE = 0x1046, SMSG_GUILD_RANK = 0x1218, SMSG_GUILD_RANKS_UPDATE = 0xBADD, - SMSG_GUILD_RECIPES = 0xBADD, + SMSG_GUILD_RECIPES = 0x1078, SMSG_GUILD_RENAMED = 0xBADD, SMSG_GUILD_REPUTATION_REACTION_CHANGED = 0xBADD, SMSG_GUILD_REPUTATION_WEEKLY_CAP = 0xBADD, @@ -974,7 +979,8 @@ enum OpcodeServer : uint32 SMSG_HOTFIX_NOTIFY = 0xBADD, SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0xBADD, SMSG_IGNORE_REQUIREMENTS_CHEAT = 0xBADD, - SMSG_INITIALIZE_FACTIONS = 0xBADD, + SMSG_INITIALIZE_FACTIONS = 0x0B10, + SMSG_INITIAL_SETUP = 0x0B07, SMSG_INITIAL_SPELLS = 0x0A81, SMSG_INIT_CURRENCY = 0x0B06, SMSG_INIT_WORLD_STATES = 0x0B04, @@ -1013,7 +1019,7 @@ enum OpcodeServer : uint32 SMSG_LFG_OFFER_CONTINUE = 0xBADD, SMSG_LFG_OPEN_FROM_GOSSIP = 0xBADD, SMSG_LFG_PARTY_INFO = 0xBADD, - SMSG_LFG_PLAYER_INFO = 0xBADD, + SMSG_LFG_PLAYER_INFO = 0x0F36, SMSG_LFG_PLAYER_REWARD = 0xBADD, SMSG_LFG_PROPOSAL_UPDATE = 0xBADD, SMSG_LFG_QUEUE_STATUS = 0xBADD, @@ -1139,7 +1145,7 @@ enum OpcodeServer : uint32 SMSG_PARTY_MEMBER_STATS = 0xBADD, SMSG_PARTY_MEMBER_STATS_FULL = 0xBADD, SMSG_PAUSE_MIRROR_TIMER = 0xBADD, - SMSG_PERIODICAURALOG = 0xBADD, + SMSG_PERIODICAURALOG = 0x0B1B, SMSG_PETGODMODE = 0xBADD, SMSG_PETITION_ALREADY_SIGNED = 0xBADD, SMSG_PETITION_QUERY_RESPONSE = 0xBADD, @@ -1179,7 +1185,7 @@ enum OpcodeServer : uint32 SMSG_PLAY_SPELL_VISUAL_KIT = 0xBADD, SMSG_PLAY_TIME_WARNING = 0xBADD, SMSG_PONG = 0x17CA, - SMSG_POWER_UPDATE = 0xBADD, + SMSG_POWER_UPDATE = 0x0B27, SMSG_PRE_RESURRECT = 0xBADD, SMSG_PROCRESIST = 0xBADD, SMSG_PROPOSE_LEVEL_GRANT = 0xBADD, @@ -1196,8 +1202,8 @@ enum OpcodeServer : uint32 SMSG_QUESTGIVER_QUEST_INVALID = 0xBADD, SMSG_QUESTGIVER_QUEST_LIST = 0xBADD, SMSG_QUESTGIVER_REQUEST_ITEMS = 0xBADD, - SMSG_QUESTGIVER_STATUS = 0xBADD, - SMSG_QUESTGIVER_STATUS_MULTIPLE = 0xBADD, + SMSG_QUESTGIVER_STATUS = 0x1567, + SMSG_QUESTGIVER_STATUS_MULTIPLE = 0x17C6, SMSG_QUESTLOG_FULL = 0xBADD, SMSG_QUESTUPDATE_ADD_ITEM = 0xBADD, SMSG_QUESTUPDATE_ADD_KILL = 0xBADD, @@ -1205,7 +1211,7 @@ enum OpcodeServer : uint32 SMSG_QUESTUPDATE_COMPLETE = 0xBADD, SMSG_QUESTUPDATE_FAILED = 0xBADD, SMSG_QUESTUPDATE_FAILEDTIMER = 0xBADD, - SMSG_QUEST_NPC_QUERY_RESPONSE = 0xBADD, + SMSG_QUEST_NPC_QUERY_RESPONSE = 0x1591, SMSG_QUEST_CONFIRM_ACCEPT = 0xBADD, SMSG_QUEST_FORCE_REMOVE = 0xBADD, SMSG_QUEST_POI_QUERY_RESPONSE = 0x1715, @@ -1245,7 +1251,6 @@ enum OpcodeServer : uint32 SMSG_RWHOIS = 0xBADD, SMSG_SELL_ITEM = 0xBADD, SMSG_SEND_MAIL_RESULT = 0x0302, - SMSG_SEND_SERVER_LOCATION = 0x153E, SMSG_SEND_UNLEARN_SPELLS = 0x1A82, SMSG_SERVERTIME = 0xBADD, SMSG_SERVER_FIRST_ACHIEVEMENT = 0xBADD, @@ -1268,6 +1273,7 @@ enum OpcodeServer : uint32 SMSG_SET_PLAY_HOVER_ANIM = 0xBADD, SMSG_SET_PROFICIENCY = 0x00D3, SMSG_SET_PROJECTILE_POSITION = 0xBADD, + SMSG_SET_TIME_ZONE_INFORMATION = 0x153E, SMSG_SET_VIGNETTE = 0x09AC, SMSG_SHOWTAXINODES = 0xBADD, SMSG_SHOW_BANK = 0x0204, @@ -1278,11 +1284,11 @@ enum OpcodeServer : uint32 SMSG_SPELLBREAKLOG = 0xBADD, SMSG_SPELLDAMAGESHIELD = 0xBADD, SMSG_SPELLDISPELLOG = 0xBADD, - SMSG_SPELLENERGIZELOG = 0xBADD, - SMSG_SPELLHEALLOG = 0xBADD, + SMSG_SPELLENERGIZELOG = 0x137C, + SMSG_SPELLHEALLOG = 0x0114, SMSG_SPELLINSTAKILLLOG = 0xBADD, SMSG_SPELLINTERRUPTLOG = 0xBADD, - SMSG_SPELLLOGEXECUTE = 0xBADD, + SMSG_SPELLLOGEXECUTE = 0x1823, SMSG_SPELLLOGMISS = 0xBADD, SMSG_SPELLNONMELEEDAMAGELOG = 0x11FB, SMSG_SPELLORDAMAGE_IMMUNE = 0xBADD, @@ -1358,14 +1364,16 @@ enum OpcodeServer : uint32 SMSG_TRAINER_BUY_SUCCEEDED = 0xBADD, SMSG_TRAINER_LIST = 0x0BA9, SMSG_TRANSFER_ABORTED = 0xBADD, - SMSG_TRANSFER_PENDING = 0xBADD, + SMSG_TRANSFER_PENDING = 0x172A, SMSG_TRIGGER_CINEMATIC = 0xBADD, SMSG_TRIGGER_MOVIE = 0xBADD, SMSG_TURN_IN_PETITION_RESULTS = 0xBADD, SMSG_TUTORIAL_FLAGS = 0x020F, + SMSG_UNDELETE_CHARACTER_RESPONSE = 0x0344, + SMSG_UNDELETE_COOLDOWN_STATUS_RESPONSE = 0x0310, SMSG_UNIT_HEALTH_FREQUENT = 0xBADD, SMSG_UNIT_SPELLCAST_START = 0xBADD, - SMSG_UPDATE_ACCOUNT_DATA = 0xBADD, + SMSG_UPDATE_ACCOUNT_DATA = 0x1520, SMSG_UPDATE_ACCOUNT_DATA_COMPLETE = 0xBADD, SMSG_UPDATE_COMBO_POINTS = 0xBADD, SMSG_UPDATE_CURRENCY = 0xBADD, diff --git a/src/server/game/Server/WorldPacket.h b/src/server/game/Server/WorldPacket.h index d04679adbd4..e7d6d4fb5b1 100644 --- a/src/server/game/Server/WorldPacket.h +++ b/src/server/game/Server/WorldPacket.h @@ -23,8 +23,6 @@ #include "Opcodes.h" #include "ByteBuffer.h" -struct z_stream_s; - class WorldPacket : public ByteBuffer { public: diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 1bc2dc7cc82..ac25da2d559 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -21,7 +21,6 @@ */ #include "WorldSocket.h" -#include "Packet.h" #include <zlib.h> #include "Config.h" #include "Common.h" @@ -49,6 +48,7 @@ #include "WardenWin.h" #include "WardenMac.h" #include "BattlenetServerManager.h" +#include "CharacterPackets.h" namespace { @@ -1042,25 +1042,32 @@ void WorldSession::SetPlayer(Player* player) void WorldSession::InitializeQueryCallbackParameters() { // Callback parameters that have pointers in them should be properly - // initialized to NULL here. - _charCreateCallback.SetParam(NULL); + // initialized to nullptr here. + _charRenameCallback.SetParam(nullptr); } void WorldSession::ProcessQueryCallbacks() { PreparedQueryResult result; - //! HandleCharEnumOpcode - if (_charEnumCallback.valid() && _charEnumCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + //! HandleCharEnumOpcode and HandleCharUndeleteEnumOpcode + if (_charEnumCallback.IsReady()) { - result = _charEnumCallback.get(); - HandleCharEnum(result); + _charEnumCallback.GetResult(result); + + if (bool undelete = _charEnumCallback.GetParam()) + HandleCharUndeleteEnum(result); + else + HandleCharEnum(result); + + _charEnumCallback.FreeResult(); } + //! HandleCharCreateOpcode if (_charCreateCallback.IsReady()) { _charCreateCallback.GetResult(result); - HandleCharCreateCallback(result, _charCreateCallback.GetParam()); + HandleCharCreateCallback(result, _charCreateCallback.GetParam().get()); } //! HandlePlayerLoginOpcode @@ -1083,12 +1090,27 @@ void WorldSession::ProcessQueryCallbacks() if (_charRenameCallback.IsReady()) { _charRenameCallback.GetResult(result); - CharacterRenameInfo* renameInfo = _charRenameCallback.GetParam(); - HandleChangePlayerNameOpcodeCallBack(result, renameInfo); + WorldPackets::Character::CharacterRenameInfo* renameInfo = _charRenameCallback.GetParam(); + HandleCharRenameCallBack(result, renameInfo); delete renameInfo; _charRenameCallback.Reset(); } + /// HandleUndeleteCooldownStatusOpcode + /// wait until no char undelete is in progress + if (!_charUndeleteCallback.GetStage() && _undeleteCooldownStatusCallback.IsReady()) + { + _undeleteCooldownStatusCallback.GetResult(result); + HandleUndeleteCooldownStatusCallback(result); + } + + /// HandleCharUndeleteOpcode + if (_charUndeleteCallback.IsReady()) + { + _charUndeleteCallback.GetResult(result); + HandleCharUndeleteCallback(result, _charUndeleteCallback.GetParam().get()); + } + //- HandleCharAddIgnoreOpcode if (_addIgnoreCallback.valid() && _addIgnoreCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index f195bf2908d..6ab85070925 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -54,6 +54,7 @@ struct DeclinedName; struct ItemTemplate; struct MovementInfo; struct TradeStatusInfo; +struct z_stream_s; namespace lfg { @@ -73,10 +74,17 @@ class RBACData; namespace WorldPackets { - class ServerPacket; - namespace Character { + struct CharacterCreateInfo; + struct CharacterRenameInfo; + struct CharacterCustomizeInfo; + struct CharacterFactionChangeInfo; + struct CharacterUndeleteInfo; + + class CharacterCreate; + class CharacterDelete; + class UndeleteCharacter; class PlayerLogin; } } @@ -176,6 +184,7 @@ private: PacketFilter(PacketFilter const& right) = delete; PacketFilter& operator=(PacketFilter const& right) = delete; }; + //process only thread-safe packets in Map::Update() class MapSessionFilter : public PacketFilter { @@ -199,62 +208,6 @@ public: virtual bool Process(WorldPacket* packet) override; }; -// 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: - /// User specified variables - std::string Name; - uint8 Race = 0; - uint8 Class = 0; - uint8 Gender = GENDER_NONE; - uint8 Skin = 0; - uint8 Face = 0; - uint8 HairStyle = 0; - uint8 HairColor = 0; - uint8 FacialHair = 0; - uint8 OutfitId = 0; - - /// Server side data - uint8 CharCount = 0; -}; - -struct CharacterRenameInfo -{ - friend class WorldSession; - - protected: - ObjectGuid Guid; - std::string Name; -}; - -struct CharacterCustomizeInfo : public CharacterRenameInfo -{ - friend class Player; - friend class WorldSession; - - protected: - uint8 Gender = GENDER_NONE; - uint8 Skin = 0; - uint8 Face = 0; - uint8 HairStyle = 0; - uint8 HairColor = 0; - uint8 FacialHair = 0; -}; - -struct CharacterFactionChangeInfo : public CharacterCustomizeInfo -{ - friend class Player; - friend class WorldSession; - - protected: - uint8 Race = 0; -}; - struct PacketCounter { time_t lastReceiveTime; @@ -337,6 +290,9 @@ class WorldSession /// Handle the authentication waiting queue (to be completed) void SendAuthWaitQue(uint32 position); + void SendSetTimeZoneInformation(); + void SendFeatureSystemStatusGlueScreen(); + void SendNameQueryOpcode(ObjectGuid guid); void SendTrainerList(ObjectGuid guid); @@ -471,25 +427,38 @@ class WorldSession void Handle_ServerSide(WorldPacket& recvPacket); // sever side only, can't be accepted from client void Handle_Deprecated(WorldPacket& recvPacket); // never used anymore by client - void HandleCharEnumOpcode(WorldPacket& recvPacket); - void HandleCharDeleteOpcode(WorldPacket& recvPacket); - void HandleCharCreateOpcode(WorldPacket& recvPacket); - void HandleCharCreateCallback(PreparedQueryResult result, CharacterCreateInfo* createInfo); + void HandleCharEnum(PreparedQueryResult result); + void HandleCharEnumOpcode(WorldPacket& /*recvData*/); + void HandleCharUndeleteEnum(PreparedQueryResult result); + void HandleCharUndeleteEnumOpcode(WorldPacket& /*recvData*/); + void HandleCharDeleteOpcode(WorldPackets::Character::CharacterDelete& charDelete); + void HandleCharCreateOpcode(WorldPackets::Character::CharacterCreate& charCreate); + void HandleCharCreateCallback(PreparedQueryResult result, WorldPackets::Character::CharacterCreateInfo* createInfo); void HandlePlayerLoginOpcode(WorldPackets::Character::PlayerLogin& playerLogin); void HandleLoadScreenOpcode(WorldPacket& recvPacket); - void HandleCharEnum(PreparedQueryResult result); void HandlePlayerLogin(LoginQueryHolder * holder); + void HandleCharRenameOpcode(WorldPacket& recvData); + void HandleCharRenameCallBack(PreparedQueryResult result, WorldPackets::Character::CharacterRenameInfo const* renameInfo); + void HandleSetPlayerDeclinedNames(WorldPacket& recvData); + void HandleAlterAppearance(WorldPacket& recvData); void HandleCharFactionOrRaceChange(WorldPacket& recvData); void HandleRandomizeCharNameOpcode(WorldPacket& recvData); void HandleReorderCharacters(WorldPacket& recvData); void HandleOpeningCinematic(WorldPacket& recvData); + void HandleUndeleteCooldownStatusQuery(WorldPacket& /*recvData*/); + void HandleUndeleteCooldownStatusCallback(PreparedQueryResult result); + void HandleCharUndeleteOpcode(WorldPackets::Character::UndeleteCharacter& undeleteInfo); + void HandleCharUndeleteCallback(PreparedQueryResult result, WorldPackets::Character::CharacterUndeleteInfo* undeleteInfo); + void SendCharCreate(ResponseCodes result); void SendCharDelete(ResponseCodes result); - void SendCharRename(ResponseCodes result, CharacterRenameInfo const& renameInfo); - void SendCharCustomize(ResponseCodes result, CharacterCustomizeInfo const& customizeInfo); - void SendCharFactionChange(ResponseCodes result, CharacterFactionChangeInfo const& factionChangeInfo); + void SendCharRename(ResponseCodes result, WorldPackets::Character::CharacterRenameInfo const& renameInfo); + void SendCharCustomize(ResponseCodes result, WorldPackets::Character::CharacterCustomizeInfo const& customizeInfo); + void SendCharFactionChange(ResponseCodes result, WorldPackets::Character::CharacterFactionChangeInfo const& factionChangeInfo); void SendSetPlayerDeclinedNamesResult(DeclinedNameResult result, ObjectGuid guid); void SendBarberShopResult(BarberShopResult result); + void SendUndeleteCooldownStatusResponse(uint32 currentCooldown, uint32 maxCooldown); + void SendUndeleteCharacterResponse(CharacterUndeleteResult result, WorldPackets::Character::CharacterUndeleteInfo const* undeleteInfo); // played time void HandlePlayedTime(WorldPacket& recvPacket); @@ -858,10 +827,6 @@ class WorldSession void HandleSetActionBarToggles(WorldPacket& recvData); - void HandleCharRenameOpcode(WorldPacket& recvData); - void HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult result, CharacterRenameInfo const* renameInfo); - void HandleSetPlayerDeclinedNames(WorldPacket& recvData); - void HandleTotemDestroyed(WorldPacket& recvData); void HandleDismissCritter(WorldPacket& recvData); @@ -1026,7 +991,6 @@ class WorldSession // Miscellaneous void HandleSpellClick(WorldPacket& recvData); void HandleMirrorImageDataRequest(WorldPacket& recvData); - void HandleAlterAppearance(WorldPacket& recvData); void HandleRemoveGlyph(WorldPacket& recvData); void HandleCharCustomize(WorldPacket& recvData); void HandleQueryInspectAchievements(WorldPacket& recvData); @@ -1057,15 +1021,17 @@ class WorldSession void InitializeQueryCallbackParameters(); void ProcessQueryCallbacks(); - PreparedQueryResultFuture _charEnumCallback; PreparedQueryResultFuture _addIgnoreCallback; PreparedQueryResultFuture _stablePetCallback; - QueryCallback<PreparedQueryResult, CharacterRenameInfo*> _charRenameCallback; + QueryCallback<PreparedQueryResult, bool> _charEnumCallback; QueryCallback<PreparedQueryResult, std::string> _addFriendCallback; QueryCallback<PreparedQueryResult, uint32> _unstablePetCallback; QueryCallback<PreparedQueryResult, uint32> _stableSwapCallback; QueryCallback<PreparedQueryResult, ObjectGuid> _sendStabledPetCallback; - QueryCallback<PreparedQueryResult, CharacterCreateInfo*, true> _charCreateCallback; + QueryCallback<PreparedQueryResult, std::shared_ptr<WorldPackets::Character::CharacterCreateInfo>, true> _charCreateCallback; + QueryCallback<PreparedQueryResult, WorldPackets::Character::CharacterRenameInfo*> _charRenameCallback; + QueryCallback<PreparedQueryResult, bool, true> _undeleteCooldownStatusCallback; + QueryCallback<PreparedQueryResult, std::shared_ptr<WorldPackets::Character::CharacterUndeleteInfo>, true> _charUndeleteCallback; QueryResultHolderFuture _charLoginCallback; friend class World; diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index 556913c60a6..79d96200d80 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -389,7 +389,7 @@ void fixNULLfields(std::string &line) DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, std::string name, ObjectGuid::LowType guid) { uint32 charcount = AccountMgr::GetCharactersCount(account); - if (charcount >= 10) + if (charcount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) return DUMP_TOO_MANY_CHARS; FILE* fin = fopen(file.c_str(), "r"); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 7e3f8351dbc..875bf2bba19 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -271,6 +271,10 @@ void World::AddSession_(WorldSession* s) } s->SendAuthResponse(AUTH_OK, false); + + s->SendSetTimeZoneInformation(); + s->SendFeatureSystemStatusGlueScreen(); + s->SendAddonsInfo(); s->SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION)); s->SendTutorialsData(); @@ -707,11 +711,11 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK] = sConfigMgr->GetIntDefault("CharacterCreating.Disabled.RaceMask", 0); m_int_configs[CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK] = sConfigMgr->GetIntDefault("CharacterCreating.Disabled.ClassMask", 0); - m_int_configs[CONFIG_CHARACTERS_PER_REALM] = sConfigMgr->GetIntDefault("CharactersPerRealm", 10); - if (m_int_configs[CONFIG_CHARACTERS_PER_REALM] < 1 || m_int_configs[CONFIG_CHARACTERS_PER_REALM] > 10) + m_int_configs[CONFIG_CHARACTERS_PER_REALM] = sConfigMgr->GetIntDefault("CharactersPerRealm", MAX_CHARACTERS_PER_REALM); + if (m_int_configs[CONFIG_CHARACTERS_PER_REALM] < 1 || m_int_configs[CONFIG_CHARACTERS_PER_REALM] > MAX_CHARACTERS_PER_REALM) { TC_LOG_ERROR("server.loading", "CharactersPerRealm (%i) must be in range 1..10. Set to 10.", m_int_configs[CONFIG_CHARACTERS_PER_REALM]); - m_int_configs[CONFIG_CHARACTERS_PER_REALM] = 10; + m_int_configs[CONFIG_CHARACTERS_PER_REALM] = MAX_CHARACTERS_PER_REALM; } // must be after CONFIG_CHARACTERS_PER_REALM @@ -1226,6 +1230,11 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_WARDEN_CLIENT_FAIL_ACTION] = sConfigMgr->GetIntDefault("Warden.ClientCheckFailAction", 0); m_int_configs[CONFIG_WARDEN_CLIENT_RESPONSE_DELAY] = sConfigMgr->GetIntDefault("Warden.ClientResponseDelay", 600); + // Feature System + m_bool_configs[CONFIG_FEATURE_SYSTEM_BPAY_STORE_ENABLED] = sConfigMgr->GetBoolDefault("FeatureSystem.BpayStore.Enabled", false); + m_bool_configs[CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED] = sConfigMgr->GetBoolDefault("FeatureSystem.CharacterUndelete.Enabled", false); + m_int_configs[CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_COOLDOWN] = sConfigMgr->GetIntDefault("FeatureSystem.CharacterUndelete.Cooldown", 2592000); + // Dungeon finder m_int_configs[CONFIG_LFG_OPTIONSMASK] = sConfigMgr->GetIntDefault("DungeonFinder.OptionsMask", 1); @@ -3190,7 +3199,7 @@ void World::ProcessQueryCallbacks() { PreparedQueryResult result; - for (std::deque<std::future<PreparedQueryResult>>::iterator itr = m_realmCharCallbacks.begin(); itr != m_realmCharCallbacks.end(); ) + for (std::deque<PreparedQueryResultFuture>::iterator itr = m_realmCharCallbacks.begin(); itr != m_realmCharCallbacks.end(); ) { if ((*itr).wait_for(std::chrono::seconds(0)) != std::future_status::ready) { diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index ae07a5434f1..4f4c3451b02 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -170,6 +170,8 @@ enum WorldBoolConfigs CONFIG_ALLOW_TRACK_BOTH_RESOURCES, CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA, CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, + CONFIG_FEATURE_SYSTEM_BPAY_STORE_ENABLED, + CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED, BOOL_CONFIG_VALUE_COUNT }; @@ -358,6 +360,7 @@ enum WorldIntConfigs CONFIG_BG_REWARD_WINNER_CONQUEST_LAST, CONFIG_CREATURE_PICKPOCKET_REFILL, CONFIG_AHBOT_UPDATE_INTERVAL, + CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_COOLDOWN, INT_CONFIG_VALUE_COUNT }; @@ -510,6 +513,8 @@ enum WorldStates WS_GUILD_WEEKLY_RESET_TIME = 20050, // Next guild week reset time }; +#define MAX_CHARACTERS_PER_REALM 11 + /// Storage class for commands issued for delayed execution struct CliCommandHolder { @@ -878,7 +883,7 @@ class World void LoadCharacterNameData(); void ProcessQueryCallbacks(); - std::deque<std::future<PreparedQueryResult>> m_realmCharCallbacks; + std::deque<PreparedQueryResultFuture> m_realmCharCallbacks; }; extern Battlenet::RealmHandle realmHandle; diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index cfde57f3184..0b351e310f4 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -105,7 +105,7 @@ public: if (isNumeric(searchString.c_str())) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID); - stmt->setUInt64(0, uint32(atoi(searchString.c_str()))); + stmt->setUInt64(0, strtoull(searchString.c_str(), nullptr, 10)); result = CharacterDatabase.Query(stmt); } // search by name @@ -205,7 +205,7 @@ public: // check character count uint32 charcount = AccountMgr::GetCharactersCount(delInfo.accountId); - if (charcount >= 10) + if (charcount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) { handler->PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_FULL, delInfo.name.c_str(), delInfo.guid.ToString().c_str(), delInfo.accountId); return; diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 8abf6571af2..796b5d27a1e 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -51,12 +51,21 @@ void CharacterDatabaseConnection::DoPrepareStatements() "cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? " "LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_UNDELETE_ENUM, "SELECT c.guid, c.deleteInfos_Name, c.race, c.class, c.gender, c.playerBytes, c.playerBytes2, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, " + "gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot " + "FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid " + "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.deleteInfos_Account = ? AND c.deleteInfos_Name IS NOT NULL", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_UNDELETE_ENUM_DECLINED_NAME, "SELECT c.guid, c.deleteInfos_Name, c.race, c.class, c.gender, c.playerBytes, c.playerBytes2, c.level, c.zone, c.map, " + "c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, " + "cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? " + "LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid " + "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.deleteInfos_Account = ? AND c.deleteInfos_Name IS NOT NULL", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT name FROM characters WHERE guid = ? AND account = ? AND (at_login & ?) = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GUID_RACE_ACC_BY_NAME, "SELECT guid, race, account FROM characters WHERE name = ?", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_CHAR_RACE, "SELECT race FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_LEVEL, "SELECT level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH); @@ -432,7 +441,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_CHARACTER_POSITION, "UPDATE characters SET position_x = ?, position_y = ?, position_z = ?, orientation = ?, map = ?, zone = ?, trans_x = 0, trans_y = 0, trans_z = 0, transguid = 0, taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name, character_aura.remaintime FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_ONLINE, "SELECT name, account, map, zone FROM characters WHERE online > 0", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_DEL_INFO, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID, "SELECT guid FROM characters WHERE account = ?", CONNECTION_SYNCH); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 1a1b2f56669..329f2a5c23d 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -63,6 +63,8 @@ enum CharacterDatabaseStatements CHAR_SEL_MAIL_LIST_ITEMS, CHAR_SEL_ENUM, CHAR_SEL_ENUM_DECLINED_NAME, + CHAR_SEL_UNDELETE_ENUM, + CHAR_SEL_UNDELETE_ENUM_DECLINED_NAME, CHAR_SEL_FREE_NAME, CHAR_SEL_GUID_RACE_ACC_BY_NAME, CHAR_SEL_CHAR_RACE, diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp index 75915a4eeaf..c0c9751a804 100644 --- a/src/server/shared/Database/Implementation/LoginDatabase.cpp +++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp @@ -136,4 +136,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_UPD_BNET_ACCOUNT_LOCK, "UPDATE battlenet_accounts SET locked = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_ACCOUNT_LOCK_CONTRY, "UPDATE battlenet_accounts SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT, "SELECT battlenet_account FROM account WHERE id = ?", CONNECTION_SYNCH); + + PrepareStatement(LOGIN_SEL_LAST_CHAR_UNDELETE, "SELECT LastCharacterUndelete FROM battlenet_accounts WHERE Id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_LAST_CHAR_UNDELETE, "UPDATE battlenet_accounts SET LastCharacterUndelete = UNIX_TIMESTAMP() WHERE Id = ?", CONNECTION_ASYNC); } diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h index 78d5c55559f..bf4b8511e67 100644 --- a/src/server/shared/Database/Implementation/LoginDatabase.h +++ b/src/server/shared/Database/Implementation/LoginDatabase.h @@ -153,6 +153,9 @@ enum LoginDatabaseStatements LOGIN_UPD_BNET_ACCOUNT_LOCK_CONTRY, LOGIN_SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT, + LOGIN_SEL_LAST_CHAR_UNDELETE, + LOGIN_UPD_LAST_CHAR_UNDELETE, + MAX_LOGINDATABASE_STATEMENTS }; diff --git a/src/server/shared/Threading/Callback.h b/src/server/shared/Threading/Callback.h index b462fce25d5..03073d4a792 100644 --- a/src/server/shared/Threading/Callback.h +++ b/src/server/shared/Threading/Callback.h @@ -45,7 +45,7 @@ class QueryCallback return _result; } - int IsReady() + bool IsReady() { return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready; } @@ -128,7 +128,7 @@ class QueryCallback_2 return _result; } - int IsReady() + bool IsReady() { return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready; } diff --git a/src/server/shared/Utilities/Util.h b/src/server/shared/Utilities/Util.h index e2d8cd3a195..216fe08d64a 100644 --- a/src/server/shared/Utilities/Util.h +++ b/src/server/shared/Utilities/Util.h @@ -30,9 +30,11 @@ template<typename T> struct Optional -{ - T value; - bool HasValue; +{ + Optional() : value(), HasValue(false) { } + + T value; + bool HasValue; }; // Searcher for map of structs diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index a927e45e89e..4d40365451e 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1074,10 +1074,31 @@ Account.PasswordChangeSecurity = 0 # Description: Set to date of project's birth in UNIX time. By default the date when # TrinityCore was started (Thu Oct 2, 2008) # Default: 1222964635 + +BirthdayTime = 1222964635 + +# +# FeatureSystem.BpayStore.Enabled +# Description: Not yet implemented +# Default: 0 - (Disabled) +# 1 - (Enabled) + +FeatureSystem.BpayStore.Enabled = 0 + # +# FeatureSystem.CharacterUndelete.Enabled +# Description: Controls Feature in CharacterList to restore delete Characters. +# Default: 0 - (Disabled) +# 1 - (Enabled, Experimental) + +FeatureSystem.CharacterUndelete.Enabled = 0 + # +# FeatureSystem.CharacterUndelete.Cooldown +# Description: Time between available character restorations. (in sec) +# Default: 2592000 (30 days) -BirthdayTime = 1222964635 +FeatureSystem.CharacterUndelete.Cooldown = 2592000 # ################################################################################################### |
