[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: 31f64c354c60cc748a3c0956b14def5b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,78 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace FishNet.Managing.Timing.Editing
{
[CustomEditor(typeof(TimeManager), true)]
[CanEditMultipleObjects]
public class TimeManagerEditor : Editor
{
private SerializedProperty _updateOrder;
private SerializedProperty _timingType;
private SerializedProperty _tickRate;
private SerializedProperty _allowTickDropping;
private SerializedProperty _maximumFrameTicks;
private SerializedProperty _pingInterval;
// private SerializedProperty _timingInterval;
private SerializedProperty _physicsMode;
protected virtual void OnEnable()
{
_updateOrder = serializedObject.FindProperty("_updateOrder");
_timingType = serializedObject.FindProperty("_timingType");
_tickRate = serializedObject.FindProperty("_tickRate");
_allowTickDropping = serializedObject.FindProperty("_allowTickDropping");
_maximumFrameTicks = serializedObject.FindProperty("_maximumFrameTicks");
_pingInterval = serializedObject.FindProperty("_pingInterval");
// _timingInterval = serializedObject.FindProperty("_timingInterval");
_physicsMode = serializedObject.FindProperty("_physicsMode");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
GUI.enabled = false;
EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((TimeManager)target), typeof(TimeManager), false);
GUI.enabled = true;
// Timing.
EditorGUILayout.LabelField("Timing", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_updateOrder);
EditorGUILayout.PropertyField(_timingType);
EditorGUILayout.PropertyField(_allowTickDropping);
if (_allowTickDropping.boolValue == true)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(_maximumFrameTicks);
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(_tickRate);
EditorGUILayout.PropertyField(_pingInterval);
// EditorGUILayout.PropertyField(_timingInterval);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
// Physics.
EditorGUILayout.LabelField("Physics", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
if (_physicsMode.intValue == (int)PhysicsMode.TimeManager)
EditorGUILayout.HelpBox($"Time.fixedDeltaTime will be overridden with TimeManager.TickDelta ({(1f / (float)_tickRate.intValue).ToString("0.###")})", MessageType.Info);
else
EditorGUILayout.HelpBox("If you are using physics interactions be sure to change the PhysicsMode to TimeManager and implement physics within the TimeManager tick events. NetworkTransform may also jitter when not using PhysicsMode.TimeManager.", MessageType.Warning);
EditorGUILayout.PropertyField(_physicsMode);
EditorGUI.indentLevel--;
////Prediction.
// EditorGUILayout.LabelField("Prediction", EditorStyles.boldLabel);
// EditorGUI.indentLevel++;
// EditorGUILayout.PropertyField(_maximumBufferedInputs);
// EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 12987a8c0302190489ecb55f6fbd494e
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/Managing/Timing/Editor/TimeManagerEditor.cs
uploadId: 866910
@@ -0,0 +1,96 @@
using FishNet.Documenting;
using System;
namespace FishNet.Managing.Timing
{
[APIExclude]
public class MovingAverage : IDisposable
{
#region Public.
/// <summary>
/// Average from samples favoring the most recent sample.
/// </summary>
public float Average { get; private set; }
/// <summary>
/// Sample size being used.
/// </summary>
public int SampleSize { get; private set; }
#endregion
/// <summary>
/// Next index to write a sample to.
/// </summary>
private int _writeIndex;
/// <summary>
/// Collected samples.
/// </summary>
private float[] _samples;
/// <summary>
/// Number of samples written. Will be at most samples size.
/// </summary>
private int _writtenSamples;
/// <summary>
/// Samples accumulated over queue.
/// </summary>
private float _sampleAccumulator;
public MovingAverage(int sampleSize)
{
if (sampleSize < 2)
{
NetworkManagerExtensions.LogWarning("Using a sampleSize of less than 2 will always return the most recent value as Average.");
sampleSize = 1;
}
SampleSize = sampleSize;
_samples = new float[sampleSize];
}
/// <summary>
/// Computes a new windowed average each time a new sample arrives
/// </summary>
/// <param name = "newSample"></param>
public void ComputeAverage(float newSample)
{
if (_samples.Length <= 1)
{
Average = newSample;
return;
}
_sampleAccumulator += newSample;
_samples[_writeIndex] = newSample;
// Increase writeIndex.
_writeIndex++;
_writtenSamples = Math.Max(_writtenSamples, _writeIndex);
if (_writeIndex >= _samples.Length)
_writeIndex = 0;
Average = _sampleAccumulator / _writtenSamples;
/* If samples are full then drop off
* the oldest sample. This will always be
* the one just after written. The entry isn't
* actually removed from the array but will
* be overwritten next sample. */
if (_writtenSamples >= _samples.Length)
_sampleAccumulator -= _samples[_writeIndex];
}
/// <summary>
/// Resets values.
/// </summary>
public void Reset()
{
_sampleAccumulator = 0f;
_writeIndex = 0;
_writtenSamples = 0;
}
public void Dispose()
{
Reset();
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 03d05f88778c5c744810e48f251f2d3b
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/Managing/Timing/MovingAverage.cs
uploadId: 866910
@@ -0,0 +1,21 @@
namespace FishNet.Managing.Timing
{
/// <summary>
/// How to simulate physics.
/// </summary>
public enum PhysicsMode
{
/// <summary>
/// Unity performs physics every FixedUpdate.
/// </summary>
Unity = 0,
/// <summary>
/// TimeManager performs physics each tick.
/// </summary>
TimeManager = 1,
/// <summary>
/// Physics will be disabled.
/// </summary>
Disabled = 2
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 502e9f31bebd41f4f9088a19eae53735
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/Managing/Timing/PhysicsMode.cs
uploadId: 866910
@@ -0,0 +1,195 @@
using System;
using FishNet.Serializing;
using GameKit.Dependencies.Utilities;
namespace FishNet.Managing.Timing
{
public readonly struct PreciseTick : IEquatable<PreciseTick>
{
/// <summary>
/// The current tick.
/// </summary>
public readonly uint Tick;
/// <summary>
/// Percentage of the tick returned between 0d and 1d.
/// </summary>
public readonly double PercentAsDouble;
/// <summary>
/// Percentage of the tick returned between 0 and 100.
/// </summary>
public readonly byte PercentAsByte;
/// <summary>
/// Maximum value a percent can be as a double.
/// </summary>
public const double MAXIMUM_DOUBLE_PERCENT = 1d;
/// <summary>
/// Maximum value a percent can be as a byte.
/// </summary>
public const byte MAXIMUM_BYTE_PERCENT = 100;
/// <summary>
/// Value to use when a precise tick is unset.
/// </summary>
public static PreciseTick GetUnsetValue() => new(TimeManager.UNSET_TICK, (byte)0);
/// <summary>
/// Creates a precise tick where the percentage is 0.
/// </summary>
public PreciseTick(uint tick)
{
Tick = tick;
PercentAsByte = 0;
PercentAsDouble = 0d;
}
/// <summary>
/// Creates a precise tick where the percentage is a byte between 0 and 100.
/// </summary>
public PreciseTick(uint tick, byte percentAsByte)
{
Tick = tick;
percentAsByte = Maths.ClampByte(percentAsByte, 0, MAXIMUM_BYTE_PERCENT);
PercentAsByte = percentAsByte;
PercentAsDouble = percentAsByte / 100d;
}
/// <summary>
/// Creates a precise tick where the percentage is a double between 0d and 1d.
/// </summary>
public PreciseTick(uint tick, double percent)
{
Tick = tick;
percent = Maths.ClampDouble(percent, 0d, MAXIMUM_DOUBLE_PERCENT);
PercentAsByte = (byte)(percent * 100d);
PercentAsDouble = percent;
}
public bool IsValid() => Tick != TimeManager.UNSET_TICK;
/// <summary>
/// Prints PreciseTick information as a string.
/// </summary>
/// <returns></returns>
public override string ToString() => $"Tick {Tick}, Percent {PercentAsByte.ToString("000")}";
public static bool operator ==(PreciseTick a, PreciseTick b)
{
return a.Tick == b.Tick && a.PercentAsByte == b.PercentAsByte;
}
public static bool operator !=(PreciseTick a, PreciseTick b)
{
return !(a == b);
}
public static bool operator >=(PreciseTick a, PreciseTick b)
{
if (b.Tick > a.Tick)
return false;
if (a.Tick > b.Tick)
return true;
// If here ticks are the same.
return a.PercentAsByte >= b.PercentAsByte;
}
public static bool operator <=(PreciseTick a, PreciseTick b) => b >= a;
public static bool operator >(PreciseTick a, PreciseTick b)
{
if (b.Tick > a.Tick)
return false;
if (a.Tick > b.Tick)
return true;
// if here ticks are the same.
return a.PercentAsByte > b.PercentAsByte;
}
public static bool operator <(PreciseTick a, PreciseTick b) => b > a;
public bool Equals(PreciseTick other) => Tick == other.Tick && PercentAsByte == other.PercentAsByte;
public override bool Equals(object obj) => obj is PreciseTick other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Tick, PercentAsDouble, PercentAsByte);
}
public static class PreciseTickExtensions
{
/// <summary>
/// Adds value onto a PreciseTick.
/// </summary>
/// <param name = "value">Value to add.</param>
/// <param name = "delta">Tick delta.</param>
/// <returns></returns>
public static PreciseTick Add(this PreciseTick pt, PreciseTick value, double delta)
{
double ptDouble = pt.AsDouble(delta);
double valueDouble = value.AsDouble(delta);
double next = ptDouble + valueDouble;
return next.AsPreciseTick(delta);
}
/// <summary>
/// Subtracts value from a PreciseTick.
/// </summary>
/// <param name = "value">Value to subtract.</param>
/// <param name = "delta">Tick delta.</param>
/// <returns></returns>
public static PreciseTick Subtract(this PreciseTick pt, PreciseTick value, double delta)
{
double ptDouble = pt.AsDouble(delta);
double valueDouble = value.AsDouble(delta);
double remainder = ptDouble - valueDouble;
return remainder.AsPreciseTick(delta);
}
/// <summary>
/// Converts a PreciceTick to a double.
/// </summary>
/// <param name = "delta">Tick delta.</param>
/// <returns></returns>
public static double AsDouble(this PreciseTick pt, double delta)
{
return (double)pt.Tick * delta + pt.PercentAsDouble * delta;
}
/// <summary>
/// Converts a double to a PreciseTick.
/// </summary>
/// <param name = "delta">Tick delta.</param>
/// <returns></returns>
public static PreciseTick AsPreciseTick(this double ptDouble, double delta)
{
if (ptDouble <= 0)
return new(0, 0);
ulong whole = (ulong)Math.Floor(ptDouble / delta);
// Overflow.
if (whole >= uint.MaxValue)
return PreciseTick.GetUnsetValue();
double remainder = ptDouble % delta;
double percent = remainder / delta;
return new((uint)whole, percent);
}
}
public static class PreciseTickSerializer
{
public static void WritePreciseTick(this Writer writer, PreciseTick value)
{
writer.WriteTickUnpacked(value.Tick);
writer.WriteUInt8Unpacked(value.PercentAsByte);
}
public static PreciseTick ReadPreciseTick(this Reader reader)
{
uint tick = reader.ReadTickUnpacked();
byte percentByte = reader.ReadUInt8Unpacked();
return new(tick, percentByte);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a96dd6b21066a424199583b80746464f
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/Managing/Timing/PreciseTick.cs
uploadId: 866910
@@ -0,0 +1,21 @@
namespace FishNet.Managing.Timing
{
/// <summary>
/// How ticks are rounded when using time.
/// </summary>
public enum TickRounding
{
/// <summary>
/// Rounds up.
/// </summary>
RoundUp,
/// <summary>
/// Rounds down.
/// </summary>
RoundDown,
/// <summary>
/// Rounds to the nearest whole.
/// </summary>
RoundNearest
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d12f8894fc7343b4bbe332464dc4bce5
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/Managing/Timing/TickRounding.cs
uploadId: 866910
@@ -0,0 +1,9 @@
namespace FishNet.Managing.Timing
{
public enum TickType : byte
{
Tick = 0,
LocalTick = 1,
LastPacketTick = 2
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 489fba8b0da3c9b4b9ff4e7a46804473
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/Managing/Timing/TickType.cs
uploadId: 866910
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3fdaae44044276a49a52229c1597e33b
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/Managing/Timing/TimeManager.cs
uploadId: 866910