using System; using UnityEngine; namespace Unity.Netcode { /// /// The method of NetworkPrefab override used to identify the source prefab /// public enum NetworkPrefabOverride { /// /// No oeverride is present /// None, /// /// Override the prefab when the given SourcePrefabToOverride is requested /// Prefab, /// /// Override the prefab when the given SourceHashToOverride is requested /// Used in situations where the server assets do not exist in client builds /// Hash } /// /// Class that represents a NetworkPrefab /// [Serializable] public class NetworkPrefab { /// /// The override settings for this NetworkPrefab /// public NetworkPrefabOverride Override; /// /// Asset reference of the network prefab /// public GameObject Prefab; /// /// Used when prefab is selected for the source prefab to override value (i.e. direct reference, the prefab is within the same project) /// We keep a separate value as the user might want to have something different than the default Prefab for the SourcePrefabToOverride /// public GameObject SourcePrefabToOverride; /// /// Used when hash is selected for the source prefab to override value (i.e. a direct reference is not possible such as in a multi-project pattern) /// public uint SourceHashToOverride; /// /// The prefab to replace (override) the source prefab with /// public GameObject OverridingTargetPrefab; public bool Equals(NetworkPrefab other) { return Override == other.Override && Prefab == other.Prefab && SourcePrefabToOverride == other.SourcePrefabToOverride && SourceHashToOverride == other.SourceHashToOverride && OverridingTargetPrefab == other.OverridingTargetPrefab; } public uint SourcePrefabGlobalObjectIdHash { get { switch (Override) { case NetworkPrefabOverride.None: if (Prefab != null && Prefab.TryGetComponent(out NetworkObject no)) { return no.GlobalObjectIdHash; } throw new InvalidOperationException("Prefab field isn't set or isn't a Network Object"); case NetworkPrefabOverride.Prefab: if (SourcePrefabToOverride != null && SourcePrefabToOverride.TryGetComponent(out no)) { return no.GlobalObjectIdHash; } throw new InvalidOperationException("Source Prefab field isn't set or isn't a Network Object"); case NetworkPrefabOverride.Hash: return SourceHashToOverride; default: throw new ArgumentOutOfRangeException(); } } } public uint TargetPrefabGlobalObjectIdHash { get { switch (Override) { case NetworkPrefabOverride.None: return 0; case NetworkPrefabOverride.Prefab: case NetworkPrefabOverride.Hash: if (OverridingTargetPrefab != null && OverridingTargetPrefab.TryGetComponent(out NetworkObject no)) { return no.GlobalObjectIdHash; } throw new InvalidOperationException("Target Prefab field isn't set or isn't a Network Object"); default: throw new ArgumentOutOfRangeException(); } } } public bool Validate(int index = -1) { NetworkObject networkObject; if (Override == NetworkPrefabOverride.None) { if (Prefab == null) { NetworkLog.LogWarning($"{nameof(NetworkPrefab)} cannot be null ({nameof(NetworkPrefab)} at index: {index})"); return false; } networkObject = Prefab.GetComponent(); if (networkObject == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning($"{NetworkManager.PrefabDebugHelper(this)} is missing " + $"a {nameof(NetworkObject)} component (entry will be ignored)."); } return false; } return true; } // Validate source prefab override values first switch (Override) { case NetworkPrefabOverride.Hash: { if (SourceHashToOverride == 0) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {nameof(SourceHashToOverride)} is zero " + "(entry will be ignored)."); } return false; } break; } case NetworkPrefabOverride.Prefab: { if (SourcePrefabToOverride == null) { // This is a leftover side-effect from NetworkManager's OnValidate. It's a usability // adjustment to automatically set the "Prefab" field as the source prefab when a user // swaps from the default Inspector to the override one. if (Prefab != null) { SourcePrefabToOverride = Prefab; } else if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {nameof(SourcePrefabToOverride)} is null (entry will be ignored)."); return false; } } if (!SourcePrefabToOverride.TryGetComponent(out networkObject)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning($"{nameof(NetworkPrefab)} ({SourcePrefabToOverride.name}) " + $"is missing a {nameof(NetworkObject)} component (entry will be ignored)."); } return false; } break; } } // Validate target prefab override values next if (OverridingTargetPrefab == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {nameof(OverridingTargetPrefab)} is null!"); } switch (Override) { case NetworkPrefabOverride.Hash: { Debug.LogWarning($"{nameof(NetworkPrefab)} override entry {SourceHashToOverride} will be removed and ignored."); break; } case NetworkPrefabOverride.Prefab: { Debug.LogWarning($"{nameof(NetworkPrefab)} override entry ({SourcePrefabToOverride.name}) will be removed and ignored."); break; } } return false; } return true; } public override string ToString() { return $"{{SourceHash: {SourceHashToOverride}, TargetHash: {TargetPrefabGlobalObjectIdHash}}}"; } } }