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; [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; private List playersToSpawn = new List(); void Awake() { if (FindObjectsOfType().Length > 1) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); } void Start() { StartDummyRunner(); if (lobbyNameInput != null) lobbyNameInput.onValueChanged.AddListener(OnLobbyNameChanged); OnLobbyNameChanged(lobbyNameInput.text); waitingText.gameObject.SetActive(false); 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; } async void StartDummyRunner() { if (runner != null && runner.IsRunning) return; await EnsureFreshRunner(); 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}"); } } private async Task EnsureFreshRunner() { playerCount = 0; gameplayLoaded = false; connectedToServer = false; playersToSpawn.Clear(); if (runner != null && runner.IsRunning) { await runner.Shutdown(); Destroy(runner.gameObject); } if (refreshCoroutine != null) { StopCoroutine(refreshCoroutine); refreshCoroutine = null; } var go = new GameObject("NetworkRunnerGO"); DontDestroyOnLoad(go); runner = go.AddComponent(); runner.ProvideInput = true; runner.AddCallbacks(this); var inputProv = go.AddComponent(); runner.AddCallbacks(inputProv); Debug.Log($"[Launcher] Spawned {go.name} → ProvideInput={runner.ProvideInput}; FusionInputProvider attached"); } public async void CreateLobby() { var name = lobbyNameInput.text.Trim(); if (string.IsNullOrEmpty(name)) return; 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}"); } } public async void JoinLobby(SessionInfo info) { 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}"); } } public void RefreshLobbyList() { if (!connectedToServer) return; runner.JoinSessionLobby(SessionLobby.ClientServer); } public void OnConnectedToServer(NetworkRunner r) { connectedToServer = true; Debug.Log("✔ Connected to server; can refresh lobbies."); RefreshLobbyList(); } public void OnSessionListUpdated(NetworkRunner runner, List sessionList) { sessionListUIHandler.ClearList(); if (sessionList.Count == 0) { sessionListUIHandler.ShowNoSessionsMessage(); return; } 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++; playersToSpawn.Add(p); Debug.Log($"➡ Player joined: {p}"); 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; var provider = FindObjectOfType(); if (provider == null || provider.spawnPoints == null || provider.spawnPoints.Length == 0) { Debug.LogError("❌ No SpawnPointProvider or no spawnPoints assigned!"); return; } for (int i = 0; i < playersToSpawn.Count && i < provider.spawnPoints.Length; i++) { PlayerRef player = playersToSpawn[i]; Transform spawnPoint = provider.spawnPoints[i]; runner.Spawn(playerPrefab, spawnPoint.position, spawnPoint.rotation, inputAuthority: player); Debug.Log($"✔ Spawned player with authority: {player}"); } playersToSpawn.Clear(); } // Stub callbacks 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 OnSceneLoadStart(NetworkRunner r) { } public void OnHostMigration(NetworkRunner r, HostMigrationToken t) { } public void OnCustomAuthenticationResponse(NetworkRunner r, Dictionary data) { } }