Recover from disconnects
Reconnect players or continue a match after its host leaves.
Separate a dropped connection from an intentional exit. UCIK remembers the last lobby, session, and validated address across restarts; a saved destination may no longer exist.
Rejoin the last match
Rejoin the last match
Loading the interactive viewer.
Clear a match after leaving
Loading the interactive viewer.
Game module dependencies: UCIKCore, BetideCore.
#include "Engine/GameInstance.h"
#include "Travel/UCIKTravelSubsystem.h"
#include "UCIKCore.h"
void ForgetMatchAfterLeaving(UGameInstance &GameInstance)
{
if (auto *Service = GameInstance.GetSubsystem<UUCIKTravelSubsystem>())
Service->ClearLastMatch();
}
void ReconnectToLastMatch(UGameInstance &GameInstance)
{
auto *Service = GameInstance.GetSubsystem<UUCIKTravelSubsystem>();
if (!Service)
return;
if (!Service->HasLastMatch())
return;
Service->RejoinLastMatch(
true, FBetideCallback<FUCIK_ConnectInfo>::CreateWeakLambda(
&GameInstance, [](const TBetideResult<FUCIK_ConnectInfo> &Result) {
if (!Result.bOk)
{
UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
return;
}
UE_LOG(LogUCIK, Log, TEXT("Rejoin destination resolved"));
}));
}Rejoin tries the last lobby, then session, then address. If it fails, return to a usable menu. Call Clear Last Crossplay Match after the player deliberately leaves; it clears memory and disk.
Host migration
Choose Host Migration Mode in Betide → Crossplay:
| Mode | Behavior |
|---|---|
| Off | No recovery; Unreal returns to the default map. |
| Manual | Your game reacts to host-loss/election events and controls hosting/reconnection. |
| Seamless | UCIK elects a host, reconnects members, and restores marked actor state. |
Manual host handover
Use a GameInstance Blueprint and set migration mode to Manual. Choose either the explicit election flow or Run Crossplay Host Migration; bind before host loss and stop watching when leaving the match. Reconnect To Migrated Crossplay Host succeeds when travel is issued; On Migration Completed confirms recovery.
Hand over ownership
Loading the interactive viewer.
Handle host election explicitly
Loading the interactive viewer.
Use the default migration flow
Loading the interactive viewer.
Game module dependencies: UCIKCore, BetideCore.
Hand over ownership
#include "Engine/GameInstance.h"
#include "HostMigration/UCIKHostMigrationSubsystem.h"
#include "UCIKCore.h"
void HandOverToMember(UGameInstance &GameInstance, const FUCIK_PlayerHandle &PreferredMember)
{
auto *Migration = GameInstance.GetSubsystem<UUCIKHostMigrationSubsystem>();
if (!Migration)
return;
Migration->HandOverHost(
PreferredMember,
FBetideCallback<FUCIK_PlayerHandle>::CreateWeakLambda(
&GameInstance, [](const TBetideResult<FUCIK_PlayerHandle> &Result) {
if (!Result.bOk)
{
UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
return;
}
UE_LOG(LogUCIK, Log, TEXT("New owner: %s"), *Result.GetValue().ToString());
}));
}Handle host election
Store FMigrationBindings on your GameInstance. Choose the default flow or the explicit host/wait/reconnect sequence with bUseDefaultFlow.
#include "Engine/GameInstance.h"
#include "HostMigration/UCIKHostMigrationSubsystem.h"
#include "UCIKCore.h"
struct FMigrationBindings
{
FDelegateHandle Elected;
FDelegateHandle Completed;
FDelegateHandle Failed;
};
void StopWatchingHostElection(UGameInstance &GameInstance, FMigrationBindings &Bindings)
{
if (auto *Migration = GameInstance.GetSubsystem<UUCIKHostMigrationSubsystem>())
{
Migration->OnHostElectedNative.Remove(Bindings.Elected);
Migration->OnMigrationCompletedNative.Remove(Bindings.Completed);
Migration->OnMigrationFailedNative.Remove(Bindings.Failed);
}
Bindings = {};
}
void WatchHostElection(UGameInstance &GameInstance, FMigrationBindings &Bindings,
bool bUseDefaultFlow)
{
StopWatchingHostElection(GameInstance, Bindings);
auto *Migration = GameInstance.GetSubsystem<UUCIKHostMigrationSubsystem>();
if (!Migration)
return;
const TWeakObjectPtr<UUCIKHostMigrationSubsystem> WeakMigration(Migration);
Bindings.Completed = Migration->OnMigrationCompletedNative.AddWeakLambda(
&GameInstance, [WeakMigration](const FUCIK_MigrationContext &) {
if (WeakMigration.IsValid())
UE_LOG(LogUCIK, Log, TEXT("Migration phase: %d"),
static_cast<int32>(WeakMigration->GetPhase()));
});
Bindings.Failed = Migration->OnMigrationFailedNative.AddWeakLambda(
&GameInstance, [](const FUCIK_MigrationContext &, const FBetideError &Error) {
UE_LOG(LogUCIK, Warning, TEXT("%s"), *Error.GetLogString());
});
Bindings.Elected = Migration->OnHostElectedNative.AddWeakLambda(
&GameInstance, [WeakMigration, bUseDefaultFlow](const FUCIK_MigrationContext &Context) {
if (!WeakMigration.IsValid())
return;
if (bUseDefaultFlow)
{
WeakMigration->RunMigration();
return;
}
if (Context.bLocalIsElected)
{
WeakMigration->BecomeHost(
Context.MapName,
FBetideCallback<FUCIK_ConnectInfo>::CreateWeakLambda(
WeakMigration.Get(), [](const TBetideResult<FUCIK_ConnectInfo> &Result) {
if (!Result.bOk)
UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
else
UE_LOG(LogUCIK, Log, TEXT("New host ready"));
}));
return;
}
WeakMigration->WaitForHost(
30.0f,
FBetideCallback<FString>::CreateWeakLambda(
WeakMigration.Get(), [WeakMigration](const TBetideResult<FString> &Result) {
if (!WeakMigration.IsValid())
return;
if (!Result.bOk)
{
UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
return;
}
WeakMigration->ReconnectToHost(FBetideCallback<bool>::CreateWeakLambda(
WeakMigration.Get(), [](const TBetideResult<bool> &Reconnect) {
if (!Reconnect.bOk)
UE_LOG(LogUCIK, Warning, TEXT("%s"),
*Reconnect.Error.GetLogString());
else
UE_LOG(LogUCIK, Log, TEXT("Reconnect travel requested"));
}));
}));
});
}For Seamless, add UCIK Migration State Component to actors that must survive. Mark custom state as SaveGame. The component restores enabled transforms, velocity, SaveGame properties, and references between marked actors; it is not a copy of every runtime object.
Migration limits and testing
Migration uses a lobby/session this process actually joined, not just a record loaded from disk. Provider-native owner election is used where available; otherwise UCIK ranks peers. Direct-IP peers need reachable addresses registered through Register Crossplay Migration Peer.
Only the elected host publishes the new ready state and migration epoch. Do not rewrite UCIK_ host/connection attributes from members. Test both a graceful handover and a killed host process, including restored pawns, timers, inventory, and your failure screen.