From 600063fcb88b328cfc4783167d2796269e7aee8d Mon Sep 17 00:00:00 2001 From: FirstGearGames Date: Fri, 23 Feb 2024 18:53:42 -0500 Subject: [PATCH] 4.1.2 - Improved missing Newtsonsoft dependency for Edgegap will warn less frequently now. - Changed revert ReadPermission values due to some experiencing a Unity serialization bug. - Improved client and server now check for duplicate sceneIds at runtime when a scene is loaded. - Fixed a critical TimeManager error where timing updates were not sending to clients. - Fixed regression where server would kick client for sending an invalid LOD while client owned multiple objects. - Added ColliderExtensions utility. - Fixed detection accuracy on NetworkCollision/Trigger components. --- .../Models/SDK/ApiModelContainercrashdata.cs | 126 +++++------ .../Edgegap/Newtonsoft_Package_Patch.cs | 38 +++- .../Editor/Configuring/ConfigurationEditor.cs | 10 +- .../Prediction/PredictedObject.Rigidbodies.cs | 2 +- .../Component/Utility/NetworkCollider.cs | 14 +- .../Managing/Client/Object/ClientObjects.cs | 4 +- .../Managing/Server/Object/ServerObjects.cs | 2 +- .../Runtime/Managing/Timing/TimeManager.cs | 1 - .../Object/Prediction/PredictonRigidbody.cs | 202 ++++++++++++++++++ .../Prediction/PredictonRigidbody.cs.meta | 11 + .../Object/Synchronizing/ReadPermissions.cs | 8 +- .../Dependencies/Utilities/Colliders.cs | 121 +++++++++++ .../Dependencies/Utilities/Colliders.cs.meta | 11 + .../Runtime/Utility/Extension/Scenes.cs | 41 +++- Assets/FishNet/VERSION.txt | 2 +- Assets/FishNet/package.json | 2 +- 16 files changed, 501 insertions(+), 94 deletions(-) create mode 100644 Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta diff --git a/Assets/FishNet/Plugins/Edgegap/Models/SDK/ApiModelContainercrashdata.cs b/Assets/FishNet/Plugins/Edgegap/Models/SDK/ApiModelContainercrashdata.cs index 172e8ad7..4eb54980 100644 --- a/Assets/FishNet/Plugins/Edgegap/Models/SDK/ApiModelContainercrashdata.cs +++ b/Assets/FishNet/Plugins/Edgegap/Models/SDK/ApiModelContainercrashdata.cs @@ -1,63 +1,63 @@ -using System; -using System.Text; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; - -namespace IO.Swagger.Model { - - /// - /// - /// - [DataContract] - public class ApiModelContainercrashdata { - /// - /// Auto Generated Field for exit_code - /// - /// Auto Generated Field for exit_code - [DataMember(Name="exit_code", EmitDefaultValue=false)] - [JsonProperty(PropertyName = "exit_code")] - public int? ExitCode { get; set; } - - /// - /// Auto Generated Field for message - /// - /// Auto Generated Field for message - [DataMember(Name="message", EmitDefaultValue=false)] - [JsonProperty(PropertyName = "message")] - public string Message { get; set; } - - /// - /// Auto Generated Field for restart_count - /// - /// Auto Generated Field for restart_count - [DataMember(Name="restart_count", EmitDefaultValue=false)] - [JsonProperty(PropertyName = "restart_count")] - public int? RestartCount { get; set; } - - - /// - /// Get the string presentation of the object - /// - /// String presentation of the object - public override string ToString() { - StringBuilder sb = new StringBuilder(); - sb.Append("class ApiModelContainercrashdata {\n"); - sb.Append(" ExitCode: ").Append(ExitCode).Append("\n"); - sb.Append(" Message: ").Append(Message).Append("\n"); - sb.Append(" RestartCount: ").Append(RestartCount).Append("\n"); - sb.Append("}\n"); - return sb.ToString(); - } - - /// - /// Get the JSON string presentation of the object - /// - /// JSON string presentation of the object - public string ToJson() { - return JsonConvert.SerializeObject(this, Formatting.Indented); - } - -} -} +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Newtonsoft.Json; + +namespace IO.Swagger.Model { + + /// + /// + /// + [DataContract] + public class ApiModelContainercrashdata { + /// + /// Auto Generated Field for exit_code + /// + /// Auto Generated Field for exit_code + [DataMember(Name="exit_code", EmitDefaultValue=false)] + [JsonProperty(PropertyName = "exit_code")] + public int? ExitCode { get; set; } + + /// + /// Auto Generated Field for message + /// + /// Auto Generated Field for message + [DataMember(Name="message", EmitDefaultValue=false)] + [JsonProperty(PropertyName = "message")] + public string Message { get; set; } + + /// + /// Auto Generated Field for restart_count + /// + /// Auto Generated Field for restart_count + [DataMember(Name="restart_count", EmitDefaultValue=false)] + [JsonProperty(PropertyName = "restart_count")] + public int? RestartCount { get; set; } + + + /// + /// Get the string presentation of the object + /// + /// String presentation of the object + public override string ToString() { + StringBuilder sb = new StringBuilder(); + sb.Append("class ApiModelContainercrashdata {\n"); + sb.Append(" ExitCode: ").Append(ExitCode).Append("\n"); + sb.Append(" Message: ").Append(Message).Append("\n"); + sb.Append(" RestartCount: ").Append(RestartCount).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Get the JSON string presentation of the object + /// + /// JSON string presentation of the object + public string ToJson() { + return JsonConvert.SerializeObject(this, Formatting.Indented); + } + +} +} diff --git a/Assets/FishNet/Plugins/Edgegap/Newtonsoft_Package_Patch.cs b/Assets/FishNet/Plugins/Edgegap/Newtonsoft_Package_Patch.cs index 09e8832d..209b106e 100644 --- a/Assets/FishNet/Plugins/Edgegap/Newtonsoft_Package_Patch.cs +++ b/Assets/FishNet/Plugins/Edgegap/Newtonsoft_Package_Patch.cs @@ -3,17 +3,47 @@ #if UNITY_EDITOR using UnityEditor; - + namespace Newtonsoft.Json -{ +{ public static class FallbackDisplayer { + private const string WARN_TIME_NAME = "EdgegapWarnTime"; + + internal static void ResetWarnTime() + { + EditorPrefs.SetString(WARN_TIME_NAME, DateTime.Now.ToBinary().ToString()); + } [InitializeOnLoadMethod] private static void Initialize() - { - UnityEngine.Debug.LogWarning($"Edgegap requires Json.NET to be imported to function. To import Json.NET navigate to Window -> Package Manager -> Click the + symbol and choose 'Add package by name' -> com.unity.nuget.newtonsoft-json -> Leave version blank and click Add."); + { + string dtStr = EditorPrefs.GetString(WARN_TIME_NAME, string.Empty); + //Somehow got cleared. Reset. + if (string.IsNullOrWhiteSpace(dtStr)) + { + ResetWarnTime(); + } + else + { + long binary; + //Failed to parse. + if (!long.TryParse(dtStr, out binary)) + { + ResetWarnTime(); + } + else + { + //Not enough time passed. + DateTime dt = DateTime.FromBinary(binary); + if ((DateTime.Now - dt).TotalMinutes < 30) + return; + } + + } + + UnityEngine.Debug.LogWarning($"Edgegap requires Json.NET to be imported to function. To import Json.NET navigate to Window -> Package Manager -> Click the + symbol and choose 'Add package by name' -> com.unity.nuget.newtonsoft-json -> Leave version blank and click Add. If you are not currently using Edgegap you may ignore this message."); } } } diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs index 6be355ea..040c4a62 100644 --- a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs @@ -140,6 +140,12 @@ public static void RebuildSceneIds() return; } #endif + if (ApplicationState.IsPlaying()) + { + Debug.Log($"SceneIds cannot be rebuilt while in play mode."); + return; + } + int generatedCount = 0; int processedScenes = 0; for (int i = 0; i < SceneManager.sceneCount; i++) @@ -153,7 +159,7 @@ public static void RebuildSceneIds() processedScenes++; List nobs = CollectionCaches.RetrieveList(); - Scenes.GetSceneNetworkObjects(s, false, ref nobs); + Scenes.GetSceneNetworkObjects(s, false, false, ref nobs); int nobCount = nobs.Count; for (int z = 0; z < nobCount; z++) { @@ -225,7 +231,7 @@ public static void RemoveDuplicateNetworkObjects() Scene s = SceneManager.GetSceneAt(i); List nobs = CollectionCaches.RetrieveList(); - Scenes.GetSceneNetworkObjects(s, false, ref nobs); + Scenes.GetSceneNetworkObjects(s, false, false, ref nobs); int nobsCount = nobs.Count; for (int z = 0; z < nobsCount; z++) { diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs index 2f49e2a9..2926ab00 100644 --- a/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/PredictedObject.Rigidbodies.cs @@ -203,7 +203,7 @@ private void Rigidbodies_OnOwnershipClient(NetworkConnection prevOwner) if (_graphicalAnimators.Length > 0) { for (int i = 0; i < _graphicalAnimators.Length; i++) - _graphicalAnimators[i].keepAnimatorControllerStateOnDisable = true; + _graphicalAnimators[i].keepAnimatorStateOnDisable = true; /* True if at least one animator is on the graphical root. * Unity gets components in order so it's safe to assume diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/NetworkCollider.cs b/Assets/FishNet/Runtime/Generated/Component/Utility/NetworkCollider.cs index 07a927d6..2fa55949 100644 --- a/Assets/FishNet/Runtime/Generated/Component/Utility/NetworkCollider.cs +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/NetworkCollider.cs @@ -408,8 +408,8 @@ int GetHistoryIndex(uint lTick, bool getClosest) /// Number of colliders hit. private int GetSphereColliderHits(SphereCollider sphereCollider, int layerMask) { - float scaledRadius = (sphereCollider.radius * transform.lossyScale.magnitude); - return Physics.OverlapSphereNonAlloc(sphereCollider.bounds.center, scaledRadius, _hits, layerMask); + sphereCollider.GetSphereCastParams(out Vector3 center, out float radius); + return Physics.OverlapSphereNonAlloc(center, radius, _hits, layerMask); } /// @@ -441,10 +441,8 @@ private int GetCapsuleColliderHits(CapsuleCollider capsuleCollider, int layerMas return 0; } - Vector3 start = (center + offset); - Vector3 end = (center - offset); - float scaledRadius = (capsuleCollider.radius * transform.lossyScale.magnitude); - return Physics.OverlapCapsuleNonAlloc(start, end, scaledRadius, _hits, layerMask); + capsuleCollider.GetCapsuleCastParams(out Vector3 start, out Vector3 end, out float radius); + return Physics.OverlapCapsuleNonAlloc(start, end, radius, _hits, layerMask); } /// @@ -453,8 +451,8 @@ private int GetCapsuleColliderHits(CapsuleCollider capsuleCollider, int layerMas /// Number of colliders hit. private int GetBoxColliderHits(BoxCollider boxCollider, Quaternion rotation, int layerMask) { - Bounds bounds = boxCollider.bounds; - return Physics.OverlapBoxNonAlloc(bounds.center, bounds.extents, _hits, rotation, layerMask); + boxCollider.GetBoxCastParams(out Vector3 center, out Vector3 halfExtents); + return Physics.OverlapBoxNonAlloc(center, halfExtents, _hits, rotation, layerMask); } /// diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs index 4195622c..be20b588 100644 --- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs @@ -93,7 +93,7 @@ internal void OnClientConnectionState(ClientConnectionStateArgs args) else { foreach (NetworkObject n in Spawned.Values) - { + { n.InvokeStopCallbacks(false); n.SetInitializedStatus(false, false); } @@ -263,7 +263,7 @@ internal void RegisterAndDespawnSceneObjects() private void RegisterAndDespawnSceneObjects(Scene s) { List nobs = CollectionCaches.RetrieveList(); - Scenes.GetSceneNetworkObjects(s, false, ref nobs); + Scenes.GetSceneNetworkObjects(s, false, true, ref nobs); int nobsCount = nobs.Count; for (int i = 0; i < nobsCount; i++) diff --git a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs index 3f304544..787c6d70 100644 --- a/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs @@ -373,7 +373,7 @@ private void SetupSceneObjects(Scene s) return; List sceneNobs = CollectionCaches.RetrieveList(); - Scenes.GetSceneNetworkObjects(s, false, ref sceneNobs); + Scenes.GetSceneNetworkObjects(s, false, true, ref sceneNobs); //Sort the nobs based on initialization order. bool initializationOrderChanged = false; diff --git a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs index 4d231a75..64352f90 100644 --- a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs +++ b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs @@ -1073,7 +1073,6 @@ private void TryIterateData(bool incoming) /// private void SendTimingAdjustment() { - return; //Send every second. if (LocalTick % _tickRate == 0) { diff --git a/Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs b/Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs new file mode 100644 index 00000000..118375a0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs @@ -0,0 +1,202 @@ +using FishNet.CodeGenerating; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Object.Prediction + +#if PREDICTION_V2 +{ + public static class PredictionRigidbodySerializers + { + //public static void WritePredictionRigidbody(this Writer w, PredictionRigidbody pr) + //{ + // Debug.Log("Writing "); + // w.WriteList(pr.PendingForces); + //} + + //public static PredictionRigidbody ReadPredictionRigidbody(this Reader r) + //{ + // List lst = CollectionCaches.RetrieveList(); + // r.ReadList(ref lst); + // PredictionRigidbody pr = ResettableObjectCaches.Retrieve(); + // pr.PendingForces = lst; + // Debug.Log($"{lst == null}, {pr.PendingForces == null}"); + // return pr; + //} + } + + public class PredictionRigidbody : IResettable + { + #region Types. + internal struct ForceData + { + public Vector3 Force; + public ForceMode Mode; + public bool IsVelocity; + + public ForceData(ForceData fd) + { + Force = fd.Force; + Mode = fd.Mode; + IsVelocity = fd.IsVelocity; + } + public ForceData(Vector3 force, ForceMode mode, bool velocity) + { + Force = force; + Mode = mode; + IsVelocity = velocity; + } + } + #endregion + + #region Public. + /// + /// Rigidbody which force is applied. + /// + public Rigidbody Rigidbody { get; private set; } + #endregion + + #region Internal. + /// + /// Forces waiting to be applied. + /// + [ExcludeSerialization] + internal List PendingForces; + #endregion + + ~PredictionRigidbody() + { + if (PendingForces != null) + CollectionCaches.StoreAndDefault(ref PendingForces); + Rigidbody = null; + } + + /// + /// Rigidbody which force is applied. + /// + /// + public void Initialize(Rigidbody rb) + { + Debug.LogError($"This utility is a work in progress. Please do not use it at this time."); + Rigidbody = rb; + if (PendingForces == null) + PendingForces = CollectionCaches.RetrieveList(); + else + PendingForces.Clear(); + } + + /// + /// Adds Velocity force to the Rigidbody. + /// + public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force) + { + PendingForces.Add(new ForceData(force, mode, true)); + } + + public void AddAngularForce(Vector3 force, ForceMode mode = ForceMode.Force) + { + PendingForces.Add(new ForceData(force, mode, false)); + } + + /// + /// Sets velocity while clearing pending forces. + /// Simulate should still be called normally. + /// + public void Velocity(Vector3 force) + { + Rigidbody.velocity = force; + RemoveForces(true); + } + + /// + /// Sets angularVelocity while clearning pending forces. + /// Simulate should still be called normally. + /// + public void AngularVelocity(Vector3 force) + { + Rigidbody.angularVelocity = force; + RemoveForces(false); + } + + /// + /// Applies pending forces to rigidbody in the order they were added. + /// + public void Simulate() + { + foreach (ForceData item in PendingForces) + { + if (item.IsVelocity) + Rigidbody.AddForce(item.Force, item.Mode); + else + Rigidbody.AddTorque(item.Force, item.Mode); + } + PendingForces.Clear(); + } + + /// + /// Manually clears pending forces. + /// + /// True to clear velocities, false to clear angular velocities. + public void ClearPendingForces(bool velocity) + { + RemoveForces(velocity); + } + /// + /// Clears pending velocity and angular velocity forces. + /// + public void ClearPendingForces() + { + PendingForces.Clear(); + } + + /// + /// Reconciles to a state. + /// + public void Reconcile(PredictionRigidbody pr) + { + PendingForces.Clear(); + foreach (ForceData item in pr.PendingForces) + PendingForces.Add(new ForceData(item)); + + ResettableObjectCaches.Store(pr); + } + + /// + /// Removes forces from pendingForces. + /// + /// True to remove if velocity. + private void RemoveForces(bool velocity) + { + if (PendingForces.Count > 0) + { + List newDatas = CollectionCaches.RetrieveList(); + foreach (ForceData item in PendingForces) + { + if (item.IsVelocity != velocity) + newDatas.Add(item); + } + //Add back to _pendingForces if changed. + if (newDatas.Count != PendingForces.Count) + { + PendingForces.Clear(); + foreach (ForceData item in newDatas) + PendingForces.Add(item); + } + CollectionCaches.Store(newDatas); + } + } + + public void ResetState() + { + CollectionCaches.StoreAndDefault(ref PendingForces); + Rigidbody = null; + } + + public void InitializeState() { } + } +#endif + +} + diff --git a/Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs.meta new file mode 100644 index 00000000..30941e8a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/PredictonRigidbody.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7c964c0b90f389c4899a8120cc19d8b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs index 056497c5..4c7858fa 100644 --- a/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs @@ -2,20 +2,20 @@ namespace FishNet.Object.Synchronizing { /// /// Which clients may receive synchronization updates. - /// + /// //Remove on V5. Just rename file to ReadPermission.cs, do not remove. public enum ReadPermission : byte { /// /// All observers will receive updates. /// - Observers = 1, + Observers = 0, /// /// Only owner will receive updates. /// - OwnerOnly = 2, + OwnerOnly = 1, /// /// Send to all observers except owner. /// - ExcludeOwner = 3, + ExcludeOwner = 2, } } diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs new file mode 100644 index 00000000..2df810f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs @@ -0,0 +1,121 @@ +using System; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class ColliderExtensions + { + public static void GetBoxCastParams(this BoxCollider boxCollider, out Vector3 center, out Vector3 halfExtents) + { + Transform cachedTransform = boxCollider.transform; + + // DO NOT USE UNITY'S VECTOR OPERATIONS IN HOT PATHS, UNITY DOESN'T OPTIMISE THEM + + center = cachedTransform.TransformPoint(boxCollider.center); + + Vector3 lossyScale = cachedTransform.lossyScale; + + Vector3 size = boxCollider.size; + + float x = size.x * 0.5f * lossyScale.x; + float y = size.y * 0.5f * lossyScale.y; + float z = size.z * 0.5f * lossyScale.z; + + halfExtents = new Vector3(x, y, z); + } + + public static void GetCapsuleCastParams(this CapsuleCollider capsuleCollider, out Vector3 point1, out Vector3 point2, out float radius) + { + Transform cachedTransform = capsuleCollider.transform; + + Vector3 lossyScale = cachedTransform.lossyScale; + + // Use System.Math instead of UnityEngine.Mathf because it's much faster. + + float absX = Math.Abs(lossyScale.x); + float absY = Math.Abs(lossyScale.y); + float absZ = Math.Abs(lossyScale.z); + + float height; + + Vector3 direction; + + switch (capsuleCollider.direction) + { + case 1: + { + radius = capsuleCollider.radius * Math.Max(absX, absZ); + + height = capsuleCollider.height * absY; + + direction = Vector3.up; + + break; + } + + case 2: + { + radius = capsuleCollider.radius * Math.Max(absX, absY); + + height = capsuleCollider.height * absZ; + + direction = Vector3.forward; + + break; + } + + default: + { + // Falling back to X is Unity's default behaviour. + + radius = capsuleCollider.radius * Math.Max(absY, absZ); + + height = capsuleCollider.height * absX; + + direction = Vector3.right; + + break; + } + } + + Vector3 center = cachedTransform.TransformPoint(capsuleCollider.center); + + Vector3 offset = height < radius * 2.0f ? Vector3.zero : cachedTransform.TransformDirection(direction * (height * 0.5f - radius)); + + // DO NOT USE UNITY'S VECTOR OPERATIONS IN HOT PATHS, UNITY DOESN'T OPTIMISE THEM + + float x1 = center.x + offset.x; + float y1 = center.y + offset.y; + float z1 = center.z + offset.z; + + float x2 = center.x - offset.x; + float y2 = center.y - offset.y; + float z2 = center.z - offset.z; + + point1 = new Vector3(x1, y1, z1); + + point2 = new Vector3(x2, y2, z2); + } + + public static void GetSphereCastParams(this SphereCollider sphereCollider, out Vector3 center, out float radius) + { + Transform cachedTransform = sphereCollider.transform; + + center = cachedTransform.TransformPoint(sphereCollider.center); + + Vector3 lossyScale = cachedTransform.lossyScale; + + // Use System.Math instead of UnityEngine.Mathf because it's much faster. + + float x = Math.Abs(lossyScale.x); + float y = Math.Abs(lossyScale.y); + float z = Math.Abs(lossyScale.z); + + // Two calls of Math.Max are faster than a single Mathf.Max call because Math.Max doesn't allocate memory and doesn't use loops. + + radius = sphereCollider.radius * Math.Max(Math.Max(x, y), z); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta new file mode 100644 index 00000000..8042e188 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 29e69fa855dd3634d9e66313e7748db4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs b/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs index f8406449..5457e22f 100644 --- a/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs +++ b/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs @@ -1,4 +1,5 @@ -using FishNet.Object; +using FishNet.Managing; +using FishNet.Object; using GameKit.Dependencies.Utilities; using System.Collections.Generic; using UnityEngine; @@ -8,19 +9,20 @@ namespace FishNet.Utility.Extension { public static class Scenes - { + { /// /// Gets all NetworkObjects in a scene. /// /// Scene to get objects in. /// True to only return the first NetworkObject within an object chain. False will return nested NetworkObjects. - /// ListCache of found NetworkObjects. /// - public static void GetSceneNetworkObjects(Scene s, bool firstOnly, ref List result) + public static void GetSceneNetworkObjects(Scene s, bool firstOnly, bool errorOnDuplicates, ref List result) { List nobCacheA = CollectionCaches.RetrieveList(); List nobCacheB = CollectionCaches.RetrieveList(); List gameObjectCache = CollectionCaches.RetrieveList(); + Dictionary sceneIds = CollectionCaches.RetrieveDictionary(); + //Iterate all root objects for the scene. s.GetRootGameObjects(gameObjectCache); foreach (GameObject go in gameObjectCache) @@ -42,18 +44,45 @@ public static void GetSceneNetworkObjects(Scene s, bool firstOnly, ref List(true, nobCacheB); //No extra nobs, only this one. - if (nobCacheB.Count == 1) + if (nobCacheB.Count == 1 && !TryDisplayDuplicateError(nob)) result.Add(nob); } } //Not first only, add them all. else { - result.AddRange(nobCacheA); + foreach (NetworkObject item in nobCacheA) + { + if (!TryDisplayDuplicateError(item)) + result.Add(item); + } } } } + + CollectionCaches.Store(sceneIds); + + bool TryDisplayDuplicateError(NetworkObject nob) + { + if (!errorOnDuplicates) + return false; + + ulong id = nob.SceneId; + //There is a duplicate. + if (sceneIds.TryGetValue(id, out NetworkObject originalNob)) + { + string err = $"Object {nob.name} and {originalNob.name} in scene {nob.gameObject.scene.name} have the same sceneId of {id}. This will result in spawning errors. Exit play mode and use the Fish-Networking menu to rebuild sceneIds for scene {nob.gameObject.scene.name}."; + NetworkManagerExtensions.LogError(err); + return true; + } + else + { + sceneIds[id] = nob; + return false; + } + } + } } diff --git a/Assets/FishNet/VERSION.txt b/Assets/FishNet/VERSION.txt index 2582dddf..cd9b8f55 100644 --- a/Assets/FishNet/VERSION.txt +++ b/Assets/FishNet/VERSION.txt @@ -1 +1 @@ -4.1.1 \ No newline at end of file +4.1.2 \ No newline at end of file diff --git a/Assets/FishNet/package.json b/Assets/FishNet/package.json index 6db418cb..a87a1915 100644 --- a/Assets/FishNet/package.json +++ b/Assets/FishNet/package.json @@ -1,6 +1,6 @@ { "name": "com.firstgeargames.fishnet", - "version": "4.1.1", + "version": "4.1.2", "displayName": "FishNet: Networking Evolved", "description": "A feature-rich Unity networking solution aimed towards reliability, ease of use, efficiency, and flexibility.", "unity": "2021.3",