Skip to content

Commit 0a3680d

Browse files
committed
implementing reconnecting state
1 parent 96575b7 commit 0a3680d

File tree

7 files changed

+147
-44
lines changed

7 files changed

+147
-44
lines changed

Assets/BossRoom/Scripts/Gameplay/ConnectionManagement/ConnectionManager.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,31 @@ public class ConnectionManager : MonoBehaviour
4040

4141
[SerializeField]
4242
NetworkObject m_GameState;
43+
public NetworkObject GameState => m_GameState;
4344

4445
ProfileManager m_ProfileManager;
4546
LobbyServiceFacade m_LobbyServiceFacade;
4647
LocalLobby m_LocalLobby;
4748
IPublisher<ConnectionEventMessage> m_ConnectionEventPublisher;
4849
IPublisher<QuitGameSessionMessage> m_QuitGameSessionPublisher;
4950
IPublisher<ConnectStatus> m_ConnectStatusPublisher;
51+
IPublisher<ReconnectMessage> m_ReconnectMessagePublisher;
5052

5153
DisconnectReason m_DisconnectReason = new DisconnectReason();
5254
public DisconnectReason DisconnectReason => m_DisconnectReason;
5355

5456
[Inject]
5557
void InjectDependencies(ProfileManager profileManager, LobbyServiceFacade lobbyServiceFacade, LocalLobby localLobby,
5658
IPublisher<ConnectionEventMessage> connectionEventPublisher, IPublisher<QuitGameSessionMessage> quitGameSessionPublisher,
57-
IPublisher<ConnectStatus> connectStatusPublisher)
59+
IPublisher<ConnectStatus> connectStatusPublisher, IPublisher<ReconnectMessage> reconnectMessagePublisher)
5860
{
5961
m_ProfileManager = profileManager;
6062
m_LobbyServiceFacade = lobbyServiceFacade;
6163
m_LocalLobby = localLobby;
6264
m_ConnectionEventPublisher = connectionEventPublisher;
6365
m_QuitGameSessionPublisher = quitGameSessionPublisher;
6466
m_ConnectStatusPublisher = connectStatusPublisher;
67+
m_ReconnectMessagePublisher = reconnectMessagePublisher;
6568
}
6669

6770
void Awake()
@@ -76,43 +79,34 @@ void Start()
7679
[ConnectionStateType.Offline] = new OfflineConnectionState(this, m_LobbyServiceFacade, m_LocalLobby),
7780
[ConnectionStateType.Connecting] = new ConnectingConnectionState(this, m_QuitGameSessionPublisher, m_ConnectStatusPublisher),
7881
[ConnectionStateType.Connected] = new ConnectedConnectionState(this, m_QuitGameSessionPublisher, m_ConnectStatusPublisher),
79-
[ConnectionStateType.Reconnecting] = new ReconnectingConnectionState(this),
82+
[ConnectionStateType.Reconnecting] = new ReconnectingConnectionState(this, m_LobbyServiceFacade, m_LocalLobby, m_ReconnectMessagePublisher),
8083
[ConnectionStateType.Hosting] = new HostingConnectionState(this, m_LobbyServiceFacade, m_ConnectionEventPublisher)
8184
};
8285
m_CurrentState = ConnectionStateType.Offline;
8386

8487
NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback;
8588
NetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback;
86-
NetworkManager.OnServerStarted += OnServerStarted;
8789
NetworkManager.ConnectionApprovalCallback += ApprovalCheck;
8890
}
8991

9092
void OnDestroy()
9193
{
9294
NetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback;
9395
NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback;
94-
NetworkManager.OnServerStarted -= OnServerStarted;
9596

9697
}
9798

9899
public void ChangeState(ConnectionStateType newState)
99100
{
100101
Debug.Log(newState);
102+
m_Logics[m_CurrentState].Exit();
101103
m_CurrentState = newState;
104+
m_Logics[m_CurrentState].Enter();
102105
}
103106

