278 lines
8.7 KiB
C#
278 lines
8.7 KiB
C#
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<PlayerRef> playersToSpawn = new List<PlayerRef>();
|
||
|
||
void Awake()
|
||
{
|
||
if (FindObjectsOfType<FusionLauncher>().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<NetworkSceneManagerDefault>()
|
||
});
|
||
|
||
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<NetworkRunner>();
|
||
runner.ProvideInput = true;
|
||
runner.AddCallbacks(this);
|
||
|
||
var inputProv = go.AddComponent<FusionInputProvider>();
|
||
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<NetworkSceneManagerDefault>()
|
||
});
|
||
|
||
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<NetworkSceneManagerDefault>()
|
||
});
|
||
|
||
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<SessionInfo> 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<SpawnPointProvider>();
|
||
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<byte> 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<string, object> data) { }
|
||
} |