Version: 2017.2
네트워크 클라이언트와 서버
전송 레이어 API 사용(Using the Transport Layer API)

호스트 이송(Host Migration)

전용 서버가 없는 멀티플레이어 네트워크 게임의 경우, 게임 피어 중 하나가 게임 권한의 중심 역할을 합니다. 이 피어는 “호스트”라고 하며, 서버와 “로컬 클라이언트”를 동시에 실행합니다. 다른 피어는 “원격 클라이언트”만 실행합니다.

게임 호스트가 사라지면 게임을 더 이상 진행할 수 없게됩니다. 플레이어가 떠나거나, 호스트 프로세스가 종료되거나 충돌한 경우, 호스트 장치가 꺼진 경우, 호스트 네트워크 연결이 끊긴 경우에 이러한 현상이 발생합니다.

“호스트이송” 기능을 사용하면 원격 클라이언트 중 하나를 새로운 호스트로 설정하여 멀티플레이어 게임을 계속 진행할 수 있습니다.

작동 방식(How it works)

호스트 이송이 활성화된 멀티플레이어 게임 중에는 피어 주소가 게임의 다른 피어에게 배포됩니다. 호스트가 사라지면 한 피어가 새로운 호스트가 될 수 있습니다. 이후 다른 피어가 새로운 호스트에 연결하여 게임을 속행할 수 있습니다.

새로운 NetworkMigrationManager 컴포넌트는 Unity 네트워킹 HLAPI를 사용하는 멀티플레이어 게임에 드롭할 수 있으며, 이를 통해 원래 호스트가 사라지더라도 새로운 호스트를 통해 게임을 진행할 수 있도록 합니다. 아래는 에디터 인스펙터에서의 NetworkMigrationManager 스크린샷입니다. 현재 이송 상태를 표시하고 있습니다.

Network Migration Manager 컴포넌트
Network Migration Manager 컴포넌트

NetworkMigrationManager는 NetworkManagerHUD와 유사한 단순한 사용자 인터페이스를 제공합니다. 이 사용자 인터페이스는 테스트와 프로토타이핑을 목적으로 합니다. 실제 게임은 호스트 이송용으로 커스텀 사용자 인터페이스를 구현하게되며, 사용자 입력을 요구하지 않으면서 새로운 호스트를 자동으로 선택할 수 있도록 하는 커스텀 로직 역시 포함됩니다.

HUD 프로토타이핑 네트워크 이송 관리자
HUD 프로토타이핑 네트워크 이송 관리자

기존 호스트가 연결이 끊어지거나 게임을 종료하여 이송이 발생했다고 하더라도 게임에 새로운 호스트의 클라이언트로서 다시 참여할 수 있습니다.

호스트 이송 동안 씬에 있는 모든 네트워크 오브젝트의 SyncVars와 SyncLists 상태는 그대로 유지됩니다. 오브젝트의 커스텀 직렬화 데이터의 경우에도 마찬가지입니다.

호스트가 사라지면 게임의 모든 플레이어 오브젝트는 비활성화됩니다. 그 이후 다른 클라이언트가 새로운 호스트 상으로 새 게임에 다시 참여하면 각각의 클라이언트에 대한 플레이어가 다시 호스트에서 활성화되며, 다른 클라이언트에서 다시 스폰됩니다. 따라서 호스트 이송 동안 플레이어 상태 데이터 역시 그대로 유지됩니다.

참고: 클라이언트가 사용할 수 있는 데이터만 호스트 이송 도중 보존됩니다. 만일 데이터가 서버에만 있는 경우, 새로운 호스트가 된 클라이언트는 이 데이터를 사용할 수 없습니다. 따라서 SyncVars나 SyncLists에 저장되지 않은 호스트의 데이터는 호스트 이송 이후 사용할 수 없습니다.

클라이언트가 새로운 호스트가 되면 모든 네트워크 오브젝트에 대해OnStartServer 콜백 함수가 호출됩니다.

