-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathClientReconnectingState.cs
141 lines (126 loc) · 6.12 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
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;
int m_NbAttempts;
const float k_TimeBeforeFirstAttempt = 1;
const float k_TimeBetweenAttempts = 5;
public override void Enter()
{
m_NbAttempts = 0;
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));
// If first attempt, wait some time before attempting to reconnect to give time to services to update
// (i.e. if in a Lobby and the host shuts down unexpectedly, this will give enough time for the lobby to be
// properly deleted so that we don't reconnect to an empty lobby
if (m_NbAttempts == 0)
{
yield return new WaitForSeconds(k_TimeBeforeFirstAttempt);
}
m_NbAttempts++;
var reconnectingSetupTask = m_ConnectionMethod.SetupClientReconnectionAsync();
yield return new WaitUntil(() => reconnectingSetupTask.IsCompleted);
if (!reconnectingSetupTask.IsFaulted && reconnectingSetupTask.Result.success)
{
// If this fails, the OnClientDisconnect callback will be invoked by Netcode
var connectingTask = ConnectClientAsync();
yield return new WaitUntil(() => connectingTask.IsCompleted);
}
else
{
if (!reconnectingSetupTask.Result.shouldTryAgain)
{
// setting number of attempts to max so no new attempts are made
m_NbAttempts = m_ConnectionManager.NbReconnectAttempts;
}
// 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);
}
}
}
}