유니티(Unity) 릴레이 Services 사용 방법 (#Relay 3편)

[출처] - Unity Documentation

#1. Initialize Unity Services

await UnityServices.InitializeAsync();

를 사용해 UnityServices 를 초기화 해야합니다
 

#2. Authenticate the player

public async void OnSignIn()
{
    	await  AuthenticationService.Instance.SignInAnonymouslyAsync();
    	playerId = AuthenticationService.Instance.PlayerId;

    	Debug.Log($"Signed in. Player ID: {playerId}");
}

호스트 플레이어와 연결중인 플레이어 모두 인증해야합니다
가장 쉬운 방법은 SignInAnonymouslyAsync() 함수를 이용하는 것입니다


#3.Host player(Host Player 기준!!)

호스트 플레이어로써 게임을 시작할 때, allocation, request a join code, configure the connection type, and create an instance of the NetworkDriver를 해야합니다, 자세한 내용은 아래 포스팅을 참고하면 됩니다

유니티(Unity) Relay 서버에 대해

#릴레이 서버란? Unity에서 제공하는 멀티플레이어 게임용 Relay 서비스입니다 Relay 서버는 게임 세션 생명주기와 독립적으로 동작하는 특징을 가지고 있습니다 Relay 서버는 플레이어 연결 시간이

wlsdn629.tistory.com

유니티(Unity) Untiy Transport Package(UTP)에 대해서 및 SetUp방법 (#Relay 2편)

Relay SDK는 Unity Transport Package (UTP)에서 잘 작동됩니다 About Unity Transport | Unity Multiplayer Networking Unity Transport provides the com.unity.transport package, used to add multiplayer and network features to your project. docs-multiplay

wlsdn629.tistory.com

 

#4. The host player update loop

allocation flow 스텝을 밟기 전호스트 플레이어의 Update loop문을 설정해야 합니다
쉽게 하기 위해서, Update문에서 매번 아래 접은 글에 달려 있는 코드를 집어 넣으면 됩니다

더보기
void UpdateHost()
{
    	// Skip update logic if the Host isn't yet bound.
    	if (!hostDriver.IsCreated || !hostDriver.Bound)
    	{
        	return;
    	}

    	// This keeps the binding to the Relay server alive,
    	// preventing it from timing out due to inactivity.
    	hostDriver.ScheduleUpdate().Complete();

    	// Clean up stale connections.
    	for (int i = 0; i < serverConnections.Length; i++)
    	{
        	if (!serverConnections[i].IsCreated)
        	{
            	Debug.Log("Stale connection removed");
            	serverConnections.RemoveAt(i);
            	--i;
        	}
    	}

    	// Accept incoming client connections.
    	NetworkConnection incomingConnection;
    	while ((incomingConnection = hostDriver.Accept()) != default(NetworkConnection))
    	{
        	// Adds the requesting Player to the serverConnections list.
        	// This also sends a Connect event back the requesting Player,
        	// as a means of acknowledging acceptance.
        	Debug.Log("Accepted an incoming connection.");
        	serverConnections.Add(incomingConnection);
    	}

    	// Process events from all connections.
    	for (int i = 0; i < serverConnections.Length; i++)
    	{
        	Assert.IsTrue(serverConnections[i].IsCreated);

        	// Resolve event queue.
        	NetworkEvent.Type eventType;
        	while ((eventType = hostDriver.PopEventForConnection(serverConnections[i], out var stream)) != NetworkEvent.Type.Empty)
        	{
            	switch (eventType)
            	{
                	// Handle Relay events.
                	case NetworkEvent.Type.Data:
                    	FixedString32Bytes msg = stream.ReadFixedString32();
                    	Debug.Log($"Server received msg: {msg}");
                    	hostLatestMessageReceived = msg.ToString();
                    	break;

                	// Handle Disconnect events.
                	case NetworkEvent.Type.Disconnect:
                    	Debug.Log("Server received disconnect from client");
                    	serverConnections[i] = default(NetworkConnection);
                    	break;
            	}
        	}
    	}
}

 

#5.Create an allocation

설정을 다 했으면 allocation을 만들 차례입니다
allocation 또한 스닙셋 코드가 존재하며 다음과 같습니다

public async void OnAllocate()
{
    	Debug.Log("Host - Creating an allocation. Upon success, I have 10 seconds to BIND to the Relay server that I've allocated.");

    	// Determine region to use (user-selected or auto-select/QoS)
    	string region = GetRegionOrQosDefault();
    	Debug.Log($"The chosen region is: {region ?? autoSelectRegionName}");

    	// Set max connections. Can be up to 100, but note the more players connected, the higher the bandwidth/latency impact.
    	int maxConnections = 4;

    	// Important: After the allocation is created, you have ten seconds to BIND, else the allocation times out.
    	hostAllocation = await RelayService.Instance.CreateAllocationAsync(maxConnections, region);
    	Debug.Log($"Host Allocation ID: {hostAllocation.AllocationId}, region: {hostAllocation.Region}");

    	// Initialize NetworkConnection list for the server (Host).
    	// This list object manages the NetworkConnections which represent connected players.
    	serverConnections = new NativeList<NetworkConnection>(maxConnections, Allocator.Persistent);
}

hostAllocation = await RelayService.Instance.CreateAllocationAsync(maxConnections, region);
 

#6. Bind to the Relay server and listen for connections

릴레이 서버와 연결을 Bind하는 방법은 OnBindHost 스닙셋 코드를 이용하여 쉽게 할 수 있습니다

public void OnBindHost()
{
    	Debug.Log("Host - Binding to the Relay server using UTP.");

    	// Extract the Relay server data from the Allocation response.
    	var relayServerData = new RelayServerData(hostAllocation, "udp");

    	// Create NetworkSettings using the Relay server data.
    	var settings = new NetworkSettings();
    	settings.WithRelayParameters(ref relayServerData);

    	// Create the Host's NetworkDriver from the NetworkSettings.
    	hostDriver = NetworkDriver.Create(settings);

    	// Bind to the Relay server.
    	if (hostDriver.Bind(NetworkEndPoint.AnyIpv4) != 0)
    	{
        	Debug.LogError("Host client failed to bind");
    	}
    	else
    	{
        	if (hostDriver.Listen() != 0)
        	{
            	Debug.LogError("Host client failed to listen");
        	}
        	else
        	{
            	Debug.Log("Host client bound to Relay server");
        	}
    	}
}

#7. Request a join code

Allocation이 성공적으로 마무리 되면 그 다음으론
OnJoinCode스닙셋 코드를 이용해서 호스트 플레이어는 게임 세션안에 호스트 플레이어와 참여 플레이어들이 함께
Join할 수 있게 Join Code를 공유해줍니다

public async void OnJoinCode()
{
    	Debug.Log("Host - Getting a join code for my allocation. I would share that join code with the other players so they can join my session.");

    	try
    	{
        	joinCode = await RelayService.Instance.GetJoinCodeAsync(hostAllocation.AllocationId);
        	Debug.Log("Host - Got join code: " + joinCode);
    	}
    	catch (RelayServiceException ex)
    	{
        	Debug.LogError(ex.Message + "\n" + ex.StackTrace);
    	}
}

#8. Joining player(여기 부터는 Join Player 기준!!)

참여하는 플레이어 또한, Allocation, Connection Type 구성, NetworkDriver 인스턴화하기가 필요합니다
이 또한,

유니티(Unity) Relay 서버에 대해

#릴레이 서버란? Unity에서 제공하는 멀티플레이어 게임용 Relay 서비스입니다 Relay 서버는 게임 세션 생명주기와 독립적으로 동작하는 특징을 가지고 있습니다 Relay 서버는 플레이어 연결 시간이

wlsdn629.tistory.com

유니티(Unity) Untiy Transport Package(UTP)에 대해서 및 SetUp방법 (#Relay 2편)

Relay SDK는 Unity Transport Package (UTP)에서 잘 작동됩니다 About Unity Transport | Unity Multiplayer Networking Unity Transport provides the com.unity.transport package, used to add multiplayer and network features to your project. docs-multiplay

wlsdn629.tistory.com

이전 포스팅을 참고하면 됩니다
 
호스트 플레이어의 게임 세션에 참여하기 전에 , 참여하는 플레이어의 Update문을 설정해주어야 합니다

void UpdatePlayer()
{
    	// Skip update logic if the Player isn't yet bound.
    	if (!playerDriver.IsCreated || !playerDriver.Bound)
    	{
        	return;
    	}

    	// This keeps the binding to the Relay server alive,
    	// preventing it from timing out due to inactivity.
    	playerDriver.ScheduleUpdate().Complete();

    	// Resolve event queue.
    	NetworkEvent.Type eventType;
    	while ((eventType = clientConnection.PopEvent(playerDriver, out var stream)) != NetworkEvent.Type.Empty)
    	{
        	switch (eventType)
        	{
            	// Handle Relay events.
            	case NetworkEvent.Type.Data:
                	FixedString32Bytes msg = stream.ReadFixedString32();
                	Debug.Log($"Player received msg: {msg}");
                	playerLatestMessageReceived = msg.ToString();
                	break;

            	// Handle Connect events.
            	case NetworkEvent.Type.Connect:
                	Debug.Log("Player connected to the Host");
                	break;

            	// Handle Disconnect events.
            	case NetworkEvent.Type.Disconnect:
                	Debug.Log("Player got disconnected from the Host");
                	clientConnection = default(NetworkConnection);
                	break;
        	}
    	}
}

 

#10.Join an allocation

설정이 끝나면 호스트 플레이어가 만들어둔 Allocation에 Join하면 됩니다
OnJoin 스닙셋 코드를 이용해 쉽게 Join할 수 있습니다

public async void OnJoin()
{
    	// Input join code in the respective input field first.
    	if (String.IsNullOrEmpty(JoinCodeInput.text))
    	{
        	Debug.LogError("Please input a join code.");
        	return;
    	}

    	Debug.Log("Player - Joining host allocation using join code. Upon success, I have 10 seconds to BIND to the Relay server that I've allocated.");

    	try
    	{
        	playerAllocation = await RelayService.Instance.JoinAllocationAsync(JoinCodeInput.text);
        	Debug.Log("Player Allocation ID: " + playerAllocation.AllocationId);
    	}
    	catch (RelayServiceException ex)
    	{
        	Debug.LogError(ex.Message + "\n" + ex.StackTrace);
    	}
}

PlayerAllocation = await RelayService.Instance.JoinAllocationAsync(JoinCodeInput.text);
 

#11. Bind to the Relay server and connect to the host player

호스트플레이어와 Relay server에 Bind하기 위해 OnBindPlayer스닙셋 코드를 이용해서 쉽게 할 수 있습니다

public void OnBindPlayer()
{
    	Debug.Log("Player - Binding to the Relay server using UTP.");

    	// Extract the Relay server data from the Join Allocation response.
    	var relayServerData = new RelayServerData(playerAllocation, "udp");
    	// Create NetworkSettings using the Relay server data.
    	var settings = new NetworkSettings();
    	settings.WithRelayParameters(ref relayServerData);

    	// Create the Player's NetworkDriver from the NetworkSettings object.
    	playerDriver = NetworkDriver.Create(settings);

    	// Bind to the Relay server.
    	if (playerDriver.Bind(NetworkEndPoint.AnyIpv4) != 0)
    	{
        	Debug.LogError("Player client failed to bind");
    	}
    	else
    	{
        	Debug.Log("Player client bound to Relay server");
    	}
}
public void OnConnectPlayer()
{
    	Debug.Log("Player - Connecting to Host's client.");
    	// Sends a connection request to the Host Player.
    	clientConnection = playerDriver.Connect();
}

 

12. Sending messages

1) as the host

더보기
public void OnHostSendMessage()
{
    	if (serverConnections.Length == 0)
    	{
        	Debug.LogError("No players connected to send messages to.");
        	return;
    	}

    	// Get message from the input field, or default to the placeholder text.
    	var msg = !String.IsNullOrEmpty(HostMessageInput.text) ? HostMessageInput.text : HostMessageInput.placeholder.GetComponent<Text>().text;

    	// In this sample, we will simply broadcast a message to all connected clients.
    	for (int i = 0; i < serverConnections.Length; i++)
    	{
        	if (hostDriver.BeginSend(serverConnections[i], out var writer) == 0)
        	{
            	// Send the message. Aside from FixedString32, many different types can be used.
            	writer.WriteFixedString32(msg);
            	hostDriver.EndSend(writer);
        	}
    	}
}

2) as a player

더보기
public void OnPlayerSendMessage()
{
    	if (!clientConnection.IsCreated)
    	{
        	Debug.LogError("Player isn't connected. No Host client to send message to.");
        	return;
    	}

    	// Get message from the input field, or default to the placeholder text.
    	var msg = !String.IsNullOrEmpty(PlayerMessageInput.text) ? PlayerMessageInput.text : PlayerMessageInput.placeholder.GetComponent<Text>().text;
    	if (playerDriver.BeginSend(clientConnection, out var writer) == 0)
    	{
        	// Send the message. Aside from FixedString32, many different types can be used.
        	writer.WriteFixedString32(msg);
        	playerDriver.EndSend(writer);
    	}
}

 

#13. Disconnecting

1) a player as the host

