|
1 | 1 | using System;
|
| 2 | +using System.Collections; |
2 | 3 | using System.Threading.Tasks;
|
| 4 | +using Unity.Multiplayer.Samples.BossRoom.ApplicationLifecycle.Messages; |
| 5 | +using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure; |
| 6 | +using Unity.Multiplayer.Samples.BossRoom.Shared.Net.UnityServices.Lobbies; |
| 7 | +using Unity.Multiplayer.Samples.Utilities; |
3 | 8 | using Unity.Netcode;
|
| 9 | +using Unity.Netcode.Transports.UTP; |
| 10 | +using UnityEngine; |
4 | 11 |
|
5 | 12 | namespace Unity.Multiplayer.Samples.BossRoom
|
6 | 13 | {
|
7 | 14 | public class ReconnectingConnectionState : ConnectionState
|
8 | 15 | {
|
9 |
| - public ReconnectingConnectionState(ConnectionManager connectionManager) |
| 16 | + Coroutine m_ReconnectCoroutine; |
| 17 | + string m_LobbyCode = ""; |
| 18 | + |
| 19 | + LobbyServiceFacade m_LobbyServiceFacade; |
| 20 | + LocalLobby m_LocalLobby; |
| 21 | + IPublisher<ReconnectMessage> m_ReconnectMessagePublisher; |
| 22 | + |
| 23 | + const int k_NbReconnectAttempts = 2; |
| 24 | + |
| 25 | + public ReconnectingConnectionState(ConnectionManager connectionManager, LobbyServiceFacade lobbyServiceFacade, |
| 26 | + LocalLobby localLobby, IPublisher<ReconnectMessage> reconnectMessagePublisher) |
10 | 27 | : base(connectionManager)
|
11 | 28 | {
|
| 29 | + m_LobbyServiceFacade = lobbyServiceFacade; |
| 30 | + m_LocalLobby = localLobby; |
| 31 | + m_ReconnectMessagePublisher = reconnectMessagePublisher; |
12 | 32 | }
|
13 | 33 |
|
14 |
| - public override void OnClientConnected(ulong clientId) |
| 34 | + public override void Enter() |
15 | 35 | {
|
16 |
| - throw new NotImplementedException(); |
| 36 | + m_LobbyCode = m_LobbyServiceFacade.CurrentUnityLobby != null ? m_LobbyServiceFacade.CurrentUnityLobby.LobbyCode : ""; |
| 37 | + m_ConnectionManager.StartCoroutine(ReconnectCoroutine()); |
17 | 38 | }
|
18 | 39 |
|
19 |
| - public override void OnClientDisconnect(ulong clientId) |
| 40 | + public override void Exit() |
20 | 41 | {
|
21 |
| - throw new NotImplementedException(); |
| 42 | + if (m_ReconnectCoroutine != null) |
| 43 | + { |
| 44 | + m_ConnectionManager.StopCoroutine(m_ReconnectCoroutine); |
| 45 | + m_ReconnectCoroutine = null; |
| 46 | + m_ReconnectMessagePublisher.Publish(new ReconnectMessage(k_NbReconnectAttempts, k_NbReconnectAttempts)); |
| 47 | + } |
22 | 48 | }
|
23 | 49 |
|
24 |
| - public override void OnServerStarted() |
| 50 | + public override void OnClientConnected(ulong clientId) |
25 | 51 | {
|
26 |
| - throw new NotImplementedException(); |
| 52 | + m_ConnectionManager.ChangeState(ConnectionStateType.Connected); |
27 | 53 | }
|
28 | 54 |
|
29 |
| - public override void StartClientIP(string playerId, string playerName, string ipaddress, int port) |
| 55 | + public override void OnClientDisconnect(ulong clientId) |
30 | 56 | {
|
31 |
| - throw new NotImplementedException(); |
| 57 | + switch (m_ConnectionManager.DisconnectReason.Reason) |
| 58 | + { |
| 59 | + case ConnectStatus.UserRequestedDisconnect: |
| 60 | + case ConnectStatus.HostEndedSession: |
| 61 | + case ConnectStatus.ServerFull: |
| 62 | + m_ConnectionManager.ChangeState(ConnectionStateType.Offline); |
| 63 | + break; |
| 64 | + } |
32 | 65 | }
|
33 | 66 |
|
34 |
| - public override Task StartClientLobbyAsync(string playerName, string playerId, Action<string> onFailure) |
| 67 | + public override void OnUserRequestedShutdown() |
35 | 68 | {
|
36 |
| - throw new NotImplementedException(); |
| 69 | + m_ConnectionManager.ChangeState(ConnectionStateType.Offline); |
37 | 70 | }
|
38 | 71 |
|
39 |
| - public override bool StartHostIP(string playerId, string playerName, string ipaddress, int port) |
| 72 | + IEnumerator ReconnectCoroutine() |
40 | 73 | {
|
41 |
| - throw new NotImplementedException(); |
42 |
| - } |
| 74 | + Debug.Log("Lost connection to host, trying to reconnect..."); |
| 75 | + int nbTries = 0; |
| 76 | + while (nbTries < k_NbReconnectAttempts) |
| 77 | + { |
| 78 | + NetworkManager.Singleton.Shutdown(); |
43 | 79 |
|
44 |
| - public override Task StartHostLobbyAsync(string playerId, string playerName) |
45 |
| - { |
46 |
| - throw new NotImplementedException(); |
47 |
| - } |
| 80 | + yield return new WaitWhile(() => NetworkManager.Singleton.ShutdownInProgress); // wait until NetworkManager completes shutting down |
| 81 | + Debug.Log($"Reconnecting attempt {nbTries + 1}/{k_NbReconnectAttempts}..."); |
| 82 | + m_ReconnectMessagePublisher.Publish(new ReconnectMessage(nbTries, k_NbReconnectAttempts)); |
| 83 | + if (!string.IsNullOrEmpty(m_LobbyCode)) |
| 84 | + { |
| 85 | + var leavingLobby = m_LobbyServiceFacade.EndTracking(); |
| 86 | + yield return new WaitUntil(() => leavingLobby.IsCompleted); |
| 87 | + var joiningLobby = m_LobbyServiceFacade.TryJoinLobbyAsync("", m_LobbyCode); |
| 88 | + yield return new WaitUntil(() => joiningLobby.IsCompleted); |
| 89 | + if (joiningLobby.Result.Success) |
| 90 | + { |
| 91 | + m_LobbyServiceFacade.SetRemoteLobby(joiningLobby.Result.Lobby); |
| 92 | + var joiningRelay = JoinRelayServerAsync(); |
| 93 | + yield return new WaitUntil(() => joiningRelay.IsCompleted); |
| 94 | + } |
| 95 | + else |
| 96 | + { |
| 97 | + Debug.Log("Failed joining lobby."); |
| 98 | + } |
| 99 | + } |
| 100 | + else |
| 101 | + { |
| 102 | + ConnectClient(); |
| 103 | + } |
48 | 104 |
|
49 |
| - public override void OnUserRequestedShutdown() |
50 |
| - { |
51 |
| - throw new NotImplementedException(); |
| 105 | + yield return new WaitForSeconds(1.1f * NetworkManager.Singleton.NetworkConfig.ClientConnectionBufferTimeout + ((UnityTransport) NetworkManager.Singleton.NetworkConfig.NetworkTransport).DisconnectTimeoutMS / 1000.0f); // wait a bit longer than the timeout duration to make sure we have enough time to stop this coroutine if successful |
| 106 | + nbTries++; |
| 107 | + } |
52 | 108 | }
|
53 | 109 |
|
54 |
| - public override void OnServerShutdown() |
| 110 | + async Task JoinRelayServerAsync() |
55 | 111 | {
|
56 |
| - throw new NotImplementedException(); |
| 112 | + try |
| 113 | + { |
| 114 | + var (ipv4Address, port, allocationIdBytes, connectionData, hostConnectionData, key) = |
| 115 | + await UnityRelayUtilities.JoinRelayServerFromJoinCode(m_LocalLobby.RelayJoinCode); |
| 116 | + |
| 117 | + await m_LobbyServiceFacade.UpdatePlayerRelayInfoAsync(allocationIdBytes.ToString(), m_LocalLobby.RelayJoinCode); |
| 118 | + var utp = (UnityTransport)m_ConnectionManager.NetworkManager.NetworkConfig.NetworkTransport; |
| 119 | + utp.SetClientRelayData(ipv4Address, port, allocationIdBytes, key, connectionData, hostConnectionData, isSecure: true); |
| 120 | + } |
| 121 | + catch (Exception e) |
| 122 | + { |
| 123 | + return;//not re-throwing, but still not allowing to connect |
| 124 | + } |
| 125 | + |
| 126 | + ConnectClient(); |
57 | 127 | }
|
58 | 128 |
|
59 |
| - public override void ApprovalCheck(byte[] connectionData, ulong clientId, NetworkManager.ConnectionApprovedDelegate connectionApprovedCallback) |
| 129 | + void ConnectClient() |
60 | 130 | {
|
61 |
| - throw new NotImplementedException(); |
| 131 | + //and...we're off! Netcode will establish a socket connection to the host. |
| 132 | + // If the socket connection fails, we'll hear back by getting an ReceiveServerToClientSetDisconnectReason_CustomMessage callback for ourselves and get a message telling us the reason |
| 133 | + // If the socket connection succeeds, we'll get our ReceiveServerToClientConnectResult_CustomMessage invoked. This is where game-layer failures will be reported. |
| 134 | + m_ConnectionManager.NetworkManager.StartClient(); |
| 135 | + SceneLoaderWrapper.Instance.AddOnSceneEventCallback(); |
| 136 | + m_ConnectionManager.RegisterCustomMessages(); |
62 | 137 | }
|
63 | 138 | }
|
64 | 139 | }
|
0 commit comments