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; } } }