Skip to content

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:

  1. Auth / Identity ServiceFOnlineAuthService
  2. EOS House (Lobby + Session + Networking) ServiceFEosHouseService
  3. Nakama House ServiceFNakamaHouseService

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) and FGameIdentity/CachedIdentity lifetime.

  • 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, any OnEosLobbyCreated multicast, and flags like bPendingListenServerEnable, 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 to FHouseSummary, CurrentHouse, etc. (they appear below the truncated part).

  • House host/list/join orchestration at Nakama level:

  • The HostHouse/ListHouses/JoinHouse APIs 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::LoginWithEOSIdentity and 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/.cpp
  • Core/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

  1. Confirm current behavior and references

  2. Search for all references to UOnlineManagerSubsystem in the project (C++ & Blueprint).

  3. Note which methods are externally used (BlueprintCallable or external C++ call sites). These public surfaces must remain stable.

  4. Add a comment banner to UOnlineManagerSubsystem

  5. 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.

  1. Forward declarations & members in OnlineManagerSubsystem.h

  2. Add forward declarations:

    class FOnlineAuthService;
    class FEosHouseService;
    class FNakamaHouseService;
    
  3. Add private members (likely as TUniquePtr):

    TUniquePtr<FOnlineAuthService> AuthService;
    TUniquePtr<FEosHouseService>  EosHouseService;
    TUniquePtr<FNakamaHouseService> NakamaHouseService;
    
  4. Optionally add small getter helpers (private or public):

    FOnlineAuthService*       GetAuthService()       { return AuthService.Get(); }
    FEosHouseService*         GetEosHouseService()   { return EosHouseService.Get(); }
    FNakamaHouseService*      GetNakamaHouseService(){ return NakamaHouseService.Get(); }
    
  5. 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.h with FEosHouseService.
  • OnlineNakamaHouseService.h with FNakamaHouseService.

  • Create .cpp stubs

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.

  1. 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();
  1. Compile & run quick smoke test

  2. 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 FOnlineAuthService as 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; }

In OnlineManagerSubsystem.cpp:

  1. 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.

  1. Move implementations

  2. Physically move the function bodies from UOnlineManagerSubsystem into OnlineAuthService.cpp, adjusting to use the service’s member variables instead of the subsystem’s.

  3. Where you previously accessed this as UOnlineManagerSubsystem*, you now have Owner (the subsystem). Use Owner to:

    • Access delegates like OnLoginComplete, OnNakamaLoginCompleteInternal.
    • Call into other services later (if needed).
    • Access GEngine, show debug.
  4. 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);
            }
        }
    }
    
  • HandleCreateDeviceIdComplete now lives on FOnlineAuthService.

Repeat this pattern for OnDeleteDeviceIdComplete, OnConnectLoginComplete, OnCreateUserComplete.

  1. Update Initialize/Deinitialize

In UOnlineManagerSubsystem::Initialize:

  • Move Online Services / Auth initialization into AuthService::Initialize:

    • Getting Services via GetServices().
    • Getting Auth via Services->GetAuthInterface().
    • Binding to OnOnlineServicesCreated (the delegate for new instances).
    • DeviceId login mode flags.
    • The actual LoginForLocalPlayer() call (or call it from within AuthService::Initialize).

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.

  1. 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.

  1. Compile and test login flows

  2. Verify that:

    • DeviceId login still works in PIE/Standalone.
    • HandleAuthLoginComplete still triggers Nakama login via NakamaSubsystem->LoginWithEOSIdentity(Identity.AccountId);.
    • OnNakamaLoginComplete still 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 / OnHouseJoin delegates 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

  1. Lobby cleanup + creation

Move:

  • CleanupAndCreateEosLobby
  • OnCleanupLobbySearchCompleted
  • CreateEosLobby
  • OnCreateLobbyComplete
  • OnJoinLobbySearchCompleted
  • OnEosLobbyJoinCompleted

from UOnlineManagerSubsystem into FEosHouseService, adjusting:

  • Use Owner for logging category and to call any high-level events (e.g., Owner.OnEosLobbyCreated.Broadcast(LobbyId);).
  • Access LocalPuid via Owner.GetLocalPuid() instead of the service storing its own copy (or copy it once from the auth service when needed).

  • Session cleanup + creation

Move:

  • CleanupAndCreateEosSession
  • OnDestroyedEosSession
  • CreateEosSession
  • OnCreatedEosSession

into FEosHouseService.

Ensure OnCreatedEosSession still updates CurrentHouse.EosSessionId and calls OpenHouseWithIds on the Nakama service (later phase) via Owner.

  1. Networking / NetDriver enablement

