2026-04-03 22:46:17 +07:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2026-04-05 00:08:43 +07:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Linq;
|
2026-04-03 22:46:17 +07:00
|
|
|
using Fusion;
|
|
|
|
|
using Fusion.Sockets;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.SceneManagement;
|
2026-04-05 00:08:43 +07:00
|
|
|
using Random = UnityEngine.Random;
|
2026-04-03 22:46:17 +07:00
|
|
|
|
2026-04-05 00:08:43 +07:00
|
|
|
public struct PlayerInputData : INetworkInput
|
2026-04-03 22:46:17 +07:00
|
|
|
{
|
2026-04-05 00:08:43 +07:00
|
|
|
public Vector2 Direction;
|
2026-04-03 22:46:17 +07:00
|
|
|
public Quaternion rot;
|
|
|
|
|
public bool sprint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class BasicSpawner : MonoBehaviour, INetworkRunnerCallbacks
|
|
|
|
|
{
|
|
|
|
|
private NetworkRunner _runner;
|
2026-04-09 09:34:46 +07:00
|
|
|
private string _roomName = "Room1";
|
2026-04-03 22:46:17 +07:00
|
|
|
|
2026-04-05 00:08:43 +07:00
|
|
|
public LobbyManager LobbyManager;
|
2026-04-03 22:46:17 +07:00
|
|
|
[SerializeField] private NetworkPrefabRef _playerPrefab;
|
|
|
|
|
private Dictionary<PlayerRef, NetworkObject> _spawnedCharacters = new Dictionary<PlayerRef, NetworkObject>();
|
|
|
|
|
|
2026-04-05 00:08:43 +07:00
|
|
|
public PlayerProfile LocalPlayerProfile { get; private set; }
|
|
|
|
|
public void SetLocalPlayerProfile(PlayerProfile profile)
|
2026-04-03 22:46:17 +07:00
|
|
|
{
|
2026-04-05 00:08:43 +07:00
|
|
|
LocalPlayerProfile = profile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
|
{
|
|
|
|
|
if (_runner == null) _runner = gameObject.AddComponent<NetworkRunner>();
|
2026-04-03 22:46:17 +07:00
|
|
|
DontDestroyOnLoad(gameObject);
|
2026-04-05 00:08:43 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task StartGame(GameMode mode, string sessionName = "TestRoom")
|
|
|
|
|
{
|
2026-04-22 13:22:42 +07:00
|
|
|
Debug.Log($"<color=yellow>Fusion:</color> Starting with Mode: {mode} | Room: {sessionName}");
|
2026-04-05 00:08:43 +07:00
|
|
|
|
|
|
|
|
if (_runner == null) _runner = gameObject.AddComponent<NetworkRunner>();
|
|
|
|
|
_runner.ProvideInput = true;
|
|
|
|
|
|
|
|
|
|
var sceneManager = gameObject.GetComponent<NetworkSceneManagerDefault>();
|
|
|
|
|
if (sceneManager == null) sceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>();
|
|
|
|
|
|
|
|
|
|
var result = await _runner.StartGame(new StartGameArgs()
|
2026-04-03 22:46:17 +07:00
|
|
|
{
|
|
|
|
|
GameMode = mode,
|
2026-04-05 00:08:43 +07:00
|
|
|
SessionName = sessionName,
|
2026-04-03 22:46:17 +07:00
|
|
|
Scene = SceneRef.FromIndex(1),
|
2026-04-05 00:08:43 +07:00
|
|
|
SceneManager = sceneManager
|
2026-04-03 22:46:17 +07:00
|
|
|
});
|
2026-04-05 00:08:43 +07:00
|
|
|
|
2026-04-22 13:22:42 +07:00
|
|
|
if (!result.Ok)
|
2026-04-05 00:08:43 +07:00
|
|
|
{
|
2026-04-22 13:22:42 +07:00
|
|
|
Debug.LogError($"<color=red>Fusion failed:</color> {result.ShutdownReason}");
|
|
|
|
|
if (_runner != null) { Destroy(_runner); _runner = null; }
|
2026-04-05 00:08:43 +07:00
|
|
|
}
|
2026-04-03 22:46:17 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnGUI()
|
|
|
|
|
{
|
2026-04-05 00:08:43 +07:00
|
|
|
if (_runner == null || !_runner.IsRunning)
|
|
|
|
|
{
|
2026-04-22 13:22:42 +07:00
|
|
|
float width = 400; float height = 300;
|
|
|
|
|
float x = (Screen.width - width) / 2f; float y = (Screen.height - height) / 2f;
|
2026-04-09 09:34:46 +07:00
|
|
|
GUI.Box(new Rect(x, y, width, height), "FUSION MULTIPLAYER");
|
2026-04-22 13:22:42 +07:00
|
|
|
float innerX = x + 20; float innerY = y + 40; float contentWidth = width - 40;
|
|
|
|
|
GUI.Label(new Rect(innerX, innerY, 100, 30), "Room Name:");
|
2026-04-09 09:34:46 +07:00
|
|
|
_roomName = GUI.TextField(new Rect(innerX + 100, innerY, contentWidth - 100, 30), _roomName);
|
|
|
|
|
if (GUI.Button(new Rect(innerX, innerY + 50, contentWidth, 60), "VÀO PHÒNG\n(Tự động Host/Client)"))
|
2026-04-05 00:08:43 +07:00
|
|
|
{
|
2026-04-09 09:34:46 +07:00
|
|
|
_ = StartGame(GameMode.AutoHostOrClient, _roomName);
|
2026-04-05 00:08:43 +07:00
|
|
|
}
|
2026-04-09 09:34:46 +07:00
|
|
|
|
|
|
|
|
if (GUI.Button(new Rect(innerX, innerY + 120, (contentWidth / 2) - 5, 50), "Tạo phòng\n(Host)"))
|
|
|
|
|
{
|
|
|
|
|
_ = StartGame(GameMode.Host, _roomName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (GUI.Button(new Rect(innerX + (contentWidth / 2) + 5, innerY + 120, (contentWidth / 2) - 5, 50), "Tham gia\n(Client)"))
|
|
|
|
|
{
|
|
|
|
|
_ = StartGame(GameMode.Client, _roomName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GUI.Label(new Rect(innerX, innerY + 180, contentWidth, 50), "Gợi ý: Nhập cùng tên phòng để chơi chung.\nNếu phòng chưa có, máy sẽ tự tạo mới.");
|
2026-04-05 00:08:43 +07:00
|
|
|
}
|
|
|
|
|
else
|
2026-04-03 22:46:17 +07:00
|
|
|
{
|
2026-04-05 00:08:43 +07:00
|
|
|
string region = (_runner.SessionInfo != null && _runner.SessionInfo.IsValid) ? _runner.SessionInfo.Region : "Connecting...";
|
|
|
|
|
int playerCount = 0;
|
|
|
|
|
foreach (var p in _runner.ActivePlayers) playerCount++;
|
|
|
|
|
|
|
|
|
|
string info = $"Mode: {_runner.GameMode} | Region: {region} | Players: {playerCount}";
|
|
|
|
|
GUI.Box(new Rect(10, 10, 400, 30), info);
|
|
|
|
|
|
|
|
|
|
if (GUI.Button(new Rect(10, 50, 100, 30), "Thoát"))
|
|
|
|
|
{
|
|
|
|
|
_runner.Shutdown();
|
|
|
|
|
}
|
2026-04-03 22:46:17 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
|
|
|
|
|
{
|
|
|
|
|
if (runner.IsServer)
|
|
|
|
|
{
|
2026-04-22 13:22:42 +07:00
|
|
|
Maze maze = GameObject.FindAnyObjectByType<Maze>();
|
|
|
|
|
Vector3 spawnPosition = (maze != null) ? maze.GetPlayerSpawnPoint() : new Vector3(0, 2f, 0);
|
2026-04-05 00:08:43 +07:00
|
|
|
|
|
|
|
|
var networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
|
2026-04-03 22:46:17 +07:00
|
|
|
runner.SetPlayerObject(player, networkPlayerObject);
|
|
|
|
|
_spawnedCharacters.Add(player, networkPlayerObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:08:43 +07:00
|
|
|
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
|
2026-04-03 22:46:17 +07:00
|
|
|
{
|
2026-04-05 00:08:43 +07:00
|
|
|
if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
|
|
|
|
|
{
|
|
|
|
|
if (networkObject != null) runner.Despawn(networkObject);
|
|
|
|
|
_spawnedCharacters.Remove(player);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnInput(NetworkRunner runner, NetworkInput input)
|
|
|
|
|
{
|
|
|
|
|
var data = new PlayerInputData();
|
|
|
|
|
data.Direction = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
|
|
|
|
|
data.sprint = Input.GetKey(KeyCode.LeftShift);
|
2026-04-03 22:46:17 +07:00
|
|
|
|
|
|
|
|
if (OnlyScove.Scripts.PlayerStateMachine.Local != null)
|
|
|
|
|
{
|
|
|
|
|
var sm = OnlyScove.Scripts.PlayerStateMachine.Local;
|
2026-04-05 00:08:43 +07:00
|
|
|
if (sm.Cam != null) data.rot = sm.Cam.PlanarRotation;
|
|
|
|
|
else data.rot = sm.NetworkedCameraRotation;
|
2026-04-03 22:46:17 +07:00
|
|
|
}
|
|
|
|
|
input.Set(data);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:08:43 +07:00
|
|
|
public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)
|
2026-04-03 22:46:17 +07:00
|
|
|
{
|
2026-04-05 00:08:43 +07:00
|
|
|
if (LobbyManager != null) LobbyManager.DisplayRoomList(sessionList);
|
2026-04-03 22:46:17 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
|
|
|
|
|
public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { }
|
|
|
|
|
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 OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data) { }
|
|
|
|
|
public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { }
|
|
|
|
|
public void OnSceneLoadDone(NetworkRunner runner) { }
|
|
|
|
|
public void OnSceneLoadStart(NetworkRunner runner) { }
|
|
|
|
|
public void OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
|
|
|
|
|
public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
|
|
|
|
|
public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment<byte> data) { }
|
|
|
|
|
public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { }
|
2026-04-05 00:08:43 +07:00
|
|
|
}
|