Files

203 lines
6.1 KiB
C#

using PashaBibko.Pacore.Attributes;
using Unity.Netcode.Transports.UTP;
using UnityEngine.SceneManagement;
using System.Collections;
using Unity.Netcode;
using UnityEngine;
using System;
namespace PashaBibko.PenguinChase.Network
{
public enum TransportType
{
UnityRelay,
Localhost,
None
}
public interface INetworkTransport
{
public IEnumerator Join(string code, Action callback);
public IEnumerator Host(Action callback);
}
public class Network : MonoBehaviour
{
private static Network sInstance;
[Header("Transports")]
[SerializeField] private GameObject LocalHostTransport;
[SerializeField] private GameObject UnityRelayTransport;
[Header("View only")] [SerializeField, InspectorReadOnly]
private TransportType InternalCurrentTransport = TransportType.None;
public static TransportType CurrentTransport
{
get => sInstance?.InternalCurrentTransport ?? TransportType.None;
set => sInstance?.StartCoroutine(sInstance.ChangeTransport(value));
}
#if UNITY_EDITOR
[Header("Editor Only")]
[SerializeField] private string GameJoinCode;
#endif // UNITY_EDITOR
private static INetworkTransport sConnectionManager;
private GameObject mCurrentChildTransport;
private bool mSafeToConnect = true;
public static UnityTransport CurrentTransportComponent
{
get
{
UnityTransport component = null;
sInstance?.mCurrentChildTransport?.TryGetComponent(out component);
return component;
}
}
private void Awake()
{
// Stops overlapping instances
if (sInstance is not null)
{
Debug.LogError($"Multiple of [{nameof(Network)}] cannot exist.");
Destroy(gameObject);
return;
}
sInstance = this;
}
private IEnumerator HostInternal()
{
// Waits for it to be safe to connect, quits after 5 seconds
float timeout = 0f;
while (!mSafeToConnect)
{
if (timeout > 5f)
{
Debug.LogError("Host timed out");
yield break;
}
timeout += Time.deltaTime;
yield return null;
}
// Then actually hosts the lobby
yield return sConnectionManager.Host(() =>
{
NetworkManager.Singleton.SceneManager.LoadScene("LobbyScene", LoadSceneMode.Single);
Debug.Log("Hosted");
});
}
public static void Host()
{
if (sConnectionManager is null)
{
throw new InvalidOperationException("No connection manager has been set.");
}
if (sInstance is null)
{
throw new InvalidOperationException("No network manager has been set.");
}
sInstance.StartCoroutine(sInstance.HostInternal());
}
private IEnumerator JoinInternal(string code)
{
// Waits for it to be safe to connect, quits after 5 seconds
float timeout = 0f;
while (!mSafeToConnect)
{
if (timeout > 5f)
{
Debug.LogError("Host timed out");
yield break;
}
timeout += Time.deltaTime;
yield return null;
}
// Then connects to the lobby
yield return sConnectionManager.Join(code, () =>
{
Debug.Log($"Joined {code}");
});
}
public static void Join(string code)
{
if (sConnectionManager is null)
{
throw new InvalidOperationException("No connection manager has been set.");
}
if (sInstance is null)
{
throw new InvalidOperationException("No network manager has been set.");
}
sInstance.StartCoroutine(sInstance.JoinInternal(code));
}
private IEnumerator ChangeTransport(TransportType transport)
{
// Stops network from being restarted when unneeded
if (transport == InternalCurrentTransport)
{
yield break;
}
mSafeToConnect = false;
InternalCurrentTransport = transport;
// Shutdown existing network manager (if there is one) to stop multiple at once
if (NetworkManager.Singleton is not null)
{
NetworkManager.Singleton.Shutdown();
while (NetworkManager.Singleton.ShutdownInProgress)
{
yield return null; // Waits for next frame
}
Destroy(mCurrentChildTransport);
}
// Loads the new transport controller and lets it setup
switch (InternalCurrentTransport)
{
case TransportType.UnityRelay:
mCurrentChildTransport = Instantiate(UnityRelayTransport);
sConnectionManager = new UnityRelayTransport();
break;
case TransportType.Localhost:
mCurrentChildTransport = Instantiate(LocalHostTransport);
sConnectionManager = new LocalhostTransport();
break;
case TransportType.None:
// Nothing needs to be done here as there is no network controller
mCurrentChildTransport = null;
yield break;
default:
mSafeToConnect = true;
throw new ArgumentOutOfRangeException();
}
DontDestroyOnLoad(mCurrentChildTransport);
mSafeToConnect = true;
}
}
}