[Add] FishNet

This commit is contained in:
2026-03-30 20:11:57 +07:00
parent ee793a3361
commit c22c08753a
1797 changed files with 197950 additions and 1 deletions
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aea01893c4a887048868eaa5b37c656a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,79 @@
fileFormatVersion: 2
guid: 1907658b89c1bbe42a0063df40b7ca24
labels:
- RoslynAnalyzer
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll
uploadId: 866910
@@ -0,0 +1,40 @@
fileFormatVersion: 2
guid: 620557a5e202e644cb322b8fcc9422ea
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll
uploadId: 866910
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Abdelfattah-Radwan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 93ef12b9e040fa8429d9ef686212ed4e
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/CodeAnalysis/LICENSE.txt
uploadId: 866910
@@ -0,0 +1,2 @@
Git URL:
https://github.com/Abdelfattah-Radwan/FishNet.CodeAnalysis
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 835336ee0aec7ef41a1cfda40886f443
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/CodeAnalysis/README.txt
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fa7c8a088aea3f04580b62c8e2a388ec
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1 @@
This software uses the license for Fish-Networking: https://github.com/FirstGearGames/FishNet/blob/main/LICENSE.md
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: ff1ecc2a24fd9684a862c4b99cfc2fcd
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 06703c0fac6f21140ab99ef3eeb8a4f7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,55 @@
#if FISHNET_THREADED_COLLIDER_ROLLBACK
using System.Collections.Generic;
using FishNet.Object;
using GameKit.Dependencies.Utilities;
using Unity.Mathematics;
using UnityEngine;
namespace FishNet.Component.ColliderRollback
{
public class ColliderRollback : NetworkBehaviour
{
#region Serialized.
#pragma warning disable CS0414
/// <summary>
/// How to configure the bounding box check.
/// </summary>
[Tooltip("How to configure the bounding box check.")]
[SerializeField]
private RollbackManager.BoundingBoxType _boundingBox = RollbackManager.BoundingBoxType.Disabled;
/// <summary>
/// Physics type to generate a bounding box for.
/// </summary>
[Tooltip("Physics type to generate a bounding box for.")]
[SerializeField]
private RollbackPhysicsType _physicsType = RollbackPhysicsType.Physics;
/// <summary>
/// Size for the bounding box. This is only used when BoundingBox is set to Manual.
/// </summary>
[Tooltip("Size for the bounding box.. This is only used when BoundingBox is set to Manual.")]
[SerializeField]
private Vector3 _boundingBoxSize = new(3f, 3f, 3f);
/// <summary>
/// Center for the bounding box. This is only used when BoundingBox is set to Manual.
/// </summary>
[Tooltip("Center for the bounding box.. This is only used when BoundingBox is set to Manual.")]
[SerializeField]
private Vector3 _boundingBoxCenter = new(0f, 0f, 0f);
/// <summary>
/// Local Rotation for the bounding box. This is only used when BoundingBox is set to Manual.
/// </summary>
[Tooltip("Center for the bounding box.. This is only used when BoundingBox is set to Manual.")]
[SerializeField]
private Quaternion _boundingBoxLocalRotation = Quaternion.identity;
/// <summary>
/// Objects holding colliders which can rollback.
/// </summary>
[Tooltip("Objects holding colliders which can rollback.")]
[SerializeField]
private GameObject[] _colliderParents = new GameObject[0];
#pragma warning restore CS0414
#endregion
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e8e604e0a2709484193cda1a6d03b5b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Threaded.cs
uploadId: 866910
@@ -0,0 +1,25 @@
#if !FISHNET_THREADED_COLLIDER_ROLLBACK
using System;
using System.Runtime.CompilerServices;
using GameKit.Dependencies.Utilities;
using UnityEngine;
namespace FishNet.Component.ColliderRollback
{
public partial class ColliderRollback
{
internal enum BoundingBoxType
{
/// <summary>
/// Disable this feature.
/// </summary>
Disabled,
/// <summary>
/// Manually specify the dimensions of a bounding box.
/// </summary>
Manual
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 83b62057280ff8e4faf4a7344b9f3bda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Types.cs
uploadId: 866910
@@ -0,0 +1,43 @@
#if !FISHNET_THREADED_COLLIDER_ROLLBACK
using System.Collections.Generic;
using FishNet.Managing;
using FishNet.Object;
using GameKit.Dependencies.Utilities;
using UnityEngine;
namespace FishNet.Component.ColliderRollback
{
public partial class ColliderRollback : NetworkBehaviour
{
#region Serialized.
#pragma warning disable CS0414
/// <summary>
/// How to configure the bounding box check.
/// </summary>
[Tooltip("How to configure the bounding box check.")]
[SerializeField]
private BoundingBoxType _boundingBox = BoundingBoxType.Disabled;
/// <summary>
/// Physics type to generate a bounding box for.
/// </summary>
[Tooltip("Physics type to generate a bounding box for.")]
[SerializeField]
private RollbackPhysicsType _physicsType = RollbackPhysicsType.Physics;
/// <summary>
/// Size for the bounding box. This is only used when BoundingBox is set to Manual.
/// </summary>
[Tooltip("Size for the bounding box.. This is only used when BoundingBox is set to Manual.")]
[SerializeField]
private Vector3 _boundingBoxSize = new(3f, 3f, 3f);
/// <summary>
/// Objects holding colliders which can rollback.
/// </summary>
[Tooltip("Objects holding colliders which can rollback.")]
[SerializeField]
private GameObject[] _colliderParents = new GameObject[0];
#pragma warning restore CS0414
#endregion
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 01a271dd97d875347b0cea860df29a9d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs
uploadId: 866910
@@ -0,0 +1,88 @@
#if UNITY_EDITOR && FISHNET_THREADED_COLLIDER_ROLLBACK
using UnityEditor;
using UnityEngine;
using static FishNet.Component.ColliderRollback.ColliderRollback;
namespace FishNet.Component.ColliderRollback
{
[CustomEditor(typeof(ColliderRollback), true)]
[CanEditMultipleObjects]
public class ColliderRollbackEditor : Editor
{
private SerializedProperty _boundingBox;
private SerializedProperty _physicsType;
private SerializedProperty _boundingBoxSize;
private SerializedProperty _boundingBoxCenter;
private SerializedProperty _boundingBoxLocalRotation;
private SerializedProperty _colliderParents;
protected virtual void OnEnable()
{
_boundingBox = serializedObject.FindProperty(nameof(_boundingBox));
_physicsType = serializedObject.FindProperty(nameof(_physicsType));
_boundingBoxSize = serializedObject.FindProperty(nameof(_boundingBoxSize));
_boundingBoxCenter = serializedObject.FindProperty(nameof(_boundingBoxCenter));
_boundingBoxLocalRotation = serializedObject.FindProperty(nameof(_boundingBoxLocalRotation));
_colliderParents = serializedObject.FindProperty(nameof(_colliderParents));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
ColliderRollback nob = (ColliderRollback)target;
GUI.enabled = false;
EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(nob), typeof(ColliderRollback), false);
GUI.enabled = true;
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_boundingBox);
if ((RollbackManager.BoundingBoxType)_boundingBox.intValue != RollbackManager.BoundingBoxType.Disabled)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_physicsType);
EditorGUILayout.PropertyField(_boundingBoxSize);
EditorGUILayout.PropertyField(_boundingBoxCenter);
EditorGUILayout.PropertyField(_boundingBoxLocalRotation);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_colliderParents);
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
}
private void OnSceneGUI()
{
ColliderRollback cr = (ColliderRollback)target;
RollbackManager.BoundingBoxData bb = cr.GetBoundingBoxData();
if (bb.boundingBoxType == RollbackManager.BoundingBoxType.Disabled)
return;
Transform tr = cr.transform;
Vector3 centerWS = tr.TransformPoint(bb.center);
Quaternion rotWS = tr.rotation * bb.localRotation;
Vector3 lossy = tr.lossyScale;
Vector3 absScale = new Vector3(Mathf.Abs(lossy.x), Mathf.Abs(lossy.y), Mathf.Abs(lossy.z));
Vector3 sizeWS = Vector3.Scale((bb.extends * 2f), absScale);
Matrix4x4 prevMatrix = Handles.matrix;
Color prevColor = Handles.color;
Handles.matrix = Matrix4x4.TRS(centerWS, rotWS, Vector3.one);
Handles.color = Color.green;
Handles.DrawWireCube(Vector3.zero, sizeWS);
Handles.color = new Color(1f, 0.6f, 0f, 1f);
Handles.SphereHandleCap(0, Vector3.zero, Quaternion.identity, 0.03f * Mathf.Max(sizeWS.x, sizeWS.y, sizeWS.z), EventType.Repaint);
Handles.color = prevColor;
Handles.matrix = prevMatrix;
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 519ce5f9fc54f5342a8a4eaf09a947b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.Threaded.cs
uploadId: 866910
@@ -0,0 +1,54 @@
#if UNITY_EDITOR && !FISHNET_THREADED_COLLIDER_ROLLBACK
using UnityEditor;
using UnityEngine;
using static FishNet.Component.ColliderRollback.ColliderRollback;
namespace FishNet.Component.ColliderRollback
{
[CustomEditor(typeof(ColliderRollback), true)]
[CanEditMultipleObjects]
public class ColliderRollbackEditor : Editor
{
private SerializedProperty _boundingBox;
private SerializedProperty _physicsType;
private SerializedProperty _boundingBoxSize;
private SerializedProperty _colliderParents;
protected virtual void OnEnable()
{
_boundingBox = serializedObject.FindProperty(nameof(_boundingBox));
_physicsType = serializedObject.FindProperty(nameof(_physicsType));
_boundingBoxSize = serializedObject.FindProperty(nameof(_boundingBoxSize));
_colliderParents = serializedObject.FindProperty(nameof(_colliderParents));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
ColliderRollback nob = (ColliderRollback)target;
GUI.enabled = false;
EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(nob), typeof(ColliderRollback), false);
GUI.enabled = true;
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_boundingBox);
if ((BoundingBoxType)_boundingBox.intValue != BoundingBoxType.Disabled)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_physicsType);
EditorGUILayout.PropertyField(_boundingBoxSize);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_colliderParents);
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b0fd7d7c980dbbc49b3ab071b1974d33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs
uploadId: 866910
@@ -0,0 +1,51 @@
#if FISHNET_THREADED_COLLIDER_ROLLBACK
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Jobs;
namespace FishNet.Component.ColliderRollback
{
/// <summary>
/// Used to store where colliders are during the snapshot.
/// </summary>
public struct ColliderSnapshot
{
public ColliderSnapshot(Transform t)
{
t.GetPositionAndRotation(out Vector3 pos, out Quaternion rot);
WorldPosition = pos;
WorldRotation = rot;
}
public ColliderSnapshot(TransformAccess ta)
{
ta.GetPositionAndRotation(out Vector3 pos, out Quaternion rot);
WorldPosition = pos;
WorldRotation = rot;
}
public void SetValues(Transform t)
{
t.GetPositionAndRotation(out Vector3 pos, out Quaternion rot);
WorldPosition = pos;
WorldRotation = rot;
}
public void SetValues(TransformAccess ta)
{
ta.GetPositionAndRotation(out Vector3 pos, out Quaternion rot);
WorldPosition = pos;
WorldRotation = rot;
}
/// <summary>
/// WorldPosition of transform during snapshot.
/// </summary>
public float3 WorldPosition;
/// <summary>
/// WorldRotation of transform during snapshot.
/// </summary>
public quaternion WorldRotation;
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7bdaf4b1542a66d4e80646758ccfd525
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderSnapshot.Threaded.cs
uploadId: 866910
@@ -0,0 +1,667 @@
#if FISHNET_THREADED_COLLIDER_ROLLBACK
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using FishNet.Managing;
using Unity.Collections;
using Unity.Jobs;
using Unity.Jobs.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Jobs;
namespace FishNet.Component.ColliderRollback
{
/// <summary>
/// Holds all job-friendly data for collider rollback: flattened transform table,
/// ring buffers of TRS snapshots, and a persistent rolled-back mask.
/// </summary>
public sealed partial class RollbackCollection
{
#region Private.
/// <summary>
/// NetworkManager.
/// </summary>
private NetworkManager _networkManager;
/// <summary>
/// True if the collection is configured and has valid buffers.
/// </summary>
private bool _ready;
/// <summary>
/// Ring buffer length per rolling entry.
/// </summary>
private int _maxSnapshots;
/// <summary>
/// Physics used when rolling back.
/// </summary>
private RollbackPhysicsType _rollbackPhysics;
/// <summary>
/// List of ColliderRollback.
/// </summary>
private readonly List<ColliderRollback> _colliderRollbacks = new();
/// <summary>
/// Map of ColliderRollback -> index.
/// </summary>
private readonly Dictionary<ColliderRollback, int> _colliderRollbacksIndices = new();
/// <summary>
/// Write array for Rollback requests for deferred Rollback.
/// </summary>
private NativeList<RollbackManager.RollbackRequest> _writeRequests;
/// <summary>
/// Read array for Rollback requests for deferred Rollback.
/// </summary>
private NativeList<RollbackManager.RollbackRequest> _readRequests;
/// <summary>
/// Physics that requested for deferred rollback.
/// </summary>
private RollbackPhysicsType _requestsRollbackPhysics;
/// <summary>
/// TransformAccessArray for ColliderRollbacks.
/// </summary>
private TransformAccessArray _colliderRollbacksTAA;
/// <summary>
/// TransformAccessArray for RollingColliders.
/// </summary>
private TransformAccessArray _rollingCollidersTAA;
/// <summary>
/// Snapshots of ColliderRollbacks: [colliderRollbackIndex].
/// </summary>
private NativeList<ColliderSnapshot> _colliderRollbacksSnapshots;
/// <summary>
/// Flattened snapshots ring-buffer of RollingColliders: [rollingColliderIndex * MaxSnapshots + frame].
/// </summary>
private NativeList<ColliderSnapshot> _rollingCollidersSnapshots;
/// <summary>
/// ColliderRollbacks-level rolled-back flags (0 = write, 1 = freeze).
/// </summary>
private NativeList<byte> _colliderRollbacksRolledBackMask;
/// <summary>
/// Per-colliderRollbacks scene handle to allow filtering in jobs without masks.
/// </summary>
private NativeList<int> _colliderRollbacksSceneHandles;
/// <summary>
/// Per-colliderRollbacks number of available history frames for lerp (clamped to MaxSnapshots).
/// </summary>
private NativeList<int> _colliderRollbacksLerpFrames;
/// <summary>
/// Per-colliderRollbacks BoundingBoxData.
/// </summary>
private NativeList<RollbackManager.BoundingBoxData> _colliderRollbacksBoundingBoxData;
/// <summary>
/// Maps rolling-collider index to its colliderRollbacks index.
/// </summary>
private NativeList<int> _rollingColliderToColliderRollbacks;
/// <summary>x
/// Per-rolling write pointer (next frame slot in the ring).
/// </summary>
private NativeList<int> _rollingCollidersWriteIndices;
#endregion
~RollbackCollection() { Deinitialize(); }
/// <summary>
/// Initialize ring size based on network timing. Call once on startup or when timing changes.
/// </summary>
internal void Initialize(NetworkManager networkManager, double tickDelta, float maximumRollbackTime)
{
_networkManager = networkManager;
_maxSnapshots = Mathf.Max(2, Mathf.CeilToInt((float)(maximumRollbackTime / tickDelta)));
if (!_writeRequests.IsCreated) _writeRequests = new NativeList<RollbackManager.RollbackRequest>(64, Allocator.Persistent);
if (!_readRequests.IsCreated) _readRequests = new NativeList<RollbackManager.RollbackRequest>(64, Allocator.Persistent);
if (!_colliderRollbacksTAA.isCreated) _colliderRollbacksTAA = new TransformAccessArray(64);
if (!_rollingCollidersTAA.isCreated) _rollingCollidersTAA = new TransformAccessArray(64);
if (!_colliderRollbacksSnapshots.IsCreated) _colliderRollbacksSnapshots = new NativeList<ColliderSnapshot>(64, Allocator.Persistent);
if (!_rollingCollidersSnapshots.IsCreated) _rollingCollidersSnapshots = new NativeList<ColliderSnapshot>(64, Allocator.Persistent);
if (!_colliderRollbacksRolledBackMask.IsCreated) _colliderRollbacksRolledBackMask = new NativeList<byte>(64, Allocator.Persistent);
if (!_colliderRollbacksSceneHandles.IsCreated) _colliderRollbacksSceneHandles = new NativeList<int>(64, Allocator.Persistent);
if (!_colliderRollbacksLerpFrames.IsCreated) _colliderRollbacksLerpFrames = new NativeList<int>(64, Allocator.Persistent);
if (!_colliderRollbacksBoundingBoxData.IsCreated) _colliderRollbacksBoundingBoxData = new NativeList<RollbackManager.BoundingBoxData>(64, Allocator.Persistent);
if (!_rollingColliderToColliderRollbacks.IsCreated) _rollingColliderToColliderRollbacks = new NativeList<int>(64, Allocator.Persistent);
if (!_rollingCollidersWriteIndices.IsCreated) _rollingCollidersWriteIndices = new NativeList<int>(64, Allocator.Persistent);
_ready = true;
}
/// <summary>
/// Deinitialize all native buffers and transform access arrays.
/// </summary>
internal void Deinitialize()
{
_networkManager = null;
if (_writeRequests.IsCreated) _writeRequests.Dispose();
if (_readRequests.IsCreated) _readRequests.Dispose();
if (_colliderRollbacksTAA.isCreated) _colliderRollbacksTAA.Dispose();
if (_rollingCollidersTAA.isCreated) _rollingCollidersTAA.Dispose();
if (_colliderRollbacksSnapshots.IsCreated) _colliderRollbacksSnapshots.Dispose();
if (_rollingCollidersSnapshots.IsCreated) _rollingCollidersSnapshots.Dispose();
if (_colliderRollbacksRolledBackMask.IsCreated) _colliderRollbacksRolledBackMask.Dispose();
if (_colliderRollbacksSceneHandles.IsCreated) _colliderRollbacksSceneHandles.Dispose();
if (_colliderRollbacksLerpFrames.IsCreated) _colliderRollbacksLerpFrames.Dispose();
if (_colliderRollbacksBoundingBoxData.IsCreated) _colliderRollbacksBoundingBoxData.Dispose();
if (_rollingColliderToColliderRollbacks.IsCreated) _rollingColliderToColliderRollbacks.Dispose();
if (_rollingCollidersWriteIndices.IsCreated) _rollingCollidersWriteIndices.Dispose();
_colliderRollbacks.Clear();
_colliderRollbacksIndices.Clear();
_ready = false;
}
/// <summary>
/// Ensure capacities for upcoming additions without reallocations.
/// </summary>
private void EnsureCapacity(int addRollingColliders, int addColliderRollbacks)
{
if (!_ready)
{
_networkManager.LogError("RollbackCollection is not configured. Call Configure(NetworkManager, double, float) first.");
return;
}
int newColliderRollbacksCount = _colliderRollbacksTAA.length + Math.Max(0, addColliderRollbacks);
int newRollingCollidersCount = _rollingCollidersTAA.length + Math.Max(0, addRollingColliders);
if (_colliderRollbacksTAA.capacity < newColliderRollbacksCount)
_colliderRollbacksTAA.capacity = newColliderRollbacksCount;
if (_rollingCollidersTAA.capacity < newRollingCollidersCount)
_rollingCollidersTAA.capacity = newRollingCollidersCount;
if (_colliderRollbacksSnapshots.Capacity < newColliderRollbacksCount)
_colliderRollbacksSnapshots.Capacity = newColliderRollbacksCount;
if (_rollingCollidersSnapshots.Capacity < newRollingCollidersCount * _maxSnapshots)
_rollingCollidersSnapshots.Capacity = newRollingCollidersCount * _maxSnapshots;
if (_colliderRollbacksRolledBackMask.Capacity < newColliderRollbacksCount)
_colliderRollbacksRolledBackMask.Capacity = newColliderRollbacksCount;
if (_colliderRollbacksSceneHandles.Capacity < newColliderRollbacksCount)
_colliderRollbacksSceneHandles.Capacity = newColliderRollbacksCount;
if (_colliderRollbacksLerpFrames.Capacity < newColliderRollbacksCount)
_colliderRollbacksLerpFrames.Capacity = newColliderRollbacksCount;
if (_colliderRollbacksBoundingBoxData.Capacity < newColliderRollbacksCount)
_colliderRollbacksBoundingBoxData.Capacity = newColliderRollbacksCount;
if (_rollingColliderToColliderRollbacks.Capacity < newRollingCollidersCount)
_rollingColliderToColliderRollbacks.Capacity = newRollingCollidersCount;
if (_rollingCollidersWriteIndices.Capacity < newRollingCollidersCount)
_rollingCollidersWriteIndices.Capacity = newRollingCollidersCount;
}
/// <summary>
/// Registers a ColliderRollback with all its RollingColliders.
/// Adds new rolling entries at the end (dense indexing).
/// </summary>
internal void RegisterColliderRollback(ColliderRollback colliderRollback)
{
if (!_ready)
{
_networkManager.LogError("RollbackCollection is not configured. Call Configure(NetworkManager, double, float) first.");
return;
}
if (_colliderRollbacksIndices.ContainsKey(colliderRollback)) return;
IReadOnlyList<Transform> list = colliderRollback.GetRollingColliders();
int addColliders = list.Count;
int newColliderRollbacksCount = _colliderRollbacks.Count + 1;
int newRollingCollidersCount = _rollingCollidersTAA.length + addColliders;
EnsureCapacity(addColliders, 1);
_colliderRollbacks.Add(colliderRollback);
_colliderRollbacksIndices[colliderRollback] = newColliderRollbacksCount - 1;
_colliderRollbacksTAA.Add(colliderRollback.transform);
_colliderRollbacksSnapshots.ResizeUninitialized(newColliderRollbacksCount);
_colliderRollbacksRolledBackMask.Add(0);
_colliderRollbacksSceneHandles.Add(colliderRollback.gameObject.scene.handle);
_colliderRollbacksLerpFrames.Add(0);
_colliderRollbacksBoundingBoxData.Add(colliderRollback.GetBoundingBoxData());
_rollingCollidersSnapshots.ResizeUninitialized(newRollingCollidersCount * _maxSnapshots);
for (int i = 0; i < addColliders; i++)
{
Transform rollingCollider = list[i];
_rollingCollidersWriteIndices.Add(0);
_rollingColliderToColliderRollbacks.Add(newColliderRollbacksCount - 1);
_rollingCollidersTAA.Add(rollingCollider);
}
}
/// <summary>
/// Unregisters a ColliderRollback. Removes all its rolling entries.
/// Uses swap-back removal for both rolling entries and the colliderRollbacks, keeping indices dense.
/// </summary>
internal void UnregisterColliderRollback(ColliderRollback cr)
{
if (!_ready) return;
if (!_colliderRollbacksIndices.Remove(cr, out int colliderRollbacksIndex)) return;
int lastColliderRollbacks = _colliderRollbacks.Count - 1;
// Remove all rolling entries belonging to this colliderRollbacks (scan backwards for stability).
for (int i = _rollingColliderToColliderRollbacks.Length - 1; i >= 0; --i)
{
if (_rollingColliderToColliderRollbacks[i] == colliderRollbacksIndex)
RemoveRollingColliderAtSwapBack(i);
}
// Remove the colliderRollbacks by swapping with the last colliderRollbacks.
if (colliderRollbacksIndex != lastColliderRollbacks)
{
ColliderRollback tempCr = _colliderRollbacks[lastColliderRollbacks];
_colliderRollbacksIndices[tempCr] = colliderRollbacksIndex;
_colliderRollbacks[colliderRollbacksIndex] = tempCr;
// Re-tag colliders that belonged to lastColliderRollbacks to now point to colliderRollbacksIndex.
for (int i = 0; i < _rollingColliderToColliderRollbacks.Length; i++)
{
if (_rollingColliderToColliderRollbacks[i] == lastColliderRollbacks)
_rollingColliderToColliderRollbacks[i] = colliderRollbacksIndex;
}
}
_colliderRollbacksTAA.RemoveAtSwapBack(colliderRollbacksIndex);
_colliderRollbacksRolledBackMask.RemoveAtSwapBack(colliderRollbacksIndex);
_colliderRollbacksSceneHandles.RemoveAtSwapBack(colliderRollbacksIndex);
_colliderRollbacksLerpFrames.RemoveAtSwapBack(colliderRollbacksIndex);
_colliderRollbacksBoundingBoxData.RemoveAtSwapBack(colliderRollbacksIndex);
_colliderRollbacks.RemoveAt(lastColliderRollbacks);
}
/// <summary>
/// Removes one rolling entry at index by swapping with the last item.
/// Updates all parallel structures and GlobalIndex on the moved entry.
/// </summary>
internal void RemoveRollingColliderAtSwapBack(int rollingColliderIndex)
{
int last = _rollingCollidersTAA.length - 1;
if (last < 0) return;
_rollingCollidersTAA.RemoveAtSwapBack(rollingColliderIndex);
_rollingCollidersWriteIndices.RemoveAtSwapBack(rollingColliderIndex);
_rollingColliderToColliderRollbacks.RemoveAtSwapBack(rollingColliderIndex);
if (rollingColliderIndex != last)
{
// Move last snapshots ring over the removed slot.
int dst = rollingColliderIndex * _maxSnapshots;
int src = last * _maxSnapshots;
for (int k = 0; k < _maxSnapshots; k++)
_rollingCollidersSnapshots[dst + k] =_rollingCollidersSnapshots[src + k];
}
_rollingCollidersSnapshots.ResizeUninitialized(_rollingCollidersSnapshots.Length - _maxSnapshots);
}
/// <summary>
/// Populates one snapshot for every non-rolled-back transform using a parallel job.
/// Call this once per tick on the server (e.g., from OnPostTick).
/// </summary>
internal void CreateSnapshots()
{
if (!_ready) return;
if (_colliderRollbacksTAA.length > 0 && _rollingCollidersTAA.length > 0)
{
JobHandle first = new RollbackManager.IncrementGroupsFramesJob
{
maxSnapshots = _maxSnapshots,
colliderRollbacksLerpFrames = _colliderRollbacksLerpFrames.AsArray(),
colliderRollbacksRolledBackMask = _colliderRollbacksRolledBackMask.AsArray(),
}.Schedule(_colliderRollbacksTAA.length, 64);
JobHandle second = new RollbackManager.PopulateColliderRollbackSnapshotsJob
{
colliderRollbackSnapshots = _colliderRollbacksSnapshots.AsArray(),
colliderRollbacksRolledBackMask = _colliderRollbacksRolledBackMask.AsArray()
}.Schedule(_colliderRollbacksTAA, first);
JobHandle third = new RollbackManager.PopulateRollingColliderSnapshotsJob
{
maxSnapshots = _maxSnapshots,
rollingCollidersSnapshots = _rollingCollidersSnapshots.AsArray(),
rollingCollidersWriteIndices = _rollingCollidersWriteIndices.AsArray(),
colliderRollbacksRolledBackMask = _colliderRollbacksRolledBackMask.AsArray(),
colliderToColliderRollbacks = _rollingColliderToColliderRollbacks.AsArray()
}.Schedule(_rollingCollidersTAA, second);
third.Complete();
}
}
#region Sinlge ColliderRollback
/// <summary>
/// Computes lerp mode/endFrame/percent based on 'time' and applies rollback to the whole colliderRollbacks.
/// </summary>
internal void Rollback(ColliderRollback colliderRollback, float time, RollbackPhysicsType rollbackPhysicsType)
{
if (!_ready) return;
if (!_colliderRollbacksIndices.TryGetValue(colliderRollback, out int colliderRollbacksIndex)) return;
// Already rolled back.
if (IsRolledBack(colliderRollback))
{
_networkManager.LogWarning("Colliders are already rolled back. Returning colliders forward first.");
Return(colliderRollback, rollbackPhysicsType);
}
int frames = _colliderRollbacksLerpFrames[colliderRollbacksIndex];
if (frames == 0) return;
/* If time were 0.3f and delta was 0.2f then the
* result would be 1.5f. This indicates to lerp between
* the first snapshot, and one after. */
float decimalFrame = time / (float)_networkManager.TimeManager.TickDelta;
RollbackManager.FrameRollbackTypes mode;
int endFrame;
float percent;
/* Rollback is beyond written quantity.
* Set to use the last snapshot. */
if (decimalFrame > frames)
{
mode = RollbackManager.FrameRollbackTypes.Exact;
// Be sure to subtract 1 to get last entry in snapshots.
endFrame = frames - 1;
// Not needed for exact but must be set.
percent = 1f;
}
else
{
percent = decimalFrame % 1f;
endFrame = Mathf.CeilToInt(decimalFrame);
/* If the end frame is larger than or equal to 1
* then a lerp between two snapshots can occur. If
* equal to 1 then the lerp would occur between 0 and 1. */
if (endFrame >= 1)
{
mode = RollbackManager.FrameRollbackTypes.LerpMiddle;
}
// Rolling back only 1 frame.
else
{
endFrame = 0;
mode = RollbackManager.FrameRollbackTypes.LerpFirst;
}
}
// Apply to all rolling entries belonging to this colliderRollbacks.
for (int i = 0; i < _rollingColliderToColliderRollbacks.Length; i++)
if (_rollingColliderToColliderRollbacks[i] == colliderRollbacksIndex)
ApplyRollbackIndex(i, endFrame, percent, mode);
_colliderRollbacksRolledBackMask[colliderRollbacksIndex] = 1;
_rollbackPhysics |= rollbackPhysicsType;
SyncTransforms(rollbackPhysicsType);
}
/// <summary>
/// Called when a specific colliderRollbacks should return.
/// </summary>
internal void Return(ColliderRollback colliderRollback, RollbackPhysicsType rollbackPhysicsType)
{
if (!_ready) return;
if (!_colliderRollbacksIndices.TryGetValue(colliderRollback, out int colliderRollbacksIndex)) return;
if (!IsRolledBack(colliderRollback))
return;
// Iterate dense rolling entries and return only those that belong to this colliderRollbacks.
for (int i = 0; i < _rollingColliderToColliderRollbacks.Length; i++)
{
if (_rollingColliderToColliderRollbacks[i] == colliderRollbacksIndex)
{
int frames = _colliderRollbacksLerpFrames[colliderRollbacksIndex];
if (frames <= 0)
continue;
int writeIdx = _rollingCollidersWriteIndices[i];
int baseOffset = i * _maxSnapshots;
int lastIdx = (writeIdx - 1 + _maxSnapshots) % _maxSnapshots;
// Return to the newest (last written) snapshot
int snapshotIndex = baseOffset + lastIdx;
ColliderSnapshot s = _rollingCollidersSnapshots[snapshotIndex];
Transform t = _rollingCollidersTAA[i];
t.SetPositionAndRotation(s.WorldPosition, s.WorldRotation);
}
}
_colliderRollbacksRolledBackMask[colliderRollbacksIndex] = 0;
_rollbackPhysics |= rollbackPhysicsType;
SyncTransforms(rollbackPhysicsType);
}
/// <summary>
/// Applies a rollback for a specific global transform index using the provided interpolation mode.
/// </summary>
/// <param name="rollingColliderIdx">RollingCollider index into the ColliderRollback.</param>
/// <param name="mode">Frame interpolation mode.</param>
/// <param name="endFrame">Target history frame index (0 = newest).</param>
/// <param name="percent">Lerp factor for interpolation modes.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ApplyRollbackIndex(int rollingColliderIdx, int endFrame, float percent,
RollbackManager.FrameRollbackTypes mode)
{
if (!_ready || rollingColliderIdx < 0 || rollingColliderIdx >= _rollingCollidersTAA.length)
return;
int colliderRollbacksIndex = _rollingColliderToColliderRollbacks[rollingColliderIdx];
int writeIdx = _rollingCollidersWriteIndices[rollingColliderIdx];
int baseOffset = rollingColliderIdx * _maxSnapshots;
int lastIdx = (writeIdx - 1 + _maxSnapshots) % _maxSnapshots;
bool isRecycled = _colliderRollbacksLerpFrames[colliderRollbacksIndex] >= _maxSnapshots;
Transform t = _rollingCollidersTAA[rollingColliderIdx];
if (mode == RollbackManager.FrameRollbackTypes.Exact)
{
ColliderSnapshot s = _rollingCollidersSnapshots[BufIndex(baseOffset, endFrame, lastIdx, isRecycled, _maxSnapshots)];
t.SetPositionAndRotation(s.WorldPosition, s.WorldRotation);
}
else if (mode == RollbackManager.FrameRollbackTypes.LerpFirst)
{
ColliderSnapshot s = _rollingCollidersSnapshots[BufIndex(baseOffset, endFrame, lastIdx, isRecycled, _maxSnapshots)];
t.GetPositionAndRotation(out Vector3 curPos, out Quaternion curRot);
t.SetPositionAndRotation(Vector3.Lerp(curPos, s.WorldPosition, percent),
Quaternion.Lerp(curRot, s.WorldRotation, percent));
}
else if (mode == RollbackManager.FrameRollbackTypes.LerpMiddle)
{
ColliderSnapshot s0 = _rollingCollidersSnapshots[BufIndex(baseOffset, endFrame - 1, lastIdx, isRecycled, _maxSnapshots)];
ColliderSnapshot s1 = _rollingCollidersSnapshots[BufIndex(baseOffset, endFrame, lastIdx, isRecycled, _maxSnapshots)];
t.SetPositionAndRotation(Vector3.Lerp(s0.WorldPosition, s1.WorldPosition, percent),
Quaternion.Lerp(s0.WorldRotation, s1.WorldRotation, percent));
}
return;
// compute buffer index with negative-safe modulo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static int BufIndex(int baseOffset, int history, int lastIdx, bool isRecycled, int maxSnapshots)
{
int idx = baseOffset + lastIdx - history;
// If negative value start taking from the back.
if (idx < 0)
{
/* Cannot take from back, snapshots aren't filled yet.
* Instead take the oldest snapshot, which in this case
* would be index baseOffset. */
if (!isRecycled)
return baseOffset;
// Snapshots filled, take from back.
else
return idx + maxSnapshots;
}
// Not a negative value, return as is.
else return idx;
}
}
/// <summary>
/// Sets rolled-back state for a colliderRollbacks in O(1). No per-rolling writes.
/// </summary>
/// <summary>True if the colliderRollbacks is currently rolled back.</summary>
public bool IsRolledBack(ColliderRollback cr)
=> _ready && _colliderRollbacksIndices.TryGetValue(cr, out int g) && _colliderRollbacksRolledBackMask[g] != 0;
#endregion
#region Job ColliderRollback
/// <summary>
/// Run rollback for ALL colliderRollbacks in parallel (jobified).
/// </summary>
internal void Rollback(int sceneHandle, float time, RollbackPhysicsType rollbackPhysicsType)
{
if (!_ready || _rollingCollidersTAA.length == 0)
return;
Return();
JobHandle job = new RollbackManager.ApplyRollbackJob
{
sceneHandle = sceneHandle,
maxSnapshots = _maxSnapshots,
decimalFrame = time / (float)_networkManager.TimeManager.TickDelta,
colliderToColliderRollbacks = _rollingColliderToColliderRollbacks.AsArray(),
colliderRollbacksSceneHandles = _colliderRollbacksSceneHandles.AsArray(),
colliderRollbacksLerpFrames = _colliderRollbacksLerpFrames.AsArray(),
rollingCollidersWriteIndices = _rollingCollidersWriteIndices.AsArray(),
colliderRollbacksRolledBackMask = _colliderRollbacksRolledBackMask.AsArray(),
rollingCollidersSnapshots = _rollingCollidersSnapshots.AsArray()
}.Schedule(_rollingCollidersTAA);
job.Complete();
_rollbackPhysics |= rollbackPhysicsType;
SyncTransforms(rollbackPhysicsType);
}
/// <summary>
/// Run rollback for intersected colliderRollbacks by ray in parallel (jobified).
/// </summary>
internal void Rollback(RollbackManager.RollbackRequest rollbackRequest)
{
if (!_ready || _rollingCollidersTAA.length == 0)
return;
Return();
JobHandle job = new RollbackManager.ApplyRollbackRaycastJob
{
sceneHandle = rollbackRequest.sceneHandle,
maxSnapshots = _maxSnapshots,
decimalFrame = rollbackRequest.time / (float)_networkManager.TimeManager.TickDelta,
origin = rollbackRequest.origin,
dir = rollbackRequest.direction,
distance = rollbackRequest.distance,
physicsType = (int)rollbackRequest.rollbackPhysicsType,
colliderToColliderRollbacks = _rollingColliderToColliderRollbacks.AsArray(),
colliderRollbacksBoundingBoxData = _colliderRollbacksBoundingBoxData.AsArray(),
colliderRollbacksSceneHandles = _colliderRollbacksSceneHandles.AsArray(),
colliderRollbacksLerpFrames = _colliderRollbacksLerpFrames.AsArray(),
rollingCollidersWriteIndices = _rollingCollidersWriteIndices.AsArray(),
colliderRollbacksRolledBackMask = _colliderRollbacksRolledBackMask.AsArray(),
colliderRollbacksSnapshots = _colliderRollbacksSnapshots.AsArray(),
rollingCollidersSnapshots = _rollingCollidersSnapshots.AsArray()
}.Schedule(_rollingCollidersTAA);
job.Complete();
_rollbackPhysics |= rollbackRequest.rollbackPhysicsType;
SyncTransforms(rollbackRequest.rollbackPhysicsType);
}
/// <summary>
/// Request rollback for deferred rollback for intersected colliderRollbacks by ray in parallel (jobified).
/// </summary>
internal void RequestRollbackDeferred(RollbackManager.RollbackRequest rollbackRequest)
{
_writeRequests.Add(rollbackRequest);
_requestsRollbackPhysics |= rollbackRequest.rollbackPhysicsType;
}
/// <summary>
/// Run rollback for all requests intersected colliderRollbacks by ray in parallel (jobified).
/// </summary>
/// <returns>Count of requests.</returns>
internal int RollbackDeferred()
{
if (!_ready || _rollingCollidersTAA.length == 0 || _writeRequests.Length == 0)
return 0;
Return();
(_readRequests, _writeRequests) = (_writeRequests, _readRequests);
_writeRequests.Clear();
int groupCount = _colliderRollbacksTAA.length;
int batchSize = ComputeBatchSize(groupCount);
NativeArray<float> sum = new NativeArray<float>(groupCount, Allocator.TempJob);
NativeArray<int> cnt = new NativeArray<int>(groupCount, Allocator.TempJob);
try
{
JobHandle computeHandle = new RollbackManager.ComputeDeferredRollbackSumsJob
{
tickDelta = (float)_networkManager.TimeManager.TickDelta,
requests = _readRequests.AsArray(),
colliderRollbacksSceneHandles = _colliderRollbacksSceneHandles.AsArray(),
colliderRollbacksLerpFrames = _colliderRollbacksLerpFrames.AsArray(),
colliderRollbacksBoundingBoxData = _colliderRollbacksBoundingBoxData.AsArray(),
colliderRollbacksSnapshots = _colliderRollbacksSnapshots.AsArray(),
sumDecimalFrame = sum,
hitCount = cnt
}.Schedule(groupCount, batchSize);
JobHandle applyHandle = new RollbackManager.ApplyDeferredRollbackJob
{
maxSnapshots = _maxSnapshots,
colliderToColliderRollbacks = _rollingColliderToColliderRollbacks.AsArray(),
colliderRollbacksLerpFrames = _colliderRollbacksLerpFrames.AsArray(),
rollingCollidersWriteIndices = _rollingCollidersWriteIndices.AsArray(),
colliderRollbacksRolledBackMask = _colliderRollbacksRolledBackMask.AsArray(),
rollingCollidersSnapshots = _rollingCollidersSnapshots.AsArray(),
sumDecimalFrame = sum,
hitCount = cnt
}.Schedule(_rollingCollidersTAA, computeHandle);
applyHandle.Complete();
}
finally
{
sum.Dispose();
cnt.Dispose();
}
_rollbackPhysics |= _requestsRollbackPhysics;
_requestsRollbackPhysics = 0;
SyncTransforms(_rollbackPhysics);
return _readRequests.Length;
}
/// <summary>
/// Run Return for ALL colliderRollbacks in parallel (jobified).
/// </summary>
internal void Return()
{
if (!_ready || _rollingCollidersTAA.length == 0)
return;
JobHandle job = new RollbackManager.ReturnRollbackAllJob
{
maxSnapshots = _maxSnapshots,
colliderToColliderRollbacks = _rollingColliderToColliderRollbacks.AsArray(),
colliderRollbacksLerpFrames = _colliderRollbacksLerpFrames.AsArray(),
rollingCollidersWriteIndices = _rollingCollidersWriteIndices.AsArray(),
colliderRollbacksRolledBackMask = _colliderRollbacksRolledBackMask.AsArray(),
rollingCollidersSnapshots = _rollingCollidersSnapshots.AsArray()
}.Schedule(_rollingCollidersTAA);
job.Complete();
SyncTransforms(_rollbackPhysics);
}
#endregion
/// <summary>
/// Applies transforms for the specified physics type.
/// </summary>
/// <param name = "physicsType"></param>
private static void SyncTransforms(RollbackPhysicsType physicsType)
{
if ((physicsType & RollbackPhysicsType.Physics) > 0)
Physics.SyncTransforms();
if ((physicsType & RollbackPhysicsType.Physics2D) > 0)
Physics2D.SyncTransforms();
}
private static int ComputeBatchSize(int length, int minBatch = 1, int maxBatch = 128)
{
if (length <= 0) return 1;
// +1: main thread + worker threads
int workers = JobsUtility.JobWorkerCount + 1;
// Aim for ~4 waves of batches across all workers.
int targetBatches = Mathf.Max(1, workers * 4);
// CeilDiv to get iterations per batch
int batch = (length + targetBatches - 1) / targetBatches;
return Mathf.Clamp(batch, minBatch, maxBatch);
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a06ebb5c8a2323940b9eab5fea5ae220
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackCollection.Threaded.cs
uploadId: 866910
@@ -0,0 +1,77 @@
#if FISHNET_THREADED_COLLIDER_ROLLBACK
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Jobs;
namespace FishNet.Component.ColliderRollback
{
public partial class RollbackManager
{
internal enum DeferredRollbackOrder : byte
{
PreTick = 0,
Tick = 1,
PostTick = 2
}
internal enum BoundingBoxType
{
/// <summary>
/// Disable this feature.
/// </summary>
Disabled,
/// <summary>
/// Manually specify the dimensions of a bounding box.
/// </summary>
Manual
}
internal struct BoundingBoxData
{
public RollbackPhysicsType rollbackPhysicsType;
public BoundingBoxType boundingBoxType;
public float3 extends;
public float3 center;
public quaternion localRotation;
public BoundingBoxData(RollbackPhysicsType rollbackPhysicsType, BoundingBoxType boundingBoxType,
float3 extends, float3 center, quaternion localRotation)
{
this.rollbackPhysicsType = rollbackPhysicsType;
this.boundingBoxType = boundingBoxType;
this.extends = extends;
this.center = center;
this.localRotation = localRotation;
}
}
/// <summary>
/// A deferred rollback request.
/// </summary>
public struct RollbackRequest
{
public int sceneHandle;
public float3 origin;
public float3 direction;
public float distance;
public float time;
public RollbackPhysicsType rollbackPhysicsType;
public RollbackRequest(int sceneHandle, float3 origin, float3 direction, float distance, float time, RollbackPhysicsType rollbackPhysicsType)
{
this.sceneHandle = sceneHandle;
this.origin = origin;
this.direction = direction;
this.distance = distance;
this.time = time;
this.rollbackPhysicsType = rollbackPhysicsType;
}
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3b7001cad72b98c4a85dbde737dcaf43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.Threaded.Types.cs
uploadId: 866910
@@ -0,0 +1,244 @@
#if FISHNET_THREADED_COLLIDER_ROLLBACK
using FishNet.Managing;
using FishNet.Managing.Timing;
using FishNet.Transporting;
using System;
using UnityEngine;
using Unity.Profiling;
using UnityEngine.SceneManagement;
namespace FishNet.Component.ColliderRollback
{
public partial class RollbackManager : MonoBehaviour
{
#region Serialized.
/// <summary>
/// </summary>
[Tooltip("Maximum time in the past colliders can be rolled back to.")]
[SerializeField]
private float _maximumRollbackTime = 1.25f;
/// <summary>
/// </summary>
[Tooltip("When to invoke OnRollbackDeferred.")]
[SerializeField]
private DeferredRollbackOrder _deferredRollbackOrder = DeferredRollbackOrder.PreTick;
/// <summary>
/// Maximum time in the past colliders can be rolled back to.
/// </summary>
internal float MaximumRollbackTime => _maximumRollbackTime;
/// <summary>
/// </summary>
[Tooltip("Interpolation value for the NetworkTransforms or objects being rolled back.")]
[Range(0, 250)]
[SerializeField]
internal ushort Interpolation = 2;
#endregion
#region Private
#region Private Profiler Markers
private static readonly ProfilerMarker _pm_OnPreTick = new("RollbackManager.TimeManager_OnPreTick()");
private static readonly ProfilerMarker _pm_OnTick = new("RollbackManager.TimeManager_OnTick()");
private static readonly ProfilerMarker _pm_OnPostTick = new("RollbackManager.TimeManager_OnPostTick()");
private static readonly ProfilerMarker _pm_CreateSnapshots = new("RollbackManager.CreateSnapshots()");
private static readonly ProfilerMarker _pm_Rollback0 = new("RollbackManager.Rollback(int, PreciseTick, RollbackPhysicsType, bool)");
private static readonly ProfilerMarker _pm_Rollback1 = new("RollbackManager.Rollback(int, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType, bool)");
private static readonly ProfilerMarker _pm_RequestRollbackDeferred = new("RollbackManager.RequestRollbackDeferred(int, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType, bool)");
private static readonly ProfilerMarker _pm_RollbackDeferred = new("RollbackManager.RollbackDeferred()");
private static readonly ProfilerMarker _pm_Return = new("RollbackManager.Return()");
private static readonly ProfilerMarker _pm_RegisterColliderRollback = new("RollbackManager.RegisterColliderRollback(ColliderRollback)");
private static readonly ProfilerMarker _pm_UnregisterColliderRollback = new("RollbackManager.UnregisterColliderRollback(ColliderRollback)");
#endregion
#region Public.
/// <summary>
/// Called when deferred rollback occured for all requests.
/// </summary>
public event Action OnRollbackDeferred;
/// <summary>
/// Called when deferred rollback in past occured for all requests.
/// </summary>
public event Action OnPostRollbackDeferred;
#endregion
#endregion
/// <summary>
/// Initializes this script for use.
/// </summary>
/// <param name = "manager"></param>
internal void InitializeOnce_Internal(NetworkManager manager)
{
}
[Obsolete("Use Rollback(Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5
public void Rollback(Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(Scene, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5
public void Rollback(Scene scene, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(int, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5
public void Rollback(int sceneHandle, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(Scene, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics2D, bool) instead.")] //Remove on V5
public void Rollback(Scene scene, Vector2 origin, Vector2 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics2D, bool) instead.")] //Remove on V5
public void Rollback(Vector2 origin, Vector2 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back all colliders.
/// </summary>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back all colliders in a scene.
/// </summary>
/// <param name = "scene">Scene containing colliders.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(Scene scene, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back all colliders in a scene.
/// </summary>
/// <param name = "sceneHandle">Scene handle containing colliders.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(int sceneHandle, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
using (_pm_Rollback0.Auto())
{
}
}
/// <summary>
/// Rolls back colliders hit by a test cast against bounding boxes.
/// </summary>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back colliders hit by a test cast against bounding boxes, in a specific scene.
/// </summary>
/// <param name = "scene">Scene containing colliders.</param>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(Scene scene, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back colliders hit by a test cast against bounding boxes, in a specific scene.
/// </summary>
/// <param name = "sceneHandle">Scene handle containing colliders.</param>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(int sceneHandle, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
using (_pm_Rollback1.Auto())
{
}
}
/// <summary>
/// Requests deferred rollback for colliders hit by a test cast against bounding boxes.
/// </summary>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void RequestRollbackDeferred(Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Requests deferred rollback for colliders hit by a test cast against bounding boxes, in a specific scene.
/// </summary>
/// <param name = "scene">Scene containing colliders.</param>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void RequestRollbackDeferred(Scene scene, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Requests deferred rollback for colliders hit by a test cast against bounding boxes, in a specific scene.
/// </summary>
/// <param name = "sceneHandle">Scene handle containing colliders.</param>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void RequestRollbackDeferred(int sceneHandle, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
using (_pm_RequestRollbackDeferred.Auto())
{
}
}
/// <summary>
/// Rolls back for all RollbackRequests.
/// </summary>
public void RollbackDeferred()
{
}
/// <summary>
/// Returns all ColliderRollback objects back to their original position.
/// </summary>
public void Return()
{
using (_pm_Return.Auto())
{
}
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d26b760e7ad25ab409143779624d67c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.Threaded.cs
uploadId: 866910
@@ -0,0 +1,187 @@
#if !FISHNET_THREADED_COLLIDER_ROLLBACK
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Scened;
using FishNet.Managing.Timing;
using FishNet.Transporting;
using GameKit.Dependencies.Utilities;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace FishNet.Component.ColliderRollback
{
public class RollbackManager : MonoBehaviour
{
#region Internal.
/// <summary>
/// Cached value for bounding box layermask.
/// </summary>
internal int? BoundingBoxLayerNumber
{
get
{
if (_boundingBoxLayerNumber == null)
{
for (int i = 0; i < 32; i++)
{
if (1 << i == BoundingBoxLayer.value)
{
_boundingBoxLayerNumber = i;
break;
}
}
}
return _boundingBoxLayerNumber;
}
}
private int? _boundingBoxLayerNumber;
#endregion
#region Serialized.
/// <summary>
/// </summary>
[Tooltip("Layer to use when creating and checking against bounding boxes. This should be different from any layer used.")]
[SerializeField]
private LayerMask _boundingBoxLayer = 0;
/// <summary>
/// Layer to use when creating and checking against bounding boxes. This should be different from any layer used.
/// </summary>
internal LayerMask BoundingBoxLayer => _boundingBoxLayer;
/// <summary>
/// </summary>
[Tooltip("Maximum time in the past colliders can be rolled back to.")]
[SerializeField]
private float _maximumRollbackTime = 1.25f;
/// <summary>
/// Maximum time in the past colliders can be rolled back to.
/// </summary>
internal float MaximumRollbackTime => _maximumRollbackTime;
/// <summary>
/// </summary>
[Tooltip("Interpolation value for the NetworkTransforms or objects being rolled back.")]
[Range(0, 250)]
[SerializeField]
internal ushort Interpolation = 2;
#endregion
/// <summary>
/// Initializes this script for use.
/// </summary>
/// <param name = "manager"></param>
internal void InitializeOnce_Internal(NetworkManager manager)
{
}
[Obsolete("Use Rollback(Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5
public void Rollback(Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(Scene, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5
public void Rollback(Scene scene, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(int, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5
public void Rollback(int sceneHandle, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(Scene, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics2D, bool) instead.")] //Remove on V5
public void Rollback(Scene scene, Vector2 origin, Vector2 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
[Obsolete("Use Rollback(Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics2D, bool) instead.")] //Remove on V5
public void Rollback(Vector2 origin, Vector2 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back all colliders.
/// </summary>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back all colliders in a scene.
/// </summary>
/// <param name = "scene">Scene containing colliders.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(Scene scene, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back all colliders in a scene.
/// </summary>
/// <param name = "sceneHandle">Scene handle containing colliders.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(int sceneHandle, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back colliders hit by a test cast against bounding boxes.
/// </summary>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back colliders hit by a test cast against bounding boxes, in a specific scene.
/// </summary>
/// <param name = "scene">Scene containing colliders.</param>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(Scene scene, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Rolls back colliders hit by a test cast against bounding boxes, in a specific scene.
/// </summary>
/// <param name = "sceneHandle">Scene handle containing colliders.</param>
/// <param name = "origin">Ray origin.</param>
/// <param name = "normalizedDirection">Direction to cast.</param>
/// <param name = "distance">Distance of cast.</param>
/// <param name = "pt">Precise tick received from the client.</param>
/// <param name = "physicsType">Type of physics to rollback; this is often what your casts will use.</param>
/// <param name = "asOwnerAndClientHost">True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost.</param>
public void Rollback(int sceneHandle, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false)
{
}
/// <summary>
/// Returns all ColliderRollback objects back to their original position.
/// </summary>
public void Return()
{
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b185516acd802904383e2a5f1a666750
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs
uploadId: 866910
@@ -0,0 +1,13 @@
namespace FishNet.Component.ColliderRollback
{
/// <summary>
/// Which physics to apply after rolling back colliders.
/// </summary>
[System.Serializable]
[System.Flags]
public enum RollbackPhysicsType
{
Physics = 1,
Physics2D = 2
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 82b31e74d64a0c44d8fa2f3b6b08ebca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a72c0fe8d07e9fd49911db527eddbc39
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cd9a0ca39fab66c448fdc3e25da9d482
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e92e9b5fef66ccb4d991a260767c3be4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,42 @@
using UnityEngine;
namespace Sirenix.OdinInspector
{
#if !ODIN_INSPECTOR
public class TabGroupAttribute : PropertyAttribute
{
public string name;
public bool foldEverything;
public TabGroupAttribute(string name, bool foldEverything = false)
{
this.foldEverything = foldEverything;
this.name = name;
}
}
public class ShowIfAttribute : PropertyAttribute
{
#region Fields
public string comparedPropertyName { get; private set; }
public object comparedValue { get; private set; }
public DisablingType disablingType { get; private set; }
public enum DisablingType
{
ReadOnly = 2,
DontDraw = 3
}
#endregion
public ShowIfAttribute(string comparedPropertyName, object comparedValue, DisablingType disablingType = DisablingType.DontDraw)
{
this.comparedPropertyName = comparedPropertyName;
this.comparedValue = comparedValue;
this.disablingType = disablingType;
}
}
#endif
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 8d18dbd89f49c7a4888bbc0e330675a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs
uploadId: 866910
@@ -0,0 +1,22 @@
{
"name": "GameKit.Dependencies",
"rootNamespace": "",
"references": [
"GUID:6055be8ebefd69e48b49212b09b47b2f"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.textmeshpro",
"expression": "",
"define": "TEXTMESHPRO"
}
],
"noEngineReferences": false
}
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 1d82bdf40e2465b44b34adf79595e74c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 53002e457d153bf49aad4b2b28d4353c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,76 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace GameKit.Dependencies.Utilities
{
#if UNITY_EDITOR
[InitializeOnLoad]
#endif
public static class ApplicationState
{
#if !UNITY_EDITOR
/// <summary>
/// True if application is quitting.
/// </summary>
private static bool _isQuitting;
#endif
static ApplicationState()
{
#if !UNITY_EDITOR
_isQuitting = false;
#endif
Application.quitting -= Application_quitting;
Application.quitting += Application_quitting;
}
private static void Application_quitting()
{
#if !UNITY_EDITOR
_isQuitting = true;
#endif
}
/// <summary>
/// Returns if the application is quitting for editor or builds.
/// </summary>
/// <returns></returns>
public static bool IsQuitting()
{
#if UNITY_EDITOR
if ((!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying) || !EditorApplication.isPlaying)
return true;
else
return false;
#else
return _isQuitting;
#endif
}
/// <summary>
/// Returns if the application is playing for editor or builds.
/// </summary>
/// <returns></returns>
public static bool IsPlaying()
{
#if UNITY_EDITOR
return EditorApplication.isPlaying;
#else
return Application.isPlaying;
#endif
}
/// <summary>
/// Quits the application for editor or builds.
/// </summary>
public static void Quit()
{
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 54eb82a57a65e8548b57f5ca2a62bb76
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs
uploadId: 866910
@@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.Text;
using GameKit.Dependencies.Utilities.Types;
namespace GameKit.Dependencies.Utilities
{
public static class Arrays
{
/// <summary>
/// Randomizer used for shuffling.
/// </summary>
private static readonly System.Random _random = new();
/// <summary>
/// StringBuilder to save performance.
/// </summary>
private static readonly StringBuilder _stringBuilder = new();
/// <summary>
/// Cast each item in the collection ToString and returns all values.
/// </summary>
/// <returns></returns>
public static string ToString<T>(this IEnumerable<T> collection, string delimeter = ", ")
{
if (collection == null)
return string.Empty;
_stringBuilder.Clear();
foreach (T item in collection)
_stringBuilder.Append(item.ToString() + delimeter);
// Remove ending delimeter.
if (_stringBuilder.Length > delimeter.Length)
_stringBuilder.Length -= delimeter.Length;
return _stringBuilder.ToString();
}
/// <summary>
/// Removes an object from a list through re-ordering. This breaks the order of the list for a faster remove.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "list"></param>
/// <param name = "value"></param>
/// <returns></returns>
public static bool FastReferenceRemove<T>(this List<T> list, object value)
{
for (int i = 0; i < list.Count; i++)
{
if (ReferenceEquals(list[i], value))
{
FastIndexRemove(list, i);
return true;
}
}
return false;
}
/// <summary>
/// Removes an index from a list through re-ordering. This breaks the order of the list for a faster remove.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "list"></param>
/// <param name = "index"></param>
public static void FastIndexRemove<T>(this List<T> list, int index)
{
list[index] = list[^1];
list.RemoveAt(list.Count - 1);
}
/// <summary>
/// Shuffles an array.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "array"></param>
public static void Shuffle<T>(this T[] array)
{
int n = array.Length;
for (int i = 0; i < n - 1; i++)
{
int r = i + _random.Next(n - i);
T t = array[r];
array[r] = array[i];
array[i] = t;
}
}
/// <summary>
/// Shuffles a list.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "lst"></param>
public static void Shuffle<T>(this List<T> lst)
{
int n = lst.Count;
for (int i = 0; i < n - 1; i++)
{
int r = i + _random.Next(n - i);
T t = lst[r];
lst[r] = lst[i];
lst[i] = t;
}
}
/// <summary>
/// Adds an item to a collection, ordering it's position based on itemOrder. Lower values are inserted near the beginning of the collection.
/// </summary>
public static void AddOrdered<T>(this List<T> collection, T item) where T : IOrderable
{
int count = collection.Count;
int itemOrder = item.Order;
/* If no entries or is equal or larger to last
* entry then value can be added onto the end. */
if (count == 0 || itemOrder >= collection[^1].Order)
{
collection.Add(item);
}
else
{
for (int i = 0; i < count; i++)
{
/* If item being sorted is lower than the one in already added.
* then insert it before the one already added. */
if (itemOrder <= collection[i].Order)
{
collection.Insert(i, item);
break;
}
}
}
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 1b93eae9ff81b3e4b892128ca4b392ed
timeCreated: 1530140103
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs
uploadId: 866910
@@ -0,0 +1,13 @@
namespace GameKit.Dependencies.Utilities
{
public static class Booleans
{
/// <summary>
/// Converts a boolean to an integer, 1 for true 0 for false.
/// </summary>
public static int ToInt(this bool b)
{
return b ? 1 : 0;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 602eeac4b016b174f90ae5e85254ac86
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs
uploadId: 866910
@@ -0,0 +1,69 @@
using System.Runtime.CompilerServices;
using System.Text;
namespace GameKit.Dependencies.Utilities
{
/// <summary>
/// Various utility classes relating to floats.
/// </summary>
public static class Bytes
{
/// <summary>
/// Used to encode and decode strings.
/// </summary>
private static readonly UTF8Encoding _encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
/// <summary>
/// Pads an index a specified value. Preferred over typical padding so that pad values used with skins can be easily found in the code.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Pad(this byte value, int padding) => Ints.PadInt(value, padding);
/// <summary>
/// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max.
/// </summary>
/// <param name = "minimum">Inclusive minimum value.</param>
/// <param name = "maximum">Inclusive maximum value.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte RandomInclusiveRange(byte minimum, byte maximum) => (byte)Ints.RandomInclusiveRange(minimum, maximum);
/// <summary>
/// Provides a random exclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max.
/// </summary>
/// <param name = "minimum">Inclusive minimum value.</param>
/// <param name = "maximum">Exclusive maximum value.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte RandomExclusiveRange(byte minimum, byte maximum) => (byte)Ints.RandomExclusiveRange(minimum, maximum);
/// <summary>
/// Returns a clamped int within a specified range.
/// </summary>
/// <param name = "value">Value to clamp.</param>
/// <param name = "minimum">Minimum value.</param>
/// <param name = "maximum">Maximum value.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte Clamp(byte value, byte minimum, byte maximum) => (byte)Ints.Clamp(value, minimum, maximum);
/// <summary>
/// Returns whichever value is lower.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte Min(byte a, byte b) => a < b ? a : b;
/// <summary>
/// Determins if all values passed in are the same.
/// </summary>
/// <param name = "values">Values to check.</param>
/// <returns>True if all values are the same.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool ValuesMatch(params byte[] values) => Ints.ValuesMatch((int[])(object)values);
/// <summary>
/// Converts bytes to a string without error checking.
/// </summary>
public static string ToString(this byte[] bytes, int offset, int count) => _encoding.GetString(bytes, offset, count);
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 26489022ceafbfe4d85bfd5cccf37303
timeCreated: 1527268448
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs
uploadId: 866910
@@ -0,0 +1,90 @@
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
/// <summary>
/// Ways a CanvasGroup can have it's blocking properties modified.
/// </summary>
public enum CanvasGroupBlockingType
{
Unchanged = 0,
DoNotBlock = 1,
Block = 2
}
public static class CanvaseGroups
{
public static void SetBlockingType(this CanvasGroup group, CanvasGroupBlockingType blockingType)
{
if (blockingType == CanvasGroupBlockingType.Unchanged)
return;
bool block = blockingType == CanvasGroupBlockingType.Block;
group.blocksRaycasts = block;
group.interactable = block;
}
/// <summary>
/// Sets a CanvasGroup blocking type and alpha.
/// </summary>
/// <param name = "blockingType">How to handle interactions.</param>
/// <param name = "alpha">Alpha for CanvasGroup.</param>
public static void SetActive(this CanvasGroup group, CanvasGroupBlockingType blockingType, float alpha)
{
group.SetBlockingType(blockingType);
group.alpha = alpha;
}
/// <summary>
/// Sets a canvasGroup active with specified alpha.
/// </summary>
public static void SetActive(this CanvasGroup group, float alpha)
{
group.SetActive(true, false);
group.alpha = alpha;
}
/// <summary>
/// Sets a canvasGroup inactive with specified alpha.
/// </summary>
public static void SetInactive(this CanvasGroup group, float alpha)
{
group.SetActive(false, false);
group.alpha = alpha;
}
/// <summary>
/// Sets a group active state by changing alpha and interaction toggles.
/// </summary>
public static void SetActive(this CanvasGroup group, bool active, bool setAlpha)
{
if (group == null)
return;
if (setAlpha)
{
if (active)
group.alpha = 1f;
else
group.alpha = 0f;
}
group.interactable = active;
group.blocksRaycasts = active;
}
/// <summary>
/// Sets a group active state by changing alpha and interaction toggles with a custom alpha.
/// </summary>
public static void SetActive(this CanvasGroup group, bool active, float alpha)
{
if (group == null)
return;
group.alpha = alpha;
group.interactable = active;
group.blocksRaycasts = active;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c0e7937b287d3d24d807a115c1a3a464
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs
uploadId: 866910
@@ -0,0 +1,160 @@
using System;
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public static class ColliderExtensions
{
public static void GetBoxOverlapParams(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(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(x1, y1, z1);
point2 = new(x2, y2, z2);
}
public static void GetSphereOverlapParams(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);
}
}
public static class Collider2DExtensions
{
public static void GetBox2DOverlapParams(this BoxCollider2D 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.offset);
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(x, y, z);
}
public static void GetCircleOverlapParams(this CircleCollider2D circleCollider, out Vector3 center, out float radius)
{
Transform cachedTransform = circleCollider.transform;
Vector3 offset = new(circleCollider.offset.x, circleCollider.offset.y, circleCollider.transform.position.z);
center = cachedTransform.TransformPoint(offset);
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 = circleCollider.radius * Math.Max(Math.Max(x, y), z);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 29e69fa855dd3634d9e66313e7748db4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs
uploadId: 866910
@@ -0,0 +1,79 @@
using System.Collections.Generic;
namespace GameKit.Dependencies.Utilities
{
public static class DictionaryFN
{
/// <summary>
/// Uses a hacky way to TryGetValue on a dictionary when using IL2CPP and on mobile.
/// This is to support older devices that don't properly handle IL2CPP builds.
/// </summary>
public static bool TryGetValueIL2CPP<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, TKey key, out TValue value)
{
#if ENABLE_IL2CPP && UNITY_IOS || UNITY_ANDROID
if (dict.ContainsKey(key))
{
value = dict[key];
return true;
}
value = default;
return false;
#else
return dict.TryGetValue(key, out value);
#endif
}
/// <summary>
/// Returns values as a list.
/// </summary>
/// <returns></returns>
public static List<TValue> ValuesToList<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, bool useCache)
{
List<TValue> result = useCache ? CollectionCaches<TValue>.RetrieveList() : new(dict.Count);
//No need to clear the list since it's already clear.
dict.ValuesToList(ref result, clearLst: false);
return result;
}
/// <summary>
/// Adds values to a list.
/// </summary>
public static void ValuesToList<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, ref List<TValue> result, bool clearLst)
{
if (clearLst)
result.Clear();
foreach (TValue item in dict.Values)
result.Add(item);
}
/// <summary>
/// Returns keys as a list.
/// </summary>
public static List<TKey> KeysToList<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, bool useCache)
{
List<TKey> result = useCache ? CollectionCaches<TKey>.RetrieveList() : new(dict.Count);
//No need to clear the list since it's already clear.
dict.KeysToList(ref result, clearLst: false);
return result;
}
/// <summary>
/// Adds keys to a list.
/// </summary>
public static void KeysToList<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, ref List<TKey> result, bool clearLst)
{
result.Clear();
foreach (TKey item in dict.Keys)
result.Add(item);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9d31d19bc39eb6041bad18d8eb68ed68
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs
uploadId: 866910
@@ -0,0 +1,99 @@
using System;
using System.IO;
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public static class Disks
{
/// <summary>
/// Writes specified text to a file path.
/// </summary>
/// <param name = "text"></param>
/// <param name = "path"></param>
/// <param name = "formatPath">True to format the path to the current platform.</param>
public static void WriteToFile(string text, string path, bool formatPath = true)
{
// If to format the path for the platform.
if (formatPath)
path = FormatPlatformPath(path);
// Path came back or was passed in as an empty string.
if (path == string.Empty)
{
Debug.LogError("Path cannot be null.");
return;
}
try
{
// Get directory path.
string directory = Path.GetDirectoryName(path);
// If directory doesn't exist try to create it.
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
//Try to write the file data.
using (FileStream fs = new(path, FileMode.Create))
{
using (StreamWriter writer = new(fs))
{
writer.Write(text);
}
}
}
catch (Exception ex)
{
Debug.LogError($"An error occured during a file write. Error: {ex.Message} {Environment.NewLine} File path: {path} {Environment.NewLine} Text: {text}");
}
/* If within the editor then refresh the asset database so changes
* reflect in the project folder. */
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
}
/// <summary>
/// Formats a file path to the current platform.
/// </summary>
/// <param name = "path"></param>
/// <returns></returns>
public static string FormatPlatformPath(string path)
{
//No path specified.
if (path == string.Empty)
{
Debug.LogError("Path cannot be empty.");
return string.Empty;
}
string convertedPath = string.Empty;
//Get the directories as an array.
string[] directories = path.Split(Path.DirectorySeparatorChar);
//Go through each directory.
for (int i = 0; i < directories.Length; i++)
{
/* If only one entry in array then the path
* is in the root of the Resources folder. */
if (directories.Length == 1)
{
//Append to converted path and break from the loop.
convertedPath = directories[i];
break;
}
//More than one entry, meaning there are sub paths.
else
{
/* Set converted path to the current
* convertedPath combined with the next directory. */
convertedPath = Path.Combine(convertedPath, directories[i]);
}
}
return convertedPath;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a3a909760282d284591c20c873f20837
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs
uploadId: 866910
@@ -0,0 +1,13 @@
// #if UNITY_EDITOR
// using System;
// using UnityEditor;
// using UnityEngine;
//
// namespace GameKit.Dependencies.Utilities
// {
//
// }
//
// #endif
// Remove in V5
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: dd42f76391fc1254f82767dbf1a4bc8b
timeCreated: 1525378031
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs
uploadId: 866910
@@ -0,0 +1,327 @@
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public enum EditorLayoutEnableType
{
Enabled = 0,
Disabled = 1,
DisabledWhilePlaying = 2
}
public static class EditorGuiLayoutTools
{
/// <summary>
/// Adds a helpbox field.
/// </summary>
public static void AddHelpBox(string text, MessageType messageType = MessageType.Info)
{
EditorGUILayout.HelpBox(text, messageType);
}
/// <summary>
/// Adds a property field.
/// </summary>
public static void AddPropertyField(SerializedProperty sp, string fieldName, string tooltip = "")
{
if (tooltip == "")
tooltip = sp.tooltip;
EditorGUILayout.PropertyField(sp, new GUIContent(fieldName, tooltip));
}
/// <summary>
/// Adds a property field.
/// </summary>
public static void AddPropertyField(SerializedProperty sp, GUIContent guiContent)
{
EditorGUILayout.PropertyField(sp, guiContent);
}
/// <summary>
/// Adds a property field.
/// </summary>
public static void AddPropertyField(SerializedProperty sp, GUIContent guiContent = null, EditorLayoutEnableType enableType = EditorLayoutEnableType.Enabled, params GUILayoutOption[] options)
{
bool disable = IsDisableLayoutType(enableType);
if (disable)
GUI.enabled = false;
EditorGUILayout.PropertyField(sp, guiContent, options);
if (disable)
GUI.enabled = true;
}
/// <summary>
/// Adds a property field.
/// </summary>
/// <param name = "enabled">True to have property enabled.</param>
[Obsolete("Use AddPropertyField(SerializedProperty, GUIContent, EditorLayoutEnableType, GUILayoutOption.")]
public static void AddPropertyField(SerializedProperty sp, GUIContent guiContent = null, bool enabled = true, params GUILayoutOption[] options)
{
EditorLayoutEnableType enableType = enabled ? EditorLayoutEnableType.Enabled : EditorLayoutEnableType.Disabled;
bool disable = IsDisableLayoutType(enableType);
if (disable)
GUI.enabled = false;
EditorGUILayout.PropertyField(sp, guiContent, options);
if (disable)
GUI.enabled = true;
}
/// <summary>
/// Adds an object field.
/// </summary>
public static void AddObjectField(string label, MonoScript ms, Type type, bool allowSceneObjects, EditorLayoutEnableType enableType = EditorLayoutEnableType.Enabled, params GUILayoutOption[] options)
{
bool disable = IsDisableLayoutType(enableType);
if (disable)
GUI.enabled = false;
EditorGUILayout.ObjectField("Script:", ms, type, allowSceneObjects, options);
if (disable)
GUI.enabled = true;
}
/// <summary>
/// Disables GUI if playing.
/// </summary>
public static void DisableGUIIfPlaying()
{
if (Application.isPlaying)
GUI.enabled = false;
}
/// <summary>
/// Enables GUI if playing.
/// </summary>
public static void EnableGUIIfPlaying()
{
if (Application.isPlaying)
GUI.enabled = true;
}
/// <summary>
/// Returns if a layout field should be disabled.
/// </summary>
/// <param name = "enableType"></param>
/// <returns></returns>
private static bool IsDisableLayoutType(EditorLayoutEnableType enableType)
{
return enableType == EditorLayoutEnableType.Disabled || (enableType == EditorLayoutEnableType.DisabledWhilePlaying && Application.isPlaying);
}
}
public static class PropertyDrawerToolExtensions
{
/// <summary>
/// Returns GetPropertyHeight value based on drawerTool properties.
/// </summary>
public static float GetPropertyHeight(this PropertyDrawerTool drawerTool)
{
if (drawerTool == null)
return EditorGUIUtility.singleLineHeight;
return EditorGUIUtility.singleLineHeight * drawerTool.LineSpacingMultiplier * drawerTool.PropertiesDrawn;
}
}
/// <summary>
/// Various utility classes relating to floats.
/// </summary>
public class PropertyDrawerTool
{
public PropertyDrawerTool()
{
Debug.LogError($"This initializer is not supported. Use the initializer with arguments.");
}
public PropertyDrawerTool(Rect position, float lineSpacingMultiplier = 1f)
{
Position = position;
LineSpacingMultiplier = lineSpacingMultiplier;
Position = position;
_startingIndent = EditorGUI.indentLevel;
}
/// <summary>
/// Starting position as indicated by the OnGUI method.
/// </summary>
/// <remarks>This value may be modified by user code.</remarks>
public Rect Position = default;
/// <summary>
/// Preferred spacing between each draw.
/// </summary>
public float LineSpacingMultiplier;
/// <summary>
/// Number of entries drawn by this object.
/// </summary>
public int PropertiesDrawn = 0;
/// <summary>
/// Additional position Y of next draw.
/// </summary>
private float _additionalPositionY = 0;
/// <summary>
/// Indent level during initialization.
/// </summary>
private readonly int _startingIndent;
/// <summary>
/// Sets EditorGUI.Indent to the level it were when initializing this class.
/// </summary>
public void SetIndentToStarting() => EditorGUI.indentLevel = _startingIndent;
/// <summary>
/// Draws a label.
/// </summary>
public void DrawLabel(GUIContent lLabel) => DrawLabel(lLabel, EditorStyles.label.fontStyle, indent: 0);
/// <summary>
/// Draws a label.
/// </summary>
public void DrawLabel(GUIContent lLabel, FontStyle styleOverride) => DrawLabel(lLabel, styleOverride, indent: 0);
/// <summary>
/// Draws a label.
/// </summary>
public void DrawLabel(GUIContent lLabel, FontStyle styleOverride, int indent)
{
PropertiesDrawn++;
if (indent != 0)
EditorGUI.indentLevel += indent;
// Set style.
FontStyle startingStyle = EditorStyles.label.fontStyle;
EditorStyles.label.fontStyle = styleOverride;
EditorGUI.PrefixLabel(GetRect(), GUIUtility.GetControlID(FocusType.Passive), lLabel);
EditorStyles.label.fontStyle = startingStyle;
if (indent != 0)
EditorGUI.indentLevel -= indent;
}
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop) => DrawProperty(prop, lLabel: "", indent: 0);
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, string label) => DrawProperty(prop, new GUIContent(label), EditorStyles.label.fontStyle, indent: 0);
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, GUIContent content) => DrawProperty(prop, content, EditorStyles.label.fontStyle, indent: 0);
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, int indent) => DrawProperty(prop, lLabel: "", indent);
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, string lLabel, int indent) => DrawProperty(prop, lLabel, EditorStyles.label.fontStyle, indent);
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, GUIContent content, int indent) => DrawProperty(prop, content, EditorStyles.label.fontStyle, indent);
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, GUIContent content, FontStyle labelStyle) => DrawProperty(prop, content, labelStyle, indent: 0);
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, string lLabel, FontStyle labelStyle, int indent)
{
GUIContent content = lLabel == "" ? default : new GUIContent(lLabel);
DrawProperty(prop, content, labelStyle, indent);
}
/// <summary>
/// Draws a property.
/// </summary>
public void DrawProperty(SerializedProperty prop, GUIContent content, FontStyle labelStyle, int indent)
{
PropertiesDrawn++;
EditorGUI.indentLevel += indent;
FontStyle startingStyle = EditorStyles.label.fontStyle;
EditorStyles.label.fontStyle = labelStyle;
EditorGUI.PropertyField(GetRect(), prop, content);
EditorStyles.label.fontStyle = startingStyle;
EditorGUI.indentLevel -= indent;
}
/// <summary>
/// Draws a help box.
/// </summary>
public void DrawHelpBox(string message, MessageType type = MessageType.Info, int indent = 0)
{
PropertiesDrawn++;
if (indent != 0)
EditorGUI.indentLevel += indent;
// Calculate how much height the help box needs based on the current width
// We subtract the indent spacing from the width to ensure text wrapping is calculated correctly
float indentSpacing = indent * 15f;
float height = EditorStyles.helpBox.CalcHeight(new(message), Position.width - indentSpacing);
// Get the rect and draw
EditorGUI.HelpBox(GetRectForHelpBox(height), message, type);
if (indent != 0)
EditorGUI.indentLevel -= indent;
}
/// <summary>
/// Specialized Rect getter for elements with variable heights like HelpBoxes.
/// </summary>
private Rect GetRectForHelpBox(float height)
{
Rect result = new(Position.x, Position.y + _additionalPositionY, Position.width, height);
// Advance the Y position by the specific height of the box + standard spacing
_additionalPositionY += height + EditorGUIUtility.standardVerticalSpacing;
return result;
}
/// <summary>
/// Gets the next Rect to draw at.
/// </summary>
/// <returns></returns>
public Rect GetRect(float? lineSpacingMultiplierOverride = null)
{
float multiplier = lineSpacingMultiplierOverride ?? LineSpacingMultiplier;
Rect result = new(Position.x, Position.y + _additionalPositionY, Position.width, EditorGUIUtility.singleLineHeight * multiplier);
_additionalPositionY += EditorGUIUtility.singleLineHeight * multiplier;
return result;
}
}
}
#endif
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: dc822ae23a249704184fa571f551f9c8
timeCreated: 1527268448
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs
uploadId: 866910
@@ -0,0 +1,130 @@
using System;
namespace GameKit.Dependencies.Utilities
{
public static class Enums
{
public const int SHIFT_EVERYTHING_INT = ~0;
public const uint SHIFT_EVERYTHING_UINT = ~0u;
// 65535
/// <summary>
/// Determine an enum value from a given string. This can be an expensive function.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "text">Text of string.</param>
/// <param name = "defaultValue">Default value if enum couldn't be found.</param>
/// <returns>Enum found or default value if no enum is found.</returns>
public static T FromString<T>(string text, T defaultValue)
{
// If string is empty or null return default value.
if (string.IsNullOrEmpty(text))
return defaultValue;
// If enum isn't defined return default value.
if (!Enum.IsDefined(typeof(T), (string)text))
return defaultValue;
//Return parsed value.
return (T)Enum.Parse(typeof(T), text, true);
}
/// <summary>
/// Returns if whole(extended enum) has any of the part values.
/// </summary>
/// <param name = "whole"></param>
/// <param name = "part">Values to check for within whole.</param>
/// <returns>Returns true part is within whole.</returns>
public static bool ContainsAllocated(this Enum whole, Enum part)
{
//If not the same type of Enum return false.
/* Commented out for performance. Designer
* should know better than to compare two different
* enums. */
//if (!SameType(value, target))
// return false;
/* Convert enum values to ulong. With so few
* values a uint would be safe, but should
* the options expand ulong is safer. */
ulong wholeNum = Convert.ToUInt64(whole);
ulong partNum = Convert.ToUInt64(part);
return (wholeNum & partNum) != 0;
}
/// <summary>
/// Returns if part values contains any of whole(extended enum).
/// </summary>
/// <param name = "whole"></param>
/// <param name = "part"></param>
/// <returns>Returns true whole is within part.</returns>
public static bool ReverseContains(this Enum whole, Enum part)
{
//If not the same type of Enum return false.
/* Commented out for performance. Designer
* should know better than to compare two different
* enums. */
//if (!SameType(value, target))
// return false;
/* Convert enum values to ulong. With so few
* values a uint would be safe, but should
* the options expand ulong is safer. */
ulong wholeNum = Convert.ToUInt64(whole);
ulong partNum = Convert.ToUInt64(part);
return (partNum & wholeNum) != 0;
}
/// <summary>
/// Returns if an enum equals a specified value.
/// </summary>
/// <param name = "value"></param>
/// <param name = "target"></param>
/// <returns></returns>
public static bool Equals(this Enum value, Enum target)
{
//If not the same type of Enum return false.
/* Commented out for performance. Designer
* should know better than to compare two different
* enums. */
//if (!SameType(value, target))
// return false;
ulong valueNum = Convert.ToUInt64(value);
ulong wholeNum = Convert.ToUInt64(target);
return valueNum == wholeNum;
}
/// <summary>
/// Returns if a is the same Enum as b.
/// </summary>
/// <param name = "a"></param>
/// <param name = "target"></param>
/// <returns></returns>
public static bool SameType(Enum a, Enum b)
{
return a.GetType() == b.GetType();
}
/// <summary>
/// Returns the highest numeric value for T.
/// </summary>
public static int GetHighestValue<T>()
{
Type enumType = typeof(T);
/* Brute force enum values.
* Linq Last/Max lookup throws for IL2CPP. */
int highestValue = 0;
Array pidValues = Enum.GetValues(enumType);
foreach (T pid in pidValues)
{
object obj = Enum.Parse(enumType, pid.ToString());
int value = Convert.ToInt32(obj);
highestValue = Math.Max(highestValue, value);
}
return highestValue;
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: e6c66aec505f9254491b2b126a2d4745
timeCreated: 1522959833
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs
uploadId: 866910
@@ -0,0 +1,228 @@
using System;
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public static class Floats
{
/// <summary>
/// Used to randomize float values.
/// </summary>
private static System.Random _random = new();
/// <summary>
/// Sets a source float to value if equal to or greater than tolerance.
/// </summary>
/// <param name = "source">Float to check against tolerance.</param>
/// <param name = "tolerance">Tolerance float must be equal to or greater than to change to value.</param>
/// <param name = "value">Value source is set to when breaking tolerance.</param>
public static float SetIfOverTolerance(this float source, float tolerance, float value)
{
if (source >= tolerance)
source = value;
return source;
}
/// <summary>
/// Sets a source float to value if equal to or less than tolerance.
/// </summary>
/// <param name = "source">Float to check against tolerance.</param>
/// <param name = "tolerance">Tolerance float must be equal to or less than to change to value.</param>
/// <param name = "value">Value source is set to when breaking tolerance.</param>
public static float SetIfUnderTolerance(this float source, float tolerance, float value)
{
if (source <= tolerance)
source = value;
return source;
}
/// <summary>
/// Returns how much time is left on an endTime. Returns -1 if no time is left.
/// </summary>
/// <returns></returns>
public static float TimeRemainingValue(this float endTime)
{
float remaining = endTime - Time.time;
// None remaining.
if (remaining < 0f)
return -1f;
return endTime - Time.time;
}
/// <summary>
/// Returns how much time is left on an endTime. Returns -1 if no time is left.
/// </summary>
/// <returns></returns>
public static int TimeRemainingValue(this float endTime, bool useFloor = true)
{
float remaining = endTime - Time.time;
// None remaining.
if (remaining < 0f)
return -1;
float result = endTime - Time.time;
return useFloor ? Mathf.FloorToInt(result) : Mathf.CeilToInt(result);
}
/// <summary>
/// Returns time remaining as a string using hh:mm:ss.
/// </summary>
/// <param name = "value"></param>
/// <param name = "segments">Number of places to return. 1 is seconds, 2 is minutes, 3 is hours. If a placement does not exist it is replaced with 00.</param>
/// <param name = "emptyOnZero">True to return an empty string when value is 0 or less.</param>
/// <returns></returns>
public static string TimeRemainingText(this float value, byte segments, bool emptyOnZero = false)
{
if (emptyOnZero && value <= 0f)
return string.Empty;
int timeRounded = Math.Max(Mathf.RoundToInt(value), 0);
TimeSpan t = TimeSpan.FromSeconds(timeRounded);
int hours = Mathf.FloorToInt(t.Hours);
int minutes = Mathf.FloorToInt(t.Minutes);
int seconds = Mathf.FloorToInt(t.Seconds);
string timeText;
if (segments == 1)
{
seconds += minutes * 60;
seconds += hours * 3600;
timeText = string.Format("{0:D2}", seconds);
}
else if (segments == 2)
{
minutes += hours * 60;
timeText = string.Format("{0:D2}:{1:D2}", minutes, seconds);
}
else
{
timeText = string.Format("{0:D2}:{1:D2}:{2:D2}", hours, minutes, seconds);
}
return timeText;
}
/// <summary>
/// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max.
/// </summary>
/// <param name = "minimum">Inclusive minimum value.</param>
/// <param name = "maximum">Inclusive maximum value.</param>
/// <returns></returns>
public static float RandomInclusiveRange(float minimum, float maximum)
{
double min = Convert.ToDouble(minimum);
double max = Convert.ToDouble(maximum);
double result = _random.NextDouble() * (max - min) + min;
return Convert.ToSingle(result);
}
/// <summary>
/// Returns a random float between 0f and 1f.
/// </summary>
/// <returns></returns>
public static float Random01()
{
return RandomInclusiveRange(0f, 1f);
}
/// <summary>
/// Returns if a target float is within variance of the source float.
/// </summary>
/// <param name = "a"></param>
/// <param name = "b"></param>
/// <param name = "tolerance"></param>
public static bool Near(this float a, float b, float tolerance = 0.01f)
{
return Mathf.Abs(a - b) <= tolerance;
}
/// <summary>
/// Clamps a float and returns if the float required clamping.
/// </summary>
/// <param name = "value"></param>
/// <param name = "min"></param>
/// <param name = "max"></param>
/// <param name = "clamped"></param>
/// <returns></returns>
public static float Clamp(float value, float min, float max, ref bool clamped)
{
clamped = value < min;
if (clamped)
return min;
clamped = value > min;
if (clamped)
return max;
clamped = false;
return value;
}
/// <summary>
/// Returns a float after being adjusted by the specified variance.
/// </summary>
/// <param name = "source"></param>
/// <param name = "variance"></param>
/// <returns></returns>
public static float Variance(this float source, float variance)
{
float pickedVariance = RandomInclusiveRange(1f - variance, 1f + variance);
return source * pickedVariance;
}
/// <summary>
/// Sets a float value to result after being adjusted by the specified variance.
/// </summary>
/// <param name = "source"></param>
/// <param name = "variance"></param>
/// <returns></returns>
public static void Variance(this float source, float variance, ref float result)
{
float pickedVariance = RandomInclusiveRange(1f - variance, 1f + variance);
result = source * pickedVariance;
}
/// <summary>
/// Returns negative-one, zero, or postive-one of a value instead of just negative-one or positive-one.
/// </summary>
/// <param name = "value">Value to sign.</param>
/// <returns>Precise sign.</returns>
public static float PreciseSign(float value)
{
if (value == 0f)
return 0f;
else
return Mathf.Sign(value);
}
/// <summary>
/// Returns if a float is within a range.
/// </summary>
/// <param name = "source">Value of float.</param>
/// <param name = "rangeMin">Minimum of range.</param>
/// <param name = "rangeMax">Maximum of range.</param>
/// <returns></returns>
public static bool InRange(this float source, float rangeMin, float rangeMax)
{
return source >= rangeMin && source <= rangeMax;
}
/// <summary>
/// Randomly flips a float value.
/// </summary>
/// <param name = "value"></param>
/// <returns></returns>
public static float RandomlyFlip(this float value)
{
if (Ints.RandomInclusiveRange(0, 1) == 0)
return value;
else
return value *= -1f;
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 4ab517aa5c3b6e34ca20461339adda04
timeCreated: 1526172456
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs
uploadId: 866910
@@ -0,0 +1,10 @@
namespace GameKit.Dependencies.Utilities
{
public static class Guids
{
/// <summary>
/// A buffer convert data and discard.
/// </summary>
public static byte[] Buffer = new byte[16];
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 51b4d6f1925ec014d8e37fc1d8c89c71
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Guids.cs
uploadId: 866910
@@ -0,0 +1,44 @@
using System.Collections.Generic;
namespace GameKit.Dependencies.Utilities
{
public static class HashSetsFN
{
/// <summary>
/// Adds a collection of items.
/// </summary>
public static void AddRange<T>(this HashSet<T> hashSet, IEnumerable<T> items)
{
foreach (T item in items)
hashSet.Add(item);
}
/// <summary>
/// Returns values as a list.
/// </summary>
/// <returns></returns>
public static List<T> ToList<T>(this HashSet<T> collection, bool useCache)
{
List<T> result = useCache ? CollectionCaches<T>.RetrieveList() : new(collection.Count);
//No need to clear the list since it's already clear.
collection.ToList(ref result, clearLst: false);
return result;
}
/// <summary>
/// Adds values to a list.
/// </summary>
/// <returns></returns>
public static void ToList<T>(this HashSet<T> collection, ref List<T> lst, bool clearLst)
{
if (clearLst)
lst.Clear();
foreach (T item in collection)
lst.Add(item);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 552af765f3c527b4bb6d72c672044024
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/HashSets.cs
uploadId: 866910
@@ -0,0 +1,74 @@
namespace GameKit.Dependencies.Utilities
{
public static class Hashing
{
private const uint FNV_offset_basis32 = 2166136261;
private const uint FNV_prime32 = 16777619;
private const ulong FNV_offset_basis64 = 14695981039346656037;
private const ulong FNV_prime64 = 1099511628211;
/// <summary>
/// non cryptographic stable hash code,
/// it will always return the same hash for the same
/// string.
/// This is simply an implementation of FNV-1 32 bit xor folded to 16 bit
/// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
/// </summary>
/// <returns>The stable hash32.</returns>
/// <param name = "txt">Text.</param>
public static ushort GetStableHashU16(this string txt)
{
uint hash32 = txt.GetStableHashU32();
return (ushort)((hash32 >> 16) ^ hash32);
}
/// <summary>
/// non cryptographic stable hash code,
/// it will always return the same hash for the same
/// string.
/// This is simply an implementation of FNV-1 32 bit
/// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
/// </summary>
/// <returns>The stable hash32.</returns>
/// <param name = "txt">Text.</param>
public static uint GetStableHashU32(this string txt)
{
unchecked
{
uint hash = FNV_offset_basis32;
for (int i = 0; i < txt.Length; i++)
{
uint ch = txt[i];
hash = hash * FNV_prime32;
hash = hash ^ ch;
}
return hash;
}
}
/// <summary>
/// non cryptographic stable hash code,
/// it will always return the same hash for the same
/// string.
/// This is simply an implementation of FNV-1 64 bit
/// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
/// </summary>
/// <returns>The stable hash32.</returns>
/// <param name = "txt">Text.</param>
public static ulong GetStableHashU64(this string txt)
{
unchecked
{
ulong hash = FNV_offset_basis64;
for (int i = 0; i < txt.Length; i++)
{
ulong ch = txt[i];
hash = hash * FNV_prime64;
hash = hash ^ ch;
}
return hash;
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7be723c9549bdd041ac1dc8e8c6d2d18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs
uploadId: 866910
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.IO;
namespace GameKit.Dependencies.Utilities
{
public static class IOs
{
/// <summary>
/// Finds all prefab files in a path.
/// </summary>
/// <param name = "startingPath">Path to begin searching in; this is typically "Assets".</param>
/// <param name = "excludedPaths">Paths to exclude when searching.</param>
/// <param name = "recursive">True to search subpaths.</param>
/// <returns></returns>
public static string[] GetDirectoryFiles(string startingPath, HashSet<string> excludedPaths, bool recursive, string extension)
{
// Opportunity to exit early if there are no excluded paths.
if (excludedPaths.Count == 0)
{
string[] strResults = Directory.GetFiles(startingPath, extension, SearchOption.AllDirectories);
return strResults;
}
// starting path is excluded.
if (excludedPaths.Contains(startingPath))
return new string[0];
// Folders remaining to be iterated.
List<string> enumeratedCollection = new() { startingPath };
// Only check other directories if recursive.
if (recursive)
{
// Find all folders which aren't excluded.
for (int i = 0; i < enumeratedCollection.Count; i++)
{
string[] allFolders = Directory.GetDirectories(enumeratedCollection[i], "*", SearchOption.TopDirectoryOnly);
for (int z = 0; z < allFolders.Length; z++)
{
string current = allFolders[z];
// Not excluded.
if (!excludedPaths.Contains(current))
enumeratedCollection.Add(current);
}
}
}
// Valid prefab files.
List<string> results = new();
// Build files from folders.
int count = enumeratedCollection.Count;
for (int i = 0; i < count; i++)
{
string[] r = Directory.GetFiles(enumeratedCollection[i], extension, SearchOption.TopDirectoryOnly);
results.AddRange(r);
}
return results.ToArray();
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b1994e262a1e963479497289602e4461
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs
uploadId: 866910
@@ -0,0 +1,89 @@
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
/// <summary>
/// Various utility classes relating to floats.
/// </summary>
public static class Ints
{
private static System.Random _random = new();
/// <summary>
/// Pads an index a specified value. Preferred over typical padding so that pad values used with skins can be easily found in the code.
/// </summary>
/// <param name = "value"></param>
/// <param name = "padding"></param>
/// <returns></returns>
public static string PadInt(int value, int padding)
{
return value.ToString().PadLeft(padding, '0');
}
/// <summary>
/// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max.
/// </summary>
/// <param name = "minimum">Inclusive minimum value.</param>
/// <param name = "maximum">Inclusive maximum value.</param>
/// <returns></returns>
public static int RandomInclusiveRange(int minimum, int maximum)
{
return _random.Next(minimum, maximum + 1);
}
/// <summary>
/// Provides a random exclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max.
/// </summary>
/// <param name = "minimum">Inclusive minimum value.</param>
/// <param name = "maximum">Exclusive maximum value.</param>
/// <returns></returns>
public static int RandomExclusiveRange(int minimum, int maximum)
{
return _random.Next(minimum, maximum);
}
/// <summary>
/// Returns a clamped int within a specified range.
/// </summary>
/// <param name = "value">Value to clamp.</param>
/// <param name = "minimum">Minimum value.</param>
/// <param name = "maximum">Maximum value.</param>
/// <returns></returns>
public static int Clamp(int value, int minimum, int maximum)
{
if (value < minimum)
value = minimum;
else if (value > maximum)
value = maximum;
return value;
}
/// <summary>
/// Determins if all values passed in are the same.
/// </summary>
/// <param name = "values">Values to check.</param>
/// <returns>True if all values are the same.</returns>
public static bool ValuesMatch(params int[] values)
{
if (values.Length == 0)
{
Debug.Log("Ints -> ValuesMatch -> values array is empty.");
return false;
}
// Assign first value as element in first array.
int firstValue = values[0];
// Check all values.
for (int i = 1; i < values.Length; i++)
{
// If any value doesn't match first value return false.
if (firstValue != values[i])
return false;
}
// If this far all values match.
return true;
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: c673118198f5c4b41986d52762828363
timeCreated: 1527268448
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs
uploadId: 866910
@@ -0,0 +1,104 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public static class Layers
{
/* GetInteractableLayer methods is an implementation from this
* link: https://forum.unity.com/threads/is-there-a-way-to-get-the-layer-collision-matrix.260744/#post-3483886 */
/// <summary>
/// Lookup of interactable layers for each layer.
/// </summary>
private static Dictionary<int, int> _interactablesLayers;
/// <summary>
/// Tries to initializes InteractableLayers.
/// </summary>
private static void TryInitializeInteractableLayers()
{
if (_interactablesLayers != null)
return;
_interactablesLayers = new();
for (int i = 0; i < 32; i++)
{
int mask = 0;
for (int j = 0; j < 32; j++)
{
if (!Physics.GetIgnoreLayerCollision(i, j))
{
mask |= 1 << j;
}
}
// Setting without add check is quicker.
_interactablesLayers[i] = mask;
}
}
/// <summary>
/// Returns interactable layers value for layer.
/// </summary>
public static int GetInteractableLayersValue(int layer)
{
TryInitializeInteractableLayers();
return _interactablesLayers[layer];
}
/// <summary>
/// Returns interactable layers LayerMask for a GameObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static LayerMask GetInteractableLayersMask(int layer) => (LayerMask)GetInteractableLayersValue(layer);
/// <summary>
/// Returns interactable layers value for a GameObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetInteractableLayersValue(GameObject go) => GetInteractableLayersValue(go.layer);
/// <summary>
/// Returns interactable layers LayerMask for a GameObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static LayerMask GetInteractableLayersMask(GameObject go) => (LayerMask)GetInteractableLayersValue(go.layer);
/// <summary>
/// Converts a layer mask to a layer number.
/// </summary>
/// <param name = "mask"></param>
/// <returns></returns>
public static int LayerMaskToLayerNumber(LayerMask mask)
{
return LayerValueToLayerNumber(mask.value);
}
/// <summary>
/// Converts a layer value int to a layer int.
/// </summary>
/// <param name = "bitmask"></param>
/// <returns></returns>
public static int LayerValueToLayerNumber(int bitmask)
{
int result = bitmask > 0 ? 0 : 31;
while (bitmask > 1)
{
bitmask = bitmask >> 1;
result++;
}
return result;
}
/// <summary>
/// Returns if a LayerMask contains a specified layer.
/// </summary>
/// <param name = "layerMask">LayerMask to check for layer in.</param>
/// <param name = "layer">Layer to check within LayerMask.</param>
/// <returns></returns>
public static bool ContainsLayer(LayerMask layerMask, int layer)
{
return layerMask == (layerMask | (1 << layer));
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 1c18e15e44d21a94d8919f4b6b125a1f
timeCreated: 1522349045
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs
uploadId: 866910
@@ -0,0 +1,17 @@
using UnityEngine;
using UnityEngine.UI;
namespace GameKit.Dependencies.Utilities
{
public static class LayoutGroups
{
/// <summary>
/// Returns how many entries can fit into a GridLayoutGroup
/// </summary>
public static int EntriesPerWidth(this GridLayoutGroup lg)
{
RectTransform rectTransform = lg.GetComponent<RectTransform>();
return Mathf.CeilToInt(rectTransform.rect.width / lg.cellSize.x);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e330113395c59ca4dba5de001e010f08
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs
uploadId: 866910
@@ -0,0 +1,45 @@
using System.Collections.Generic;
namespace GameKit.Dependencies.Utilities
{
public static class ListsFN
{
/// <summary>
/// Adds items to collection while preventing duplicates, returning number of items added.
/// </summary>
public static int AddRangeUnique<T>(this List<T> collection, IEnumerable<T> items)
{
int added = 0;
foreach (T item in items)
{
if (!collection.Contains(item))
{
collection.Add(item);
added++;
}
}
return added;
}
/// <summary>
/// Adds item to collection while preventing duplicates, returning if added.
/// </summary>
public static bool AddUnique<T>(this List<T> collection, T item)
{
if (!collection.Contains(item))
{
collection.Add(item);
return true;
}
return false;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b5ae7a9f43f78a345839f65ff75ba384
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Lists.cs
uploadId: 866910
@@ -0,0 +1,34 @@
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public static class Materials
{
/// <summary>
/// Returns the color or tint color property for a material.
/// </summary>
/// <param name = "material"></param>
/// <returns></returns>
public static Color GetColor(this Material material)
{
if (material.HasProperty("_Color"))
return material.color;
else if (material.HasProperty("_TintColor"))
return material.GetColor("_TintColor");
return Color.white;
}
/// <summary>
/// Sets the color or tint color property for a material.
/// </summary>
/// <param name = "material"></param>
public static void SetColor(this Material material, Color color)
{
if (material.HasProperty("_Color"))
material.color = color;
else if (material.HasProperty("_TintColor"))
material.SetColor("_TintColor", color);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 27a618c551d5fdb4ca70bf07e1905580
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs
uploadId: 866910
@@ -0,0 +1,44 @@
namespace GameKit.Dependencies.Utilities
{
public static class Maths
{
/// <summary>
/// Returns a clamped SBytte.
/// </summary>
public static sbyte ClampSByte(long value, sbyte min, sbyte max)
{
if (value < min)
return min;
else if (value > max)
return max;
else
return (sbyte)value;
}
/// <summary>
/// Returns a clamped double.
/// </summary>
public static double ClampDouble(double value, double min, double max)
{
if (value < min)
return min;
else if (value > max)
return max;
else
return value;
}
/// <summary>
/// Returns a clamped byte.
/// </summary>
public static byte ClampByte(byte value, byte min, byte max)
{
if (value < min)
return min;
else if (value > max)
return max;
else
return value;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 18a583dc22a9a0f4cabec0c4a0219c6e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs
uploadId: 866910
@@ -0,0 +1,200 @@
#if NEW_INPUTSYSTEM
using UnityEngine;
using UnityEngine.InputSystem;
namespace GameKit.Dependencies.Utilities
{
public static class NewInput
{
/// <summary>
/// Current Keyboard.
/// </summary>
public static Keyboard Keyboard => Keyboard.current;
/// <summary>
/// Current Mouse.
/// </summary>
public static Mouse Mouse => Mouse.current;
/// <summary>
/// Returns if a button is held on any map.
/// </summary>
public static bool GetButtonHeld(Key key)
{
return (Keyboard != null) ? Keyboard[key].isPressed : false;
}
/// <summary>
/// Returns if a button is pressed on any map.
/// </summary>
public static bool GetButtonPressed(Key key)
{
return (Keyboard != null) ? Keyboard[key].wasPressedThisFrame : false;
}
/// <summary>
/// Returns if a button is released on any map.
/// </summary>
public static bool GetButtonReleased(Key key)
{
return (Keyboard != null) ? Keyboard[key].wasReleasedThisFrame : false;
}
}
public static class MouseExtensions
{
public static Vector3 GetPosition(this Mouse m)
{
return m.position.ReadValue();
}
}
public static class KeyboardExtensions
{
public static bool GetKeyPressed(this Keyboard kb, Key kc)
{
return kb[kc].wasPressedThisFrame;
}
public static bool GetKeyHeld(this Keyboard kb, Key kc)
{
return kb[kc].isPressed;
}
public static bool GetKeyReleased(this Keyboard kb, Key kc)
{
return kb[kc].wasReleasedThisFrame;
}
}
public static class InputActionMapExtensions
{
#region Strings.
public static float GetAxisRaw(this InputActionMap map, string negativeName, string positiveName)
{
return map.GetAxisRaw(negativeName, positiveName, out _);
}
public static float GetAxisRaw(this InputActionMap map, string negativeName, string positiveName, out bool found)
{
found = false;
InputAction negativeIa = map.FindAction(negativeName);
InputAction positiveIa = map.FindAction(positiveName);
if (negativeIa == null || positiveIa == null)
return 0f;
found = true;
bool negativePressed = negativeIa.IsPressed();
bool positivePressed = positiveIa.IsPressed();
/* If both are pressed then they cancel each other out.
* And if neither are pressed then result is naturally
* 0f. */
if (negativePressed == positivePressed)
return 0f;
else if (negativePressed)
return -1f;
else
return 1f;
}
public static float GetAxisRaw(this InputActionMap map, string inputName)
{
return map.GetAxisRaw(inputName, out _);
}
public static float GetAxisRaw(this InputActionMap map, string inputName, out bool found)
{
found = false;
InputAction ia = map.FindAction(inputName);
if (ia == null)
return 0f;
found = true;
float axis = ia.ReadValue<float>();
if (axis == 0f)
return 0f;
else
return Mathf.Sign(axis);
}
public static bool GetButtonHeld(this InputActionMap map, string inputName)
{
InputAction ia = map.FindAction(inputName);
return (ia == null) ? false : ia.IsPressed();
}
public static bool GetButtonPressed(this InputActionMap map, string inputName)
{
InputAction ia = map.FindAction(inputName);
return (ia == null) ? false : ia.WasPressedThisFrame();
}
public static bool GetButtonReleased(this InputActionMap map, string inputName)
{
InputAction ia = map.FindAction(inputName);
return (ia == null) ? false : ia.WasReleasedThisFrame();
}
#endregion
#region InputActions.
public static float GetAxisRaw(InputAction negativeIa, InputAction positiveIa)
{
return GetAxisRaw(negativeIa, positiveIa, out _);
}
public static float GetAxisRaw(InputAction negativeIa, InputAction positiveIa, out bool found)
{
found = false;
if (negativeIa == null || positiveIa == null)
return 0f;
found = true;
bool negativePressed = negativeIa.IsPressed();
bool positivePressed = positiveIa.IsPressed();
/* If both are pressed then they cancel each other out.
* And if neither are pressed then result is naturally
* 0f. */
if (negativePressed == positivePressed)
return 0f;
else if (negativePressed)
return -1f;
else
return 1f;
}
public static float GetAxisRaw(this InputAction ia)
{
return GetAxisRaw(ia, out _);
}
public static float GetAxisRaw(this InputAction ia, out bool found)
{
found = false;
if (ia == null)
return 0f;
found = true;
float axis = ia.ReadValue<float>();
if (axis == 0f)
return 0f;
else
return Mathf.Sign(axis);
}
public static bool GetButtonHeld(this InputAction ia)
{
return (ia == null) ? false : ia.IsPressed();
}
public static bool GetButtonPressed(this InputAction ia)
{
return (ia == null) ? false : ia.WasPressedThisFrame();
}
public static bool GetButtonReleased(this InputAction ia)
{
return (ia == null) ? false : ia.WasReleasedThisFrame();
}
#endregion
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ea6d91237dc169249b4a375e7e9eef00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs
uploadId: 866910
@@ -0,0 +1,892 @@
using System;
using System.Collections.Concurrent;
using GameKit.Dependencies.Utilities.Types;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
// ReSharper disable ThreadStaticFieldHasInitializesr
namespace GameKit.Dependencies.Utilities
{
/// <summary>
/// Implement to use type with Caches.
/// </summary>
public interface IResettable
{
/// <summary>
/// Resets values when being placed in a cache.
/// </summary>
void ResetState();
/// <summary>
/// Initializes values after being retrieved from a cache.
/// </summary>
void InitializeState();
}
#region Resettable caches.
/// <summary>
/// Caches collections of multiple generics.
/// </summary>
public static class ResettableCollectionCaches<T1, T2> where T1 : IResettable, new() where T2 : IResettable, new()
{
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static Dictionary<T1, T2> RetrieveDictionary() => CollectionCaches<T1, T2>.RetrieveDictionary();
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref Dictionary<T1, T2> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(Dictionary<T1, T2> value)
{
if (value == null)
return;
foreach (KeyValuePair<T1, T2> kvp in value)
{
ResettableObjectCaches<T1>.Store(kvp.Key);
ResettableObjectCaches<T2>.Store(kvp.Value);
}
value.Clear();
CollectionCaches<T1, T2>.Store(value);
}
}
/// <summary>
/// Caches collections of multiple generics.
/// </summary>
public static class ResettableT1CollectionCaches<T1, T2> where T1 : IResettable, new()
{
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static Dictionary<T1, T2> RetrieveDictionary() => CollectionCaches<T1, T2>.RetrieveDictionary();
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref Dictionary<T1, T2> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(Dictionary<T1, T2> value)
{
if (value == null)
return;
foreach (T1 item in value.Keys)
ResettableObjectCaches<T1>.Store(item);
value.Clear();
CollectionCaches<T1, T2>.Store(value);
}
}
/// <summary>
/// Caches collections of multiple generics.
/// </summary>
public static class ResettableT2CollectionCaches<T1, T2> where T2 : IResettable, new()
{
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static Dictionary<T1, T2> RetrieveDictionary() => CollectionCaches<T1, T2>.RetrieveDictionary();
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref Dictionary<T1, T2> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(Dictionary<T1, T2> value)
{
if (value == null)
return;
foreach (T2 item in value.Values)
ResettableObjectCaches<T2>.Store(item);
value.Clear();
CollectionCaches<T1, T2>.Store(value);
}
}
/// <summary>
/// Caches collections of a single generic.
/// </summary>
public static class ResettableCollectionCaches<T> where T : IResettable, new()
{
/// <summary>
/// Cache for ResettableRingBuffer.
/// </summary>
private static readonly ConcurrentStack<ResettableRingBuffer<T>> _resettableRingBufferCache = new();
/// <summary>
/// Maximum number of entries allowed for the cache.
/// </summary>
private const int MAXIMUM_CACHE_COUNT = 50;
/// <summary>
/// Retrieves a collection.
/// </summary>
public static ResettableRingBuffer<T> RetrieveRingBuffer()
{
ResettableRingBuffer<T> result;
if (!_resettableRingBufferCache.TryPop(out result))
result = new();
return result;
}
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static T[] RetrieveArray() => CollectionCaches<T>.RetrieveArray();
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static List<T> RetrieveList() => CollectionCaches<T>.RetrieveList();
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static SortedSet<T> RetrieveSortedSet() => CollectionCaches<T>.RetrieveSortedSet();
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static HashSet<T> RetrieveHashSet() => CollectionCaches<T>.RetrieveHashSet();
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static Queue<T> RetrieveQueue() => CollectionCaches<T>.RetrieveQueue();
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static BasicQueue<T> RetrieveBasicQueue() => CollectionCaches<T>.RetrieveBasicQueue();
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
/// <param name = "count">Number of entries in the array from the beginning.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref ResettableRingBuffer<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
/// <param name = "count">Number of entries in the array from the beginning.</param>
public static void Store(ResettableRingBuffer<T> value)
{
if (value == null)
return;
value.ResetState();
if (_resettableRingBufferCache.Count < MAXIMUM_CACHE_COUNT)
_resettableRingBufferCache.Push(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
/// <param name = "count">Number of entries in the array from the beginning.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref T[] value, int count)
{
Store(value, count);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
/// <param name = "count">Number of entries in the array from the beginning.</param>
public static void Store(T[] value, int count)
{
if (value == null)
return;
for (int i = 0; i < count; i++)
ResettableObjectCaches<T>.Store(value[i]);
CollectionCaches<T>.Store(value, count);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref List<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(List<T> value)
{
if (value == null)
return;
for (int i = 0; i < value.Count; i++)
ResettableObjectCaches<T>.Store(value[i]);
value.Clear();
CollectionCaches<T>.Store(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref SortedSet<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(SortedSet<T> value)
{
if (value == null)
return;
foreach (T item in value)
ResettableObjectCaches<T>.Store(item);
value.Clear();
CollectionCaches<T>.Store(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref HashSet<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(HashSet<T> value)
{
if (value == null)
return;
foreach (T item in value)
ResettableObjectCaches<T>.Store(item);
value.Clear();
CollectionCaches<T>.Store(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref Queue<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(Queue<T> value)
{
if (value == null)
return;
foreach (T item in value)
ResettableObjectCaches<T>.Store(item);
value.Clear();
CollectionCaches<T>.Store(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref BasicQueue<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(BasicQueue<T> value)
{
if (value == null)
return;
while (value.TryDequeue(out T result))
ResettableObjectCaches<T>.Store(result);
value.Clear();
CollectionCaches<T>.Store(value);
}
}
/// <summary>
/// Caches objects of a single generic.
/// </summary>
public static class ResettableObjectCaches<T> where T : IResettable, new()
{
/// <summary>
/// Retrieves an instance of T.
/// </summary>
public static T Retrieve()
{
T result = ObjectCaches<T>.Retrieve();
result.InitializeState();
return result;
}
/// <summary>
/// Stores an instance of T and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref T value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores an instance of T.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(T value)
{
if (value == null)
return;
value.ResetState();
ObjectCaches<T>.Store(value);
}
}
#endregion
#region NonResettable caches.
/// <summary>
/// Caches collections of multiple generics.
/// </summary>
public static class CollectionCaches<T1, T2>
{
/// <summary>
/// Cache for dictionaries.
/// </summary>
private static readonly ConcurrentStack<Dictionary<T1, T2>> _dictionaryCache = new();
/// <summary>
/// Maximum number of entries allowed for the cache.
/// </summary>
private const int MAXIMUM_CACHE_COUNT = 50;
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static Dictionary<T1, T2> RetrieveDictionary()
{
Dictionary<T1, T2> result;
if (!_dictionaryCache.TryPop(out result))
result = new();
return result;
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref Dictionary<T1, T2> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(Dictionary<T1, T2> value)
{
if (value == null)
return;
value.Clear();
if (_dictionaryCache.Count < MAXIMUM_CACHE_COUNT)
_dictionaryCache.Push(value);
}
}
/// <summary>
/// Caches collections of a single generic.
/// </summary>
public static partial class CollectionCaches<T>
{
/// <summary>
/// Cache for arrays.
/// </summary>
private static readonly ConcurrentStack<T[]> _arrayCache;
/// <summary>
/// Cache for lists.
/// </summary>
private static readonly ConcurrentStack<List<T>> _listCache;
/// <summary>
/// Cache for sortedset.
/// </summary>
private static readonly ConcurrentStack<SortedSet<T>> _sortedSetCache;
/// <summary>
/// Cache for queues.
/// </summary>
private static readonly ConcurrentStack<Queue<T>> _queueCache;
/// <summary>
/// Cache for queues.
/// </summary>
private static readonly ConcurrentStack<BasicQueue<T>> _basicQueueCache;
/// <summary>
/// Cache for hashset.
/// </summary>
private static readonly ConcurrentStack<HashSet<T>> _hashSetCache;
/// <summary>
/// Maximum number of entries allowed for the cache.
/// </summary>
private const int MAXIMUM_CACHE_COUNT = 50;
static CollectionCaches()
{
_arrayCache = new();
_listCache = new();
_sortedSetCache = new();
_queueCache = new();
_basicQueueCache = new();
_hashSetCache = new();
}
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static T[] RetrieveArray()
{
T[] result;
if (!_arrayCache.TryPop(out result))
result = new T[0];
return result;
}
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static List<T> RetrieveList()
{
List<T> result;
if (!_listCache.TryPop(out result))
result = new();
return result;
}
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static SortedSet<T> RetrieveSortedSet()
{
SortedSet<T> result;
if (!_sortedSetCache.TryPop(out result))
result = new();
return result;
}
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static Queue<T> RetrieveQueue()
{
Queue<T> result;
if (!_queueCache.TryPop(out result))
result = new();
return result;
}
/// <summary>
/// Retrieves a collection.
/// </summary>
/// <returns></returns>
public static BasicQueue<T> RetrieveBasicQueue()
{
BasicQueue<T> result;
if (!_basicQueueCache.TryPop(out result))
result = new();
return result;
}
/// <summary>
/// Retrieves a collection adding one entry.
/// </summary>
/// <returns></returns>
public static Queue<T> RetrieveQueue(T entry)
{
Queue<T> result;
if (!_queueCache.TryPop(out result))
result = new();
result.Enqueue(entry);
return result;
}
/// <summary>
/// Retrieves a collection adding one entry.
/// </summary>
/// <returns></returns>
public static List<T> RetrieveList(T entry)
{
List<T> result;
if (!_listCache.TryPop(out result))
result = new();
result.Add(entry);
return result;
}
/// <summary>
/// Retrieves a HashSet<T>.
/// </summary>
/// <returns></returns>
public static HashSet<T> RetrieveHashSet()
{
HashSet<T> result;
if (!_hashSetCache.TryPop(out result))
result = new();
return result;
}
/// <summary>
/// Retrieves a collection adding one entry.
/// </summary>
/// <returns></returns>
public static HashSet<T> RetrieveHashSet(T entry)
{
HashSet<T> result;
if (!_hashSetCache.TryPop(out result))
return new();
result.Add(entry);
return result;
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
/// <param name = "count">Number of entries in the array set default, from the beginning.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref T[] value, int count)
{
Store(value, count);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
/// <param name = "count">Number of entries in the array from the beginning.</param>
public static void Store(T[] value, int count)
{
if (value == null)
return;
for (int i = 0; i < count; i++)
value[i] = default;
if (_arrayCache.Count < MAXIMUM_CACHE_COUNT)
_arrayCache.Push(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref List<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(List<T> value)
{
if (value == null)
return;
value.Clear();
if (_listCache.Count < MAXIMUM_CACHE_COUNT)
_listCache.Push(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref SortedSet<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(SortedSet<T> value)
{
if (value == null)
return;
value.Clear();
if (_sortedSetCache.Count < MAXIMUM_CACHE_COUNT)
_sortedSetCache.Push(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref Queue<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(Queue<T> value)
{
if (value == null)
return;
value.Clear();
if (_queueCache.Count < MAXIMUM_CACHE_COUNT)
_queueCache.Push(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref BasicQueue<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(BasicQueue<T> value)
{
if (value == null)
return;
value.Clear();
if (_basicQueueCache.Count < MAXIMUM_CACHE_COUNT)
_basicQueueCache.Push(value);
}
/// <summary>
/// Stores a collection and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref HashSet<T> value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a collection.
/// </summary>
/// <param name = "value">Value to store.</param>
public static void Store(HashSet<T> value)
{
if (value == null)
return;
value.Clear();
if (_hashSetCache.Count < MAXIMUM_CACHE_COUNT)
_hashSetCache.Push(value);
}
}
/// <summary>
/// Caches objects of a single generic.
/// </summary>
public static class ObjectCaches<T> where T : new()
{
/// <summary>
/// Stack to use.
/// </summary>
private static readonly ConcurrentStack<T> _stack = new();
/// <summary>
/// Maximum number of entries allowed for the cache.
/// </summary>
private const int MAXIMUM_CACHE_COUNT = 50;
/// <summary>
/// Returns a value from the stack or creates an instance when the stack is empty.
/// </summary>
/// <returns></returns>
public static T Retrieve()
{
T result;
if (!_stack.TryPop(out result))
result = new(); // Activator.CreateInstance<T>();
return result;
}
/// <summary>
/// Stores an instance of T and sets the original reference to default.
/// Method will not execute if value is null.
/// </summary>
/// <param name = "value">Value to store.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StoreAndDefault(ref T value)
{
Store(value);
value = default;
}
/// <summary>
/// Stores a value to the stack.
/// </summary>
/// <param name = "value"></param>
public static void Store(T value)
{
if (value == null)
return;
if (_stack.Count < MAXIMUM_CACHE_COUNT)
_stack.Push(value);
}
}
#endregion
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3cb13274f7491a941b6e89a767905f56
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs
uploadId: 866910
@@ -0,0 +1,341 @@
// /* This implementation uses better naming as well provides
// * a few new features.
// *
// * Object and Collection caches are now one class. */
//
// // TODO In V5 disappear ResettableRingBuffer and have regular check implementation on start -- this will let us use this class for caching ringbuffer with resettable types.
// using System;
// using System.Collections;
// using System.Collections.Generic;
// using System.Runtime.CompilerServices;
//
// namespace GameKit.Dependencies.Utilities
// {
// /// <summary>
// /// Implement to use type with Caches.
// /// </summary>
// public interface IResettable
// {
// /// <summary>
// /// Resets values when being placed in a cache.
// /// </summary>
// void ResetState();
//
// /// <summary>
// /// Initializes values after being retrieved from a cache.
// /// </summary>
// void InitializeState();
// }
//
// public static class ResettableObjectPool<T> where T : IResettable, new() { }
//
// /// <summary>
// /// Caches collections and objects of T.
// /// </summary>
// public static class ObjectPool<T> where T : new()
// {
// /// <summary>
// /// Cache for List<T>.
// /// </summary>
// private static readonly Stack<List<T>> _listCache = new();
// /// <summary>
// /// Resettable cache for List<T>.
// /// </summary>
// private static readonly Stack<List<T>> _resettableListCache = new();
//
// /// <summary>
// /// Cache for HashSet<T>.
// /// </summary>
// private static readonly Stack<HashSet<T>> _hashSetCache = new();
// /// <summary>
// /// Resettable cache for HashSet<T>.
// /// </summary>
// private static readonly Stack<HashSet<T>> _resettableHashSetCache = new();
//
// /// <summary>
// /// Cache for Queue<T>.
// /// </summary>
// private static readonly Stack<Queue<T>> _queueCache = new();
// /// <summary>
// /// Resettable cache for Queue<T>.
// /// </summary>
// private static readonly Stack<Queue<T>> _resettableQueueCache = new();
//
// /// <summary>
// /// Cache for T[].
// /// </summary>
// private static readonly Stack<T[]> _arrayCache = new();
// /// <summary>
// /// Resettable cache for T[].
// /// </summary>
// private static readonly Stack<T[]> _resettableArrayCache = new();
//
// /// <summary>
// /// Cache for T.
// /// </summary>
// private static readonly Stack<T> _tCache = new();
// /// <summary>
// /// Resettable cache for T.
// /// </summary>
// private static readonly Stack<T> _resettableTCache = new();
//
// /// <summary>
// /// True if T is a value type.
// /// </summary>
// private static readonly bool _isValueType;
// /// <summary>
// /// True if T implements IResettable.
// /// </summary>
// private static readonly bool _isResettable;
//
// static ObjectPool()
// {
// // Used at runtime to prevent nested collections.
// bool isTCollection = typeof(ICollection).IsAssignableFrom(typeof(T));
//
// if (isTCollection)
// throw new NotSupportedException($"ObjectPool element cannot be a collection. Type is [{typeof(T).FullName}].");
//
// _isValueType = typeof(T).IsValueType;
// _isResettable = typeof(T).IsAssignableFrom(typeof(IResettable));
// }
//
// /// <summary>
// /// Clears all pools for T.
// /// </summary>
// public static void ClearPools()
// {
// _listCache.Clear();
// _resettableListCache.Clear();
//
// _hashSetCache.Clear();
// _resettableHashSetCache.Clear();
//
// _queueCache.Clear();
// _resettableQueueCache.Clear();
//
// _arrayCache.Clear();
// _resettableArrayCache.Clear();
//
// _tCache.Clear();
// _resettableTCache.Clear();
// }
//
// /// <summary>
// /// Returns a List<T> automatically resetting entries when IResettable is implemented,
// /// and pooling entries when they are a reference type.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static void Return(List<T> value)
// {
// if (value == null) return;
//
// Stack<List<T>> stack = _isResettable ? _resettableListCache : _listCache;
//
// IterateICollectionElements(value);
//
// value.Clear();
// stack.Push(value);
// }
//
// /// <summary>
// /// Returns a HashSet<T> automatically resetting entries when IResettable is implemented,
// /// and pooling entries when they are a reference type.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static void Return(HashSet<T> value)
// {
// if (value == null) return;
//
// Stack<HashSet<T>> stack = _isResettable ? _resettableHashSetCache : _hashSetCache;
//
// IterateICollectionElements(value);
//
// value.Clear();
// stack.Push(value);
// }
//
// /// <summary>
// /// Returns a Queue<T> automatically resetting entries when IResettable is implemented,
// /// and pooling entries when they are a reference type.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static void Return(Queue<T> value)
// {
// if (value == null) return;
//
// Stack<Queue<T>> stack = _isResettable ? _resettableQueueCache : _queueCache;
//
// IterateICollectionElements(value);
//
// value.Clear();
// stack.Push(value);
// }
//
// /// <summary>
// /// Returns an array automatically resetting entries when IResettable is implemented,
// /// and pools each array entry if the array element is a reference type.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static void Return(T[] value)
// {
// if (value == null) return;
//
// Stack<T[]> stack = _isResettable ? _resettableArrayCache : _arrayCache;
//
// IterateICollectionElements(value);
//
// Array.Clear(value, 0, value.Length);
// stack.Push(value);
// }
//
// /// <summary>
// /// Returns value without resetting the state.
// /// </summary>
// public static void Return(T value)
// {
// _tCache.Push(value);
// }
//
// /// <summary>
// /// Creates a new List<T>.
// /// </summary>
// private static List<T> CreateList() => new();
//
// /// <summary>
// /// Creates a new HashSet<T>.
// /// </summary>
// private static HashSet<T> CreateHashSet() => new();
//
// /// <summary>
// /// Creates a new Queue<T>.
// /// </summary>
// private static Queue<T> CreateQueue() => new();
//
// /// <summary>
// /// Creates a new array of length 0 (empty array).
// /// </summary>
// private static T[] CreateArray() => Array.Empty<T>();
//
// /// <summary>
// /// Rents a List<T>.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static List<T> RentList()
// {
// Stack<List<T>> stack = _isResettable ? _resettableListCache : _listCache;
//
// return RentCollection(stack, CreateList);
// }
//
// /// <summary>
// /// Rents a HashSet<T>.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static HashSet<T> RentHashSet()
// {
// Stack<HashSet<T>> stack = _isResettable ? _resettableHashSetCache : _hashSetCache;
//
// return RentCollection(stack, CreateHashSet);
// }
//
// /// <summary>
// /// Rents a Queue<T>.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static Queue<T> RentQueue()
// {
// Stack<Queue<T>> stack = _isResettable ? _resettableQueueCache : _queueCache;
//
// return RentCollection(stack, CreateQueue);
// }
//
// /// <summary>
// /// Rents an array.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static T[] RentArray()
// {
// Stack<T[]> stack = _isResettable ? _resettableArrayCache : _arrayCache;
//
// return RentCollection(stack, CreateArray);
// }
//
// /// <summary>
// /// Rents an object.
// /// </summary>
// public static T Rent()
// {
// Stack<T> stack = _isResettable ? _resettableTCache : _tCache;
//
// if (!stack.TryPop(out T result))
// result = new();
//
// return result;
// }
//
// /// <summary>
// /// Rents a collection using the supplied stack. Returns using defaultFactory if stack is empty.
// /// </summary>
// private static TCollection RentCollection<TCollection>(Stack<TCollection> stack, Func<TCollection> defaultFactory)
// {
// if (!stack.TryPop(out TCollection result))
// result = defaultFactory();
//
// return result;
// }
//
// /// <summary>
// /// Iterates ICollection elements, returning and resetting as needed.
// /// </summary>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// private static void IterateICollectionElements(IReadOnlyCollection<T> value)
// {
// // Reset T if possible.
// if (_isResettable)
// {
// // Value type.
// if (_isValueType)
// {
// foreach (T item in value)
// ((IResettable)item).ResetState();
// }
// // Reference type.
// else
// {
// foreach (T item in value)
// ReturnReferenceIResettable(item);
// }
// }
// // Type is not resettable.
// else
// {
// // Only need to Return if is not a value type.
// if (!_isValueType)
// {
// foreach (T item in value)
// ReturnReference(item);
// }
// }
// }
//
// /// <summary>
// /// Returns value expecting it to be a reference type that does not implement IResettable.
// /// </summary>
// private static void ReturnReference(T value)
// {
// _resettableTCache.Push(value);
// }
//
// /// <summary>
// /// Returns value expecting it to be a reference type which implement IResettable.
// /// </summary>
// internal static void ReturnReferenceIResettable(T value)
// {
// ((IResettable)value).ResetState();
//
// _resettableTCache.Push(value);
// }
// }
// }
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6065d663177eb614bb52a41071982835
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectPool.cs
uploadId: 866910
@@ -0,0 +1,63 @@
using GameKit.Dependencies.Utilities.Types;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace GameKit.Dependencies.Utilities
{
public static class Objects
{
/// <summary>
/// Returns if an object has been destroyed from memory.
/// </summary>
/// <param name = "gameObject"></param>
/// <returns></returns>
public static bool IsDestroyed(this GameObject gameObject)
{
// UnityEngine overloads the == operator for the GameObject type
// and returns null when the object has been destroyed, but
// actually the object is still there but has not been cleaned up yet
// if we test both we can determine if the object has been destroyed.
return gameObject == null && !ReferenceEquals(gameObject, null);
}
/// <summary>
/// Finds all objects in the scene of type. This method is very expensive.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "requireSceneLoaded">True if the scene must be fully loaded before trying to seek objects.</param>
/// <returns></returns>
public static List<T> FindAllObjectsOfType<T>(bool activeSceneOnly = true, bool requireSceneLoaded = false, bool includeDDOL = true, bool includeInactive = true)
{
List<T> results = new();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
// If to include only current scene.
if (activeSceneOnly)
{
if (SceneManager.GetActiveScene() != scene)
continue;
}
// If the scene must be fully loaded to seek objects within.
if (!scene.isLoaded && requireSceneLoaded)
continue;
GameObject[] allGameObjects = scene.GetRootGameObjects();
for (int j = 0; j < allGameObjects.Length; j++)
{
results.AddRange(allGameObjects[j].GetComponentsInChildren<T>(includeInactive));
}
}
// If to also include DDOL.
if (includeDDOL)
{
GameObject ddolGo = DDOL.GetDDOL().gameObject;
results.AddRange(ddolGo.GetComponentsInChildren<T>(includeInactive));
}
return results;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4fa6d28a28dbf6b4295602abad3de328
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs
uploadId: 866910
@@ -0,0 +1,106 @@
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public static class Particles
{
/// <summary>
/// Issues stop on the specified particle systems.
/// </summary>
/// <param name = "systems"></param>
public static float StopParticleSystem(ParticleSystem[] systems, bool stopLoopingOnly)
{
return StopParticleSystem(systems, stopLoopingOnly, ParticleSystemStopBehavior.StopEmitting);
}
/// <summary>
/// Issues stop on the specified particle systems while returning the time required to play out.
/// </summary>
/// <param name = "systems"></param>
public static float StopParticleSystem(ParticleSystem[] systems, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting)
{
return StopParticleSystem(systems, false, stopBehavior);
}
/// <summary>
/// Issues stop on the specified particle systems while returning the time required to play out.
/// </summary>
/// <param name = "systems"></param>
public static float StopParticleSystem(ParticleSystem[] systems, bool stopLoopingOnly, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting)
{
if (systems == null)
return 0f;
float playOutDuration = 0f;
for (int i = 0; i < systems.Length; i++)
playOutDuration = Mathf.Max(playOutDuration, StopParticleSystem(systems[i], stopLoopingOnly, stopBehavior));
return playOutDuration;
}
/// <summary>
/// Issues stop on the specified particle systems.
/// </summary>
/// <param name = "systems"></param>
public static float StopParticleSystem(ParticleSystem system, bool stopLoopingOnly, bool stopChildren = false)
{
return StopParticleSystem(system, stopLoopingOnly, ParticleSystemStopBehavior.StopEmitting, stopChildren);
}
/// <summary>
/// Issues stop on the specified particle systems while returning the time required to play out.
/// </summary>
/// <param name = "systems"></param>
public static float StopParticleSystem(ParticleSystem system, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting, bool stopChildren = false)
{
return StopParticleSystem(system, false, stopBehavior, stopChildren);
}
/// <summary>
/// Issues stop on the specified particle system while returning the time required to play out.
/// </summary>
public static float StopParticleSystem(ParticleSystem system, bool stopLoopingOnly, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting, bool stopChildren = false)
{
if (system == null)
return 0f;
if (stopChildren)
{
ParticleSystem[] all = system.GetComponentsInChildren<ParticleSystem>();
StopParticleSystem(all, stopLoopingOnly, stopBehavior);
}
float playOutDuration = 0f;
float timeLeft = system.main.duration - system.time;
playOutDuration = Mathf.Max(playOutDuration, timeLeft);
if (stopLoopingOnly)
{
if (system.main.loop)
system.Stop(false, stopBehavior);
}
else
{
system.Stop(false, stopBehavior);
}
return playOutDuration;
}
/// <summary>
/// Returns the longest time required for all systems to stop.
/// </summary>
/// <param name = "systems"></param>
/// <returns></returns>
public static float ReturnLongestCycle(ParticleSystem[] systems)
{
float longestPlayTime = 0f;
for (int i = 0; i < systems.Length; i++)
{
float timeLeft = systems[i].main.duration - systems[i].time;
longestPlayTime = Mathf.Max(longestPlayTime, timeLeft);
}
return longestPlayTime;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5f3d973dcfa06554998575e8eef0938a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs
uploadId: 866910
@@ -0,0 +1,62 @@
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public static class Quaternions
{
/// <summary>
/// Returns how fast an object must rotate over duration to reach goal.
/// </summary>
/// <param name = "goal">Quaternion to measure distance against.</param>
/// <param name = "duration">How long it should take to move to goal.</param>
/// <param name = "interval">A multiplier applied towards interval. Typically this is used for ticks passed.</param>
/// <returns></returns>
public static float GetRate(this Quaternion a, Quaternion goal, float duration, out float angle, uint interval = 1, float tolerance = 0f)
{
angle = a.Angle(goal, true);
return angle / (duration * interval);
}
/// <summary>
/// Subtracts b quaternion from a.
/// </summary>
public static Quaternion Subtract(this Quaternion a, Quaternion b) => Quaternion.Inverse(b) * a;
/// <summary>
/// Adds quaternion b onto quaternion a.
/// </summary>
public static Quaternion Add(this Quaternion a, Quaternion b) => a * b;
/// <summary>
/// Returns if two quaternions match.
/// </summary>
/// <param name = "precise">True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return a match even when not true due to error tolerance.</param>
/// <returns></returns>
public static bool Matches(this Quaternion a, Quaternion b, bool precise = false)
{
if (precise)
return a.w == b.w && a.x == b.x && a.y == b.y && a.z == b.z;
else
return a == b;
}
/// <summary>
/// Returns the angle between two quaterions.
/// </summary>
/// <param name = "precise">True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return 0f due to error tolerance, even while there is a difference.</param>
/// <returns></returns>
public static float Angle(this Quaternion a, Quaternion b, bool precise = false)
{
if (precise)
{
// This is run Unitys implementation without the error tolerance.
float dot = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
return Mathf.Acos(Mathf.Min(Mathf.Abs(dot), 1f)) * 2f * 57.29578f;
}
else
{
return Quaternion.Angle(a, b);
}
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 02a9084f4f788cd4293cdff56a49b5dd
timeCreated: 1522043602
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs
uploadId: 866910
@@ -0,0 +1,120 @@
using System;
using System.Text;
namespace GameKit.Dependencies.Utilities
{
public static class Strings
{
/// <summary>
/// Used to encode and decode strings.
/// </summary>
private static readonly UTF8Encoding _encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
/// <summary>
/// A buffer convert data and discard.
/// </summary>
public static byte[] Buffer = new byte[1024];
/// <summary>
/// Converts a member string text to PascalCase
/// </summary>
/// <remarks>A member string is expected to be in the format '_memberName'.</remarks>
public static string MemberToPascalCase(this string txt)
{
if (txt.Length < 2)
{
UnityEngine.Debug.LogError($"Text '{txt}' is too short.");
return string.Empty;
}
if (txt[0] != '_')
{
UnityEngine.Debug.LogError($"Text '{txt}' has the incorrect member prefix.");
return string.Empty;
}
string firstLeter = txt[1].ToString().ToUpper();
string substring = txt.Length > 2 ? txt.Substring(2) : string.Empty;
return $"{firstLeter}{substring}";
}
/// <summary>
/// Converts a pascal case string to member case.
/// </summary>
/// <remarks>A PascalCase string is expected to be in the format 'PropertyName'.</remarks>
public static string PascalCaseToMember(this string txt)
{
if (txt.Length < 1)
{
UnityEngine.Debug.LogError($"Text '{txt}' is too short.");
return string.Empty;
}
string firstLeter = txt[0].ToString().ToLower();
string subString = txt.Length > 1 ? txt.Substring(1) : string.Empty;
return $"_{firstLeter}{subString}";
}
/// <summary>
/// Attachs or detaches an suffix to a string.
/// </summary>
/// <param name = "text"></param>
/// <param name = "suffix"></param>
/// <param name = "addExtension"></param>
public static string ReturnModifySuffix(string text, string suffix, bool addExtension)
{
/* Since saving to a json, add the .json extension if not present.
* Length must be greater than 6 to contain a character and .json. */
if (text.Length > suffix.Length + 1)
{
// If to add the extension.
if (addExtension)
{
// If doesn't contain the extension then add it on.
if (!text.Substring(text.Length - suffix.Length).Contains(suffix, StringComparison.CurrentCultureIgnoreCase))
return text + suffix;
// Already contains extension.
else
return text;
}
// Remove extension.
else
{
// If contains extension.
if (text.Substring(text.Length - suffix.Length).Contains(suffix, StringComparison.CurrentCultureIgnoreCase))
return text.Substring(0, text.Length - suffix.Length);
// Doesn't contain extension.
return text;
}
}
// Text isn't long enough to manipulate.
else
{
return text;
}
}
/// <summary>
/// Converts a string into a byte array buffer.
/// </summary>
/// <returns>Number of bytes written to the buffer.</returns>
public static int ToBytes(this string value, ref byte[] buffer)
{
int strLength = value.Length;
// Number of minimum bytes the buffer must be.
int bytesNeeded = _encoding.GetMaxByteCount(strLength);
// Grow string buffer if needed.
if (buffer.Length < bytesNeeded)
Array.Resize(ref buffer, bytesNeeded * 2);
return _encoding.GetBytes(value, 0, strLength, buffer, 0);
}
/// <summary>
/// Converts a string to bytes while allocating.
/// </summary>
public static byte[] ToBytesAllocated(this string value) => Encoding.Unicode.GetBytes(value);
}
}

Some files were not shown because too many files have changed in this diff Show More