using System.Collections.Generic; using UnityEngine; namespace Unity.Netcode { /// /// Interface for customizing, overriding, spawning, and destroying Network Prefabs /// Used by /// public interface INetworkPrefabInstanceHandler { /// /// Client Side Only /// Once an implementation is registered with the , this method will be called every time /// a Network Prefab associated is spawned on clients /// /// Note On Hosts: Use the /// method to register all targeted NetworkPrefab overrides manually since the host will be acting as both a server and client. /// /// Note on Pooling: If you are using a NetworkObject pool, don't forget to make the NetworkObject active /// via the method. /// /// the owner for the to be instantiated /// the initial/default position for the to be instantiated /// the initial/default rotation for the to be instantiated /// NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation); /// /// Invoked on Client and Server /// Once an implementation is registered with the , this method will be called when /// a Network Prefab associated is: /// /// Server Side: destroyed or despawned with the destroy parameter equal to true /// If is invoked with the default destroy parameter (i.e. false) then this method will NOT be invoked! /// /// Client Side: destroyed when the client receives a destroy object message from the server or host. /// /// Note on Pooling: When this method is invoked, you do not need to destroy the NetworkObject as long as you want your pool to persist. /// The most common approach is to make the inactive by calling . /// /// The being destroyed void Destroy(NetworkObject networkObject); } /// /// Primary handler to add or remove customized spawn and destroy handlers for a network prefab (i.e. a prefab with a NetworkObject component) /// Register custom prefab handlers by implementing the interface. /// public class NetworkPrefabHandler { /// /// Links a network prefab asset to a class with the INetworkPrefabInstanceHandler interface /// private readonly Dictionary m_PrefabAssetToPrefabHandler = new Dictionary(); /// /// Links the custom prefab instance's GlobalNetworkObjectId to the original prefab asset's GlobalNetworkObjectId. (Needed for HandleNetworkPrefabDestroy) /// [PrefabInstance][PrefabAsset] /// private readonly Dictionary m_PrefabInstanceToPrefabAsset = new Dictionary(); /// /// Use a to register a class that implements the interface with the /// /// the of the network prefab asset to be overridden /// class that implements the interface to be registered /// true (registered) false (failed to register) public bool AddHandler(GameObject networkPrefabAsset, INetworkPrefabInstanceHandler instanceHandler) { return AddHandler(networkPrefabAsset.GetComponent().GlobalObjectIdHash, instanceHandler); } /// /// Use a to register a class that implements the interface with the /// /// the of the network prefab asset to be overridden /// the class that implements the interface to be registered /// public bool AddHandler(NetworkObject prefabAssetNetworkObject, INetworkPrefabInstanceHandler instanceHandler) { return AddHandler(prefabAssetNetworkObject.GlobalObjectIdHash, instanceHandler); } /// /// Use a to register a class that implements the interface with the /// /// the value of the network prefab asset being overridden /// a class that implements the interface /// public bool AddHandler(uint globalObjectIdHash, INetworkPrefabInstanceHandler instanceHandler) { if (!m_PrefabAssetToPrefabHandler.ContainsKey(globalObjectIdHash)) { m_PrefabAssetToPrefabHandler.Add(globalObjectIdHash, instanceHandler); return true; } return false; } /// /// HOST ONLY! /// Since a host is unique and is considered both a client and a server, for each source NetworkPrefab you must manually /// register all potential target overrides that have the component. /// /// source NetworkPrefab to be overridden /// one or more NetworkPrefabs could be used to override the source NetworkPrefab public void RegisterHostGlobalObjectIdHashValues(GameObject sourceNetworkPrefab, List networkPrefabOverrides) { if (NetworkManager.Singleton.IsListening) { if (NetworkManager.Singleton.IsHost) { var sourceNetworkObject = sourceNetworkPrefab.GetComponent(); if (sourceNetworkPrefab != null) { var sourceGlobalObjectIdHash = sourceNetworkObject.GlobalObjectIdHash; // Now we register all foreach (var gameObject in networkPrefabOverrides) { if (gameObject.TryGetComponent(out var targetNetworkObject)) { if (!m_PrefabInstanceToPrefabAsset.ContainsKey(targetNetworkObject.GlobalObjectIdHash)) { m_PrefabInstanceToPrefabAsset.Add(targetNetworkObject.GlobalObjectIdHash, sourceGlobalObjectIdHash); } else { Debug.LogWarning($"{targetNetworkObject.name} appears to be a duplicate entry!"); } } else { throw new System.Exception($"{targetNetworkObject.name} does not have a {nameof(NetworkObject)} component!"); } } } else { throw new System.Exception($"{sourceNetworkPrefab.name} does not have a {nameof(NetworkObject)} component!"); } } else { throw new System.Exception($"You should only call {nameof(RegisterHostGlobalObjectIdHashValues)} as a Host!"); } } else { throw new System.Exception($"You can only call {nameof(RegisterHostGlobalObjectIdHashValues)} once NetworkManager is listening!"); } } /// /// Use the of the overridden network prefab asset to remove a registered class that implements the interface. /// /// of the network prefab asset that was being overridden /// true (success) or false (failure) public bool RemoveHandler(GameObject networkPrefabAsset) { return RemoveHandler(networkPrefabAsset.GetComponent().GlobalObjectIdHash); } /// /// Use the of the overridden network prefab asset to remove a registered class that implements the interface. /// /// of the source NetworkPrefab that was being overridden /// true (success) or false (failure) public bool RemoveHandler(NetworkObject networkObject) { return RemoveHandler(networkObject.GlobalObjectIdHash); } /// /// Use the of the overridden network prefab asset to remove a registered class that implements the interface. /// /// of the source NetworkPrefab that was being overridden /// true (success) or false (failure) public bool RemoveHandler(uint globalObjectIdHash) { if (m_PrefabInstanceToPrefabAsset.ContainsValue(globalObjectIdHash)) { uint networkPrefabHashKey = 0; foreach (var kvp in m_PrefabInstanceToPrefabAsset) { if (kvp.Value == globalObjectIdHash) { networkPrefabHashKey = kvp.Key; break; } } m_PrefabInstanceToPrefabAsset.Remove(networkPrefabHashKey); } return m_PrefabAssetToPrefabHandler.Remove(globalObjectIdHash); } /// /// Check to see if a with a is registered to an implementation /// /// /// true or false internal bool ContainsHandler(GameObject networkPrefab) { return ContainsHandler(networkPrefab.GetComponent().GlobalObjectIdHash); } /// /// Check to see if a is registered to an implementation /// /// /// true or false internal bool ContainsHandler(NetworkObject networkObject) { return ContainsHandler(networkObject.GlobalObjectIdHash); } /// /// Check to see if a is registered to an implementation /// /// /// true or false internal bool ContainsHandler(uint networkPrefabHash) { return m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabHash) || m_PrefabInstanceToPrefabAsset.ContainsKey(networkPrefabHash); } /// /// Returns the source NetworkPrefab's /// /// /// internal uint GetSourceGlobalObjectIdHash(uint networkPrefabHash) { if (m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabHash)) { return networkPrefabHash; } else if (m_PrefabInstanceToPrefabAsset.ContainsKey(networkPrefabHash)) { return m_PrefabInstanceToPrefabAsset[networkPrefabHash]; } return 0; } /// /// Will return back a generated via an implementation /// Note: Invoked only on the client side and called within NetworkSpawnManager.CreateLocalNetworkObject /// /// typically the "server-side" asset's prefab hash /// /// /// /// internal NetworkObject HandleNetworkPrefabSpawn(uint networkPrefabAssetHash, ulong ownerClientId, Vector3 position, Quaternion rotation) { if (m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabAssetHash)) { var networkObjectInstance = m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Instantiate(ownerClientId, position, rotation); //Now we must make sure this alternate PrefabAsset spawned in place of the prefab asset with the networkPrefabAssetHash (GlobalObjectIdHash) //is registered and linked to the networkPrefabAssetHash so during the HandleNetworkPrefabDestroy process we can identify the alternate prefab asset. if (networkObjectInstance != null && !m_PrefabInstanceToPrefabAsset.ContainsKey(networkObjectInstance.GlobalObjectIdHash)) { m_PrefabInstanceToPrefabAsset.Add(networkObjectInstance.GlobalObjectIdHash, networkPrefabAssetHash); } return networkObjectInstance; } return null; } /// /// Will invoke the implementation's Destroy method /// /// internal void HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) { var networkObjectInstanceHash = networkObjectInstance.GlobalObjectIdHash; // Do we have custom overrides registered? if (m_PrefabInstanceToPrefabAsset.ContainsKey(networkObjectInstanceHash)) { var networkPrefabAssetHash = m_PrefabInstanceToPrefabAsset[networkObjectInstanceHash]; if (m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabAssetHash)) { m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Destroy(networkObjectInstance); } } else // Otherwise the NetworkObject is the source NetworkPrefab if (m_PrefabAssetToPrefabHandler.ContainsKey(networkObjectInstanceHash)) { m_PrefabAssetToPrefabHandler[networkObjectInstanceHash].Destroy(networkObjectInstance); } } } }