From c2b0e965704d2894b0afa759d84cc1b5efbb5009 Mon Sep 17 00:00:00 2001 From: scove Date: Thu, 30 Apr 2026 15:08:19 +0700 Subject: [PATCH] Update --- .idea/.idea.HALLUCINATE/.idea/workspace.xml | 7 +- Assets/Prefabs/PlayerDataManager.prefab | 68 +++++ Assets/Prefabs/PlayerDataManager.prefab.meta | 9 + Assets/Scove/UIScaleTest.unity | 2 + Assets/Scripts/Network/BasicSpawner.cs | 114 +++++--- Assets/Scripts/Network/PlayerDataManager.cs | 16 ++ Assets/Scripts/UI/LobbyController.cs | 287 ++++++++++++++----- Assets/UI/Lobby.uxml | 30 +- 8 files changed, 416 insertions(+), 117 deletions(-) create mode 100644 Assets/Prefabs/PlayerDataManager.prefab create mode 100644 Assets/Prefabs/PlayerDataManager.prefab.meta diff --git a/.idea/.idea.HALLUCINATE/.idea/workspace.xml b/.idea/.idea.HALLUCINATE/.idea/workspace.xml index 01194da0..a7d42365 100644 --- a/.idea/.idea.HALLUCINATE/.idea/workspace.xml +++ b/.idea/.idea.HALLUCINATE/.idea/workspace.xml @@ -6,8 +6,11 @@ + - + + + diff --git a/Assets/Prefabs/PlayerDataManager.prefab b/Assets/Prefabs/PlayerDataManager.prefab new file mode 100644 index 00000000..95bde44f --- /dev/null +++ b/Assets/Prefabs/PlayerDataManager.prefab @@ -0,0 +1,68 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1290203079495312901 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6224859379081039779} + - component: {fileID: 5687802730781322084} + - component: {fileID: -7009574229380509654} + m_Layer: 0 + m_Name: PlayerDataManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6224859379081039779 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290203079495312901} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &5687802730781322084 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290203079495312901} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b3d9934ebd60c9c4ea3e464b77fd7ae0, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::PlayerDataManager + _Players: + _items: [] +--- !u!114 &-7009574229380509654 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290203079495312901} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: Fusion.Runtime.dll::Fusion.NetworkObject + SortKey: 1822710516 + ObjectInterest: 1 + Flags: 262145 + NestedObjects: [] + NetworkedBehaviours: + - {fileID: 5687802730781322084} + ForceRemoteRenderTimeframe: 0 diff --git a/Assets/Prefabs/PlayerDataManager.prefab.meta b/Assets/Prefabs/PlayerDataManager.prefab.meta new file mode 100644 index 00000000..038530ab --- /dev/null +++ b/Assets/Prefabs/PlayerDataManager.prefab.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 33a4edcf030b02446bd8e4bb9a0fb9f3 +labels: +- FusionPrefab +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scove/UIScaleTest.unity b/Assets/Scove/UIScaleTest.unity index cbada702..522b593b 100644 --- a/Assets/Scove/UIScaleTest.unity +++ b/Assets/Scove/UIScaleTest.unity @@ -150,6 +150,8 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::Hallucinate.UI.BasicSpawner _playerPrefab: RawGuidValue: 761bdf2e5c0cff4488527355acb975e5 + _playerDataManagerPrefab: + RawGuidValue: 33a4edcf030b02446bd8e4bb9a0fb9f3 --- !u!4 &417583767 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Network/BasicSpawner.cs b/Assets/Scripts/Network/BasicSpawner.cs index fe0c658b..ac2f1d21 100644 --- a/Assets/Scripts/Network/BasicSpawner.cs +++ b/Assets/Scripts/Network/BasicSpawner.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -19,6 +19,10 @@ namespace Hallucinate.UI public event Action OnJoinStartedEvent; public event Action OnJoinFailedEvent; + [Header(""Prefabs"")] + [SerializeField] private NetworkPrefabRef _playerPrefab; + [SerializeField] private NetworkPrefabRef _playerDataManagerPrefab; + private void Awake() { if (Instance != null && Instance != this) @@ -62,22 +66,23 @@ namespace Hallucinate.UI { await EnsureRunnerExists(); + if (_runner.SessionInfo.IsValid) return; + var result = await _runner.JoinSessionLobby(SessionLobby.ClientServer); if (!result.Ok) { - Debug.LogError($"Failed to join lobby: {result.ShutdownReason}. Check AppID type or Network/Firewall."); + Debug.LogWarning($""Join lobby result: {result.ShutdownReason}. This is often normal on first run if already connecting.""); } } - public async Task StartHost(string sessionName, string password = null) + public async Task StartHost(string sessionName, string displayName, 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")) + if (UnityEngine.SceneManagement.SceneUtility.GetScenePathByBuildIndex(i).Contains(""Main Scene"")) { sceneExists = true; break; @@ -86,18 +91,18 @@ namespace Hallucinate.UI if (!sceneExists) { - Debug.LogError("CRITICAL: 'Main Scene' is NOT in Build Settings!"); + 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(); if (!string.IsNullOrEmpty(password)) { - customProps.Add("pw", password); + customProps.Add(""pw"", password); } + customProps.Add(""rn"", displayName); var result = await _runner.StartGame(new StartGameArgs() { @@ -105,18 +110,23 @@ namespace Hallucinate.UI 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() ?? gameObject.AddComponent() }); if (result.Ok) { + if (_runner.IsServer && _playerDataManagerPrefab.IsValid) + { + if (FindFirstObjectByType() == null) + { + _runner.Spawn(_playerDataManagerPrefab, Vector3.zero, Quaternion.identity, null); + } + } return true; } else { - Debug.LogError($"Fusion StartHost Failed: {result.ShutdownReason}."); + Debug.LogError($""Fusion StartHost Failed: {result.ShutdownReason}.""); OnJoinFailedEvent?.Invoke(); return false; } @@ -140,39 +150,54 @@ namespace Hallucinate.UI } else { - Debug.LogError($"Fusion StartClient Failed: {result.ShutdownReason}"); + Debug.LogError($""Fusion StartClient Failed: {result.ShutdownReason}""); OnJoinFailedEvent?.Invoke(); return false; } } - // --- Các phương thức Callbacks --- - [SerializeField] private NetworkPrefabRef _playerPrefab; private Dictionary _spawnedCharacters = new Dictionary(); public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { if (player == runner.LocalPlayer) { - var pdm = FindFirstObjectByType(); - if (pdm != null) + SendLocalMetaData(player); + } + } + + private async void SendLocalMetaData(PlayerRef player) + { + PlayerDataManager pdm = null; + int retries = 0; + while (pdm == null && retries < 20) + { + pdm = FindFirstObjectByType(); + if (pdm != null) break; + await Task.Delay(500); + retries++; + } + + if (pdm != null) + { + string playerName = LocalPlayerProfile != null ? LocalPlayerProfile.Name : ""Player "" + player.PlayerId; + + // Thêm hậu tố (HOST) nếu là server để dễ phân biệt + if (_runner.IsServer) playerName += "" (HOST)""; + + _Role playerRole = _Role.Seeker; + + var metaData = new _PlayerMetaData() { - 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); - } + Name = playerName, + Role = playerRole, + IsReady = false + }; + pdm.RPC_UpdatePlayerMetaData(player, metaData); + } + else + { + Debug.LogError(""[BasicSpawner] Could not find PlayerDataManager after retries. Data will not sync.""); } } @@ -180,7 +205,7 @@ namespace Hallucinate.UI { if (_runner != null && _runner.IsServer) { - _runner.LoadScene("Main Scene"); + _runner.LoadScene(""Main Scene""); } } @@ -190,18 +215,23 @@ namespace Hallucinate.UI { 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(); - } + } + + if (runner.IsServer && player == runner.LocalPlayer) + { + runner.Shutdown(); } } public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { - Debug.LogWarning($"[Fusion] Shutdown occurred. Reason: {shutdownReason}"); + Debug.LogWarning($""[Fusion] Shutdown occurred. Reason: {shutdownReason}""); OnShutdownEvent?.Invoke(shutdownReason.ToString()); + + if (UIManager.Instance != null) + { + UIManager.Instance.OnBackToMenu(); + } } public void OnSessionListUpdated(NetworkRunner runner, List sessionList) @@ -238,7 +268,7 @@ namespace Hallucinate.UI public void OnSceneLoadDone(NetworkRunner runner) { string currentSceneName = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name; - if (runner.IsServer && currentSceneName == "Main Scene") + if (runner.IsServer && currentSceneName == ""Main Scene"") { foreach (var player in runner.ActivePlayers) { @@ -247,9 +277,9 @@ namespace Hallucinate.UI _spawnedCharacters.Add(player, networkPlayerObject); } } - if (currentSceneName == "Main Scene") + if (currentSceneName == ""Main Scene"") UIManager.Instance?.OnGameStarted(); - else if (currentSceneName == "Lobby" || currentSceneName == "Menu") + else if (currentSceneName == ""Lobby"" || currentSceneName == ""Menu"") UIManager.Instance?.OnBackToMenu(); } diff --git a/Assets/Scripts/Network/PlayerDataManager.cs b/Assets/Scripts/Network/PlayerDataManager.cs index eecf67ca..e010f1e0 100644 --- a/Assets/Scripts/Network/PlayerDataManager.cs +++ b/Assets/Scripts/Network/PlayerDataManager.cs @@ -1,3 +1,4 @@ +using System; using Fusion; using UnityEngine; @@ -11,9 +12,18 @@ public struct _PlayerMetaData : INetworkStruct public class PlayerDataManager : NetworkBehaviour { + public static PlayerDataManager Instance { get; private set; } + [Networked] public NetworkDictionary Players => default; + public event Action OnChatMessageReceived; + + public override void Spawned() + { + Instance = this; + } + [Rpc(RpcSources.All, RpcTargets.StateAuthority)] public void RPC_UpdatePlayerMetaData(PlayerRef playerRef, _PlayerMetaData metaData) { @@ -29,6 +39,12 @@ public class PlayerDataManager : NetworkBehaviour Players.Set(playerRef, data); } } + + [Rpc(RpcSources.All, RpcTargets.All)] + public void RPC_SendChatMessage(PlayerRef sender, string message) + { + OnChatMessageReceived?.Invoke(sender, message); + } public bool TryGetPlayerMetaData(PlayerRef playerRef, out _PlayerMetaData metaData) { diff --git a/Assets/Scripts/UI/LobbyController.cs b/Assets/Scripts/UI/LobbyController.cs index 666c536a..d16027e0 100644 --- a/Assets/Scripts/UI/LobbyController.cs +++ b/Assets/Scripts/UI/LobbyController.cs @@ -1,4 +1,4 @@ -using UnityEngine; +using UnityEngine; using UnityEngine.UIElements; using System.Collections.Generic; using System.Threading.Tasks; @@ -26,56 +26,71 @@ namespace Hallucinate.UI private SessionInfo _selectedSession; // Lounge Elements - private VisualElement _playerListContainer; - private Button _readyBtn, _startBtn; private Label _loungeRoomName; + private Button _readyBtn, _startBtn; + + // Host Slot + private Label _hostNameLabel, _hostStatusLabel; + private VisualElement _hostChatBox; + private Label _hostChatMessage; + + // Guest Slot + private Label _guestNameLabel, _guestStatusLabel; + private VisualElement _guestChatBox; + private Label _guestChatMessage; + + // Chat Input + private TextField _chatInput; public override void Initialize(VisualElement uxmlRoot, UIManager manager) { base.Initialize(uxmlRoot, manager); - // Query Elements + // Query Containers _joinContainer = root.Q("JoinContainer"); _createContainer = root.Q("CreateContainer"); _loungeContainer = root.Q("LoungeContainer"); _passOverlay = root.Q("PasswordOverlay"); + // Create Room Fields _roomIDInput = root.Q("RoomIDInput"); _roomNameInput = root.Q("RoomNameInput"); _roomPassInput = root.Q("RoomPassInput"); _passToggle = root.Q("PassToggle"); + + // Join Room Fields _roomList = root.Q("RoomList"); - _joinPassInput = root.Q("JoinPassInput"); _joinPassError = root.Q