1 task
This commit is contained in:
271
Game/Scripts/Networking/NetworkIdRegistry.cs
Normal file
271
Game/Scripts/Networking/NetworkIdRegistry.cs
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace MegaKoop.Game.Networking
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Centralized registry for managing Network IDs across all clients.
|
||||||
|
/// Provides collision detection, Steam ID mapping, and ID range reservation.
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkIdRegistry : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static NetworkIdRegistry instance;
|
||||||
|
|
||||||
|
private readonly Dictionary<int, NetworkIdentity> registry = new();
|
||||||
|
private readonly Dictionary<ulong, int> steamIdToNetworkId = new();
|
||||||
|
private readonly HashSet<int> reservedIds = new();
|
||||||
|
|
||||||
|
public static NetworkIdRegistry Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
GameObject go = new GameObject("NetworkIdRegistry");
|
||||||
|
instance = go.AddComponent<NetworkIdRegistry>();
|
||||||
|
DontDestroyOnLoad(go);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (instance != null && instance != this)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[NetworkIdRegistry] Duplicate instance detected. Destroying.");
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to register a NetworkIdentity with its assigned ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identity">The NetworkIdentity to register</param>
|
||||||
|
/// <returns>True if registration succeeded, false if collision detected</returns>
|
||||||
|
public static bool TryRegister(NetworkIdentity identity)
|
||||||
|
{
|
||||||
|
if (identity == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[NetworkIdRegistry] Cannot register null NetworkIdentity.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int networkId = identity.NetworkId;
|
||||||
|
|
||||||
|
if (networkId == 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[NetworkIdRegistry] NetworkIdentity '{identity.name}' has ID 0 and cannot be registered.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inst = Instance;
|
||||||
|
|
||||||
|
// Check for collision
|
||||||
|
if (inst.registry.TryGetValue(networkId, out NetworkIdentity existing))
|
||||||
|
{
|
||||||
|
if (existing == identity)
|
||||||
|
{
|
||||||
|
// Already registered
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[NetworkIdRegistry] ID collision detected! ID {networkId} is already assigned to '{existing.name}'. " +
|
||||||
|
$"Cannot register '{identity.name}'. This will cause network synchronization issues.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if ID is in reserved range
|
||||||
|
if (inst.reservedIds.Contains(networkId))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[NetworkIdRegistry] ID {networkId} is in a reserved range but being registered for '{identity.name}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inst.registry[networkId] = identity;
|
||||||
|
Debug.Log($"[NetworkIdRegistry] Registered '{identity.name}' with Network ID {networkId}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregisters a NetworkIdentity by its ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="networkId">The network ID to unregister</param>
|
||||||
|
public static void Unregister(int networkId)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance.registry.Remove(networkId))
|
||||||
|
{
|
||||||
|
Debug.Log($"[NetworkIdRegistry] Unregistered Network ID {networkId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also remove from Steam ID mapping if present
|
||||||
|
ulong steamIdToRemove = 0;
|
||||||
|
foreach (var kvp in instance.steamIdToNetworkId)
|
||||||
|
{
|
||||||
|
if (kvp.Value == networkId)
|
||||||
|
{
|
||||||
|
steamIdToRemove = kvp.Key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (steamIdToRemove != 0)
|
||||||
|
{
|
||||||
|
instance.steamIdToNetworkId.Remove(steamIdToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a NetworkIdentity by its ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="networkId">The network ID to look up</param>
|
||||||
|
/// <returns>The NetworkIdentity if found, null otherwise</returns>
|
||||||
|
public static NetworkIdentity GetById(int networkId)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.registry.TryGetValue(networkId, out NetworkIdentity identity);
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a Steam ID to a Network ID for player tracking.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="steamId">The Steam ID of the player</param>
|
||||||
|
/// <param name="networkId">The Network ID assigned to the player</param>
|
||||||
|
public static void MapSteamIdToNetworkId(ulong steamId, int networkId)
|
||||||
|
{
|
||||||
|
if (steamId == 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[NetworkIdRegistry] Cannot map Steam ID 0.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inst = Instance;
|
||||||
|
|
||||||
|
if (inst.steamIdToNetworkId.TryGetValue(steamId, out int existingId))
|
||||||
|
{
|
||||||
|
if (existingId != networkId)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[NetworkIdRegistry] Steam ID {steamId} was mapped to Network ID {existingId}, now remapping to {networkId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inst.steamIdToNetworkId[steamId] = networkId;
|
||||||
|
Debug.Log($"[NetworkIdRegistry] Mapped Steam ID {steamId} to Network ID {networkId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Network ID associated with a Steam ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="steamId">The Steam ID to look up</param>
|
||||||
|
/// <returns>The Network ID if found, 0 otherwise</returns>
|
||||||
|
public static int GetNetworkIdForSteamId(ulong steamId)
|
||||||
|
{
|
||||||
|
if (instance == null || steamId == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.steamIdToNetworkId.TryGetValue(steamId, out int networkId);
|
||||||
|
return networkId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reserves a range of IDs to prevent automatic assignment.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startId">The starting ID of the range (inclusive)</param>
|
||||||
|
/// <param name="count">The number of IDs to reserve</param>
|
||||||
|
public static void ReserveIdRange(int startId, int count)
|
||||||
|
{
|
||||||
|
if (startId <= 0 || count <= 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[NetworkIdRegistry] Invalid ID range: start={startId}, count={count}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inst = Instance;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
inst.reservedIds.Add(startId + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"[NetworkIdRegistry] Reserved ID range {startId} to {startId + count - 1} ({count} IDs)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if an ID is in a reserved range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="networkId">The ID to check</param>
|
||||||
|
/// <returns>True if the ID is reserved, false otherwise</returns>
|
||||||
|
public static bool IsIdReserved(int networkId)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance.reservedIds.Contains(networkId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all registrations and mappings. Use when transitioning between scenes or sessions.
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearRegistry()
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int registryCount = instance.registry.Count;
|
||||||
|
int mappingCount = instance.steamIdToNetworkId.Count;
|
||||||
|
int reservedCount = instance.reservedIds.Count;
|
||||||
|
|
||||||
|
instance.registry.Clear();
|
||||||
|
instance.steamIdToNetworkId.Clear();
|
||||||
|
instance.reservedIds.Clear();
|
||||||
|
|
||||||
|
Debug.Log($"[NetworkIdRegistry] Registry cleared. Removed {registryCount} identities, {mappingCount} Steam ID mappings, and {reservedCount} reserved IDs.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the total number of registered identities.
|
||||||
|
/// </summary>
|
||||||
|
public static int GetRegisteredCount()
|
||||||
|
{
|
||||||
|
return instance?.registry.Count ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a specific Network ID is currently registered.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsIdRegistered(int networkId)
|
||||||
|
{
|
||||||
|
return instance != null && instance.registry.ContainsKey(networkId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
if (instance == this)
|
||||||
|
{
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Game/Scripts/Networking/NetworkIdRegistry.cs.meta
Normal file
2
Game/Scripts/Networking/NetworkIdRegistry.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7a8b9c1d2e3f4a5b6c7d8e9f0a1b2c3d
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MegaKoop.Game.Networking
|
namespace MegaKoop.Game.Networking
|
||||||
@@ -6,19 +5,19 @@ namespace MegaKoop.Game.Networking
|
|||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public class NetworkIdentity : MonoBehaviour
|
public class NetworkIdentity : MonoBehaviour
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<int, NetworkIdentity> registry = new();
|
|
||||||
private static int nextId = 1;
|
|
||||||
|
|
||||||
[SerializeField] private int networkId;
|
[SerializeField] private int networkId;
|
||||||
[SerializeField] private bool assignOnAwake = true;
|
[SerializeField] private bool assignOnAwake = false; // Changed default to false - IDs should be assigned deterministically
|
||||||
|
|
||||||
public int NetworkId => networkId;
|
public int NetworkId => networkId;
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
|
// Note: Auto-increment removed in favor of deterministic ID generation
|
||||||
|
// IDs should be assigned via SetNetworkId() before or during Awake
|
||||||
if (assignOnAwake && networkId == 0)
|
if (assignOnAwake && networkId == 0)
|
||||||
{
|
{
|
||||||
networkId = nextId++;
|
Debug.LogWarning($"[NetworkIdentity] {name} has assignOnAwake=true but no ID assigned. " +
|
||||||
|
"Use DeterministicIdGenerator or manually call SetNetworkId().");
|
||||||
}
|
}
|
||||||
|
|
||||||
Register();
|
Register();
|
||||||
@@ -26,10 +25,7 @@ namespace MegaKoop.Game.Networking
|
|||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
if (registry.TryGetValue(networkId, out NetworkIdentity existing) && existing == this)
|
NetworkIdRegistry.Unregister(networkId);
|
||||||
{
|
|
||||||
registry.Remove(networkId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Register()
|
private void Register()
|
||||||
@@ -40,24 +36,31 @@ namespace MegaKoop.Game.Networking
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registry.TryGetValue(networkId, out NetworkIdentity existing) && existing != this)
|
if (!NetworkIdRegistry.TryRegister(this))
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"[NetworkIdentity] Duplicate network id {networkId} detected. Overwriting reference.");
|
Debug.LogError($"[NetworkIdentity] Failed to register {name} with ID {networkId}. " +
|
||||||
|
"This object will not be synchronized correctly!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registry[networkId] = this;
|
/// <summary>
|
||||||
|
/// Retrieves a NetworkIdentity by its ID using the centralized registry.
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryGet(int id, out NetworkIdentity identity)
|
||||||
|
{
|
||||||
|
identity = NetworkIdRegistry.GetById(id);
|
||||||
|
return identity != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryGet(int id, out NetworkIdentity identity) => registry.TryGetValue(id, out identity);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows deterministic assignment so IDs match across clients.
|
/// Allows deterministic assignment so IDs match across clients.
|
||||||
|
/// This should be called before the object is used in network communication.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetNetworkId(int id)
|
public void SetNetworkId(int id)
|
||||||
{
|
{
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
{
|
{
|
||||||
Debug.LogWarning("[NetworkIdentity] Cannot assign network id 0.");
|
Debug.LogError("[NetworkIdentity] Cannot assign network id 0.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,13 +69,25 @@ namespace MegaKoop.Game.Networking
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registry.TryGetValue(networkId, out NetworkIdentity existing) && existing == this)
|
// Unregister old ID if it was registered
|
||||||
|
if (networkId != 0)
|
||||||
{
|
{
|
||||||
registry.Remove(networkId);
|
NetworkIdRegistry.Unregister(networkId);
|
||||||
}
|
}
|
||||||
|
|
||||||
networkId = id;
|
networkId = id;
|
||||||
Register();
|
|
||||||
|
// Register with new ID
|
||||||
|
if (!NetworkIdRegistry.TryRegister(this))
|
||||||
|
{
|
||||||
|
Debug.LogError($"[NetworkIdentity] Failed to set network ID {id} for {name}. " +
|
||||||
|
"ID may already be in use!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current network ID without triggering registration.
|
||||||
|
/// </summary>
|
||||||
|
public int GetNetworkId() => networkId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user