-
Notifications
You must be signed in to change notification settings - Fork 494
/
ClientReconnectingState.cs
144 lines (131 loc) · 6.7 KB
/
ClientReconnectingState.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System;
using System.Collections;
using Unity.BossRoom.Infrastructure;
using UnityEngine;
using VContainer;
namespace Unity.BossRoom.ConnectionManagement
{
/// <summary>
/// Connection state corresponding to a client attempting to reconnect to a server. It will try to reconnect a
/// number of times defined by the ConnectionManager's NbReconnectAttempts property. If it succeeds, it will
/// transition to the ClientConnected state. If not, it will transition to the Offline state. If given a disconnect
/// reason first, depending on the reason given, may not try to reconnect again and transition directly to the
/// Offline state.
/// </summary>
class ClientReconnectingState : ClientConnectingState
{
[Inject]
IPublisher<ReconnectMessage> m_ReconnectMessagePublisher;
Coroutine m_ReconnectCoroutine;
string m_LobbyCode = "";
int m_NbAttempts;
const float k_TimeBetweenAttempts = 5;
public override void Enter()
{
m_NbAttempts = 0;
m_LobbyCode = m_LobbyServiceFacade.CurrentUnityLobby != null ? m_LobbyServiceFacade.CurrentUnityLobby.LobbyCode : "";
m_ReconnectCoroutine = m_ConnectionManager.StartCoroutine(ReconnectCoroutine());
}
public override void Exit()
{
if (m_ReconnectCoroutine != null)
{
m_ConnectionManager.StopCoroutine(m_ReconnectCoroutine);
m_ReconnectCoroutine = null;
}
m_ReconnectMessagePublisher.Publish(new ReconnectMessage(m_ConnectionManager.NbReconnectAttempts, m_ConnectionManager.NbReconnectAttempts));
}
public override void OnClientConnected(ulong _)
{
m_ConnectionManager.ChangeState(m_ConnectionManager.m_ClientConnected);
}
public override void OnClientDisconnect(ulong _)
{
var disconnectReason = m_ConnectionManager.NetworkManager.DisconnectReason;
if (m_NbAttempts < m_ConnectionManager.NbReconnectAttempts)
{
if (string.IsNullOrEmpty(disconnectReason))
{
m_ReconnectCoroutine = m_ConnectionManager.StartCoroutine(ReconnectCoroutine());
}
else
{
var connectStatus = JsonUtility.FromJson<ConnectStatus>(disconnectReason);
m_ConnectStatusPublisher.Publish(connectStatus);
switch (connectStatus)
{
case ConnectStatus.UserRequestedDisconnect:
case ConnectStatus.HostEndedSession:
case ConnectStatus.ServerFull:
case ConnectStatus.IncompatibleBuildType:
m_ConnectionManager.ChangeState(m_ConnectionManager.m_Offline);
break;
default:
m_ReconnectCoroutine = m_ConnectionManager.StartCoroutine(ReconnectCoroutine());
break;
}
}
}
else
{
if (string.IsNullOrEmpty(disconnectReason))
{
m_ConnectStatusPublisher.Publish(ConnectStatus.GenericDisconnect);
}
else
{
var connectStatus = JsonUtility.FromJson<ConnectStatus>(disconnectReason);
m_ConnectStatusPublisher.Publish(connectStatus);
}
m_ConnectionManager.ChangeState(m_ConnectionManager.m_Offline);
}
}
IEnumerator ReconnectCoroutine()
{
// If not on first attempt, wait some time before trying again, so that if the issue causing the disconnect
// is temporary, it has time to fix itself before we try again. Here we are using a simple fixed cooldown
// but we could want to use exponential backoff instead, to wait a longer time between each failed attempt.
// See https://en.wikipedia.org/wiki/Exponential_backoff
if (m_NbAttempts > 0)
{
yield return new WaitForSeconds(k_TimeBetweenAttempts);
}
Debug.Log("Lost connection to host, trying to reconnect...");
m_ConnectionManager.NetworkManager.Shutdown();
yield return new WaitWhile(() => m_ConnectionManager.NetworkManager.ShutdownInProgress); // wait until NetworkManager completes shutting down
Debug.Log($"Reconnecting attempt {m_NbAttempts + 1}/{m_ConnectionManager.NbReconnectAttempts}...");
m_ReconnectMessagePublisher.Publish(new ReconnectMessage(m_NbAttempts, m_ConnectionManager.NbReconnectAttempts));
m_NbAttempts++;
if (!string.IsNullOrEmpty(m_LobbyCode)) // Attempting to reconnect to lobby.
{
// When using Lobby with Relay, if a user is disconnected from the Relay server, the server will notify
// the Lobby service and mark the user as disconnected, but will not remove them from the lobby. They
// then have some time to attempt to reconnect (defined by the "Disconnect removal time" parameter on
// the dashboard), after which they will be removed from the lobby completely.
// See https://docs.unity.com/lobby/reconnect-to-lobby.html
var reconnectingToLobby = m_LobbyServiceFacade.ReconnectToLobbyAsync(m_LocalLobby?.LobbyID);
yield return new WaitUntil(() => reconnectingToLobby.IsCompleted);
// If succeeded, attempt to connect to Relay
if (!reconnectingToLobby.IsFaulted && reconnectingToLobby.Result != null)
{
// If this fails, the OnClientDisconnect callback will be invoked by Netcode
var connectingToRelay = ConnectClientAsync();
yield return new WaitUntil(() => connectingToRelay.IsCompleted);
}
else
{
Debug.Log("Failed reconnecting to lobby.");
// Calling OnClientDisconnect to mark this attempt as failed and either start a new one or give up
// and return to the Offline state
OnClientDisconnect(0);
}
}
else // If not using Lobby, simply try to reconnect to the server directly
{
// If this fails, the OnClientDisconnect callback will be invoked by Netcode
var connectingClient = ConnectClientAsync();
yield return new WaitUntil(() => connectingClient.IsCompleted);
}
}
}
}