104-
void OnServerStarted()
107+
public void InstantiateGameState()
105108
{
106-
// server spawns game state
107-
var gameState = Instantiate(m_GameState);
108109

109-
gameState.Spawn();
110-
111-
SceneLoaderWrapper.Instance.AddOnSceneEventCallback();
112-
113-
//The "BossRoom" server always advances to CharSelect immediately on start. Different games
114-
//may do this differently.
115-
SceneLoaderWrapper.Instance.LoadScene("CharSelect", useNetworkSceneManager: true);
116110
}
117111

118112
void OnClientDisconnectCallback(ulong clientId)
@@ -174,10 +168,9 @@ public void OnServerShutdown()
174168
m_Logics[m_CurrentState].OnServerShutdown();
175169
}
176170

177-
public void OnClientStarted()
171+
public void RegisterCustomMessages()
178172
{
179173
// should only do this once StartClient has been called (start client will initialize NetworkSceneManager and CustomMessagingManager)
180-
SceneLoaderWrapper.Instance.AddOnSceneEventCallback();
181174
NetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(nameof(ReceiveServerToClientSetDisconnectReason_CustomMessage), ReceiveServerToClientSetDisconnectReason_CustomMessage);
182175
}
183176

Assets/BossRoom/Scripts/Gameplay/ConnectionManagement/ConnectionState/ConnectedConnectionState.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ public ConnectedConnectionState(ConnectionManager connectionManager, IPublisher<
1818
m_ConnectStatusPublisher = connectStatusPublisher;
1919
}
2020

