Files
BABA_YAGA/Assets/Scripts/Network/BasicSpawner.cs
2026-04-30 01:30:58 +07:00

259 lines
9.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Fusion;
using Fusion.Sockets;
using UnityEngine;
using OnlyScove.Scripts;
namespace Hallucinate.UI
{
public class BasicSpawner : MonoBehaviour, INetworkRunnerCallbacks
{
public static BasicSpawner Instance { get; private set; }
private NetworkRunner _runner;
public event Action<List<SessionInfo>> OnSessionListUpdatedEvent;
public event Action<string> OnShutdownEvent;
public event Action OnJoinStartedEvent;
public event Action OnJoinFailedEvent;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
public PlayerProfile LocalPlayerProfile { get; private set; }
public void SetLocalPlayerProfile(PlayerProfile _profile)
{
LocalPlayerProfile = _profile;
}
private async Task EnsureRunnerExists()
{
if (_runner == null)
{
_runner = GetComponent<NetworkRunner>();
}
if (_runner != null && _runner.IsRunning)
{
await _runner.Shutdown();
}
if (_runner == null)
{
_runner = gameObject.AddComponent<NetworkRunner>();
}
_runner.ProvideInput = true;
_runner.RemoveCallbacks(this);
_runner.AddCallbacks(this);
}
public async Task StartLobby()
{
await EnsureRunnerExists();
var result = await _runner.JoinSessionLobby(SessionLobby.ClientServer);
if (!result.Ok)
{
Debug.LogError($"Failed to join lobby: {result.ShutdownReason}. Check AppID type or Network/Firewall.");
}
}
public async Task<bool> StartHost(string sessionName, string password = null)
{
OnJoinStartedEvent?.Invoke();
// 1. Kiểm tra Build Settings
bool sceneExists = false;
for (int i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCountInBuildSettings; i++)
{
if (UnityEngine.SceneManagement.SceneUtility.GetScenePathByBuildIndex(i).Contains("Main Scene"))
{
sceneExists = true;
break;
}
}
if (!sceneExists)
{
Debug.LogError("CRITICAL: 'Main Scene' is NOT in Build Settings!");
return false;
}
// 2. Khởi tạo Runner mới sạch sẽ
await EnsureRunnerExists();
var customProps = new Dictionary<string, SessionProperty>();
if (!string.IsNullOrEmpty(password))
{
customProps.Add("pw", password);
}
var result = await _runner.StartGame(new StartGameArgs()
{
GameMode = GameMode.Host,
SessionName = sessionName,
SessionProperties = customProps,
PlayerCount = 2,
// Thêm fixed region để tránh timeout khi tìm server
// SceneManager sẽ tự động add nếu thiếu
SceneManager = gameObject.GetComponent<NetworkSceneManagerDefault>() ?? gameObject.AddComponent<NetworkSceneManagerDefault>()
});
if (result.Ok)
{
return true;
}
else
{
Debug.LogError($"Fusion StartHost Failed: {result.ShutdownReason}.");
OnJoinFailedEvent?.Invoke();
return false;
}
}
public async Task<bool> StartClient(string sessionName, string password = null)
{
OnJoinStartedEvent?.Invoke();
await EnsureRunnerExists();
var result = await _runner.StartGame(new StartGameArgs()
{
GameMode = GameMode.Client,
SessionName = sessionName,
SceneManager = gameObject.GetComponent<NetworkSceneManagerDefault>() ?? gameObject.AddComponent<NetworkSceneManagerDefault>()
});
if (result.Ok)
{
return true;
}
else
{
Debug.LogError($"Fusion StartClient Failed: {result.ShutdownReason}");
OnJoinFailedEvent?.Invoke();
return false;
}
}
// --- Các phương thức Callbacks ---
[SerializeField] private NetworkPrefabRef _playerPrefab;
private Dictionary<PlayerRef, NetworkObject> _spawnedCharacters = new Dictionary<PlayerRef, NetworkObject>();
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
if (player == runner.LocalPlayer)
{
var pdm = FindFirstObjectByType<PlayerDataManager>();
if (pdm != null)
{
string playerName = "Player";
_Role playerRole = _Role.Seeker;
if (LocalPlayerProfile != null)
{
playerName = LocalPlayerProfile.Name;
}
var metaData = new _PlayerMetaData()
{
Name = playerName,
Role = playerRole,
IsReady = false
};
pdm.RPC_UpdatePlayerMetaData(player, metaData);
}
}
}
public void StartGame()
{
if (_runner != null && _runner.IsServer)
{
_runner.LoadScene("Main Scene");
}
}
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
{
if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
{
runner.Despawn(networkObject);
_spawnedCharacters.Remove(player);
// Chỉ Shutdown nếu người thoát chính là Server (Host)
if (runner.IsServer && player == runner.LocalPlayer)
{
runner.Shutdown();
}
}
}
public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason)
{
Debug.LogWarning($"[Fusion] Shutdown occurred. Reason: {shutdownReason}");
OnShutdownEvent?.Invoke(shutdownReason.ToString());
}
public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)
{
OnSessionListUpdatedEvent?.Invoke(sessionList);
}
public void OnInput(NetworkRunner runner, NetworkInput input)
{
var data = new PlayerInputData();
if (PlayerStateMachine.Local != null && PlayerStateMachine.Local.Input != null)
{
data.Direction = PlayerStateMachine.Local.Input.MoveInput;
data.sprint = PlayerStateMachine.Local.Input.IsSprintHeld;
if (PlayerStateMachine.Local.Cam != null)
data.rot = PlayerStateMachine.Local.Cam.PlanarRotation;
}
input.Set(data);
}
public void OnConnectedToServer(NetworkRunner runner) { }
public void OnDisconnectedFromServer(NetworkRunner runner, NetDisconnectReason reason) { }
public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) { }
public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { }
public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { }
public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment<byte> data) { }
public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { }
public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
public void OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data) { }
public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { }
public void OnSceneLoadDone(NetworkRunner runner)
{
string currentSceneName = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
if (runner.IsServer && currentSceneName == "Main Scene")
{
foreach (var player in runner.ActivePlayers)
{
Vector2 spawnPosition = (player == runner.LocalPlayer) ? new Vector2(-8, 0) : new Vector2(8, 0);
var networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
_spawnedCharacters.Add(player, networkPlayerObject);
}
}
if (currentSceneName == "Main Scene")
UIManager.Instance?.OnGameStarted();
else if (currentSceneName == "Lobby" || currentSceneName == "Menu")
UIManager.Instance?.OnBackToMenu();
}
public void OnSceneLoadStart(NetworkRunner runner) { }
}
}