diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs index ef2e79da..060726e9 100644 --- a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs @@ -71,7 +71,6 @@ internal bool ProcessLocal(TypeDefinition typeDef) * as well error if the awake does not exist, such as could not be created. */ typeDefs.Reverse(); - uint declaredSyncTypes = 0; foreach (TypeDefinition td in typeDefs) { /* Create NetworkInitialize before-hand so the other procesors @@ -102,7 +101,7 @@ internal bool ProcessLocal(TypeDefinition typeDef) * each registers to their own delegates this is possible. */ /* SyncTypes. */ - modified |= base.GetClass().ProcessLocal(td, ref declaredSyncTypes); + modified |= base.GetClass().ProcessLocal(td); //Call base networkinitialize early/late. CallBaseOnNetworkInitializeMethods(td); @@ -136,12 +135,6 @@ internal bool ProcessLocal(TypeDefinition typeDef) _processedClasses.Add(td); } - if (declaredSyncTypes > NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE) - { - base.LogError($"Found {declaredSyncTypes} SyncTypes within {typeDef.FullName} and inherited classes. The maximum number of allowed SyncTypes within type and inherited types is {NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE}. Remove SyncTypes or condense them using data containers, or a custom SyncObject."); - return false; - } - /* If here then all inerited classes for firstTypeDef have * been processed. */ //Sets UsesPrediction in NetworkBehaviours. diff --git a/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs index c6a6b52d..c461ee9d 100644 --- a/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs +++ b/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs @@ -46,12 +46,20 @@ public override bool ImportReferences() /// Processes SyncVars and Objects. /// /// Number of SyncTypes implemented in this typeDef and those inherited of. - internal bool ProcessLocal(TypeDefinition typeDef, ref uint syncTypeHash) + internal bool ProcessLocal(TypeDefinition typeDef) { bool modified = false; ValidateVersion3ToVersion4SyncVars(typeDef); + uint startingHash = GetSyncTypeCountInParents(typeDef); + uint totalSyncTypes = (startingHash + GetSyncTypeCount(typeDef)); + if (totalSyncTypes > NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE) + { + base.LogError($"Found {totalSyncTypes} SyncTypes within {typeDef.FullName} and inherited classes. The maximum number of allowed SyncTypes within type and inherited types is {NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE}. Remove SyncTypes or condense them using data containers, or a custom SyncObject."); + return false; + } + FieldDefinition[] fieldDefs = typeDef.Fields.ToArray(); foreach (FieldDefinition fd in fieldDefs) { @@ -79,13 +87,13 @@ internal bool ProcessLocal(TypeDefinition typeDef, ref uint syncTypeHash) bool isGeneric = fd.FieldType.IsGenericInstance; if (isGeneric) { - if (TryCreateGenericSyncType(syncTypeHash, fd, isSyncObject)) - syncTypeHash++; + if (TryCreateGenericSyncType(startingHash, fd, isSyncObject)) + startingHash++; } else { - if (TryCreateNonGenericSyncType(syncTypeHash, fd, isSyncObject)) - syncTypeHash++; + if (TryCreateNonGenericSyncType(startingHash, fd, isSyncObject)) + startingHash++; } modified = true; @@ -112,6 +120,24 @@ internal uint GetSyncTypeCount(TypeDefinition typeDef) return count; } + /// + /// Gets SyncType count in all of typeDefs parents, excluding typeDef itself. + /// + internal uint GetSyncTypeCountInParents(TypeDefinition typeDef) + { + uint count = 0; + while (true) + { + typeDef = typeDef.GetNextBaseClassToProcess(base.Session); + if (typeDef != null) + count += GetSyncTypeCount(typeDef); + else + break; + } + + return count; + } + /// /// Returns if fieldDef is a syncType. /// @@ -177,7 +203,8 @@ private void ValidateVersion3ToVersion4SyncVars(TypeDefinition td) //Ignore constructors. if (methodDef.IsConstructor) continue; - + if (methodDef.IsAbstract) + continue; for (int i = 0; i < methodDef.Body.Instructions.Count; i++) { Instruction inst = methodDef.Body.Instructions[i]; diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs index 969c1b33..7637a7fc 100644 --- a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs @@ -25,7 +25,7 @@ public static void ShowConfiguration() public class DeveloperMenu : MonoBehaviour { #region const. - private const string RELEASE_DEFINE = "FISHNET_RELEASE_MODE"; + private const string STABLE_DEFINE = "FISHNET_STABLE_MODE"; private const string PREDICTIONV2_DEFINE = "PREDICTION_V2"; private const string QOL_ATTRIBUTES_DEFINE = "DISABLE_QOL_ATTRIBUTES"; private const string DEVELOPER_ONLY_WARNING = "If you are not a developer or were not instructed to do this by a developer things are likely to break. You have been warned."; @@ -33,21 +33,21 @@ public class DeveloperMenu : MonoBehaviour #region Release mode. -#if !FISHNET_RELEASE_MODE - [MenuItem("Fish-Networking/Switch to Release Mode", false, -1100)] - private static void SwitchToReleaseMode() +#if !FISHNET_STABLE_MODE + [MenuItem("Fish-Networking/Switch to Stable", false, -1101)] + private static void SwitchToStable() { - bool result = RemoveOrAddDefine(RELEASE_DEFINE, false); + bool result = RemoveOrAddDefine(STABLE_DEFINE, false); if (result) - Debug.LogWarning($"Release mode has been enabled. Please note that experimental features may not function in release mode."); + Debug.LogWarning($"Fish-Networking has been switched to Stable. Please note that experimental features may not function in this mode."); } #else - [MenuItem("Fish-Networking/Switch to Development Mode", false, -1100)] - private static void SwitchToReleaseMode() + [MenuItem("Fish-Networking/Switch to Beta", false, -1100)] + private static void SwitchToBeta() { - bool result = RemoveOrAddDefine(RELEASE_DEFINE, true); + bool result = RemoveOrAddDefine(STABLE_DEFINE, true); if (result) - Debug.LogWarning($"Development mode has been enabled."); + Debug.LogWarning($"Fish-Networking has been switched to Beta."); } #endif #endregion diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs index 3f755aa0..146d4565 100644 --- a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs @@ -231,13 +231,44 @@ public enum ExtrapolateState : byte Available = 1, Active = 2 } + + /// + /// True if default state. This becomes false during an update and true when resetting state. + /// + public bool IsDefault { get; private set; } = true; + /// + /// Tick this data was received or created. + /// public uint Tick; - public bool Snapped; + /// + /// True if this data has already been checked for snapping. + /// Snapping calls may occur multiple times when data is received, depending why or how it came in. + /// This check prevents excessive work. + /// + public bool SnappingChecked; + /// + /// Local position in the data. + /// public Vector3 Position; + /// + /// Local rotation in the data. + /// public Quaternion Rotation; + /// + /// Local scale in the data. + /// public Vector3 Scale; + /// + /// Position to extrapolate towards. + /// public Vector3 ExtrapolatedPosition; + /// + /// Current state of extrapolation. + /// public ExtrapolateState ExtrapolationState; + /// + /// NetworkBehaviour which is the parent of this object for Tick. + /// public NetworkBehaviour ParentBehaviour; public TransformData() { } @@ -247,6 +278,7 @@ internal void Update(TransformData copy) } internal void Update(uint tick, Vector3 position, Quaternion rotation, Vector3 scale, Vector3 extrapolatedPosition, NetworkBehaviour parentBehaviour) { + IsDefault = false; Tick = tick; Position = position; Rotation = rotation; @@ -257,8 +289,9 @@ internal void Update(uint tick, Vector3 position, Quaternion rotation, Vector3 s public void ResetState() { + IsDefault = true; Tick = 0; - Snapped = false; + SnappingChecked = false; Position = Vector3.zero; Rotation = Quaternion.identity; Scale = Vector3.zero; @@ -1763,7 +1796,7 @@ private bool HasChanged(TransformData a, TransformData b, ref ChangedFull change private ChangedDelta GetChanged(TransformData transformData) { //If default return full changed. - if (transformData.Tick == FishNet.Managing.Timing.TimeManager.UNSET_TICK) + if (transformData.IsDefault) return _fullChanged; else /* If parent behaviour exist. @@ -1824,10 +1857,10 @@ private ChangedDelta GetChanged(ref Vector3 lastPosition, ref Quaternion lastRot private void SnapProperties(TransformData transformData, bool force = false) { //Already snapped. - if (transformData.Snapped) + if (transformData.SnappingChecked) return; - transformData.Snapped = true; + transformData.SnappingChecked = true; Transform t = transform; //Position. @@ -2205,10 +2238,10 @@ private void DataReceived(ArraySegment data, Channel channel, bool asServe _lastReceiveReliable = (channel == Channel.Reliable); /* If channel is reliable then this is a settled packet. - * Reset last received tick so next starting move eases - * in. */ + * Set tick to UNSET. When this occurs time calculations + * assume only 1 tick has passed. */ if (channel == Channel.Reliable) - nextTd.Tick = 0; + nextTd.Tick = FishNet.Managing.Timing.TimeManager.UNSET_TICK; prevTd.Update(nextTd); prevRd.Update(nextGd.Rates); diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs index ed4a6b9c..9fbf760f 100644 --- a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs @@ -206,7 +206,7 @@ private void Rigidbodies_OnOwnershipClient(NetworkConnection prevOwner) #if UNITY_2022_1_OR_NEWER _graphicalAnimators[i].keepAnimatorStateOnDisable = true; #else - _graphicalAnimators[i].keepAnimatorStateOnDisable = true; + _graphicalAnimators[i].keepAnimatorControllerStateOnDisable = true; #endif /* True if at least one animator is on the graphical root. diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs index 307104de..281b99d0 100644 --- a/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs @@ -318,10 +318,9 @@ bool PauseRigidbody(int index) /* Move only first rigidbody to save * performance. If rb has a parent then unset * parent as child objects cannot be moved. */ -#if !FISHNET_RELEASE_MODE if (rb.transform.parent != null) rb.transform.SetParent(null); -#endif + SceneManager.MoveGameObjectToScene(rb.transform.gameObject, kinematicScene); return true; @@ -350,10 +349,8 @@ bool PauseRigidbody(int index) rbData.Update(rb); _rigidbody2dDatas[index] = rbData; -#if !FISHNET_RELEASE_MODE if (rb.transform.parent != null) rb.transform.SetParent(null); -#endif SceneManager.MoveGameObjectToScene(rb.transform.gameObject, kinematicScene); return true; @@ -393,7 +390,6 @@ bool UnpauseRigidbody(int index) return false; SceneManager.MoveGameObjectToScene(rb.transform.gameObject, rbData.SimulatedScene); -#if !FISHNET_RELEASE_MODE /* If was moved while having a parent * then set back to the same parent. If the parent does * not exist then the object must have been destroyed. @@ -406,7 +402,6 @@ bool UnpauseRigidbody(int index) else MonoBehaviour.Destroy(rb.gameObject); } -#endif rb.velocity = rbData.Velocity; rb.angularVelocity = rbData.AngularVelocity; @@ -436,7 +431,6 @@ bool UnpauseRigidbody(int index) return false; SceneManager.MoveGameObjectToScene(rb.transform.gameObject, rbData.SimulatedScene); -#if !FISHNET_RELEASE_MODE if (rbData.HasParent) { Transform par = rbData.Parent; @@ -445,7 +439,6 @@ bool UnpauseRigidbody(int index) else MonoBehaviour.Destroy(rb.gameObject); } -#endif rb.velocity = rbData.Velocity; rb.angularVelocity = rbData.AngularVelocity; diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs index bea3e52b..f6a10541 100644 --- a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs @@ -598,7 +598,8 @@ private void ParseAuthenticated(PooledReader reader) * This still doesn't account for latency but * it's the best we can do until the client gets * a ping response. */ - networkManager.TimeManager.Tick = networkManager.TimeManager.LastPacketTick; + if (!networkManager.IsServerStarted) + networkManager.TimeManager.Tick = networkManager.TimeManager.LastPacketTick; //Mark as authenticated. Connection.ConnectionAuthenticated(); diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs index 2ecc758a..d93f34ed 100644 --- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs @@ -724,10 +724,7 @@ private void ReadSpawnedObject(PooledReader reader, out int? parentObjectId, out } } - //prefabId = (ushort)reader.ReadNetworkObjectId(); - // componentIndex is currently unused - _ = reader.ReadNetworkBehaviourId(out var nobId); - prefabId = (ushort)nobId; + prefabId = (ushort)reader.ReadNetworkObjectId(); } } diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs index 695a9313..9f5a2ae2 100644 --- a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs @@ -94,15 +94,10 @@ internal void SendBufferedRpcs(NetworkConnection conn) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void RegisterServerRpc(uint hash, ServerRpcDelegate del) { - if (_serverRpcDelegates.TryGetValueIL2CPP(hash, out ServerRpcDelegate currentDelegate)) - { - FishNet.Managing.NetworkManager.StaticLogError($"ServerRpc hash {hash} registered multiple times. First registration by {currentDelegate.Method.DeclaringType.GetType().FullName}. New registration by {GetType().FullName}."); - } - else - { - _serverRpcDelegates[hash] = del; + if (_serverRpcDelegates.TryAdd(hash, del)) IncreaseRpcMethodCount(); - } + else + FishNet.Managing.NetworkManager.StaticLogError($"ServerRpc key {hash} has already been added for {GetType().FullName} on {gameObject.name}"); } /// /// Registers a RPC method. @@ -114,15 +109,10 @@ internal void RegisterServerRpc(uint hash, ServerRpcDelegate del) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void RegisterObserversRpc(uint hash, ClientRpcDelegate del) { - if (_observersRpcDelegates.TryGetValueIL2CPP(hash, out ClientRpcDelegate currentDelegate)) - { - FishNet.Managing.NetworkManager.StaticLogError($"ObserverRpc hash {hash} registered multiple times. First registration by {currentDelegate.Method.DeclaringType.GetType().FullName}. New registration by {GetType().FullName}."); - } - else - { - _observersRpcDelegates[hash] = del; + if (_observersRpcDelegates.TryAdd(hash, del)) IncreaseRpcMethodCount(); - } + else + FishNet.Managing.NetworkManager.StaticLogError($"ObserversRpc key {hash} has already been added for {GetType().FullName} on {gameObject.name}"); } /// /// Registers a RPC method. @@ -134,15 +124,10 @@ internal void RegisterObserversRpc(uint hash, ClientRpcDelegate del) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void RegisterTargetRpc(uint hash, ClientRpcDelegate del) { - if (_targetRpcDelegates.TryGetValueIL2CPP(hash, out ClientRpcDelegate currentDelegate)) - { - FishNet.Managing.NetworkManager.StaticLogError($"TargetRpc hash {hash} registered multiple times. First registration by {currentDelegate.Method.DeclaringType.GetType().FullName}. New registration by {GetType().FullName}."); - } + if (_targetRpcDelegates.TryAdd(hash, del)) + IncreaseRpcMethodCount(); else - { - _targetRpcDelegates[hash] = del; - IncreaseRpcMethodCount(); - } + FishNet.Managing.NetworkManager.StaticLogError($"TargetRpc key {hash} has already been added for {GetType().FullName} on {gameObject.name}"); } /// diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs index 1c401e35..b20130f1 100644 --- a/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.SyncTypes.cs @@ -82,7 +82,8 @@ public void Reset() /// internal void RegisterSyncType(SyncBase sb, uint index) { - _syncTypes.Add(index, sb); + if (!_syncTypes.TryAdd(index, sb)) + FishNet.Managing.NetworkManager.StaticLogError($"SyncType key {index} has already been added for {GetType().FullName} on {gameObject.name}"); } /// /// Sets a SyncVar as dirty. diff --git a/Assets/FishNet/VERSION.txt b/Assets/FishNet/VERSION.txt index 7d666cb2..8b2dd6c3 100644 --- a/Assets/FishNet/VERSION.txt +++ b/Assets/FishNet/VERSION.txt @@ -1 +1 @@ -4.0.4 \ No newline at end of file +4.0.5 \ No newline at end of file diff --git a/Assets/FishNet/package.json b/Assets/FishNet/package.json index abb901bc..266bc72a 100644 --- a/Assets/FishNet/package.json +++ b/Assets/FishNet/package.json @@ -1,6 +1,6 @@ { "name": "com.firstgeargames.fishnet", - "version": "4.0.4", + "version": "4.0.5", "displayName": "FishNet: Networking Evolved", "description": "A feature-rich Unity networking solution aimed towards reliability, ease of use, efficiency, and flexibility.", "unity": "2021.3",