diff --git a/Assets/Mirror/Components/NetworkRoomManager.cs b/Assets/Mirror/Components/NetworkRoomManager.cs index 4420423c0..722153bac 100644 --- a/Assets/Mirror/Components/NetworkRoomManager.cs +++ b/Assets/Mirror/Components/NetworkRoomManager.cs @@ -451,7 +451,7 @@ public override void OnStartClient() if (roomPlayerPrefab == null || roomPlayerPrefab.gameObject == null) Debug.LogError("NetworkRoomManager no RoomPlayer prefab is registered. Please add a RoomPlayer prefab."); else - ClientScene.RegisterPrefab(roomPlayerPrefab.gameObject); + NetworkClient.RegisterPrefab(roomPlayerPrefab.gameObject); if (playerPrefab == null) Debug.LogError("NetworkRoomManager no GamePlayer prefab is registered. Please add a GamePlayer prefab."); diff --git a/Assets/Mirror/Runtime/ClientScene.cs b/Assets/Mirror/Runtime/ClientScene.cs index 834eb8c92..70d86f47a 100644 --- a/Assets/Mirror/Runtime/ClientScene.cs +++ b/Assets/Mirror/Runtime/ClientScene.cs @@ -32,19 +32,13 @@ public static class ClientScene public static NetworkConnection readyConnection { get; private set; } /// Registered spawnable prefabs by assetId. - public static readonly Dictionary prefabs = - new Dictionary(); + [Obsolete("ClientScene.prefabs was moved to NetworkClient.prefabs")] + public static Dictionary prefabs => NetworkClient.prefabs; /// Disabled scene objects that can be spawned again, by sceneId. internal static readonly Dictionary spawnableObjects = new Dictionary(); - // spawn handlers - internal static readonly Dictionary spawnHandlers = - new Dictionary(); - internal static readonly Dictionary unspawnHandlers = - new Dictionary(); - // add player ////////////////////////////////////////////////////////// // called from message handler for Owner message internal static void InternalAddPlayer(NetworkIdentity identity) @@ -171,416 +165,48 @@ public static void PrepareToSpawnSceneObjects() } // spawnable prefabs /////////////////////////////////////////////////// - /// Find the registered prefab for this asset id. - // Useful for debuggers - public static bool GetPrefab(Guid assetId, out GameObject prefab) - { - prefab = null; - return assetId != Guid.Empty && - prefabs.TryGetValue(assetId, out prefab) && prefab != null; - } + [Obsolete("ClientScene.GetPrefab was moved to NetworkClient.GetPrefab")] + public static bool GetPrefab(Guid assetId, out GameObject prefab) => NetworkClient.GetPrefab(assetId, out prefab); - /// Validates Prefab then adds it to prefabs dictionary. - static void RegisterPrefabIdentity(NetworkIdentity prefab) - { - if (prefab.assetId == Guid.Empty) - { - Debug.LogError($"Can not Register '{prefab.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead"); - return; - } + [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] + public static void RegisterPrefab(GameObject prefab, Guid newAssetId) => NetworkClient.RegisterPrefab(prefab, newAssetId); - if (prefab.sceneId != 0) - { - Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); - return; - } + [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] + public static void RegisterPrefab(GameObject prefab) => NetworkClient.RegisterPrefab(prefab); - NetworkIdentity[] identities = prefab.GetComponentsInChildren(); - if (identities.Length > 1) - { - Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); - } + [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] + public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => + NetworkClient.RegisterPrefab(prefab, newAssetId, spawnHandler, unspawnHandler); - if (prefabs.ContainsKey(prefab.assetId)) - { - GameObject existingPrefab = prefabs[prefab.assetId]; - Debug.LogWarning($"Replacing existing prefab with assetId '{prefab.assetId}'. Old prefab '{existingPrefab.name}', New prefab '{prefab.name}'"); - } + [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] + public static void RegisterPrefab(GameObject prefab, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => + NetworkClient.RegisterPrefab(prefab, spawnHandler, unspawnHandler); - if (spawnHandlers.ContainsKey(prefab.assetId) || unspawnHandlers.ContainsKey(prefab.assetId)) - { - Debug.LogWarning($"Adding prefab '{prefab.name}' with assetId '{prefab.assetId}' when spawnHandlers with same assetId already exists."); - } + [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] + public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => + NetworkClient.RegisterPrefab(prefab, newAssetId, spawnHandler, unspawnHandler); - // Debug.Log($"Registering prefab '{prefab.name}' as asset:{prefab.assetId}"); + [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] + public static void RegisterPrefab(GameObject prefab, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => + NetworkClient.RegisterPrefab(prefab, spawnHandler, unspawnHandler); - prefabs[prefab.assetId] = prefab.gameObject; - } - - /// Register spawnable prefab with custom assetId. - // Note: newAssetId can not be set on GameObjects that already have an assetId - // Note: registering with assetId is useful for assetbundles etc. a lot - // of people use this. - public static void RegisterPrefab(GameObject prefab, Guid newAssetId) - { - if (prefab == null) - { - Debug.LogError("Could not register prefab because it was null"); - return; - } - - if (newAssetId == Guid.Empty) - { - Debug.LogError($"Could not register '{prefab.name}' with new assetId because the new assetId was empty"); - return; - } - - NetworkIdentity identity = prefab.GetComponent(); - if (identity == null) - { - Debug.LogError($"Could not register '{prefab.name}' since it contains no NetworkIdentity component"); - return; - } - - if (identity.assetId != Guid.Empty && identity.assetId != newAssetId) - { - Debug.LogError($"Could not register '{prefab.name}' to {newAssetId} because it already had an AssetId, Existing assetId {identity.assetId}"); - return; - } - - identity.assetId = newAssetId; - - RegisterPrefabIdentity(identity); - } - - /// Register spawnable prefab. - public static void RegisterPrefab(GameObject prefab) - { - if (prefab == null) - { - Debug.LogError("Could not register prefab because it was null"); - return; - } - - NetworkIdentity identity = prefab.GetComponent(); - if (identity == null) - { - Debug.LogError($"Could not register '{prefab.name}' since it contains no NetworkIdentity component"); - return; - } - - RegisterPrefabIdentity(identity); - } - - /// Register a spawnable prefab with custom assetId and custom spawn/unspawn handlers. - // Note: newAssetId can not be set on GameObjects that already have an assetId - // Note: registering with assetId is useful for assetbundles etc. a lot - // of people use this. - // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? - public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) - { - // We need this check here because we don't want a null handler in the lambda expression below - if (spawnHandler == null) - { - Debug.LogError($"Can not Register null SpawnHandler for {newAssetId}"); - return; - } - - RegisterPrefab(prefab, newAssetId, msg => spawnHandler(msg.position, msg.assetId), unspawnHandler); - } - - /// Register a spawnable prefab with custom spawn/unspawn handlers. - // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? - public static void RegisterPrefab(GameObject prefab, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) - { - if (prefab == null) - { - Debug.LogError("Could not register handler for prefab because the prefab was null"); - return; - } - - NetworkIdentity identity = prefab.GetComponent(); - if (identity == null) - { - Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); - return; - } - - if (identity.sceneId != 0) - { - Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); - return; - } - - Guid assetId = identity.assetId; - - if (assetId == Guid.Empty) - { - Debug.LogError($"Can not Register handler for '{prefab.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead"); - return; - } - - // We need this check here because we don't want a null handler in the lambda expression below - if (spawnHandler == null) - { - Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); - return; - } - - RegisterPrefab(prefab, msg => spawnHandler(msg.position, msg.assetId), unspawnHandler); - } - - /// Register a spawnable prefab with custom assetId and custom spawn/unspawn handlers. - // Note: newAssetId can not be set on GameObjects that already have an assetId - // Note: registering with assetId is useful for assetbundles etc. a lot - // of people use this. - // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? - public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) - { - if (newAssetId == Guid.Empty) - { - Debug.LogError($"Could not register handler for '{prefab.name}' with new assetId because the new assetId was empty"); - return; - } - - if (prefab == null) - { - Debug.LogError("Could not register handler for prefab because the prefab was null"); - return; - } - - NetworkIdentity identity = prefab.GetComponent(); - if (identity == null) - { - Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); - return; - } - - if (identity.assetId != Guid.Empty && identity.assetId != newAssetId) - { - Debug.LogError($"Could not register Handler for '{prefab.name}' to {newAssetId} because it already had an AssetId, Existing assetId {identity.assetId}"); - return; - } - - if (identity.sceneId != 0) - { - Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); - return; - } - - identity.assetId = newAssetId; - Guid assetId = identity.assetId; - - if (spawnHandler == null) - { - Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); - return; - } - - if (unspawnHandler == null) - { - Debug.LogError($"Can not Register null UnSpawnHandler for {assetId}"); - return; - } - - if (spawnHandlers.ContainsKey(assetId) || unspawnHandlers.ContainsKey(assetId)) - { - Debug.LogWarning($"Replacing existing spawnHandlers for prefab '{prefab.name}' with assetId '{assetId}'"); - } - - if (prefabs.ContainsKey(assetId)) - { - // this is error because SpawnPrefab checks prefabs before handler - Debug.LogError($"assetId '{assetId}' is already used by prefab '{prefabs[assetId].name}', unregister the prefab first before trying to add handler"); - } - - NetworkIdentity[] identities = prefab.GetComponentsInChildren(); - if (identities.Length > 1) - { - Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); - } - - // Debug.Log("Registering custom prefab '" + prefab.name + "' as asset:" + assetId + " " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); - - spawnHandlers[assetId] = spawnHandler; - unspawnHandlers[assetId] = unspawnHandler; - } - - /// Register a spawnable prefab with custom spawn/unspawn handlers. - // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? - public static void RegisterPrefab(GameObject prefab, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) - { - if (prefab == null) - { - Debug.LogError("Could not register handler for prefab because the prefab was null"); - return; - } - - NetworkIdentity identity = prefab.GetComponent(); - if (identity == null) - { - Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); - return; - } - - if (identity.sceneId != 0) - { - Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); - return; - } - - Guid assetId = identity.assetId; - - if (assetId == Guid.Empty) - { - Debug.LogError($"Can not Register handler for '{prefab.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead"); - return; - } - - if (spawnHandler == null) - { - Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); - return; - } - - if (unspawnHandler == null) - { - Debug.LogError($"Can not Register null UnSpawnHandler for {assetId}"); - return; - } - - if (spawnHandlers.ContainsKey(assetId) || unspawnHandlers.ContainsKey(assetId)) - { - Debug.LogWarning($"Replacing existing spawnHandlers for prefab '{prefab.name}' with assetId '{assetId}'"); - } - - if (prefabs.ContainsKey(assetId)) - { - // this is error because SpawnPrefab checks prefabs before handler - Debug.LogError($"assetId '{assetId}' is already used by prefab '{prefabs[assetId].name}', unregister the prefab first before trying to add handler"); - } - - NetworkIdentity[] identities = prefab.GetComponentsInChildren(); - if (identities.Length > 1) - { - Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); - } - - // Debug.Log("Registering custom prefab '" + prefab.name + "' as asset:" + assetId + " " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); - - spawnHandlers[assetId] = spawnHandler; - unspawnHandlers[assetId] = unspawnHandler; - } - - /// Removes a registered spawn prefab that was setup with ClientScene.RegisterPrefab. - public static void UnregisterPrefab(GameObject prefab) - { - if (prefab == null) - { - Debug.LogError("Could not unregister prefab because it was null"); - return; - } - - NetworkIdentity identity = prefab.GetComponent(); - if (identity == null) - { - Debug.LogError("Could not unregister '" + prefab.name + "' since it contains no NetworkIdentity component"); - return; - } - - Guid assetId = identity.assetId; - - prefabs.Remove(assetId); - spawnHandlers.Remove(assetId); - unspawnHandlers.Remove(assetId); - } + [Obsolete("ClientScene.UnregisterPrefab was moved to NetworkClient.UnregisterPrefab")] + public static void UnregisterPrefab(GameObject prefab) => NetworkClient.UnregisterPrefab(prefab); // spawn handlers ////////////////////////////////////////////////////// - /// This is an advanced spawning function that registers a custom assetId with the spawning system. - // This can be used to register custom spawning methods for an assetId - - // instead of the usual method of registering spawning methods for a - // prefab. This should be used when no prefab exists for the spawned - // objects - such as when they are constructed dynamically at runtime - // from configuration data. - public static void RegisterSpawnHandler(Guid assetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) - { - // We need this check here because we don't want a null handler in the lambda expression below - if (spawnHandler == null) - { - Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); - return; - } + [Obsolete("ClientScene.RegisterSpawnHandler was moved to NetworkClient.RegisterSpawnHandler")] + public static void RegisterSpawnHandler(Guid assetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => + NetworkClient.RegisterSpawnHandler(assetId, spawnHandler, unspawnHandler); - RegisterSpawnHandler(assetId, msg => spawnHandler(msg.position, msg.assetId), unspawnHandler); - } + [Obsolete("ClientScene.RegisterSpawnHandler was moved to NetworkClient.RegisterSpawnHandler")] + public static void RegisterSpawnHandler(Guid assetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => + NetworkClient.RegisterSpawnHandler(assetId, spawnHandler, unspawnHandler); - /// This is an advanced spawning function that registers a custom assetId with the spawning system. - // This can be used to register custom spawning methods for an assetId - - // instead of the usual method of registering spawning methods for a - // prefab. This should be used when no prefab exists for the spawned - // objects - such as when they are constructed dynamically at runtime - // from configuration data. - public static void RegisterSpawnHandler(Guid assetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) - { - if (spawnHandler == null) - { - Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); - return; - } + [Obsolete("ClientScene.UnregisterSpawnHandler was moved to NetworkClient.UnregisterSpawnHandler")] + public static void UnregisterSpawnHandler(Guid assetId) => NetworkClient.UnregisterSpawnHandler(assetId); - if (unspawnHandler == null) - { - Debug.LogError($"Can not Register null UnSpawnHandler for {assetId}"); - return; - } - - if (assetId == Guid.Empty) - { - Debug.LogError("Can not Register SpawnHandler for empty Guid"); - return; - } - - if (spawnHandlers.ContainsKey(assetId) || unspawnHandlers.ContainsKey(assetId)) - { - Debug.LogWarning($"Replacing existing spawnHandlers for {assetId}"); - } - - if (prefabs.ContainsKey(assetId)) - { - // this is error because SpawnPrefab checks prefabs before handler - Debug.LogError($"assetId '{assetId}' is already used by prefab '{prefabs[assetId].name}'"); - } - - // Debug.Log("RegisterSpawnHandler asset '" + assetId + "' " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); - - spawnHandlers[assetId] = spawnHandler; - unspawnHandlers[assetId] = unspawnHandler; - } - - /// Removes a registered spawn handler function that was registered with ClientScene.RegisterHandler(). - public static void UnregisterSpawnHandler(Guid assetId) - { - spawnHandlers.Remove(assetId); - unspawnHandlers.Remove(assetId); - } - - /// This clears the registered spawn prefabs and spawn handler functions for this client. - public static void ClearSpawners() - { - prefabs.Clear(); - spawnHandlers.Clear(); - unspawnHandlers.Clear(); - } - - static bool InvokeUnSpawnHandler(Guid assetId, GameObject obj) - { - if (unspawnHandlers.TryGetValue(assetId, out UnSpawnDelegate handler) && handler != null) - { - handler(obj); - return true; - } - return false; - } + [Obsolete("ClientScene.ClearSpawners was moved to NetworkClient.ClearSpawners")] + public static void ClearSpawners() => NetworkClient.ClearSpawners(); // spawning //////////////////////////////////////////////////////////// internal static void ApplySpawnPayload(NetworkIdentity identity, SpawnMessage message) @@ -676,7 +302,7 @@ static NetworkIdentity SpawnPrefab(SpawnMessage msg) //Debug.Log("Client spawn handler instantiating [netId:" + msg.netId + " asset ID:" + msg.assetId + " pos:" + msg.position + " rotation: " + msg.rotation + "]"); return obj.GetComponent(); } - if (spawnHandlers.TryGetValue(msg.assetId, out SpawnHandlerDelegate handler)) + if (NetworkClient.spawnHandlers.TryGetValue(msg.assetId, out SpawnHandlerDelegate handler)) { GameObject obj = handler(msg); if (obj == null) @@ -782,7 +408,7 @@ static void DestroyObject(uint netId) localObject.OnStopClient(); // user handling - if (InvokeUnSpawnHandler(localObject.assetId, localObject.gameObject)) + if (NetworkClient.InvokeUnSpawnHandler(localObject.assetId, localObject.gameObject)) { // reset object after user's handler localObject.Reset(); @@ -822,7 +448,7 @@ public static void DestroyAllClientObjects() if (identity != null && identity.gameObject != null) { identity.OnStopClient(); - bool wasUnspawned = InvokeUnSpawnHandler(identity.assetId, identity.gameObject); + bool wasUnspawned = NetworkClient.InvokeUnSpawnHandler(identity.assetId, identity.gameObject); if (!wasUnspawned) { // scene objects are reset and disabled. diff --git a/Assets/Mirror/Runtime/NetworkClient.cs b/Assets/Mirror/Runtime/NetworkClient.cs index 515bc48ed..043e9d5ed 100644 --- a/Assets/Mirror/Runtime/NetworkClient.cs +++ b/Assets/Mirror/Runtime/NetworkClient.cs @@ -50,6 +50,16 @@ public static class NetworkClient internal static Action OnConnectedEvent; internal static Action OnDisconnectedEvent; + /// Registered spawnable prefabs by assetId. + public static readonly Dictionary prefabs = + new Dictionary(); + + // spawn handlers + internal static readonly Dictionary spawnHandlers = + new Dictionary(); + internal static readonly Dictionary unspawnHandlers = + new Dictionary(); + // initialization ////////////////////////////////////////////////////// static void AddTransportHandlers() { @@ -311,6 +321,418 @@ public static bool UnregisterHandler() return handlers.Remove(msgType); } + // spawnable prefabs /////////////////////////////////////////////////// + /// Find the registered prefab for this asset id. + // Useful for debuggers + public static bool GetPrefab(Guid assetId, out GameObject prefab) + { + prefab = null; + return assetId != Guid.Empty && + prefabs.TryGetValue(assetId, out prefab) && prefab != null; + } + + /// Validates Prefab then adds it to prefabs dictionary. + static void RegisterPrefabIdentity(NetworkIdentity prefab) + { + if (prefab.assetId == Guid.Empty) + { + Debug.LogError($"Can not Register '{prefab.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead"); + return; + } + + if (prefab.sceneId != 0) + { + Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); + return; + } + + NetworkIdentity[] identities = prefab.GetComponentsInChildren(); + if (identities.Length > 1) + { + Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); + } + + if (prefabs.ContainsKey(prefab.assetId)) + { + GameObject existingPrefab = prefabs[prefab.assetId]; + Debug.LogWarning($"Replacing existing prefab with assetId '{prefab.assetId}'. Old prefab '{existingPrefab.name}', New prefab '{prefab.name}'"); + } + + if (spawnHandlers.ContainsKey(prefab.assetId) || unspawnHandlers.ContainsKey(prefab.assetId)) + { + Debug.LogWarning($"Adding prefab '{prefab.name}' with assetId '{prefab.assetId}' when spawnHandlers with same assetId already exists."); + } + + // Debug.Log($"Registering prefab '{prefab.name}' as asset:{prefab.assetId}"); + + prefabs[prefab.assetId] = prefab.gameObject; + } + + /// Register spawnable prefab with custom assetId. + // Note: newAssetId can not be set on GameObjects that already have an assetId + // Note: registering with assetId is useful for assetbundles etc. a lot + // of people use this. + public static void RegisterPrefab(GameObject prefab, Guid newAssetId) + { + if (prefab == null) + { + Debug.LogError("Could not register prefab because it was null"); + return; + } + + if (newAssetId == Guid.Empty) + { + Debug.LogError($"Could not register '{prefab.name}' with new assetId because the new assetId was empty"); + return; + } + + NetworkIdentity identity = prefab.GetComponent(); + if (identity == null) + { + Debug.LogError($"Could not register '{prefab.name}' since it contains no NetworkIdentity component"); + return; + } + + if (identity.assetId != Guid.Empty && identity.assetId != newAssetId) + { + Debug.LogError($"Could not register '{prefab.name}' to {newAssetId} because it already had an AssetId, Existing assetId {identity.assetId}"); + return; + } + + identity.assetId = newAssetId; + + RegisterPrefabIdentity(identity); + } + + /// Register spawnable prefab. + public static void RegisterPrefab(GameObject prefab) + { + if (prefab == null) + { + Debug.LogError("Could not register prefab because it was null"); + return; + } + + NetworkIdentity identity = prefab.GetComponent(); + if (identity == null) + { + Debug.LogError($"Could not register '{prefab.name}' since it contains no NetworkIdentity component"); + return; + } + + RegisterPrefabIdentity(identity); + } + + /// Register a spawnable prefab with custom assetId and custom spawn/unspawn handlers. + // Note: newAssetId can not be set on GameObjects that already have an assetId + // Note: registering with assetId is useful for assetbundles etc. a lot + // of people use this. + // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? + public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + { + // We need this check here because we don't want a null handler in the lambda expression below + if (spawnHandler == null) + { + Debug.LogError($"Can not Register null SpawnHandler for {newAssetId}"); + return; + } + + RegisterPrefab(prefab, newAssetId, msg => spawnHandler(msg.position, msg.assetId), unspawnHandler); + } + + /// Register a spawnable prefab with custom spawn/unspawn handlers. + // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? + public static void RegisterPrefab(GameObject prefab, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + { + if (prefab == null) + { + Debug.LogError("Could not register handler for prefab because the prefab was null"); + return; + } + + NetworkIdentity identity = prefab.GetComponent(); + if (identity == null) + { + Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); + return; + } + + if (identity.sceneId != 0) + { + Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); + return; + } + + Guid assetId = identity.assetId; + + if (assetId == Guid.Empty) + { + Debug.LogError($"Can not Register handler for '{prefab.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead"); + return; + } + + // We need this check here because we don't want a null handler in the lambda expression below + if (spawnHandler == null) + { + Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); + return; + } + + RegisterPrefab(prefab, msg => spawnHandler(msg.position, msg.assetId), unspawnHandler); + } + + /// Register a spawnable prefab with custom assetId and custom spawn/unspawn handlers. + // Note: newAssetId can not be set on GameObjects that already have an assetId + // Note: registering with assetId is useful for assetbundles etc. a lot + // of people use this. + // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? + public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + { + if (newAssetId == Guid.Empty) + { + Debug.LogError($"Could not register handler for '{prefab.name}' with new assetId because the new assetId was empty"); + return; + } + + if (prefab == null) + { + Debug.LogError("Could not register handler for prefab because the prefab was null"); + return; + } + + NetworkIdentity identity = prefab.GetComponent(); + if (identity == null) + { + Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); + return; + } + + if (identity.assetId != Guid.Empty && identity.assetId != newAssetId) + { + Debug.LogError($"Could not register Handler for '{prefab.name}' to {newAssetId} because it already had an AssetId, Existing assetId {identity.assetId}"); + return; + } + + if (identity.sceneId != 0) + { + Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); + return; + } + + identity.assetId = newAssetId; + Guid assetId = identity.assetId; + + if (spawnHandler == null) + { + Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); + return; + } + + if (unspawnHandler == null) + { + Debug.LogError($"Can not Register null UnSpawnHandler for {assetId}"); + return; + } + + if (spawnHandlers.ContainsKey(assetId) || unspawnHandlers.ContainsKey(assetId)) + { + Debug.LogWarning($"Replacing existing spawnHandlers for prefab '{prefab.name}' with assetId '{assetId}'"); + } + + if (prefabs.ContainsKey(assetId)) + { + // this is error because SpawnPrefab checks prefabs before handler + Debug.LogError($"assetId '{assetId}' is already used by prefab '{prefabs[assetId].name}', unregister the prefab first before trying to add handler"); + } + + NetworkIdentity[] identities = prefab.GetComponentsInChildren(); + if (identities.Length > 1) + { + Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); + } + + // Debug.Log("Registering custom prefab '" + prefab.name + "' as asset:" + assetId + " " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); + + spawnHandlers[assetId] = spawnHandler; + unspawnHandlers[assetId] = unspawnHandler; + } + + /// Register a spawnable prefab with custom spawn/unspawn handlers. + // TODO why do we have one with SpawnDelegate and one with SpawnHandlerDelegate? + public static void RegisterPrefab(GameObject prefab, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + { + if (prefab == null) + { + Debug.LogError("Could not register handler for prefab because the prefab was null"); + return; + } + + NetworkIdentity identity = prefab.GetComponent(); + if (identity == null) + { + Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); + return; + } + + if (identity.sceneId != 0) + { + Debug.LogError($"Can not Register '{prefab.name}' because it has a sceneId, make sure you are passing in the original prefab and not an instance in the scene."); + return; + } + + Guid assetId = identity.assetId; + + if (assetId == Guid.Empty) + { + Debug.LogError($"Can not Register handler for '{prefab.name}' because it had empty assetid. If this is a scene Object use RegisterSpawnHandler instead"); + return; + } + + if (spawnHandler == null) + { + Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); + return; + } + + if (unspawnHandler == null) + { + Debug.LogError($"Can not Register null UnSpawnHandler for {assetId}"); + return; + } + + if (spawnHandlers.ContainsKey(assetId) || unspawnHandlers.ContainsKey(assetId)) + { + Debug.LogWarning($"Replacing existing spawnHandlers for prefab '{prefab.name}' with assetId '{assetId}'"); + } + + if (prefabs.ContainsKey(assetId)) + { + // this is error because SpawnPrefab checks prefabs before handler + Debug.LogError($"assetId '{assetId}' is already used by prefab '{prefabs[assetId].name}', unregister the prefab first before trying to add handler"); + } + + NetworkIdentity[] identities = prefab.GetComponentsInChildren(); + if (identities.Length > 1) + { + Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); + } + + // Debug.Log("Registering custom prefab '" + prefab.name + "' as asset:" + assetId + " " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); + + spawnHandlers[assetId] = spawnHandler; + unspawnHandlers[assetId] = unspawnHandler; + } + + /// Removes a registered spawn prefab that was setup with ClientScene.RegisterPrefab. + public static void UnregisterPrefab(GameObject prefab) + { + if (prefab == null) + { + Debug.LogError("Could not unregister prefab because it was null"); + return; + } + + NetworkIdentity identity = prefab.GetComponent(); + if (identity == null) + { + Debug.LogError("Could not unregister '" + prefab.name + "' since it contains no NetworkIdentity component"); + return; + } + + Guid assetId = identity.assetId; + + prefabs.Remove(assetId); + spawnHandlers.Remove(assetId); + unspawnHandlers.Remove(assetId); + } + + // spawn handlers ////////////////////////////////////////////////////// + /// This is an advanced spawning function that registers a custom assetId with the spawning system. + // This can be used to register custom spawning methods for an assetId - + // instead of the usual method of registering spawning methods for a + // prefab. This should be used when no prefab exists for the spawned + // objects - such as when they are constructed dynamically at runtime + // from configuration data. + public static void RegisterSpawnHandler(Guid assetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + { + // We need this check here because we don't want a null handler in the lambda expression below + if (spawnHandler == null) + { + Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); + return; + } + + RegisterSpawnHandler(assetId, msg => spawnHandler(msg.position, msg.assetId), unspawnHandler); + } + + /// This is an advanced spawning function that registers a custom assetId with the spawning system. + // This can be used to register custom spawning methods for an assetId - + // instead of the usual method of registering spawning methods for a + // prefab. This should be used when no prefab exists for the spawned + // objects - such as when they are constructed dynamically at runtime + // from configuration data. + public static void RegisterSpawnHandler(Guid assetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + { + if (spawnHandler == null) + { + Debug.LogError($"Can not Register null SpawnHandler for {assetId}"); + return; + } + + if (unspawnHandler == null) + { + Debug.LogError($"Can not Register null UnSpawnHandler for {assetId}"); + return; + } + + if (assetId == Guid.Empty) + { + Debug.LogError("Can not Register SpawnHandler for empty Guid"); + return; + } + + if (spawnHandlers.ContainsKey(assetId) || unspawnHandlers.ContainsKey(assetId)) + { + Debug.LogWarning($"Replacing existing spawnHandlers for {assetId}"); + } + + if (prefabs.ContainsKey(assetId)) + { + // this is error because SpawnPrefab checks prefabs before handler + Debug.LogError($"assetId '{assetId}' is already used by prefab '{prefabs[assetId].name}'"); + } + + // Debug.Log("RegisterSpawnHandler asset '" + assetId + "' " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); + + spawnHandlers[assetId] = spawnHandler; + unspawnHandlers[assetId] = unspawnHandler; + } + + /// Removes a registered spawn handler function that was registered with ClientScene.RegisterHandler(). + public static void UnregisterSpawnHandler(Guid assetId) + { + spawnHandlers.Remove(assetId); + unspawnHandlers.Remove(assetId); + } + + /// This clears the registered spawn prefabs and spawn handler functions for this client. + public static void ClearSpawners() + { + prefabs.Clear(); + spawnHandlers.Clear(); + unspawnHandlers.Clear(); + } + + internal static bool InvokeUnSpawnHandler(Guid assetId, GameObject obj) + { + if (unspawnHandlers.TryGetValue(assetId, out UnSpawnDelegate handler) && handler != null) + { + handler(obj); + return true; + } + return false; + } + // update ////////////////////////////////////////////////////////////// // NetworkEarlyUpdate called before any Update/FixedUpdate // (we add this to the UnityEngine in NetworkLoop) diff --git a/Assets/Mirror/Runtime/NetworkManager.cs b/Assets/Mirror/Runtime/NetworkManager.cs index 6cde4f4f4..c952bf338 100644 --- a/Assets/Mirror/Runtime/NetworkManager.cs +++ b/Assets/Mirror/Runtime/NetworkManager.cs @@ -807,10 +807,10 @@ void RegisterClientMessages() NetworkClient.RegisterHandler(OnClientSceneInternal, false); if (playerPrefab != null) - ClientScene.RegisterPrefab(playerPrefab); + NetworkClient.RegisterPrefab(playerPrefab); foreach (GameObject prefab in spawnPrefabs.Where(t => t != null)) - ClientScene.RegisterPrefab(prefab); + NetworkClient.RegisterPrefab(prefab); } /// diff --git a/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs b/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs index a03ee0a25..c882024a6 100644 --- a/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs +++ b/Assets/Mirror/Tests/Common/ClientSceneTestsBase.cs @@ -29,9 +29,9 @@ public abstract class ClientSceneTestsBase protected readonly List _createdObjects = new List(); - protected Dictionary prefabs => ClientScene.prefabs; - protected Dictionary spawnHandlers => ClientScene.spawnHandlers; - protected Dictionary unspawnHandlers => ClientScene.unspawnHandlers; + protected Dictionary prefabs => NetworkClient.prefabs; + protected Dictionary spawnHandlers => NetworkClient.spawnHandlers; + protected Dictionary unspawnHandlers => NetworkClient.unspawnHandlers; protected Dictionary spawnableObjects => ClientScene.spawnableObjects; diff --git a/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs b/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs index 84a7610f4..1417296eb 100644 --- a/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs +++ b/Assets/Mirror/Tests/Runtime/ClientSceneTests_DestroyAllClientObjects.cs @@ -49,7 +49,7 @@ public class ClientSceneTests_DestroyAllClientObjects { public static readonly List _createdObjects = new List(); Dictionary spawned => NetworkIdentity.spawned; - Dictionary unspawnHandlers => ClientScene.unspawnHandlers; + Dictionary unspawnHandlers => NetworkClient.unspawnHandlers; [TearDown] public void TearDown()