더보기
public void OnDisconnectPlayers()
{
    	if (serverConnections.Length == 0)
    	{
        	Debug.LogError("No players connected to disconnect.");
        	return;
    	}

    	// In this sample, we will simply disconnect all connected clients.
    	for (int i = 0; i < serverConnections.Length; i++)
    	{
        	// This sends a disconnect event to the destination client,
        	// letting them know they're disconnected from the Host.
        	hostDriver.Disconnect(serverConnections[i]);

        	// Here, we set the destination client's NetworkConnection to the default value.
        	// It will be recognized in the Host's Update loop as a stale connection, and be removed.
        	serverConnections[i] = default(NetworkConnection);
    	}
}

2) as a player

더보기
public void OnDisconnect()
{
    	// This sends a disconnect event to the Host client,
    	// letting them know they're disconnecting.
    	playerDriver.Disconnect(clientConnection);

    	// We remove the reference to the current connection by overriding it.
    	clientConnection = default(NetworkConnection);
}

 
 
 
#정리
1. 초기화
2. 로그인
3. 호스트플레이어 SetUp - (Allocation, configure Connection Type, NetworkDriver)
4. Bind - Relay Server and listen for Connections request form joining players
5. 조인 코드 요청
6. Join 플레이어 SetUp  - (Allocation, configure Connection Type, NetworkDriver)
7. Bind to the Relay server and connect to the host player

Connection flow

The connection flow is the process by which the Allocations service reserves slots on a Relay server to group players into a match. The process involves two types of players: a host player and joining players. The following list describes the high-level st

docs.unity.com