You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
6.1 KiB
C#
142 lines
6.1 KiB
C#
3 weeks ago
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|