새로운 호스트에서, NetworkMigrationManager는 BecomeNewHost 함수를 사용하여 현재의 ClientScene 상태에서 네트워킹된 서버 씬을 만듭니다.

호스트 이송이 활성화된 게임의 참여자들은 서버의 connectionId를 통해 식별됩니다. 한 클라이언트가 게임의 새 호스트에 재접속하면, 이 connectionId는 새 호스트에 전달되어 이 클라이언트를 기존 호스트에 연결되어 있던 클라이언트와 매치할 수 있도록 합니다. 이 Id는 ClientScene에서 “reconnectId”로서 설정됩니다.

비플레이어 오브젝트(Non-Player Objects)

클라이언트 권한이 있는 비플레이어 오브젝트 역시 호스트 이송에 의해 처리됩니다. 각 클라이언트가 소유하는 오브젝트는 플레이어 오브젝트와 같은 방식으로 비활성화되고 다시 활성화됩니다.

피어 식별(Identifying Peers)

호스트가 사라지기 전에는 모든 피어가 호스트에 연결되어 있습니다. 각각의 피어는 호스트에서 고유 connectionId를 가지게 됩니다. 이 connectionId는 호스트 이송에서는 “oldConnectionId”이라고 합니다.

새로운 호스트가 선택되어 피어가 다시 연결하려고 시도하면 클라이언트는 “oldConnectionId”를 제공하여 어떤 피어인지 식별하게 합니다. 이 과정을 통해 새로운 호스트가 다시 연결 중인 클라이언트와 해당 플레이어 오브젝트를 매치할 수 있습니다.

기존 호스트는 재연결시 특수 oldConnectionId인 0을 사용합니다. 기존 호스트는 스스로에 대한 연결이 없었기 때문입니다. 이 경우 ClientScene.ReconnectIdHost 상수를 사용하면 됩니다.

내장 사용자 인터페이스를 사용하는 경우 oldConnectionId 는 자동으로 설정됩니다. NetworkMigrationManager.ResetClientScene.SetReconnectId를 사용하여 수동으로 설정할 수도 있습니다.

호스트 이송 플로우(Host Migration Flow)

  1. MachineA가 호스트 이송을 활성화한 상태로 게임을 호스트합니다.

  2. MachineB는 클라이언트를 시작하고 호스트 이송을 활성화한 상태로 게임에 참여합니다.
    • MachineB가 피어에 대한 통보를 받습니다. (Machine A–0과 자기 자신인 MachineB–1)
  3. MachineC는 클라이언트를 시작하고 호스트 이송을 활성화한 상태로 게임에 참여합니다.
    • MachineB가 피어에 대한 통보를 받습니다. (Machine A–0과 MachineB–1, 자기 자신인 MachineC–2)
  4. MachineA가 사라져서 호스트 역시 사라집니다.

  5. MachineB가 호스트로부터 접속 해제됩니다.
    1. MachineB 콜백 함수가 클라이언트의 MigrationManager에서 호출됩니다.
    2. 모든 플레이어에 대한 MachineB 플레이어 오브젝트가 비활성화됩니다.
    3. MachineB는 온라인 씬에 남아 있습니다.
  6. MachineB는 유틸리티 함수를 사용하여 새 호스트(자기 자신)를 선택합니다.
    1. MachineB는 BecomeNewHost()를 호출합니다.
    2. MachineB는 수신을 시작합니다.
    3. 자신에 대한 MachineB 플레이어 오브젝트가 재활성화됩니다.
    4. MachineB에 대한 플레이어가 모든 기존 상태로 게임에 복귀합니다.
  7. MachineC가 호스트로부터 접속 해제됩니다.
    1. MachineC 콜백 함수가 클라이언트의 MigrationManager에서 호출됩니다.
    2. 모든 플레이어에 대한 MachineC 플레이어 오브젝트가 비활성화됩니다.
    3. MachineC는 온라인 씬에 남아 있습니다.
  8. MachineC는 유틸리티 함수를 사용하여 새 호스트(MachineB)를 선택합니다.
    • MachineC는 새 호스트에 재접속합니다.
  9. MachineB는 MachineC로부터의 접속을 받습니다.
    1. MachineC는 (AddPlayer 메시지 대신) oldConnectionId와 함께 재접속 메시지를 전송합니다.
    2. 콜백 함수가 서버의 MigrationManager에서 호출됩니다.
    3. MachineB는 oldConnectionId를 사용하여 해당 플레이어에 대해 비활성화된 플레이어 오브젝트를 찾고 이를 ReconnectPlayerForConnection()에 다시 추가합니다.
    4. 플레이어 오브젝트는 MachineC에 대해 다시 스폰됩니다.
    5. MachineC에 대한 플레이어가 모든 기존 상태로 게임에 복귀합니다.
  10. 기존 호스트인 MachineA가 복구됩니다.
    1. MachineA는 유틸리티 함수를 사용하여 새 호스트(MachineB)를 선택합니다.
    2. MachineA는 MachineB에 “재접속”합니다.
  11. MachineB는 MachineA로부터의 접속을 받습니다.

  12. MachineA는 0 값의 oldConnectionId와 함께 재접속 메시지를 전송합니다.
    1. 콜백 함수가 서버(MachineB)의 MigrationManager에서 호출됩니다.
    2. MachineB는 oldConnectionId를 사용하여 해당 플레이어에 대해 비활성화된 플레이어 오브젝트를 찾고 이를 ReconnectPlayerForConnection()에 다시 추가합니다.
    3. 플레이어 오브젝트는 MachineA에 대해 다시 스폰됩니다.
    4. MachineA에 대한 플레이어가 모든 기존 상태로 게임에 복귀합니다.

