From fe96d310d6060aa851f9cb7d7cf860c01e9bd394 Mon Sep 17 00:00:00 2001 From: nico Date: Mon, 12 Oct 2026 10:17:43 +0000 Subject: [PATCH] core: add clientless player session bootstrap --- src/server/game/Handlers/CharacterHandler.cpp | 23 ++++ src/server/game/Server/WorldSession.cpp | 12 ++ src/server/game/Server/WorldSession.h | 5 + src/server/game/World/World.cpp | 103 ++++++++++++++++++ src/server/game/World/World.h | 1 + 5 files changed, 144 insertions(+) diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index dcd6717469..5b321a8713 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -754,6 +754,29 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPackets::Character::PlayerLogin& SendConnectToInstance(WorldPackets::Auth::ConnectToSerial::WorldAttempt1); } +bool WorldSession::InitializeClientlessPlayer(ObjectGuid playerGuid) +{ + if (!IsClientless()) + { + TC_LOG_ERROR("server.worldserver", + "WorldSession::InitializeClientlessPlayer rejected a non-clientless session for account %u.", + GetAccountId()); + return false; + } + + if (!playerGuid || !_clientlessPendingPlayerGuid.IsEmpty() || PlayerLoading() || GetPlayer()) + { + TC_LOG_ERROR("server.worldserver", + "WorldSession::InitializeClientlessPlayer rejected invalid initialization state for account %u, character GUID %u.", + GetAccountId(), uint32(playerGuid.GetCounter())); + return false; + } + + _clientlessPendingPlayerGuid = playerGuid; + InitializeSession(); + return true; +} + bool WorldSession::BeginClientlessPlayerLogin(ObjectGuid playerGuid) { if (!IsClientless()) diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index be997d065e..b46c8cb7c7 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -115,6 +115,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr m_Socket[2]; std::string m_Address; // Current Remote Address // std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session! diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 32bceb480e..478c893b6f 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -379,6 +379,109 @@ bool World::RemoveClientlessSession(uint32 characterGuid) return true; } +bool World::CreateClientlessPlayerSession(uint32 accountId, uint32 characterGuid) +{ + if (!accountId || !characterGuid) + { + TC_LOG_ERROR("server.worldserver", + "World::CreateClientlessPlayerSession rejected account %u, character GUID %u.", + accountId, characterGuid); + return false; + } + + if (FindClientlessSession(characterGuid)) + { + TC_LOG_ERROR("server.worldserver", + "World::CreateClientlessPlayerSession rejected duplicate character GUID %u.", + characterGuid); + return false; + } + + if (FindSession(accountId)) + { + TC_LOG_ERROR("server.worldserver", + "World::CreateClientlessPlayerSession rejected account %u because a network session is active.", + accountId); + return false; + } + + QueryResult characterResult = CharacterDatabase.PQuery( + "SELECT account FROM characters WHERE guid = %u", characterGuid); + if (!characterResult) + { + TC_LOG_ERROR("server.worldserver", + "World::CreateClientlessPlayerSession could not find character GUID %u.", + characterGuid); + return false; + } + + uint32 characterAccountId = characterResult->Fetch()[0].GetUInt32(); + if (characterAccountId != accountId) + { + TC_LOG_ERROR("server.worldserver", + "World::CreateClientlessPlayerSession rejected character GUID %u: character account %u does not match requested account %u.", + characterGuid, characterAccountId, accountId); + return false; + } + + QueryResult accountResult = LoginDatabase.PQuery( + "SELECT username, expansion, mutetime, locale, recruiter FROM account WHERE id = %u", + accountId); + if (!accountResult) + { + TC_LOG_ERROR("server.worldserver", + "World::CreateClientlessPlayerSession could not find account %u.", + accountId); + return false; + } + + Field* accountFields = accountResult->Fetch(); + std::string accountName = accountFields[0].GetString(); + uint8 expansion = accountFields[1].GetUInt8(); + time_t muteTime = time_t(accountFields[2].GetUInt64()); + LocaleConstant locale = LocaleConstant(accountFields[3].GetUInt8()); + uint32 recruiter = accountFields[4].GetUInt32(); + + if (locale >= TOTAL_LOCALES) + locale = LOCALE_enUS; + + QueryResult recruiterResult = LoginDatabase.PQuery( + "SELECT 1 FROM account WHERE recruiter = %u LIMIT 1", accountId); + bool isRecruiter = bool(recruiterResult); + + AccountTypes security = AccountTypes(AccountMgr::GetSecurity(accountId, realm.Id.Realm)); + + WorldSession* session = new WorldSession( + accountId, + std::move(accountName), + nullptr, + security, + expansion, + muteTime, + locale, + recruiter, + isRecruiter, + WorldSession::WorldSessionMode::Clientless); + + if (!AddClientlessSession(characterGuid, session)) + { + delete session; + return false; + } + + ObjectGuid playerGuid = ObjectGuid::Create(characterGuid); + if (!session->InitializeClientlessPlayer(playerGuid)) + { + RemoveClientlessSession(characterGuid); + return false; + } + + TC_LOG_INFO("server.worldserver", + "Created clientless player session for character GUID %u (account %u).", + characterGuid, accountId); + return true; +} + bool World::RemoveSession(uint32 id) { ///- Find the session, kick the user, but we can't delete session at this moment to prevent iterator invalidation diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index dfdb13b162..79952075e1 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -612,6 +612,7 @@ class TC_GAME_API World WorldSession* FindClientlessSession(uint32 characterGuid) const; bool AddClientlessSession(uint32 characterGuid, WorldSession* session); bool RemoveClientlessSession(uint32 characterGuid); + bool CreateClientlessPlayerSession(uint32 accountId, uint32 characterGuid); SessionMap const& GetAllClientlessSessions() const { return m_clientlessSessions; } uint32 GetClientlessSessionCount() const { return uint32(m_clientlessSessions.size()); } void AddInstanceSocket(std::weak_ptr sock, uint64 connectToKey);