using Fusion; using Fusion.Sockets; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using UnityEngine.SceneManagement; using TMPro; using UnityEngine.UI; using System.Linq; public class FusionLauncher : MonoBehaviour, INetworkRunnerCallbacks { [Header("Network Runner (Drag NOTHING here)")] public NetworkRunner runner; // we'll create/destroy this at runtime [Header("Lobby UI")] public GameObject lobbyUI; public SessionListUIHandler sessionListUIHandler; public TMP_InputField lobbyNameInput; public Button createLobbyButton; public TextMeshProUGUI waitingText; [Header("Player Prefab")] public NetworkPrefabRef playerPrefab; private Coroutine refreshCoroutine; private int playerCount = 0; private const int maxPlayers = 2; private bool gameplayLoaded = false; private bool connectedToServer = false; void Awake() { // Ensure this launcher persists across loads if (FindObjectsOfType().Length > 1) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); } void Start() { // Kick off our dummy runner purely in client/browse mode: StartDummyRunner(); // UI hookups if (lobbyNameInput != null) lobbyNameInput.onValueChanged.AddListener(OnLobbyNameChanged); OnLobbyNameChanged(lobbyNameInput.text); waitingText.gameObject.SetActive(false); // Start periodic lobby refresh once connected refreshCoroutine = StartCoroutine(AutoRefreshLobbyList()); } void OnDestroy() { if (refreshCoroutine != null) StopCoroutine(refreshCoroutine); } IEnumerator AutoRefreshLobbyList() { while (true) { RefreshLobbyList(); yield return new WaitForSeconds(5f); } } void OnLobbyNameChanged(string s) { createLobbyButton.interactable = !string.IsNullOrWhiteSpace(s) && s.Length <= 15; } // —————— DUMMY RUNNER —————— // Starts a runner in CLIENT mode so we can list sessions. async void StartDummyRunner() { // If already up & running, just bail. if (runner != null && runner.IsRunning) return; // Ensure any old runner is cleared await EnsureFreshRunner(); // Start as CLIENT var result = await runner.StartGame(new StartGameArgs() { GameMode = GameMode.Client, SceneManager = runner.gameObject.AddComponent() }); if (result.Ok) { Debug.Log("✅ Browser runner connected (Client mode)."); connectedToServer = true; } else if (result.ShutdownReason == ShutdownReason.GameNotFound) { Debug.Log("ℹ️ No lobbies yet—but browser runner is live."); connectedToServer = true; } else { Debug.LogError($"❌ Browser runner failed: {result.ShutdownReason}"); } } // —————— RESET / CREATE FRESH RUNNER —————— private async Task EnsureFreshRunner() { // Reset flags playerCount = 0; gameplayLoaded = false; connectedToServer = false; // If an old runner exists & is running, shut it down & destroy its GO if (runner != null && runner.IsRunning) { await runner.Shutdown(); Destroy(runner.gameObject); } // Stop auto-refresh while we rebuild if (refreshCoroutine != null) { StopCoroutine(refreshCoroutine); refreshCoroutine = null; } // Make brand-new runner GameObject var go = new GameObject("NetworkRunnerGO"); DontDestroyOnLoad(go); // keep it alive across scenes runner = go.AddComponent(); runner.ProvideInput = true; runner.AddCallbacks(this); } // —————— CREATE LOBBY (Host) —————— public async void CreateLobby() { var name = lobbyNameInput.text.Trim(); if (string.IsNullOrEmpty(name)) { Debug.LogWarning("Empty lobby name!"); return; } // Tear down browse runner & spawn a fresh one await EnsureFreshRunner(); var sceneRef = SceneRef.FromIndex(SceneManager.GetActiveScene().buildIndex); var res = await runner.StartGame(new StartGameArgs() { GameMode = GameMode.Host, SessionName = name, PlayerCount = maxPlayers, Scene = sceneRef, SceneManager = runner.gameObject.AddComponent() }); if (res.Ok) { Debug.Log($"✔ Hosted Lobby '{name}'"); lobbyUI.SetActive(false); waitingText.text = "Waiting for player 2…"; waitingText.gameObject.SetActive(true); } else { Debug.LogError($"✖ Host failed: {res.ShutdownReason}"); } } // —————— JOIN LOBBY (Client) —————— public async void JoinLobby(SessionInfo info) { // Tear down browse runner & spawn a fresh one await EnsureFreshRunner(); var res = await runner.StartGame(new StartGameArgs() { GameMode = GameMode.Client, SessionName = info.Name, Scene = SceneRef.FromIndex(SceneManager.GetActiveScene().buildIndex), SceneManager = runner.gameObject.AddComponent() }); if (res.Ok) { Debug.Log($"✔ Joined Lobby '{info.Name}'"); lobbyUI.SetActive(false); waitingText.text = "Waiting for host…"; waitingText.gameObject.SetActive(true); } else { Debug.LogError($"✖ Join failed: {res.ShutdownReason}"); } } // —————— REFRESH LOBBY LIST —————— public void RefreshLobbyList() { if (!connectedToServer) return; //sessionListUIHandler.ClearList(); runner.JoinSessionLobby(SessionLobby.ClientServer); } // —————— FUSION CALLBACKS —————— public void OnConnectedToServer(NetworkRunner r) { connectedToServer = true; Debug.Log("✔ Connected to server; can refresh lobbies."); RefreshLobbyList(); } public void OnSessionListUpdated(NetworkRunner runner, List sessionList) { // First thing, wipe out the old items every time sessionListUIHandler.ClearList(); // If there really are no sessions, show your “no sessions” message if (sessionList.Count == 0) { sessionListUIHandler.ShowNoSessionsMessage(); return; } // Otherwise re-populate foreach (var session in sessionList) { if (session.IsOpen && session.IsVisible && session.PlayerCount < session.MaxPlayers) { sessionListUIHandler.AddToList(session, JoinLobby); } } } public void OnPlayerJoined(NetworkRunner r, PlayerRef p) { playerCount++; // Only the host in Gameplay scene spawns cars //if (r.IsServer && SceneManager.GetActiveScene().name == "Gameplay") //{ // var pos = new Vector3(Random.Range(-5, 5), 0, Random.Range(-5, 5)); // r.Spawn(playerPrefab, pos, Quaternion.identity, p); //} // Only the host triggers scene‐load when 2 players are present if (r.IsServer && playerCount == maxPlayers && !gameplayLoaded) { gameplayLoaded = true; var scene = SceneRef.FromIndex( SceneUtility.GetBuildIndexByScenePath("Assets/Scenes/Gameplay.unity") ); r.LoadScene(scene, LoadSceneMode.Single); waitingText.gameObject.SetActive(false); } } public void OnSceneLoadDone(NetworkRunner runner) { if (!runner.IsServer) return; // only run once per game start if (gameplayLoaded) { gameplayLoaded = false; // grab your provider var provider = FindObjectOfType(); if (provider == null || provider.spawnPoints == null || provider.spawnPoints.Length == 0) { Debug.LogError("No SpawnPointProvider or no spawnPoints assigned!"); return; } // convert the IEnumerable to an array PlayerRef[] players = runner.ActivePlayers.ToArray(); // pick the smaller of (number of players) vs (number of spawnPoints) int count = Mathf.Min(players.Length, provider.spawnPoints.Length); for (int i = 0; i < count; i++) { PlayerRef p = players[i]; Transform sp = provider.spawnPoints[i]; runner.Spawn(playerPrefab, sp.position, sp.rotation, p); } } } public void OnDisconnectedFromServer(NetworkRunner r, NetDisconnectReason reason) { } public void OnConnectRequest(NetworkRunner r, NetworkRunnerCallbackArgs.ConnectRequest req, byte[] tok) { } public void OnConnectFailed(NetworkRunner r, NetAddress addr, NetConnectFailedReason reason) { } public void OnUserSimulationMessage(NetworkRunner r, SimulationMessagePtr msg) { } public void OnShutdown(NetworkRunner r, ShutdownReason reason) { } public void OnPlayerLeft(NetworkRunner r, PlayerRef p) { } public void OnInput(NetworkRunner r, NetworkInput input) { } public void OnInputMissing(NetworkRunner r, PlayerRef p, NetworkInput input) { } public void OnObjectEnterAOI(NetworkRunner r, NetworkObject obj, PlayerRef p) { } public void OnObjectExitAOI(NetworkRunner r, NetworkObject obj, PlayerRef p) { } public void OnReliableDataReceived(NetworkRunner r, PlayerRef p, ReliableKey k, System.ArraySegment data) { } public void OnReliableDataProgress(NetworkRunner r, PlayerRef p, ReliableKey k, float prog) { } //public void OnSceneLoadDone(NetworkRunner r) { } public void OnSceneLoadStart(NetworkRunner r) { } public void OnHostMigration(NetworkRunner r, HostMigrationToken t) { } public void OnCustomAuthenticationResponse(NetworkRunner r, Dictionary data) { } }