콜백 함수(Callback Functions)

NetworkHostMigrationManager의 콜백 함수

// called on client after the connection to host is lost. controls whether to switch scenes
protected virtual void OnClientDisconnectedFromHost(
    NetworkConnection conn, 
    out SceneChangeOption sceneChange)

// called on host after the host is lost. host MUST change scenes
protected virtual void OnServerHostShutdown()

// called on new host (server) when a client from the old host re-connects a player
protected virtual void OnServerReconnectPlayer(
    NetworkConnection newConnection, 
    GameObject oldPlayer, 
    int oldConnectionId, 
    short playerControllerId)

// called on new host (server) when a client from the old host re-connects a player
protected virtual void OnServerReconnectPlayer(
    NetworkConnection newConnection, 
    GameObject oldPlayer, 
    int oldConnectionId, 
    short playerControllerId, 
    NetworkReader extraMessageReader)

// called on new host (server) when a client from the old host re-connects a non-player object
protected virtual void OnServerReconnectObject(
    NetworkConnection newConnection, 
    GameObject oldObject, 
    int oldConnectionId)

// called on both host and client when the set of peers is updated
protected virtual void OnPeersUpdated(
    PeerListMessage peers)

// utility function called by the default UI on client after connection to host was lost, to pick a new host.
public virtual bool FindNewHost(
    out NetworkSystem.PeerInfoMessage newHostInfo, 
    out bool youAreNewHost)

// called when the authority of a non-player object changes
protected virtual void OnAuthorityUpdated(
    GameObject go,
    int connectionId,
    bool authorityState)

제약(Constraints)

서버, 즉 호스트에만 존재하는 데이터는 호스트가 사라지면 손실됩니다. 게임이 올바르게 호스트 이송을 수행하려면 중요한 데이터는 서버 혼자 보유하도록 하지 말고, 클라이언트에게 배포해야 합니다.

이 기능은 직접 연결 게임에 적용됩니다. 매치메이커와 릴레이 서버가 있는 경우 추가 작업을 진행해야 합니다.

네트워크 클라이언트와 서버
전송 레이어 API 사용(Using the Transport Layer API)