Move:

  • PrepareNetDriverForListenServer
  • OpenLevelAsListenServer
  • PrepareForEOSNetworking
  • EnableListenServerOnCurrentMap

into FEosHouseService.

In these functions:

  • Replace direct access to bIsLoggedIn/LocalPuid with calls into AuthService (through Owner.IsEosLoginReadyForNetworking() or similar).
  • Keep the multi-instance FOnlineServicesRegistry::Get().GetAllServicesInstances logic 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 friend to 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 AuthService finishes login and sets LocalPuid, ensure there’s a clear path for FEosHouseService to see it (through getters on the subsystem).

  • Move any OnPostWorldInitialization binding into either:

    • AuthService::Initialize (if it’s truly login-related), or
    • EosHouseService::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.

  1. Compile & test House host/join

  2. Test CleanupAndCreateEosLobby + CreateEosLobby by hosting a House and confirming lobby creation logs still appear.

  3. Test EnableListenServerOnCurrentMap still 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 on Initialize).
  • Pending callback storage:

  • FOnHostHouseSuccess PendingHostHouseSuccessCallback;

  • FOnHostHouseFailure PendingHostHouseFailureCallback;
  • FOnListHousesSuccess PendingListHousesSuccessCallback;
  • FOnListHousesFailure PendingListHousesFailureCallback;
  • FOnJoinHouseSuccess PendingJoinHouseSuccessCallback;
  • FOnJoinHouseFailure PendingJoinHouseFailureCallback;

  • Any CurrentHouse fields 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

  1. Nakama helpers & callback management

Move:

  • TryGetNakamaClientAndSession into FNakamaHouseService (unchanged, but using Owner to log).
  • ClearHostCallbacks, CompleteHostSuccess, CompleteHostFailure.
  • ClearListCallbacks, CompleteListSuccess, CompleteListFailure.
  • ClearJoinLobbyCallbacks, CompleteJoinLobbySuccess, CompleteJoinLobbyFailure.

These become private methods on FNakamaHouseService.

  1. 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 FNakamaHouseService instead of UOnlineManagerSubsystem.

  • RPC result handlers & House mapping

Move the functions that parse RPC JSON into FHouseSummary arrays and update CurrentHouse:

  • OnHouseOpenSuccess, OnHouseOpenError.
  • OnListHousesSuccess, OnListHousesError.
  • Any OnJoinHouseSuccess, OnJoinHouseError methods.

These now live on FNakamaHouseService and call CompleteHostSuccess, CompleteHostFailure, etc.

  1. EOS↔Nakama handshake

Move:

  • HandleEosLobbyCreated
  • BeginEosSessionCreation
  • OpenHouseWithIds (and any other bridging methods tying LobbyId/SessionId/EOS PUID into the Nakama House open payload)

into FNakamaHouseService.

Adjust them to:

  • Use Owner to call Eos service when they need to start session creation (e.g., call Owner.GetEosHouseService()->CleanupAndCreateEosSession(...)).
  • Use Owner to read the EOS identity (PUID string) from AuthService when constructing the payload for "house_open".

  • Hook login completion → Nakama

Currently, HandleAuthLoginComplete on the subsystem:

  • Marks bIsLoggedIn = true.
  • Calls RegisterUserWithAuthInterface().
  • Binds NakamaSubsystem->OnNakamaLoginComplete and then calls NakamaSubsystem->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 FNakamaHouseService instead of doing it directly on UOnlineManagerSubsystem.

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.

  1. 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.

  1. Compile & run House flows end-to-end

  2. Host House → verify:

    • EOS lobby + session are created.
    • Nakama house_open RPC is called.
  3. List Houses → verify:

    • Nakama house_list RPC works and UI still gets FHouseSummary list.
  4. Join House → verify:

    • EOS lobby join + Nakama join behavior unchanged.

Phase 5 – Clean Up & Hardening

  1. Remove unused members from UOnlineManagerSubsystem

  2. Once all logic is moved and compiled, delete any leftover fields or helper methods that are now entirely internal to services.

  3. Ensure the subsystem only has:

    • Pointers to the three services.
    • Public/Blueprint-exposed methods that delegate to them.
    • Minimal orchestration glue.
  4. Tighten access and includes

  5. Make service headers include only what they need (forward-declare Nakama/EOS types where possible).

  6. Ensure OnlineManagerSubsystem.cpp only includes service headers and not the heavy EOS/Nakama headers directly, where possible (those can be pulled into the service .cpp files).

  7. 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.flag still 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.h to 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.