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);
}
}
}
}