21+
public override void Enter() { }
22+
23+
public override void Exit() { }
24+
2125
public override void OnClientDisconnect(ulong clientId)
2226
{
2327
// This is also called on the Host when a different client disconnects. To make sure we only handle our own disconnection, verify that we are either

Assets/BossRoom/Scripts/Gameplay/ConnectionManagement/ConnectionState/ConnectingConnectionState.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using Unity.Multiplayer.Samples.BossRoom.ApplicationLifecycle.Messages;
33
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
4+
using Unity.Multiplayer.Samples.Utilities;
45

56
namespace Unity.Multiplayer.Samples.BossRoom
67
{
@@ -17,6 +18,14 @@ public ConnectingConnectionState(ConnectionManager connectionManager, IPublisher
1718
m_ConnectStatusPublisher = connectStatusPublisher;
1819
}
1920

21+
public override void Enter()
22+
{
23+
SceneLoaderWrapper.Instance.AddOnSceneEventCallback();
24+
m_ConnectionManager.RegisterCustomMessages();
25+
}
26+
27+
public override void Exit() { }
28+
2029
public override void OnClientConnected(ulong clientId)
2130
{
2231
m_ConnectionManager.ChangeState(ConnectionStateType.Connected);

Assets/BossRoom/Scripts/Gameplay/ConnectionManagement/ConnectionState/ConnectionState.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ public ConnectionState(ConnectionManager connectionManager)
1313
m_ConnectionManager = connectionManager;
1414
}
1515

16+
public abstract void Enter();
17+
18+
public abstract void Exit();
19+
1620
public virtual void OnClientConnected(ulong clientId) {}
1721
public virtual void OnClientDisconnect(ulong clientId) {}
1822

19-
public virtual void OnServerStarted() {}
20-
2123
public virtual void StartClientIP(string playerId, string playerName, string ipaddress, int port) {}
2224

2325
public virtual Task StartClientLobbyAsync(string playerName, string playerId, Action<string> onFailure)

Assets/BossRoom/Scripts/Gameplay/ConnectionManagement/ConnectionState/HostingConnectionState.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
using System.Collections;
33
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
44
using Unity.Multiplayer.Samples.BossRoom.Shared.Net.UnityServices.Lobbies;
5+
using Unity.Multiplayer.Samples.Utilities;
56
using Unity.Netcode;
67
using UnityEngine;
8+
using Object = UnityEngine.Object;
79

810
namespace Unity.Multiplayer.Samples.BossRoom
911
{
@@ -22,6 +24,21 @@ public HostingConnectionState(ConnectionManager connectionManager, LobbyServiceF
2224
m_ConnectionEventPublisher = connectionEventPublisher;
2325
}
2426

27+
public override void Enter()
28+
{
29+
var gameState = Object.Instantiate(m_ConnectionManager.GameState);
30+
31+
gameState.Spawn();
32+
33+
SceneLoaderWrapper.Instance.AddOnSceneEventCallback();
34+
35+
//The "BossRoom" server always advances to CharSelect immediately on start. Different games
36+
//may do this differently.
37+
SceneLoaderWrapper.Instance.LoadScene("CharSelect", useNetworkSceneManager: true);
38+
}
39+
40+
public override void Exit() { }
41+
2542
public override void OnClientConnected(ulong clientId)
2643
{
2744
m_ConnectionEventPublisher.Publish(new ConnectionEventMessage() { ConnectStatus = ConnectStatus.Success, PlayerName = SessionManager<SessionPlayerData>.Instance.GetPlayerData(clientId)?.PlayerName });

Assets/BossRoom/Scripts/Gameplay/ConnectionManagement/ConnectionState/OfflineConnectionState.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public OfflineConnectionState(ConnectionManager connectionManager, LobbyServiceF
2626
m_LocalLobby = localLobby;
2727
}
2828

29+
public override void Enter() { }
30+
31+
public override void Exit() { }
32+
2933
public override void StartClientIP(string playerId, string playerName, string ipaddress, int port)
3034
{
3135
var utp = (UnityTransport)m_ConnectionManager.NetworkManager.NetworkConfig.NetworkTransport;
@@ -121,7 +125,6 @@ void SetConnectionPayload(string playerId, string playerName)
121125
var payload = JsonUtility.ToJson(new ConnectionPayload()
122126
{
123127
playerId = playerId,
124-
clientScene = SceneManager.GetActiveScene().buildIndex,
125128
playerName = playerName,
126129
isDebug = Debug.isDebugBuild
127130
});
Lines changed: 100 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,139 @@
11
using System;
2+
using System.Collections;
23
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;
38
using Unity.Netcode;
9+
using Unity.Netcode.Transports.UTP;
10+
using UnityEngine;
411

512
namespace Unity.Multiplayer.Samples.BossRoom
613
{
714
public class ReconnectingConnectionState : ConnectionState
815
{
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)
1027
: base(connectionManager)
1128
{
29+
m_LobbyServiceFacade = lobbyServiceFacade;
30+
m_LocalLobby = localLobby;
31+
m_ReconnectMessagePublisher = reconnectMessagePublisher;
1232
}
1333

14-
public override void OnClientConnected(ulong clientId)
34+
public override void Enter()
1535
{
16-
throw new NotImplementedException();
36+
m_LobbyCode = m_LobbyServiceFacade.CurrentUnityLobby != null ? m_LobbyServiceFacade.CurrentUnityLobby.LobbyCode : "";
37+
m_ConnectionManager.StartCoroutine(ReconnectCoroutine());
1738
}
1839

19-
public override void OnClientDisconnect(ulong clientId)
40+
public override void Exit()
2041
{
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+
}
2248
}
2349

24-
public override void OnServerStarted()
50+
public override void OnClientConnected(ulong clientId)
2551
{
26-
throw new NotImplementedException();
52+
m_ConnectionManager.ChangeState(ConnectionStateType.Connected);
2753
}
2854

29-
public override void StartClientIP(string playerId, string playerName, string ipaddress, int port)
55+
public override void OnClientDisconnect(ulong clientId)
3056
{
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+
}
3265
}
3366

34-
public override Task StartClientLobbyAsync(string playerName, string playerId, Action<string> onFailure)
67+
public override void OnUserRequestedShutdown()
3568
{
36-
throw new NotImplementedException();
69+
m_ConnectionManager.ChangeState(ConnectionStateType.Offline);
3770
}
3871

39-
public override bool StartHostIP(string playerId, string playerName, string ipaddress, int port)
72+
IEnumerator ReconnectCoroutine()
4073
{
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();
4379

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+
}
48104

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+
}
52108
}
53109

54-
public override void OnServerShutdown()
110+
async Task JoinRelayServerAsync()
55111
{
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();
57127
}
58128

59-
public override void ApprovalCheck(byte[] connectionData, ulong clientId, NetworkManager.ConnectionApprovedDelegate connectionApprovedCallback)
129+
void ConnectClient()
60130
{
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();
62137
}
63138
}
64139
}

0 commit comments

Comments
 (0)