연결 해제 & 재연결
개요
이 샘플 프로젝트는 연결이 끊긴 후 온라인 세션에 다시 연결하려는 플레이어를 처리하는 여러 가지 전략을 제공합니다. 이 샘플에는 공유 모드와 클라이언트 호스트 모드에서 작동하는 코드가 포함되어 있습니다.



이 샘플은 주로 재연결 기능에 중점을 두고 있기 때문에 게임 플레이는 비교적 간단합니다. 생성된 플레이어는 모자를 쓴 캡슐로 표현됩니다. 왼쪽 클릭을 하면 해당 지점으로 이동합니다. 노란 구체와 충돌하면 수집되며 플레이어의 점수가 증가합니다.
다운로드
버전 | 출시일 | 다운로드 | |
---|---|---|---|
2.0.5 | 2025년 2월 27일 | Disconnect Reconnect 2.0.5 Build 793 |
클라이언트 호스트 모드
클라이언트 호스트 모드에서는 플레이어가 연결이 끊긴 후 기존 세션에 재접속할 수 있도록 도와주는 몇 가지 기능이 있습니다.
연결 토큰
ConnectionTokens
는 플레이어가 세션에 다시 연결할 때, 이전에 제어하던 NetworkObjects
의 입력 권한을 다시 획득할 수 있도록 돕는 기능입니다.
ConnectionToken
은 바이트 배열로, NetworkRunner.StartGame
을 호출할 때 사용하는 StartGameArgs
에 포함될 수 있습니다.
세션 중에는 이 바이트 배열을 NetworkRunner.GetPlayerConnectionToken(PlayerRef player)
를 호출하여 얻을 수 있습니다.
이 샘플에서는 애플리케이션이 시작될 때 무작위로 생성된 long
값이 ConnectionToken
으로 사용됩니다.
이 값은 SessionID
를 통해 바이트 배열로 변환되며, 세션을 시작할 때 NetworkRunner.StartGame
의 StartGameArgs
에 전달됩니다.
플레이어가 세션에 참여하면, Player Host는 플레이어가 제어할 수 있는 각 NetworkObject
를 Dictionary<long, NetworkObject>
형태로 추적합니다.
이때 키는 ConnectionToken
을 다시 long
으로 변환한 값입니다.
재접속한 플레이어가 이전과 동일한 ConnectionToken
을 제공하면, 해당 객체의 Input Authority
는 새로운 플레이어 객체를 생성하는 대신 해당 플레이어에게 다시 할당됩니다.
호스트 마이그레이션
ConnectionTokens
는 클라이언트가 연결을 끊었을 때는 효과적이지만, Player Host가 끊긴 경우에는 충분하지 않습니다.
Client Host 모드에서는 Player Host가 서버 역할을 하기 때문에, Player Host가 연결을 끊으면 모든 클라이언트가 서버에서 연결이 끊기며, 보통 세션은 완전히 종료됩니다.
이를 해결하기 위해 Host Migration 기능을 사용하여 세션을 재구성하고, 새로운 클라이언트를 Player Host로 지정할 수 있습니다.
호스트 마이그레이션을 트리거 하려면 다음 작업이 수행되어야 합니다:
- 스냅샷을 푸시 해야 합니다.
이는Network Project Config Asset
의Host Migration
설정에서Enable Auto Update
를 활성화하거나,
NetworkRunner.PushHostMigrationSnapshot
을 수동으로 호출하여 수행할 수 있습니다. INetworkRunnerCallbacks
인터페이스와INetworkRunnerCallbacks.OnHostMigration
을 구현한 객체를
NetworkRunner.AddCallbacks
를 통해NetworkRunner
에 등록해야 합니다.
호스트 마이그레이션 구현
호스트 마이그레이션 구현은 다음 단계로 구성됩니다:
현재
NetworkRunner
를 종료합니다. 이때 종료 사유로ShutdownReason.HostMigration
을 제공하는 것이 좋습니다.
그러면INetworkRunnerCallbacks.OnShutdown
이 호출될 때, 클라이언트가 일반적인 종료와는 다르게 반응할 수 있습니다.
예를 들어, 이 샘플에서는NetworkRunner
가 종료되면DisconnectWindow
가 열리지만, 종료 이유가HostMigration
인 경우에는 열리지 않습니다.현재 씬을 언로드합니다.
이는 새 씬 버전이 로드될 때 이전에 존재하던NetworkObjects
가 재생성될 수 있도록 하기 위함입니다.새로운
NetworkRunner
를 생성하고,NetworkRunner.StartGame
을 새로운StartGameArgs
로 호출합니다.
이때 필수로 지정해야 하는 두 개의 필드는 다음과 같습니다:HostMigrationToken
: 앞에서 언급한 스냅샷 정보를 포함합니다.HostMigrationResume
: 호스트 마이그레이션 중에 호출될 메서드입니다.
NetworkRunner.GetResumeSnapshotNetworkObjects
를 통해, 호스트 마이그레이션이 발생하기 전에 푸시 된 마지막 스냅샷에서NetworkObjects
를 재생성합니다.NetworkRunner.GetResumeSnapshotNetworkSceneObjects
를 반복하여, 새로 로드된 씬의 객체에 이전 씬의 상태 데이터를 복사합니다.
이때NetworkObject.CopyStateFrom
을 사용하여 상태 정보를 복사합니다.
다음 코드 스니펫은 이 과정을 보여주며, GameController
에 구현되어 있습니다.
첫 번째 코드는 비동기 방식으로 구현된 INetworkRunnerCallbacks.OnHostMigration
입니다.
C#
async void INetworkRunnerCallbacks.OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken)
{
// Shutdown old Runner
await runner.Shutdown(shutdownReason: ShutdownReason.HostMigration);
// Unload the current game scene. If one is not found, we return to the main menu.
var completedLoad = new TaskCompletionSource<bool>();
var scene = SceneManager.GetSceneByName("GameScene");
if (!scene.IsValid())
{
ReturnToMainMenu();
return;
}
// We unload the game scene.
var unloadScene = SceneManager.UnloadSceneAsync(scene);
if (unloadScene != null) unloadScene.completed += (finishOp) => completedLoad.SetResult(true);
// A new runner is created
NetworkSceneInfo networkSceneInfo = default;
networkSceneInfo.AddSceneRef(SceneRef.FromIndex(1), LoadSceneMode.Additive, activeOnLoad: true);
// The host migration token will help resume the game and the host migration resume method is called once the host migration is ready
var startGameArgs = new StartGameArgs()
{
GameMode = hostMigrationToken.GameMode,
HostMigrationToken = hostMigrationToken,
HostMigrationResume = HostMigrationResume,
ConnectionToken = SessionID.ByteID,
Scene = networkSceneInfo,
};
// Create a new NetworkRunner
_instanceRunner = InstantiateRunner("GameRunner");
var result = await _instanceRunner.StartGame(startGameArgs);
if (result.Ok == false)
{
_instanceRunner = null;
}
else
{
var pushResult = await _instanceRunner.PushHostMigrationSnapshot();
}
}
다음 코드는 GameController.HostMigrationResume
의 구현을 보여줍니다:
C#
private void HostMigrationResume(NetworkRunner runnerMigration)
{
// Get a temporary reference for each NO from the old Host
foreach (var resumeNO in runnerMigration.GetResumeSnapshotNetworkObjects())
{
var hasTRSP = resumeNO.TryGetBehaviour<NetworkTRSP>(out var trsp);
var position = hasTRSP ? trsp.Data.Position : Vector3.zero;
var rotation = hasTRSP ? trsp.Data.Rotation : Quaternion.identity;
// Spawn a new object based on the previous objects
runnerMigration.Spawn(resumeNO, position: position, rotation: rotation, onBeforeSpawned: (networkRunner, newNO) =>
{
newNO.CopyStateFrom(resumeNO);
if (newNO.TryGetBehaviour<PlayerNetworkBehaviour>(out var playerBehaviour))
{
newNO.AssignInputAuthority(PlayerRef.None);
// Store mapping between Token and NetworkObject
_playersMap[playerBehaviour.Token] = newNO;
}
});
}
// Updates the state information of the scene objects loaded
foreach (var sceneObject in runnerMigration.GetResumeSnapshotNetworkSceneObjects())
{
sceneObject.Item1.CopyStateFrom(sceneObject.Item2);
if (sceneObject.Item1.TryGetBehaviour(out CollectableBehaviour sceneCollectable))
sceneCollectable.ForcePositionSync();
}
}
참고로, `CollectableBehaviour.Spawned`는 해당 오브젝트들의 위치를 무작위로 설정하기 때문에,
`GameController.HostMigrationResume` 중에 `CollectableBehaviour.ForcePositionSync`를 호출하여 이전의 `NetworkTransform` 데이터를 복원합니다.
Host Migration에 대한 자세한 내용은 [Host Migration Technical Sample](fusion-host-migration)을 참조하세요.
### CloudConnectionLost 콜백
연결 끊김을 처리하고 빠른 재접속을 시도할 수 있는 또 다른 기능은 `NetworkRunner.CloudConnectionLost` 콜백입니다.
이 콜백은 다음과 같은 정보를 제공합니다:
- 영향을 받은 `NetworkRunner`
- `ShutdownReason`
- 재접속이 시도 중인지를 나타내는 `bool`
이 콜백은 `NetworkRunner.CloudConnectionLost`에 등록하여 사용합니다.
자세한 정보는 [Photon Connection Lost & Quick Reconnect documentation](~~~/manual/connection-and-matchmaking/lost-connection-handling)을 참고하세요.
## Shared 모드
앞서 설명한 세 가지 기능은 모두 Client Host 모드에서만 사용 가능합니다.
Shared 모드에서도 `ConnectionToken`을 제공할 수는 있지만,
`NetworkRunner.GetPlayerConnectionToken(PlayerRef player)`는 서버 또는 Player Host에서만 작동하며, 그렇지 않은 경우 `null`을 반환합니다.
또한 Shared 모드에는 실질적인 Player Host가 없기 때문에, Host Migration도 발생하지 않습니다.
대신 `SharedModeMasterClient`가 연결을 끊을 경우, 해당 역할이 다른 플레이어에게 자동으로 이전되므로 호스트 마이그레이션이 필요하지 않습니다.
### Shared 모드에서의 재접속
Shared 모드에서는 `ConnectionToken` 시스템을 통해 재접속할 수 없습니다.
하지만 `SessionID`를 사용하여 이전 아바타를 가진 플레이어가 재접속했는지를 확인하는 방식은 가능합니다.
이를 위해 `PlayerNetworkBehaviour` 프리팹의 `Shared Mode Settings`를 다음과 같이 설정합니다:
- `Allow State Authority Override`: `true`
- `Destroy When State Authority Leaves`: `false`
이렇게 설정하면 `State Authority`를 잃어도 해당 `NetworkObject`는 despawn 되지 않습니다.
::: figure width="50"

:::
그 후, `GameController.OnPlayerJoined` 내에서 새 플레이어를 생성하기 전에 현재 존재하는 모든 `PlayerNetworkBehaviour` 객체를 순회하면서,
해당 객체의 `Token` `NetworkProperty`가 재접속한 플레이어의 `SessionID`와 일치하는 경우, 새 플레이어를 생성하는 대신 해당 객체의 State Authority를 요청합니다.
단, 너무 오래 기다려서 `PlayerNetworkBehaviour`가 despawn 되었다면, 새 객체가 생성됩니다.
Back to top