Online Manager Subsystem Refactor¶
Goal:
Split UOnlineManagerSubsystem into three focused “services” while keeping all existing behavior, Blueprint APIs, and call sites working throughout the refactor. The steps are designed so Codex can apply them incrementally.
For this doc I’ll name the three services:
- Auth / Identity Service –
FOnlineAuthService - EOS House (Lobby + Session + Networking) Service –
FEosHouseService - Nakama House Service –
FNakamaHouseService
If your seed uses different names, just do a global search/replace when following these steps.
1. Current Responsibilities (Quick Map)¶
1.1 Auth / Identity responsibilities¶
These live in UOnlineManagerSubsystem today and should move into FOnlineAuthService:
- Managing UE Online Services (
Services,Auth) and EOS platform handles. - Reading config to detect Steam vs DeviceId paths.
-
Device ID + EOS Connect auth flows:
-
LoginForLocalPlayer() LoginWithDeviceIdViaAuth()-
StartAuthLoginWithDeviceId()and its callback chain (HandleLoginSuccess,HandleLoginFailure,HandleAuthLoginComplete,CreateDeviceIdIfNeeded,DeleteDeviceIdForTesting,HandleCreateDeviceIdComplete,HandleConnectLoginComplete,HandleCreateUserComplete,OnCreateDeviceIdComplete,OnDeleteDeviceIdComplete,OnConnectLoginComplete,OnCreateUserComplete). -
EOS Product User Id (
LocalPuid) andFGameIdentity/CachedIdentitylifetime. - Login mode flags like
bUseDeviceIdConnectOnly,bIsLoggedIn, device-reset flags, continuation tokens, etc.
This service will own “who am I on EOS / UE Online Services?”
1.2 EOS House / Networking responsibilities¶
To move into FEosHouseService:
-
EOS Lobby & Session lifecycle for the House:
-
CleanupAndCreateEosLobby,OnCleanupLobbySearchCompleted,CreateEosLobby,OnCreateLobbyComplete. OnJoinLobbySearchCompleted&OnEosLobbyJoinCompleted(further down in .cpp).-
CleanupAndCreateEosSession,OnDestroyedEosSession,CreateEosSession,OnCreatedEosSession. -
EOS networking preparation & NetDriver wiring:
-
PrepareNetDriverForListenServer,OpenLevelAsListenServer. -
PrepareForEOSNetworking,EnableListenServerOnCurrentMap. -
Multi-instance EOS-Connect behavior for PIE (looping over
FOnlineServicesRegistry::Get().GetAllServicesInstances, logging in each with DeviceId, etc.). -
Members around:
-
PlatformHandle,ConnectHandle,InLobbyHandle,SearchHandle,HouseLobbyDefaultMaxPlayer,CurrentHouse,PendingEosLobbyIdForNakama,PendingEosSessionId,PendingSessionCreateContinuation,OnEosSessionCreated, anyOnEosLobbyCreatedmulticast, and flags likebPendingListenServerEnable,bHasAutoJoinedOnFirstLogin.
This service will own “how do I host/join a House at EOS level and ensure NetDriver/EOS networking is ready?”
1.3 Nakama House responsibilities¶
To move into FNakamaHouseService:
-
Interfacing with
UNakamaConnectionSubsystem(grab client & session, call RPCs). -
TryGetNakamaClientAndSession. -
Nakama RPC flows:
-
CallNakamaRPCForHouseOpen(calls RPC"house_open"). CallNakamaRPCForListHouses(calls RPC"house_list").-
All House RPC handlers:
OnHouseOpenSuccess,OnHouseOpenError,OnListHousesSuccess,OnListHousesError, and the ones mapping RPC payloads toFHouseSummary,CurrentHouse, etc. (they appear below the truncated part). -
House host/list/join orchestration at Nakama level:
-
The
HostHouse/ListHouses/JoinHouseAPIs and the pending callback sets:ClearHostCallbacks,CompleteHostSuccess,CompleteHostFailure.ClearListCallbacks,CompleteListSuccess,CompleteListFailure.ClearJoinLobbyCallbacks,CompleteJoinLobbySuccess,CompleteJoinLobbyFailure.
-
EOS↔Nakama handshake:
HandleEosLobbyCreated,BeginEosSessionCreation,OpenHouseWithIds, and the auto-join-on-first-login logic. -
Interaction with
UNakamaConnectionSubsystem::LoginWithEOSIdentityand its login-complete delegate.
This service will own “how does the House concept live in Nakama (storage, RPC) and tie back to EOS lobby/session IDs + PUID?”
2. Target Structure¶
After refactor, the relevant files will be:
Core/Subsystems/OnlineManagerSubsystem.h/.cppCore/Subsystems/Online/OnlineAuthService.h/.cpp(FOnlineAuthService)Core/Subsystems/Online/OnlineEosHouseService.h/.cpp(FEosHouseService)Core/Subsystems/Online/OnlineNakamaHouseService.h/.cpp(FNakamaHouseService)
You can pick slightly different paths/names; just update includes accordingly.
UOnlineManagerSubsystem stays as the facade exposing:
- Blueprint / public methods (same signatures as now).
- Minimal glue that forwards calls into the three services.
- High-level orchestration (if any cross-service coordination remains).
3. Refactor Plan – Incremental Steps¶
Each “Phase” is safe to give to Codex as one or more prompts. Within a phase, the order matters, but individual bullet points can be done in one patch.
Phase 0 – Prep & Guard Rails¶
-
Confirm current behavior and references
-
Search for all references to
UOnlineManagerSubsystemin the project (C++ & Blueprint). -
Note which methods are externally used (BlueprintCallable or external C++ call sites). These public surfaces must remain stable.
-
Add a comment banner to
UOnlineManagerSubsystem -
At the top of
.h/.cpp, add a brief comment block saying this class is being refactored into three internal services and that public API must not change during refactor. This is just for human/Codex context.
Phase 1 – Introduce Service Types (No Behavior Changes)¶
Goal: Create empty service classes and wire them into the subsystem, but don’t move logic yet.
-
Forward declarations & members in
OnlineManagerSubsystem.h -
Add forward declarations:
class FOnlineAuthService; class FEosHouseService; class FNakamaHouseService; -
Add private members (likely as
TUniquePtr):TUniquePtr<FOnlineAuthService> AuthService; TUniquePtr<FEosHouseService> EosHouseService; TUniquePtr<FNakamaHouseService> NakamaHouseService; -
Optionally add small getter helpers (private or public):
FOnlineAuthService* GetAuthService() { return AuthService.Get(); } FEosHouseService* GetEosHouseService() { return EosHouseService.Get(); } FNakamaHouseService* GetNakamaHouseService(){ return NakamaHouseService.Get(); } -
Create header for the services
Create OnlineAuthService.h with minimal skeleton:
#pragma once
#include "CoreMinimal.h"
class UOnlineManagerSubsystem;
class FOnlineAuthService
{
public:
explicit FOnlineAuthService(UOnlineManagerSubsystem& InOwner);
void Initialize();
void Deinitialize();
// Public facade API (to be filled in later)
void LoginForLocalPlayer();
// ... additional methods will be added later ...
private:
UOnlineManagerSubsystem& Owner;
};
Similarly create:
OnlineEosHouseService.hwithFEosHouseService.-
OnlineNakamaHouseService.hwithFNakamaHouseService. -
Create
.cppstubs
For each service, create a minimal implementation:
#include "Core/Subsystems/OnlineManagerSubsystem.h"
#include "Core/Subsystems/Online/OnlineAuthService.h"
FOnlineAuthService::FOnlineAuthService(UOnlineManagerSubsystem& InOwner)
: Owner(InOwner)
{}
void FOnlineAuthService::Initialize() {}
void FOnlineAuthService::Deinitialize() {}
void FOnlineAuthService::LoginForLocalPlayer()
{
// Temporary stub – calls back into existing subsystem implementation (to keep behavior).
Owner.LoginForLocalPlayer();
}
For the other two services, just implement ctor/Initialize/Deinitialize as empty for now.
- Initialize / Deinitialize services in
UOnlineManagerSubsystem
In UOnlineManagerSubsystem::Initialize just after Super::Initialize(Collection), construct the services:
AuthService = MakeUnique<FOnlineAuthService>(*this);
EosHouseService = MakeUnique<FEosHouseService>(*this);
NakamaHouseService = MakeUnique<FNakamaHouseService>(*this);
AuthService->Initialize();
EosHouseService->Initialize();
NakamaHouseService->Initialize();
In Deinitialize (before Super::Deinitialize()):
if (AuthService) { AuthService->Deinitialize(); }
if (EosHouseService) { EosHouseService->Deinitialize(); }
if (NakamaHouseService){ NakamaHouseService->Deinitialize(); }
AuthService.Reset();
EosHouseService.Reset();
NakamaHouseService.Reset();
-
Compile & run quick smoke test
-
Nothing should change at runtime yet; all real work still lives in
UOnlineManagerSubsystem.
Phase 2 – Extract Auth / Identity Service¶
Goal: Move all login/identity-specific code (but not House) into FOnlineAuthService. Keep UOnlineManagerSubsystem as a thin facade.
2.1 Move state fields¶
In OnlineManagerSubsystem.h:
-
Identify all private members that are purely related to auth/identity:
-
TSharedPtr<IOnlineServices> Services; TSharedPtr<IAuth> Auth;FGameIdentity CachedIdentity;bool bIsLoggedIn;bool bUseDeviceIdConnectOnly;EOS_ProductUserId LocalPuid;- DeviceId reset flags (
bResetDeviceIdOnThisLaunch,bRecreateAndLoginAfterDelete), continuation tokens,LoginPlatformUserId,PendingContinuationId,PendingContinuanceToken,DeviceDisplayNameAnsi,DeviceUserLoginInfo, etc. -
FDelegateHandle OnOnlineServicesCreatedHandle;and any helpers used only by auth. -
Move these fields into
FOnlineAuthServiceas private members. -
Leave lightweight getters on the subsystem that delegate to the service, e.g.:
bool IsLoggedIn() const { return AuthService ? AuthService->IsLoggedIn() : false; }
FString GetAccountIdString() const { return AuthService ? AuthService->GetAccountIdString() : FString(); }
EOS_ProductUserId GetLocalPuid() const { return AuthService ? AuthService->GetLocalPuid() : nullptr; }
2.2 Move auth-related methods¶
In OnlineManagerSubsystem.cpp:
- Create method wrappers in
FOnlineAuthService
For each auth-related method, add a corresponding public method in FOnlineAuthService, for example:
void LoginForLocalPlayer();void LoginWithDeviceIdViaAuth();void StartAuthLoginWithDeviceId();void HandleLoginSuccess(const TSharedRef<const FAccountInfo>& AccountInfo);void HandleLoginFailure(const FString& ErrorMessage);void HandleAuthLoginComplete(EGameLoginResult Result, const FGameIdentity& Identity);void CreateDeviceIdIfNeeded();void DeleteDeviceIdForTesting(bool bAndThenLogin);void HandleCreateDeviceIdComplete(const EOS_Connect_CreateDeviceIdCallbackInfo* Data);void HandleConnectLoginComplete(const EOS_Connect_LoginCallbackInfo* Data);void HandleCreateUserComplete(const EOS_Connect_CreateUserCallbackInfo* Data);void OnCreateDeviceIdComplete(const EOS_Connect_CreateDeviceIdCallbackInfo* Data);void OnDeleteDeviceIdComplete(const EOS_Connect_DeleteDeviceIdCallbackInfo* Data);void OnConnectLoginComplete(const EOS_Connect_LoginCallbackInfo* Data);void OnCreateUserComplete(const EOS_Connect_CreateUserCallbackInfo* Data);
Also any “old” login methods you’re still keeping, like OldLoginWithExternalAuthToken or LoginWithEgsAccountPortal, if you still intend to support them.
-
Move implementations
-
Physically move the function bodies from
UOnlineManagerSubsystemintoOnlineAuthService.cpp, adjusting to use the service’s member variables instead of the subsystem’s. -
Where you previously accessed
thisasUOnlineManagerSubsystem*, you now haveOwner(the subsystem). UseOwnerto:- Access delegates like
OnLoginComplete,OnNakamaLoginCompleteInternal. - Call into other services later (if needed).
- Access GEngine, show debug.
- Access delegates like
-
Convert static callbacks to call into the service
For EOS static callbacks that currently call GetFromClientData and cast to UOnlineManagerSubsystem*, e.g.:
void EOS_CALL UOnlineManagerSubsystem::OnCreateDeviceIdComplete(const EOS_Connect_CreateDeviceIdCallbackInfo* Data)
{
if (UOnlineManagerSubsystem* Subsystem = GetFromClientData(Data->ClientData))
{
Subsystem->HandleCreateDeviceIdComplete(Data);
}
}
Refactor to:
-
Leave the static function on the subsystem, but forward into the service:
void EOS_CALL UOnlineManagerSubsystem::OnCreateDeviceIdComplete(const EOS_Connect_CreateDeviceIdCallbackInfo* Data) { if (UOnlineManagerSubsystem* Subsystem = GetFromClientData(Data->ClientData)) { if (Subsystem->AuthService) { Subsystem->AuthService->HandleCreateDeviceIdComplete(Data); } } } -
HandleCreateDeviceIdCompletenow lives onFOnlineAuthService.
Repeat this pattern for OnDeleteDeviceIdComplete, OnConnectLoginComplete, OnCreateUserComplete.
- Update Initialize/Deinitialize
In UOnlineManagerSubsystem::Initialize:
-
Move Online Services / Auth initialization into
AuthService::Initialize:- Getting
ServicesviaGetServices(). - Getting
AuthviaServices->GetAuthInterface(). - Binding to
OnOnlineServicesCreated(the delegate for new instances). - DeviceId login mode flags.
- The actual
LoginForLocalPlayer()call (or call it from withinAuthService::Initialize).
- Getting
On the subsystem side, replace that block with a call like:
if (AuthService)
{
AuthService->Initialize(); // Inside this, you perform the login if needed.
}
In Deinitialize, delegate any auth-specific cleanup to AuthService::Deinitialize() and remove direct Auth.Reset()/Services.Reset() usage from the subsystem.
- Adjust subsystem facade methods
Replace the body of auth-related methods on UOnlineManagerSubsystem with thin forwards, e.g.:
void UOnlineManagerSubsystem::LoginForLocalPlayer()
{
if (AuthService)
{
AuthService->LoginForLocalPlayer();
}
}
Do this for any public/Blueprint-exposed login functions so external callers still use UOnlineManagerSubsystem, but the logic runs in FOnlineAuthService.
-
Compile and test login flows
-
Verify that:
- DeviceId login still works in PIE/Standalone.
HandleAuthLoginCompletestill triggers Nakama login viaNakamaSubsystem->LoginWithEOSIdentity(Identity.AccountId);.OnNakamaLoginCompletestill binds and fires correctly.
Phase 3 – Extract EOS House + Networking Service¶
Goal: Move EOS lobby/session + NetDriver enabling + multi-instance EOS Connect logic into FEosHouseService.
3.1 Move state fields¶
In OnlineManagerSubsystem.h, move the purely EOS House / networking-specific fields to FEosHouseService, such as:
EOS_HPlatform PlatformHandle;EOS_HConnect ConnectHandle;EOS_HLobby InLobbyHandle;EOS_HLobbySearch SearchHandle;int32 HouseLobbyDefaultMaxPlayer;FHouseInfo CurrentHouse;FString PendingEosLobbyIdForNakama;FString PendingEosSessionId;TFunction<void(bool bSuccess, const FString& SessionId)> PendingSessionCreateContinuation;TFunction<void(bool bSuccess, const FString& SessionId)> OnEosSessionCreated;bool bHasAutoJoinedOnFirstLogin;bool bPendingListenServerEnable;- Any
OnEosLobbyCreated/OnHouseJoindelegates that are EOS-level (you can keep the delegates declared on subsystem but store EOS-only state in the service).
Keep LocalPuid in Auth (source of truth about identity) but allow FEosHouseService to read it via a getter (Owner.GetLocalPuid()).
3.2 Service interface¶
In OnlineEosHouseService.h:
- Add methods:
class FEosHouseService
{
public:
explicit FEosHouseService(UOnlineManagerSubsystem& InOwner);
void Initialize();
void Deinitialize();
// Lobbies
void CleanupAndCreateEosLobby(int32 InMaxPlayers);
void CreateEosLobby(int32 MaxPlayers,
FOnHostHouseSuccess OnSuccess,
FOnHostHouseFailure OnFailure);
void OnCleanupLobbySearchCompleted(const EOS_LobbySearch_FindCallbackInfo* Data);
void OnCreateLobbyComplete(const EOS_Lobby_CreateLobbyCallbackInfo* Data);
void OnJoinLobbySearchCompleted(const EOS_LobbySearch_FindCallbackInfo* Data);
void OnEosLobbyJoinCompleted(const EOS_Lobby_JoinLobbyCallbackInfo* Data);
// Sessions
void CleanupAndCreateEosSession(TFunction<void(bool bSuccess, const FString& SessionId)> Completion);
void OnDestroyedEosSession(const EOS_Sessions_DestroySessionCallbackInfo* Data);
void CreateEosSession(TFunction<void(bool bSuccess, const FString& SessionId)> Completion);
void OnCreatedEosSession(const EOS_Sessions_UpdateSessionCallbackInfo* Data);
// Networking / NetDriver
bool PrepareNetDriverForListenServer();
bool OpenLevelAsListenServer(const UObject* WorldContextObject,
FName LevelName,
bool bAbsolute,
const FString& AdditionalOptions);
bool PrepareForEOSNetworking(const UObject* WorldContextObject);
bool EnableListenServerOnCurrentMap(const UObject* WorldContextObject);
// Hooks from Auth / World lifecycle
void OnOnlineServicesInitialized(TSharedPtr<IOnlineServices> InServices);
void OnPostWorldInitialization(UWorld* World, const UWorld::InitializationValues IVS);
private:
UOnlineManagerSubsystem& Owner;
// EOS handles and house state moved from subsystem...
};
3.3 Move implementations¶
- Lobby cleanup + creation
Move:
CleanupAndCreateEosLobbyOnCleanupLobbySearchCompletedCreateEosLobbyOnCreateLobbyCompleteOnJoinLobbySearchCompletedOnEosLobbyJoinCompleted
from UOnlineManagerSubsystem into FEosHouseService, adjusting:
- Use
Ownerfor logging category and to call any high-level events (e.g.,Owner.OnEosLobbyCreated.Broadcast(LobbyId);). -
Access
LocalPuidviaOwner.GetLocalPuid()instead of the service storing its own copy (or copy it once from the auth service when needed). -
Session cleanup + creation
Move:
CleanupAndCreateEosSessionOnDestroyedEosSessionCreateEosSessionOnCreatedEosSession
into FEosHouseService.
Ensure OnCreatedEosSession still updates CurrentHouse.EosSessionId and calls OpenHouseWithIds on the Nakama service (later phase) via Owner.
- Networking / NetDriver enablement
Move:
PrepareNetDriverForListenServerOpenLevelAsListenServerPrepareForEOSNetworkingEnableListenServerOnCurrentMap
into FEosHouseService.
In these functions:
- Replace direct access to
bIsLoggedIn/LocalPuidwith calls intoAuthService(throughOwner.IsEosLoginReadyForNetworking()or similar). -
Keep the multi-instance
FOnlineServicesRegistry::Get().GetAllServicesInstanceslogic intact, but now stored in the house service (which is responsible for ensuring EOS Connect is logged in on each platform handle). -
Subsystem facade
On UOnlineManagerSubsystem, replace the bodies of these methods with forwards:
bool UOnlineManagerSubsystem::OpenLevelAsListenServer(const UObject* WorldContextObject, FName LevelName,
bool bAbsolute, const FString& AdditionalOptions)
{
return EosHouseService
? EosHouseService->OpenLevelAsListenServer(WorldContextObject, LevelName, bAbsolute, AdditionalOptions)
: false;
}
etc. For methods that were private, either:
- Make them
friendto the service, or -
Make the service call them via small public wrappers if they truly must stay on the subsystem.
-
Hooking into Auth & World lifecycle
-
When
AuthServicefinishes login and setsLocalPuid, ensure there’s a clear path forFEosHouseServiceto see it (through getters on the subsystem). -
Move any
OnPostWorldInitializationbinding into either:AuthService::Initialize(if it’s truly login-related), orEosHouseService::Initialize(if it’s specifically to enable listen server after seamless travel).
A reasonable split: Eos networking service owns world init handling because it is the one that calls EnableListenServerOnCurrentMap when bPendingListenServerEnable is set.
-
Compile & test House host/join
-
Test
CleanupAndCreateEosLobby+CreateEosLobbyby hosting a House and confirming lobby creation logs still appear. - Test
EnableListenServerOnCurrentMapstill works as expected after seamless travel.
Phase 4 – Extract Nakama House Service¶
Goal: Move Nakama-specific House orchestration (RPCs, callbacks, host/list/join flows, EOS–Nakama handshake) into FNakamaHouseService.
4.1 Move state fields¶
From OnlineManagerSubsystem.h, move into FNakamaHouseService:
UNakamaConnectionSubsystem* NakamaSubsystem;(or keep pointer on subsystem but pass into service onInitialize).-
Pending callback storage:
-
FOnHostHouseSuccess PendingHostHouseSuccessCallback; FOnHostHouseFailure PendingHostHouseFailureCallback;FOnListHousesSuccess PendingListHousesSuccessCallback;FOnListHousesFailure PendingListHousesFailureCallback;FOnJoinHouseSuccess PendingJoinHouseSuccessCallback;-
FOnJoinHouseFailure PendingJoinHouseFailureCallback; -
Any
CurrentHousefields that are purely Nakama-level (e.g., HouseId, occupancy, age, etc.), while keeping EOS-specific IDs visible through the EosHouse service as needed.
4.2 Service interface¶
In OnlineNakamaHouseService.h:
class FNakamaHouseService
{
public:
explicit FNakamaHouseService(UOnlineManagerSubsystem& InOwner);
void Initialize();
void Deinitialize();
// High-level API mirrored from UOnlineManagerSubsystem
void HostHouse(int32 MaxPlayers,
FOnHostHouseSuccess OnSuccess,
FOnHostHouseFailure OnFailure);
void ListHouses(FOnListHousesSuccess OnSuccess,
FOnListHousesFailure OnFailure);
void JoinHouse(const FString& HouseId,
FOnJoinHouseSuccess OnSuccess,
FOnJoinHouseFailure OnFailure);
// Called when EOS lobby/session is created
void OnEosLobbyCreated(const FString& LobbyId);
void OnEosSessionCreated(const FString& SessionId);
// Internal RPC helpers
void CallNakamaRPCForHouseOpen(const FString& Payload);
void CallNakamaRPCForListHouses(const FString& Payload);
// RPC callbacks
void OnHouseOpenSuccess(const FNakamaRPC& RPCResponse);
void OnHouseOpenError(const FNakamaError& Error);
void OnListHousesSuccess(const FNakamaRPC& RPCResponse);
void OnListHousesError(const FNakamaError& Error);
// ... Join RPC handlers, etc. ...
// Nakama client/session helper
bool TryGetNakamaClientAndSession(UNakamaClient*& OutClient,
UNakamaSession*& OutSession,
const TCHAR* Context);
private:
UOnlineManagerSubsystem& Owner;
// Pending callbacks & state...
};
4.3 Move implementations¶
- Nakama helpers & callback management
Move:
TryGetNakamaClientAndSessionintoFNakamaHouseService(unchanged, but usingOwnerto log).ClearHostCallbacks,CompleteHostSuccess,CompleteHostFailure.ClearListCallbacks,CompleteListSuccess,CompleteListFailure.ClearJoinLobbyCallbacks,CompleteJoinLobbySuccess,CompleteJoinLobbyFailure.
These become private methods on FNakamaHouseService.
- Nakama RPC calls
Move:
CallNakamaRPCForHouseOpen(RPC"house_open").CallNakamaRPCForListHouses(RPC"house_list").
into the Nakama service, adjusting to:
- Use its own
TryGetNakamaClientAndSession. -
Bind callbacks to methods on
FNakamaHouseServiceinstead ofUOnlineManagerSubsystem. -
RPC result handlers & House mapping
Move the functions that parse RPC JSON into FHouseSummary arrays and update CurrentHouse:
OnHouseOpenSuccess,OnHouseOpenError.OnListHousesSuccess,OnListHousesError.- Any
OnJoinHouseSuccess,OnJoinHouseErrormethods.
These now live on FNakamaHouseService and call CompleteHostSuccess, CompleteHostFailure, etc.
- EOS↔Nakama handshake
Move:
HandleEosLobbyCreatedBeginEosSessionCreationOpenHouseWithIds(and any other bridging methods tying LobbyId/SessionId/EOS PUID into the Nakama House open payload)
into FNakamaHouseService.
Adjust them to:
- Use
Ownerto call Eos service when they need to start session creation (e.g., callOwner.GetEosHouseService()->CleanupAndCreateEosSession(...)). -
Use
Ownerto read the EOS identity (PUID string) fromAuthServicewhen constructing the payload for"house_open". -
Hook login completion → Nakama
Currently, HandleAuthLoginComplete on the subsystem:
- Marks
bIsLoggedIn = true. - Calls
RegisterUserWithAuthInterface(). - Binds
NakamaSubsystem->OnNakamaLoginCompleteand then callsNakamaSubsystem->LoginWithEOSIdentity(Identity.AccountId);.
After the refactor:
- Keep the binding and Nakama login call inside Auth service (or subsystem) — that’s still “auth”.
- Once Nakama login completes, route any “auto-join House” or “autocreate House” logic into
FNakamaHouseServiceinstead of doing it directly onUOnlineManagerSubsystem.
For example, change OnNakamaLoginCompleteInternal to call a function on FNakamaHouseService like OnNakamaLoginComplete(const FNakamaAccount& Account) where you can decide to auto-open/join House based on your seed’s design.
- Subsystem facade
Update UOnlineManagerSubsystem public methods related to House so they forward into the Nakama service:
void UOnlineManagerSubsystem::HostHouse(int32 MaxPlayers,
FOnHostHouseSuccess OnSuccess,
FOnHostHouseFailure OnFailure)
{
if (NakamaHouseService)
{
NakamaHouseService->HostHouse(MaxPlayers, OnSuccess, OnFailure);
}
}
Same pattern for ListHouses, JoinHouse, etc.
-
Compile & run House flows end-to-end
-
Host House → verify:
- EOS lobby + session are created.
- Nakama
house_openRPC is called.
-
List Houses → verify:
- Nakama
house_listRPC works and UI still getsFHouseSummarylist.
- Nakama
-
Join House → verify:
- EOS lobby join + Nakama join behavior unchanged.
Phase 5 – Clean Up & Hardening¶
-
Remove unused members from
UOnlineManagerSubsystem -
Once all logic is moved and compiled, delete any leftover fields or helper methods that are now entirely internal to services.
-
Ensure the subsystem only has:
- Pointers to the three services.
- Public/Blueprint-exposed methods that delegate to them.
- Minimal orchestration glue.
-
Tighten access and includes
-
Make service headers include only what they need (forward-declare Nakama/EOS types where possible).
-
Ensure
OnlineManagerSubsystem.cpponly includes service headers and not the heavy EOS/Nakama headers directly, where possible (those can be pulled into the service.cppfiles). -
Add comments describing boundaries
On each service header, add a short documentation comment:
FOnlineAuthService– “Owns Online Services + EOS Connect identity and login state. No House logic here.”FEosHouseService– “Owns EOS lobby/session, multi-instance EOS Connect and NetDriver setup.”-
FNakamaHouseService– “Owns Nakama House RPCs, House state, and EOS–Nakama bridging.” -
Sanity test matrix
Quickly test:
- PIE with multiple windows (to exercise multi-instance EOS Connect).
- Standalone listen server open + join.
- DeviceId reset flag workflow (
reset_id.flagstill honored). - First-time login (account creation) vs. repeat login.
- House host/list/join end-to-end with Nakama backend online.
Phase 5 Progress (completed)¶
- Trimmed
OnlineManagerSubsystem.hto forward declare services instead of including heavy headers. - Updated service headers with final ownership comments (Auth / EOS house / Nakama house).
- Build verified after phases 1–5: subsystem remains a thin facade delegating to services.
6. How to Feed This to Codex Incrementally¶
When using this doc with Codex:
- Phase per prompt – Use one phase (or even a half-phase) per Codex request so it can keep the patch small and compilable.
-
Always restate context – For each prompt, paste:
-
The relevant step(s) from this doc.
-
Snippets of the current code for the functions you’re moving.
-
After each Codex patch:
-
Run a build.
- If there are issues, fix or revert before continuing to the next step.
This plan should let you gradually carve UOnlineManagerSubsystem into the three services from your seed without breaking existing flows, while keeping Codex on a short, well-defined leash at each step.