From dd8901291997b42203e5f4be422649dd763b4820 Mon Sep 17 00:00:00 2001 From: Marek Sorokin Date: Sun, 5 Oct 2025 19:24:52 +0200 Subject: [PATCH] online game working --- .../Networking/LobbyGameSceneCoordinator.cs | 40 ++++++++++++++----- Game/Scripts/Networking/NetworkIdentity.cs | 25 ++++++++++++ UI/Scripts/UGUIMultiplayerLobbyController.cs | 23 +++++++++++ 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/Game/Scripts/Networking/LobbyGameSceneCoordinator.cs b/Game/Scripts/Networking/LobbyGameSceneCoordinator.cs index 0b24851..51cbd08 100644 --- a/Game/Scripts/Networking/LobbyGameSceneCoordinator.cs +++ b/Game/Scripts/Networking/LobbyGameSceneCoordinator.cs @@ -94,6 +94,8 @@ namespace MegaKoop.Game.Networking IsLocal = true }); } + + pendingPlayers.Sort((a, b) => string.CompareOrdinal(a.SteamId ?? string.Empty, b.SteamId ?? string.Empty)); } private void HandleSceneLoaded(Scene scene, LoadSceneMode mode) @@ -147,7 +149,7 @@ namespace MegaKoop.Game.Networking var clone = Instantiate(template, spawnPosition, baseRotation, parent); clone.name = BuildPlayerName(info, i); - ConfigureCloneForPlayer(clone, info); + ConfigureCloneForPlayer(clone, info, i); clone.SetActive(true); } @@ -184,13 +186,17 @@ namespace MegaKoop.Game.Networking return center + offset; } - private void ConfigureCloneForPlayer(GameObject clone, LobbyPlayerInfo info) + private void ConfigureCloneForPlayer(GameObject clone, LobbyPlayerInfo info, int index) { - var controller = clone.GetComponent(); - if (controller != null) - { - controller.enabled = info.IsLocal; - } + // Ensure network identity is deterministic across clients. + var identity = clone.GetComponent() ?? clone.AddComponent(); + identity.SetNetworkId(index + 1); + + var bridge = clone.GetComponent() ?? clone.AddComponent(); + bridge.AssignOwner(ParseSteamId(info.SteamId), info.IsLocal); + + var inputSender = clone.GetComponent() ?? clone.AddComponent(); + inputSender.enabled = info.IsLocal; // Local player keeps the camera and audio; remote avatars are visual only. var cameras = clone.GetComponentsInChildren(true); @@ -217,11 +223,8 @@ namespace MegaKoop.Game.Networking if (thirdPersonCamera != null) { thirdPersonCamera.enabled = info.IsLocal; - if (info.IsLocal) - { - thirdPersonCamera.SetTarget(clone.transform); - } } + } private static string BuildPlayerName(LobbyPlayerInfo info, int index) @@ -241,6 +244,21 @@ namespace MegaKoop.Game.Networking return "You"; } + private static ulong ParseSteamId(string steamId) + { + if (string.IsNullOrEmpty(steamId)) + { + return 0; + } + + if (ulong.TryParse(steamId, out var value)) + { + return value; + } + + return 0; + } + [Serializable] private struct LobbyPlayerInfo { diff --git a/Game/Scripts/Networking/NetworkIdentity.cs b/Game/Scripts/Networking/NetworkIdentity.cs index c8b4f35..e1a3937 100644 --- a/Game/Scripts/Networking/NetworkIdentity.cs +++ b/Game/Scripts/Networking/NetworkIdentity.cs @@ -49,5 +49,30 @@ namespace MegaKoop.Game.Networking } public static bool TryGet(int id, out NetworkIdentity identity) => registry.TryGetValue(id, out identity); + + /// + /// Allows deterministic assignment so IDs match across clients. + /// + public void SetNetworkId(int id) + { + if (id == 0) + { + Debug.LogWarning("[NetworkIdentity] Cannot assign network id 0."); + return; + } + + if (networkId == id) + { + return; + } + + if (registry.TryGetValue(networkId, out NetworkIdentity existing) && existing == this) + { + registry.Remove(networkId); + } + + networkId = id; + Register(); + } } } diff --git a/UI/Scripts/UGUIMultiplayerLobbyController.cs b/UI/Scripts/UGUIMultiplayerLobbyController.cs index f3b0064..3be25c5 100644 --- a/UI/Scripts/UGUIMultiplayerLobbyController.cs +++ b/UI/Scripts/UGUIMultiplayerLobbyController.cs @@ -366,7 +366,30 @@ namespace MegaKoop.UI servicesRoot.AddComponent(); } + T EnsureComponent() where T : Component + { + if (servicesRoot.TryGetComponent(out var existing)) + { + return existing; + } +#if UNITY_2023_1_OR_NEWER + var found = Object.FindFirstObjectByType(FindObjectsInactive.Include); +#else + var found = Object.FindObjectOfType(); +#endif + if (found != null) + { + return found; + } + return servicesRoot.AddComponent(); + } + + EnsureComponent(); + EnsureComponent(); + EnsureComponent(); lobbyGameCoordinator = servicesRoot.GetComponent() ?? servicesRoot.AddComponent(); + + DontDestroyOnLoad(servicesRoot); } }