[Add] FishNet
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
/// <summary>
|
||||
/// Replicated methods are to be called from clients and will run the same data and logic on the server.
|
||||
/// Only data used as method arguments will be serialized.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
|
||||
public class ReplicateAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Reconcile methods indicate how to reset your script or object after the server has replicated user data.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
|
||||
public class ReconcileAttribute : Attribute { }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b082d36535ce0404d8438bc1b0499e53
|
||||
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/Object/Prediction/Attributes.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,23 @@
|
||||
using FishNet.Connection;
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Transporting;
|
||||
using FishNet.Utility;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)]
|
||||
|
||||
namespace FishNet.Object.Prediction.Delegating
|
||||
{
|
||||
[APIExclude]
|
||||
public delegate void ReplicateRpcDelegate(PooledReader reader, NetworkConnection sender, Channel channel);
|
||||
|
||||
[APIExclude]
|
||||
public delegate void ReconcileRpcDelegate(PooledReader reader, Channel channel);
|
||||
|
||||
[APIExclude]
|
||||
public delegate void ReplicateUserLogicDelegate<T>(T data, ReplicateState state, Channel channel);
|
||||
|
||||
[APIExclude]
|
||||
public delegate void ReconcileUserLogicDelegate<T>(T data, Channel channel);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9904192dacd41a4ba7b29bc3199ec3a
|
||||
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/Object/Prediction/Delegates.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
public interface IReplicateData
|
||||
{
|
||||
/// <summary>
|
||||
/// Local tick when the data was created.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
uint GetTick();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the local tick when data was created.
|
||||
/// </summary>
|
||||
/// <param name = "value"></param>
|
||||
void SetTick(uint value);
|
||||
|
||||
/// <summary>
|
||||
/// Allows for any cleanup when the data is being discarded.
|
||||
/// </summary>
|
||||
void Dispose();
|
||||
}
|
||||
|
||||
public interface IReconcileData
|
||||
{
|
||||
/// <summary>
|
||||
/// Local tick when the data was created.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
uint GetTick();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the local tick when data was created.
|
||||
/// </summary>
|
||||
/// <param name = "value"></param>
|
||||
void SetTick(uint value);
|
||||
|
||||
/// <summary>
|
||||
/// Allows for any cleanup when the data is being discarded.
|
||||
/// </summary>
|
||||
void Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 515754257f85574438408c7f5b268590
|
||||
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/Object/Prediction/Interfaces.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,45 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Serializing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to store reconciles locally.
|
||||
/// </summary>
|
||||
/// <remarks>This is for internal use only.</remarks>
|
||||
[APIExclude]
|
||||
public struct LocalReconcile<T> where T : IReconcileData
|
||||
{
|
||||
/// <summary>
|
||||
/// Tick for reconcile.
|
||||
/// </summary>
|
||||
public uint Tick;
|
||||
/// <summary>
|
||||
/// Writer reconcile was written to.
|
||||
/// </summary>
|
||||
public PooledWriter Writer;
|
||||
/// <summary>
|
||||
/// Data inside writer.
|
||||
/// </summary>
|
||||
public T Data;
|
||||
|
||||
public void Initialize(uint tick, T data)
|
||||
{
|
||||
Tick = tick;
|
||||
Data = data;
|
||||
Writer = WriterPool.Retrieve();
|
||||
Writer.Write(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of used data.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Data.Dispose();
|
||||
if (Writer != null)
|
||||
WriterPool.Store(Writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb90cfdc07524be40a9d4d11ae7c35e6
|
||||
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/Object/Prediction/LocalReconcile.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,467 @@
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to make calculations and perform actions in moving transforms over time.
|
||||
/// </summary>
|
||||
[Preserve]
|
||||
public struct MoveRates
|
||||
{
|
||||
/// <summary>
|
||||
/// Rate at which to move Position.
|
||||
/// </summary>
|
||||
public float Position;
|
||||
/// <summary>
|
||||
/// Rate at which to move Rotation.
|
||||
/// </summary>
|
||||
public float Rotation;
|
||||
/// <summary>
|
||||
/// Rate at which to move Scale.
|
||||
/// </summary>
|
||||
public float Scale;
|
||||
/// <summary>
|
||||
/// Time remaining until the move is complete.
|
||||
/// </summary>
|
||||
public float TimeRemaining;
|
||||
/// <summary>
|
||||
/// Value used when data is not set.
|
||||
/// </summary>
|
||||
public const float UNSET_VALUE = float.NegativeInfinity;
|
||||
/// <summary>
|
||||
/// Value used when move rate should be instant.
|
||||
/// </summary>
|
||||
public const float INSTANT_VALUE = float.PositiveInfinity;
|
||||
/// <summary>
|
||||
/// True if any data is set. Once set, this will remain true until ResetState is called.
|
||||
/// </summary>
|
||||
public bool IsValid { get; private set; }
|
||||
|
||||
public MoveRates(float value) : this()
|
||||
{
|
||||
Position = value;
|
||||
Rotation = value;
|
||||
Scale = value;
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
public MoveRates(float position, float rotation) : this()
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
Scale = INSTANT_VALUE;
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
public MoveRates(float position, float rotation, float scale) : this()
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
Scale = scale;
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
public MoveRates(float position, float rotation, float scale, float timeRemaining)
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
Scale = scale;
|
||||
TimeRemaining = timeRemaining;
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if a positional move rate is set.
|
||||
/// </summary>
|
||||
public bool IsPositionSet => Position != UNSET_VALUE;
|
||||
/// <summary>
|
||||
/// True if rotation move rate is set.
|
||||
/// </summary>
|
||||
public bool IsRotationSet => Rotation != UNSET_VALUE;
|
||||
/// <summary>
|
||||
/// True if a scale move rate is set.
|
||||
/// </summary>
|
||||
public bool IsScaleSet => Scale != UNSET_VALUE;
|
||||
/// <summary>
|
||||
/// True if position move rate should be instant.
|
||||
/// </summary>
|
||||
public bool IsPositionInstantValue => Position == INSTANT_VALUE;
|
||||
/// <summary>
|
||||
/// True if rotation move rate should be instant.
|
||||
/// </summary>
|
||||
public bool IsRotationInstantValue => Rotation == INSTANT_VALUE;
|
||||
/// <summary>
|
||||
/// True if scale move rate should be instant.
|
||||
/// </summary>
|
||||
public bool IsScaleInstantValue => Scale == INSTANT_VALUE;
|
||||
|
||||
#region Private Profiler Markers
|
||||
private static readonly ProfilerMarker _pm_GetMoveRatesFull = new("MoveRates.GetMoveRates(Vector3, Vector3, Quaternion, Quaternion, Vector3, Vector3, float, float)");
|
||||
private static readonly ProfilerMarker _pm_GetMoveRatesVec = new("MoveRates.GetMoveRates(Vector3, Vector3, float, float)");
|
||||
private static readonly ProfilerMarker _pm_Move = new("MoveRates.Move(Transform, TransformPropertiesFlag, Vector3, float, Quaternion, float, Vector3, float, float, bool)");
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Sets all rates to instant.
|
||||
/// </summary>
|
||||
public void SetInstantRates() => Update(INSTANT_VALUE);
|
||||
|
||||
/// <summary>
|
||||
/// Sets all rates to the same value.
|
||||
/// </summary>
|
||||
public void Update(float value) => Update(value, value, value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets rates for each property.
|
||||
/// </summary>
|
||||
public void Update(float position, float rotation, float scale)
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
Scale = scale;
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets rates for each property.
|
||||
/// </summary>
|
||||
public void Update(float position, float rotation, float scale, float timeRemaining)
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
Scale = scale;
|
||||
TimeRemaining = timeRemaining;
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates to new values.
|
||||
/// </summary>
|
||||
public void Update(MoveRates moveRates) => Update(moveRates.Position, moveRates.Rotation, moveRates.Scale, moveRates.TimeRemaining);
|
||||
|
||||
/// <summary>
|
||||
/// Updates to new values.
|
||||
/// </summary>
|
||||
public void Update(MoveRatesCls moveRates) => Update(moveRates.Position, moveRates.Rotation, moveRates.Scale, moveRates.TimeRemaining);
|
||||
|
||||
/// <summary>
|
||||
/// Resets to unset values.
|
||||
/// </summary>
|
||||
public void ResetState()
|
||||
{
|
||||
Update(UNSET_VALUE, UNSET_VALUE, UNSET_VALUE, timeRemaining: 0f);
|
||||
|
||||
IsValid = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new MoveRates based on previous values, and a transforms current position.
|
||||
/// </summary>
|
||||
public static MoveRates GetWorldMoveRates(Transform from, Transform to, float duration, float teleportThreshold)
|
||||
{
|
||||
return GetMoveRates(from.position, to.position, from.rotation, to.rotation, from.localScale, to.localScale, duration, teleportThreshold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new MoveRates based on previous values, and a transforms current position.
|
||||
/// </summary>
|
||||
public static MoveRates GetLocalMoveRates(Transform from, Transform to, float duration, float teleportThreshold)
|
||||
{
|
||||
return GetMoveRates(from.localPosition, to.localPosition, from.localRotation, to.localRotation, from.localScale, to.localScale, duration, teleportThreshold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new MoveRates based on previous values, and a transforms current position.
|
||||
/// </summary>
|
||||
public static MoveRates GetWorldMoveRates(TransformProperties prevValues, Transform t, float duration, float teleportThreshold)
|
||||
{
|
||||
return GetMoveRates(prevValues.Position, t.position, prevValues.Rotation, t.rotation, prevValues.Scale, t.localScale, duration, teleportThreshold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new MoveRates based on previous values, and a transforms current position.
|
||||
/// </summary>
|
||||
public static MoveRates GetLocalMoveRates(TransformProperties prevValues, Transform t, float duration, float teleportThreshold)
|
||||
{
|
||||
return GetMoveRates(prevValues.Position, t.localPosition, prevValues.Rotation, t.localRotation, prevValues.Scale, t.localScale, duration, teleportThreshold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new MoveRates based on previous values, and a transforms current position.
|
||||
/// </summary>
|
||||
public static MoveRates GetMoveRates(TransformProperties prevValues, TransformProperties nextValues, float duration, float teleportThreshold)
|
||||
{
|
||||
return GetMoveRates(prevValues.Position, nextValues.Position, prevValues.Rotation, nextValues.Rotation, prevValues.Scale, nextValues.Scale, duration, teleportThreshold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new MoveRates based on previous values, and a transforms current position.
|
||||
/// </summary>
|
||||
public static MoveRates GetMoveRates(Vector3 fromPosition, Vector3 toPosition, Quaternion fromRotation, Quaternion toRotation, Vector3 fromScale, Vector3 toScale, float duration, float teleportThreshold)
|
||||
{
|
||||
using (_pm_GetMoveRatesFull.Auto())
|
||||
{
|
||||
float rate;
|
||||
|
||||
/* Position. */
|
||||
rate = toPosition.GetRate(fromPosition, duration, out float distance);
|
||||
// Basic teleport check.
|
||||
if (teleportThreshold != UNSET_VALUE && distance > teleportThreshold)
|
||||
return new(INSTANT_VALUE, INSTANT_VALUE, INSTANT_VALUE, duration);
|
||||
|
||||
//Smoothing.
|
||||
float positionRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE);
|
||||
rate = toRotation.GetRate(fromRotation, duration, out _);
|
||||
float rotationRate = rate.SetIfUnderTolerance(0.2f, INSTANT_VALUE);
|
||||
rate = toScale.GetRate(fromScale, duration, out _);
|
||||
float scaleRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE);
|
||||
|
||||
return new(positionRate, rotationRate, scaleRate, duration);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a move rate for two Vector3s.
|
||||
/// </summary>
|
||||
public static float GetMoveRate(Vector3 fromPosition, Vector3 toPosition, float duration, float teleportThreshold)
|
||||
{
|
||||
using (_pm_GetMoveRatesVec.Auto())
|
||||
{
|
||||
float rate;
|
||||
float distance;
|
||||
|
||||
/* Position. */
|
||||
rate = toPosition.GetRate(fromPosition, duration, out distance);
|
||||
//Basic teleport check.
|
||||
if (teleportThreshold != UNSET_VALUE && distance > teleportThreshold)
|
||||
{
|
||||
return INSTANT_VALUE;
|
||||
}
|
||||
//Smoothing.
|
||||
else
|
||||
{
|
||||
float positionRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE);
|
||||
return positionRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a move rate for two Quaternions.
|
||||
/// </summary>
|
||||
public static float GetMoveRate(Quaternion fromRotation, Quaternion toRotation, float duration)
|
||||
{
|
||||
float rate = toRotation.GetRate(fromRotation, duration, out _);
|
||||
float rotationRate = rate.SetIfUnderTolerance(0.2f, INSTANT_VALUE);
|
||||
return rotationRate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves transform to target values.
|
||||
/// </summary>
|
||||
public void Move(Transform movingTransform, TransformProperties goalProperties, float delta, bool useWorldSpace)
|
||||
{
|
||||
if (!IsValid)
|
||||
return;
|
||||
|
||||
Move(movingTransform, TransformPropertiesFlag.Everything, goalProperties.Position, Position, goalProperties.Rotation, Rotation, goalProperties.Scale, Scale, delta, useWorldSpace);
|
||||
TimeRemaining -= delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves transform to target values.
|
||||
/// </summary>
|
||||
public void Move(Transform movingTransform, TransformProperties goalProperties, TransformPropertiesFlag movedProperties, float delta, bool useWorldSpace)
|
||||
{
|
||||
if (!IsValid)
|
||||
return;
|
||||
|
||||
Move(movingTransform, movedProperties, goalProperties.Position, Position, goalProperties.Rotation, Rotation, goalProperties.Scale, Scale, delta, useWorldSpace);
|
||||
TimeRemaining -= delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves transform to target values.
|
||||
/// </summary>
|
||||
public static void Move(Transform movingTransform, TransformPropertiesFlag movedProperties, Vector3 posGoal, float posRate, Quaternion rotGoal, float rotRate, Vector3 scaleGoal, float scaleRate, float delta, bool useWorldSpace)
|
||||
{
|
||||
using (_pm_Move.Auto())
|
||||
{
|
||||
Transform t = movingTransform;
|
||||
|
||||
bool containsPosition = movedProperties.FastContains(TransformPropertiesFlag.Position);
|
||||
bool containsRotation = movedProperties.FastContains(TransformPropertiesFlag.Rotation);
|
||||
bool containsScale = movedProperties.FastContains(TransformPropertiesFlag.Scale);
|
||||
|
||||
//World space.
|
||||
if (useWorldSpace)
|
||||
{
|
||||
if (containsPosition)
|
||||
{
|
||||
if (posRate == INSTANT_VALUE)
|
||||
{
|
||||
t.position = posGoal;
|
||||
}
|
||||
else if (posRate == UNSET_VALUE) { }
|
||||
else
|
||||
{
|
||||
t.position = Vector3.MoveTowards(t.position, posGoal, posRate * delta);
|
||||
}
|
||||
}
|
||||
|
||||
if (containsRotation)
|
||||
{
|
||||
if (rotRate == INSTANT_VALUE)
|
||||
{
|
||||
t.rotation = rotGoal;
|
||||
}
|
||||
else if (rotRate == UNSET_VALUE) { }
|
||||
else
|
||||
{
|
||||
t.rotation = Quaternion.RotateTowards(t.rotation, rotGoal, rotRate * delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Local space.
|
||||
else
|
||||
{
|
||||
if (containsPosition)
|
||||
{
|
||||
if (posRate == INSTANT_VALUE)
|
||||
{
|
||||
t.localPosition = posGoal;
|
||||
}
|
||||
else if (posRate == UNSET_VALUE) { }
|
||||
else
|
||||
{
|
||||
t.localPosition = Vector3.MoveTowards(t.localPosition, posGoal, posRate * delta);
|
||||
}
|
||||
}
|
||||
|
||||
if (containsRotation)
|
||||
{
|
||||
if (rotRate == INSTANT_VALUE)
|
||||
{
|
||||
t.localRotation = rotGoal;
|
||||
}
|
||||
else if (rotRate == UNSET_VALUE) { }
|
||||
else
|
||||
{
|
||||
t.localRotation = Quaternion.RotateTowards(t.localRotation, rotGoal, rotRate * delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Scale always uses local.
|
||||
if (containsScale)
|
||||
{
|
||||
if (scaleRate == INSTANT_VALUE)
|
||||
{
|
||||
t.localScale = scaleGoal;
|
||||
}
|
||||
else if (scaleRate == UNSET_VALUE) { }
|
||||
else
|
||||
{
|
||||
t.localScale = Vector3.MoveTowards(t.localScale, scaleGoal, scaleRate * delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to make calculations and perform actions in moving transforms over time.
|
||||
/// </summary>
|
||||
/// <remarks>This acts as a wrapper for MoveRates struct.</remarks>
|
||||
public class MoveRatesCls : IResettable
|
||||
{
|
||||
/// <summary>
|
||||
/// Container of all move rate information.
|
||||
/// </summary>
|
||||
private MoveRates _moveRates = new();
|
||||
/// <summary>
|
||||
/// Rate at which to move Position.
|
||||
/// </summary>
|
||||
public float Position => _moveRates.Position;
|
||||
/// <summary>
|
||||
/// Rate at which to move Rotation.
|
||||
/// </summary>
|
||||
public float Rotation => _moveRates.Rotation;
|
||||
/// <summary>
|
||||
/// Rate at which to move Scale.
|
||||
/// </summary>
|
||||
public float Scale => _moveRates.Scale;
|
||||
/// <summary>
|
||||
/// Time remaining until the move is complete.
|
||||
/// </summary>
|
||||
public float TimeRemaining => _moveRates.TimeRemaining;
|
||||
/// <summary>
|
||||
/// True if position move rate should be instant.
|
||||
/// </summary>
|
||||
public bool IsPositionInstantValue => _moveRates.IsPositionInstantValue;
|
||||
/// <summary>
|
||||
/// True if rotation move rate should be instant.
|
||||
/// </summary>
|
||||
public bool IsRotationInstantValue => _moveRates.IsRotationInstantValue;
|
||||
/// <summary>
|
||||
/// True if scale move rate should be instant.
|
||||
/// </summary>
|
||||
public bool IsScaleInstantValue => _moveRates.IsScaleInstantValue;
|
||||
/// <summary>
|
||||
/// True if any data is set.
|
||||
/// </summary>
|
||||
public bool IsValid => _moveRates.IsValid;
|
||||
public MoveRatesCls(float value) => _moveRates = new(value);
|
||||
public MoveRatesCls(float position, float rotation) => _moveRates = new(position, rotation);
|
||||
public MoveRatesCls(float position, float rotation, float scale) => _moveRates = new(position, rotation, scale);
|
||||
public MoveRatesCls(float position, float rotation, float scale, float timeRemaining) => _moveRates = new(position, rotation, scale, timeRemaining);
|
||||
public MoveRatesCls() => _moveRates.ResetState();
|
||||
|
||||
/// <summary>
|
||||
/// Sets all rates to instant.
|
||||
/// </summary>
|
||||
public void SetInstantRates() => _moveRates.SetInstantRates();
|
||||
|
||||
/// <summary>
|
||||
/// Sets all rates to the same value.
|
||||
/// </summary>
|
||||
public void Update(float value) => _moveRates.Update(value);
|
||||
|
||||
/// <summary>
|
||||
/// Updates values.
|
||||
/// </summary>
|
||||
public void Update(float position, float rotation, float scale) => _moveRates.Update(position, rotation, scale);
|
||||
|
||||
/// <summary>
|
||||
/// Updates values.
|
||||
/// </summary>
|
||||
public void Update(float position, float rotation, float scale, float timeRemaining) => _moveRates.Update(position, rotation, scale, timeRemaining);
|
||||
|
||||
/// <summary>
|
||||
/// Updaes values.
|
||||
/// </summary>
|
||||
public void Update(MoveRatesCls mr) => _moveRates.Update(mr.Position, mr.Rotation, mr.Scale);
|
||||
|
||||
/// <summary>
|
||||
/// Moves transform to target values.
|
||||
/// </summary>
|
||||
public void Move(Transform movingTransform, TransformProperties goalProperties, float delta, bool useWorldSpace) => _moveRates.Move(movingTransform, goalProperties, delta, useWorldSpace);
|
||||
|
||||
/// <summary>
|
||||
/// Moves transform to target values.
|
||||
/// </summary>
|
||||
public void Move(Transform movingTransform, TransformProperties goalProperties, TransformPropertiesFlag movedProperties, float delta, bool useWorldSpace) => _moveRates.Move(movingTransform, goalProperties, movedProperties, delta, useWorldSpace);
|
||||
|
||||
public void ResetState() => _moveRates.ResetState();
|
||||
public void InitializeState() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bddf57861232884ca21f7e97a6d662d
|
||||
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/Object/Prediction/MoveRates.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,528 @@
|
||||
using FishNet.CodeGenerating;
|
||||
using FishNet.Component.Prediction;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Serializing;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
[Preserve]
|
||||
[DefaultWriter]
|
||||
public static class PredictionRigidbodySerializers
|
||||
{
|
||||
[DefaultWriter]
|
||||
public static void WriteEntryData(this Writer w, PredictionRigidbody.EntryData value)
|
||||
{
|
||||
PredictionRigidbody.ForceApplicationType appType = value.Type;
|
||||
w.WriteUInt8Unpacked((byte)appType);
|
||||
PredictionRigidbody.AllForceData data = value.Data;
|
||||
|
||||
switch (appType)
|
||||
{
|
||||
case PredictionRigidbody.ForceApplicationType.AddTorque:
|
||||
case PredictionRigidbody.ForceApplicationType.AddForce:
|
||||
case PredictionRigidbody.ForceApplicationType.AddRelativeTorque:
|
||||
case PredictionRigidbody.ForceApplicationType.AddRelativeForce:
|
||||
w.WriteVector3(data.Vector3Force);
|
||||
w.WriteInt32((byte)data.Mode);
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.AddExplosiveForce:
|
||||
w.WriteSingle(data.FloatForce);
|
||||
w.WriteVector3(data.Position);
|
||||
w.WriteSingle(data.Radius);
|
||||
w.WriteSingle(data.UpwardsModifier);
|
||||
w.WriteInt32((byte)data.Mode);
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.AddForceAtPosition:
|
||||
w.WriteVector3(data.Vector3Force);
|
||||
w.WriteVector3(data.Position);
|
||||
w.WriteInt32((byte)data.Mode);
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.MovePosition:
|
||||
w.WriteVector3(data.Position);
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.MoveRotation:
|
||||
w.WriteUInt8Unpacked((byte)data.RotationPacking);
|
||||
w.WriteQuaternion(data.Rotation, data.RotationPacking);
|
||||
break;
|
||||
default:
|
||||
w.NetworkManager.LogError($"ForceApplicationType of {appType} is not supported.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultReader]
|
||||
public static PredictionRigidbody.EntryData ReadEntryData(this Reader r)
|
||||
{
|
||||
PredictionRigidbody.EntryData fd = new();
|
||||
|
||||
PredictionRigidbody.ForceApplicationType appType = (PredictionRigidbody.ForceApplicationType)r.ReadUInt8Unpacked();
|
||||
fd.Type = appType;
|
||||
|
||||
PredictionRigidbody.AllForceData data = new();
|
||||
|
||||
switch (appType)
|
||||
{
|
||||
case PredictionRigidbody.ForceApplicationType.AddTorque:
|
||||
case PredictionRigidbody.ForceApplicationType.AddForce:
|
||||
case PredictionRigidbody.ForceApplicationType.AddRelativeTorque:
|
||||
case PredictionRigidbody.ForceApplicationType.AddRelativeForce:
|
||||
data.Vector3Force = r.ReadVector3();
|
||||
data.Mode = (ForceMode)r.ReadInt32();
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.AddExplosiveForce:
|
||||
data.FloatForce = r.ReadSingle();
|
||||
data.Position = r.ReadVector3();
|
||||
data.Radius = r.ReadSingle();
|
||||
data.UpwardsModifier = r.ReadSingle();
|
||||
data.Mode = (ForceMode)r.ReadInt32();
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.AddForceAtPosition:
|
||||
data.Vector3Force = r.ReadVector3();
|
||||
data.Position = r.ReadVector3();
|
||||
data.Mode = (ForceMode)r.ReadInt32();
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.MovePosition:
|
||||
data.Position = r.ReadVector3();
|
||||
break;
|
||||
case PredictionRigidbody.ForceApplicationType.MoveRotation:
|
||||
AutoPackType apt = (AutoPackType)r.ReadUInt8Unpacked();
|
||||
data.Rotation = r.ReadQuaternion(apt);
|
||||
break;
|
||||
default:
|
||||
r.NetworkManager.LogError($"ForceApplicationType of {appType} is not supported.");
|
||||
break;
|
||||
}
|
||||
|
||||
fd.Data = data;
|
||||
return fd;
|
||||
}
|
||||
|
||||
[DefaultWriter]
|
||||
public static void WritePredictionRigidbody(this Writer w, PredictionRigidbody pr)
|
||||
{
|
||||
w.Write(pr.Rigidbody.GetState(pr.RotationPacking));
|
||||
w.WriteList(pr.GetPendingForces());
|
||||
}
|
||||
|
||||
[DefaultReader]
|
||||
public static PredictionRigidbody ReadPredictionRigidbody(this Reader r)
|
||||
{
|
||||
List<PredictionRigidbody.EntryData> lst = CollectionCaches<PredictionRigidbody.EntryData>.RetrieveList();
|
||||
|
||||
RigidbodyState rs = r.Read<RigidbodyState>();
|
||||
r.ReadList(ref lst);
|
||||
PredictionRigidbody pr = ResettableObjectCaches<PredictionRigidbody>.Retrieve();
|
||||
|
||||
pr.SetReconcileData(rs, lst);
|
||||
return pr;
|
||||
}
|
||||
|
||||
[DefaultDeltaWriter]
|
||||
public static bool WriteDeltaEntryData(this Writer w, PredictionRigidbody.EntryData value)
|
||||
{
|
||||
w.WriteEntryData(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
[DefaultDeltaReader]
|
||||
public static PredictionRigidbody.EntryData ReadDeltaEntryData(this Reader r) => r.ReadEntryData();
|
||||
|
||||
[DefaultDeltaWriter]
|
||||
public static bool WriteDeltaPredictionRigidbody(this Writer w, PredictionRigidbody pr)
|
||||
{
|
||||
w.WritePredictionRigidbody(pr);
|
||||
return true;
|
||||
}
|
||||
|
||||
[DefaultDeltaReader]
|
||||
public static PredictionRigidbody ReadDeltaPredictionRigidbody(this Reader r) => r.ReadPredictionRigidbody();
|
||||
}
|
||||
|
||||
[UseGlobalCustomSerializer]
|
||||
[Preserve]
|
||||
public class PredictionRigidbody : IResettable
|
||||
{
|
||||
#region Types.
|
||||
public struct AllForceData
|
||||
{
|
||||
public ForceMode Mode;
|
||||
public Vector3 Vector3Force;
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
[ExcludeSerialization]
|
||||
public readonly AutoPackType RotationPacking;
|
||||
public float FloatForce;
|
||||
public float Radius;
|
||||
public float UpwardsModifier;
|
||||
|
||||
/// <summary>
|
||||
/// Used for MovePosition.
|
||||
/// </summary>
|
||||
public AllForceData(Vector3 position) : this()
|
||||
{
|
||||
Position = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for MoveRotation.
|
||||
/// </summary>
|
||||
public AllForceData(Quaternion rotation, AutoPackType rotationPacking) : this()
|
||||
{
|
||||
Rotation = rotation;
|
||||
RotationPacking = rotationPacking;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for Force and Torque.
|
||||
/// </summary>
|
||||
public AllForceData(Vector3 force, ForceMode mode) : this()
|
||||
{
|
||||
Vector3Force = force;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for Position.
|
||||
/// </summary>
|
||||
public AllForceData(Vector3 force, Vector3 position, ForceMode mode) : this()
|
||||
{
|
||||
Vector3Force = force;
|
||||
Position = position;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for Explosive.
|
||||
/// </summary>
|
||||
/// <param name = "force"></param>
|
||||
/// <param name = "position"></param>
|
||||
/// <param name = "radius"></param>
|
||||
/// <param name = "upwardsModifier"></param>
|
||||
/// <param name = "mode"></param>
|
||||
public AllForceData(float force, Vector3 position, float radius, float upwardsModifier, ForceMode mode) : this()
|
||||
{
|
||||
FloatForce = force;
|
||||
Position = position;
|
||||
Radius = radius;
|
||||
UpwardsModifier = upwardsModifier;
|
||||
Mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IForceData { }
|
||||
|
||||
// How the force was applied.
|
||||
[System.Flags]
|
||||
public enum ForceApplicationType : byte
|
||||
{
|
||||
AddForceAtPosition = 1 << 0,
|
||||
AddExplosiveForce = 1 << 1,
|
||||
AddForce = 1 << 2,
|
||||
AddRelativeForce = 1 << 3,
|
||||
AddTorque = 1 << 4,
|
||||
AddRelativeTorque = 1 << 5,
|
||||
MovePosition = 1 << 6,
|
||||
MoveRotation = 1 << 7,
|
||||
}
|
||||
|
||||
[UseGlobalCustomSerializer]
|
||||
public struct EntryData
|
||||
{
|
||||
public ForceApplicationType Type;
|
||||
public AllForceData Data;
|
||||
|
||||
public EntryData(ForceApplicationType type, AllForceData data)
|
||||
{
|
||||
Type = type;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public EntryData(EntryData fd)
|
||||
{
|
||||
Type = fd.Type;
|
||||
Data = fd.Data;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public.
|
||||
/// <summary>
|
||||
/// Rigidbody which force is applied.
|
||||
/// </summary>
|
||||
public Rigidbody Rigidbody { get; private set; }
|
||||
/// <summary>
|
||||
/// Returns if there are any pending forces.
|
||||
/// </summary>
|
||||
public bool HasPendingForces => _pendingForces != null && _pendingForces.Count > 0;
|
||||
#endregion
|
||||
|
||||
#region Internal.
|
||||
/// <summary>
|
||||
/// RigidbodyState set only as reconcile data.
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
internal RigidbodyState RigidbodyState;
|
||||
/// <summary>
|
||||
/// How much to pack rotation.
|
||||
/// </summary>
|
||||
[ExcludeSerialization]
|
||||
internal AutoPackType RotationPacking = AutoPackType.Packed;
|
||||
#endregion
|
||||
|
||||
#region Private
|
||||
/// <summary>
|
||||
/// Forces waiting to be applied.
|
||||
/// </summary>
|
||||
[ExcludeSerialization]
|
||||
private List<EntryData> _pendingForces;
|
||||
|
||||
/// <summary>
|
||||
/// Returns current pending forces.
|
||||
/// Modifying this collection could cause undesirable results.
|
||||
/// </summary>
|
||||
public List<EntryData> GetPendingForces() => _pendingForces;
|
||||
#endregion
|
||||
|
||||
~PredictionRigidbody()
|
||||
{
|
||||
if (_pendingForces != null)
|
||||
CollectionCaches<EntryData>.StoreAndDefault(ref _pendingForces);
|
||||
|
||||
Rigidbody = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rigidbody which force is applied.
|
||||
/// </summary>
|
||||
/// <param name = "rb"></param>
|
||||
public void Initialize(Rigidbody rb, AutoPackType rotationPacking = AutoPackType.Packed)
|
||||
{
|
||||
Rigidbody = rb;
|
||||
RotationPacking = rotationPacking;
|
||||
|
||||
if (_pendingForces == null)
|
||||
_pendingForces = CollectionCaches<EntryData>.RetrieveList();
|
||||
else
|
||||
_pendingForces.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Velocity force to the Rigidbody.
|
||||
/// </summary>
|
||||
public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddForce, new(force, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddRelativeForce(Vector3 force, ForceMode mode = ForceMode.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddRelativeForce, new(force, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddTorque(Vector3 force, ForceMode mode = ForceMode.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddTorque, new(force, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddRelativeTorque(Vector3 force, ForceMode mode = ForceMode.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddRelativeTorque, new(force, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddExplosiveForce(float force, Vector3 position, float radius, float upwardsModifier = 0f, ForceMode mode = ForceMode.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddExplosiveForce, new(force, position, radius, upwardsModifier, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode = ForceMode.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddForceAtPosition, new(force, position, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets velocity while clearing pending forces.
|
||||
/// Simulate should still be called normally.
|
||||
/// </summary>
|
||||
public void Velocity(Vector3 force)
|
||||
{
|
||||
#if UNITY_6000_1_OR_NEWER
|
||||
Rigidbody.linearVelocity = force;
|
||||
#else
|
||||
Rigidbody.velocity = force;
|
||||
#endif
|
||||
RemoveForces(nonAngular: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets angularVelocity while clearing pending forces.
|
||||
/// Simulate should still be called normally.
|
||||
/// </summary>
|
||||
public void AngularVelocity(Vector3 force)
|
||||
{
|
||||
Rigidbody.angularVelocity = force;
|
||||
RemoveForces(nonAngular: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the kinematic Rigidbody towards position.
|
||||
/// </summary>
|
||||
/// <param name="position">Next position.</param>
|
||||
public void MovePosition(Vector3 position)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.MovePosition, new(position));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the kinematic Rigidbody towards rotation.
|
||||
/// </summary>
|
||||
/// <param name="position">Next position.</param>
|
||||
public void MoveRotation(Quaternion rotation)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.MoveRotation, new(rotation, RotationPacking));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies pending forces to rigidbody in the order they were added.
|
||||
/// </summary>
|
||||
public void Simulate()
|
||||
{
|
||||
foreach (EntryData item in _pendingForces)
|
||||
{
|
||||
AllForceData data = item.Data;
|
||||
switch (item.Type)
|
||||
{
|
||||
case ForceApplicationType.AddTorque:
|
||||
Rigidbody.AddTorque(data.Vector3Force, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddForce:
|
||||
Rigidbody.AddForce(data.Vector3Force, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddRelativeTorque:
|
||||
Rigidbody.AddRelativeTorque(data.Vector3Force, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddRelativeForce:
|
||||
Rigidbody.AddRelativeForce(data.Vector3Force, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddExplosiveForce:
|
||||
Rigidbody.AddExplosionForce(data.FloatForce, data.Position, data.Radius, data.UpwardsModifier, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddForceAtPosition:
|
||||
Rigidbody.AddForceAtPosition(data.Vector3Force, data.Position, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.MovePosition:
|
||||
Rigidbody.MovePosition(data.Position);
|
||||
break;
|
||||
case ForceApplicationType.MoveRotation:
|
||||
Rigidbody.MoveRotation(data.Rotation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_pendingForces.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears current and pending forces for velocity and angularVelocity.
|
||||
/// </summary>
|
||||
public void ClearVelocities()
|
||||
{
|
||||
Velocity(Vector3.zero);
|
||||
AngularVelocity(Vector3.zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears pending forces for velocity, or angular velocity.
|
||||
/// </summary>
|
||||
/// <param name = "nonAngular">True to clear pending velocity forces, false to clear pending angularVelocity forces.</param>
|
||||
public void ClearPendingForces(bool nonAngular)
|
||||
{
|
||||
RemoveForces(nonAngular);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears pending forces for velocity and angularVelocity.
|
||||
/// </summary>
|
||||
public void ClearPendingForces()
|
||||
{
|
||||
_pendingForces.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reconciles to a state.
|
||||
/// </summary>
|
||||
public void Reconcile(PredictionRigidbody pr)
|
||||
{
|
||||
_pendingForces.Clear();
|
||||
|
||||
if (pr._pendingForces != null)
|
||||
{
|
||||
foreach (EntryData item in pr._pendingForces)
|
||||
_pendingForces.Add(new(item));
|
||||
}
|
||||
|
||||
// Set state.
|
||||
Rigidbody.SetState(pr.RigidbodyState);
|
||||
|
||||
ResettableObjectCaches<PredictionRigidbody>.Store(pr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes forces from pendingForces.
|
||||
/// </summary>
|
||||
/// <param name = "nonAngular">True to remove if velocity, false if to remove angular velocity.</param>
|
||||
private void RemoveForces(bool nonAngular)
|
||||
{
|
||||
if (_pendingForces.Count > 0)
|
||||
{
|
||||
ForceApplicationType velocityApplicationTypes = ForceApplicationType.AddRelativeForce | ForceApplicationType.AddForce | ForceApplicationType.AddExplosiveForce;
|
||||
|
||||
List<EntryData> datasToKeep = CollectionCaches<EntryData>.RetrieveList();
|
||||
foreach (EntryData item in _pendingForces)
|
||||
{
|
||||
if (VelocityApplicationTypesContains(item.Type) == !nonAngular || item.Type == ForceApplicationType.MovePosition || item.Type == ForceApplicationType.MoveRotation)
|
||||
datasToKeep.Add(item);
|
||||
}
|
||||
// Add back to _pendingForces if changed.
|
||||
if (datasToKeep.Count != _pendingForces.Count)
|
||||
{
|
||||
_pendingForces.Clear();
|
||||
|
||||
foreach (EntryData item in datasToKeep)
|
||||
_pendingForces.Add(item);
|
||||
}
|
||||
|
||||
CollectionCaches<EntryData>.Store(datasToKeep);
|
||||
|
||||
bool VelocityApplicationTypesContains(ForceApplicationType apt)
|
||||
{
|
||||
return (velocityApplicationTypes & apt) == apt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetReconcileData(RigidbodyState rs, List<EntryData> lst)
|
||||
{
|
||||
RigidbodyState = rs;
|
||||
_pendingForces = lst;
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
CollectionCaches<EntryData>.StoreAndDefault(ref _pendingForces);
|
||||
Rigidbody = null;
|
||||
}
|
||||
|
||||
public void InitializeState() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c964c0b90f389c4899a8120cc19d8b0
|
||||
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/Object/Prediction/PredictionRigidbody.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,445 @@
|
||||
using FishNet.CodeGenerating;
|
||||
using FishNet.Component.Prediction;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Serializing;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
public static class PredictionRigidbody2DSerializers
|
||||
{
|
||||
public static void WriteForceData(this Writer w, PredictionRigidbody2D.EntryData value)
|
||||
{
|
||||
PredictionRigidbody2D.ForceApplicationType appType = value.Type;
|
||||
w.WriteUInt8Unpacked((byte)appType);
|
||||
PredictionRigidbody2D.AllForceData data = value.Data;
|
||||
|
||||
switch (appType)
|
||||
{
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddForce:
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddRelativeForce:
|
||||
w.WriteVector3(data.Vector3Force);
|
||||
w.WriteInt32((byte)data.Mode);
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddTorque:
|
||||
w.WriteSingle(data.FloatForce);
|
||||
w.WriteInt32((byte)data.Mode);
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddForceAtPosition:
|
||||
w.WriteVector3(data.Vector3Force);
|
||||
w.WriteVector3(data.Position);
|
||||
w.WriteInt32((byte)data.Mode);
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.MovePosition:
|
||||
w.WriteVector3(data.Position);
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.MoveRotation:
|
||||
w.WriteUInt8Unpacked((byte)data.RotationPacking);
|
||||
w.WriteQuaternion(data.Rotation, data.RotationPacking);
|
||||
break;
|
||||
default:
|
||||
w.NetworkManager.LogError($"ForceApplicationType of {appType} is not supported.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static PredictionRigidbody2D.EntryData ReadForceData(this Reader r)
|
||||
{
|
||||
PredictionRigidbody2D.EntryData fd = new();
|
||||
|
||||
PredictionRigidbody2D.ForceApplicationType appType = (PredictionRigidbody2D.ForceApplicationType)r.ReadUInt8Unpacked();
|
||||
fd.Type = appType;
|
||||
|
||||
PredictionRigidbody2D.AllForceData data = new();
|
||||
|
||||
switch (appType)
|
||||
{
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddForce:
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddRelativeForce:
|
||||
data.Vector3Force = r.ReadVector3();
|
||||
data.Mode = (ForceMode2D)r.ReadUInt8Unpacked();
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddTorque:
|
||||
data.FloatForce = r.ReadSingle();
|
||||
data.Mode = (ForceMode2D)r.ReadUInt8Unpacked();
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.AddForceAtPosition:
|
||||
data.Vector3Force = r.ReadVector3();
|
||||
data.Position = r.ReadVector3();
|
||||
data.Mode = (ForceMode2D)r.ReadUInt8Unpacked();
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.MovePosition:
|
||||
data.Position = r.ReadVector3();
|
||||
break;
|
||||
case PredictionRigidbody2D.ForceApplicationType.MoveRotation:
|
||||
AutoPackType apt = (AutoPackType)r.ReadUInt8Unpacked();
|
||||
data.Rotation = r.ReadQuaternion(apt);
|
||||
break;
|
||||
default:
|
||||
r.NetworkManager.LogError($"ForceApplicationType of {appType} is not supported.");
|
||||
break;
|
||||
}
|
||||
|
||||
fd.Data = data;
|
||||
return fd;
|
||||
}
|
||||
|
||||
public static void WritePredictionRigidbody2D(this Writer w, PredictionRigidbody2D pr)
|
||||
{
|
||||
w.Write(pr.Rigidbody2D.GetState(pr.RotationPacking));
|
||||
w.WriteList(pr.GetPendingForces());
|
||||
}
|
||||
|
||||
public static PredictionRigidbody2D ReadPredictionRigidbody2D(this Reader r)
|
||||
{
|
||||
Rigidbody2DState rs = r.Read<Rigidbody2DState>();
|
||||
|
||||
List<PredictionRigidbody2D.EntryData> lst = CollectionCaches<PredictionRigidbody2D.EntryData>.RetrieveList();
|
||||
r.ReadList(ref lst);
|
||||
|
||||
PredictionRigidbody2D pr = ResettableObjectCaches<PredictionRigidbody2D>.Retrieve();
|
||||
pr.SetReconcileData(rs, lst);
|
||||
pr.SetPendingForces(lst);
|
||||
|
||||
return pr;
|
||||
}
|
||||
}
|
||||
|
||||
[UseGlobalCustomSerializer]
|
||||
[Preserve]
|
||||
public class PredictionRigidbody2D : IResettable
|
||||
{
|
||||
#region Types.
|
||||
// How the force was applied.
|
||||
[System.Flags]
|
||||
public enum ForceApplicationType : byte
|
||||
{
|
||||
AddForceAtPosition = 1 << 0,
|
||||
AddForce = 1 << 1,
|
||||
AddRelativeForce = 1 << 2,
|
||||
AddTorque = 1 << 3,
|
||||
MovePosition = 1 << 4,
|
||||
MoveRotation = 1 << 5,
|
||||
}
|
||||
|
||||
public struct AllForceData
|
||||
{
|
||||
public Vector3 Vector3Force;
|
||||
public float FloatForce;
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
[ExcludeSerialization]
|
||||
public readonly AutoPackType RotationPacking;
|
||||
public ForceMode2D Mode;
|
||||
|
||||
public AllForceData(Vector3 position) : this()
|
||||
{
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public AllForceData(Quaternion rotation, AutoPackType rotationPacking) : this()
|
||||
{
|
||||
Rotation = rotation;
|
||||
RotationPacking = rotationPacking;
|
||||
}
|
||||
|
||||
public AllForceData(Vector3 force, ForceMode2D mode) : this()
|
||||
{
|
||||
Vector3Force = force;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
public AllForceData(float force, ForceMode2D mode) : this()
|
||||
{
|
||||
FloatForce = force;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
public AllForceData(Vector3 force, Vector3 position, ForceMode2D mode) : this()
|
||||
{
|
||||
Vector3Force = force;
|
||||
Position = position;
|
||||
Mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
[UseGlobalCustomSerializer]
|
||||
public struct EntryData
|
||||
{
|
||||
public ForceApplicationType Type;
|
||||
public AllForceData Data;
|
||||
|
||||
public EntryData(ForceApplicationType type, AllForceData data)
|
||||
{
|
||||
Type = type;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public EntryData(EntryData fd)
|
||||
{
|
||||
Type = fd.Type;
|
||||
Data = fd.Data;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal.
|
||||
/// <summary>
|
||||
/// Rigidbody2DState set only as reconcile data.
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
internal Rigidbody2DState Rigidbody2DState;
|
||||
/// <summary>
|
||||
/// How much to pack rotation.
|
||||
/// </summary>
|
||||
[ExcludeSerialization]
|
||||
internal AutoPackType RotationPacking = AutoPackType.Packed;
|
||||
#endregion
|
||||
|
||||
#region Public.
|
||||
/// <summary>
|
||||
/// Rigidbody which force is applied.
|
||||
/// </summary>
|
||||
public Rigidbody2D Rigidbody2D { get; private set; }
|
||||
/// <summary>
|
||||
/// Returns if there are any pending forces.
|
||||
/// </summary>
|
||||
public bool HasPendingForces => _pendingForces != null && _pendingForces.Count > 0;
|
||||
#endregion
|
||||
|
||||
#region Private
|
||||
/// <summary>
|
||||
/// Forces waiting to be applied.
|
||||
/// </summary>
|
||||
[ExcludeSerialization]
|
||||
private List<EntryData> _pendingForces;
|
||||
|
||||
/// <summary>
|
||||
/// Returns current pending forces.
|
||||
/// Modifying this collection could cause undesirable results.
|
||||
/// </summary>
|
||||
public List<EntryData> GetPendingForces() => _pendingForces;
|
||||
#endregion
|
||||
|
||||
~PredictionRigidbody2D()
|
||||
{
|
||||
if (_pendingForces != null)
|
||||
CollectionCaches<EntryData>.StoreAndDefault(ref _pendingForces);
|
||||
|
||||
Rigidbody2D = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rigidbody which force is applied.
|
||||
/// </summary>
|
||||
/// <param name = "rb"></param>
|
||||
public void Initialize(Rigidbody2D rb, AutoPackType rotationPacking = AutoPackType.Packed)
|
||||
{
|
||||
Rigidbody2D = rb;
|
||||
RotationPacking = rotationPacking;
|
||||
|
||||
if (_pendingForces == null)
|
||||
_pendingForces = CollectionCaches<EntryData>.RetrieveList();
|
||||
else
|
||||
_pendingForces.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Velocity force to the Rigidbody.
|
||||
/// </summary>
|
||||
public void AddForce(Vector3 force, ForceMode2D mode = ForceMode2D.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddForce, new(force, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddRelativeForce(Vector3 force, ForceMode2D mode = ForceMode2D.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddRelativeForce, new(force, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddTorque(float force, ForceMode2D mode = ForceMode2D.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddTorque, new(force, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode2D mode = ForceMode2D.Force)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.AddForceAtPosition, new(force, position, mode));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets velocity while clearing pending forces.
|
||||
/// Simulate should still be called normally.
|
||||
/// </summary>
|
||||
public void Velocity(Vector3 force)
|
||||
{
|
||||
#if UNITY_6000_1_OR_NEWER
|
||||
Rigidbody2D.linearVelocity = force;
|
||||
#else
|
||||
Rigidbody2D.velocity = force;
|
||||
#endif
|
||||
RemoveForces(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets angularVelocity while clearning pending forces.
|
||||
/// Simulate should still be called normally.
|
||||
/// </summary>
|
||||
public void AngularVelocity(float force)
|
||||
{
|
||||
Rigidbody2D.angularVelocity = force;
|
||||
RemoveForces(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the kinematic Rigidbody towards position.
|
||||
/// </summary>
|
||||
/// <param name="position">Next position.</param>
|
||||
public void MovePosition(Vector3 position)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.MovePosition, new(position));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the kinematic Rigidbody towards rotation.
|
||||
/// </summary>
|
||||
/// <param name="position">Next position.</param>
|
||||
public void MoveRotation(Quaternion rotation)
|
||||
{
|
||||
EntryData fd = new(ForceApplicationType.MoveRotation, new(rotation, RotationPacking));
|
||||
_pendingForces.Add(fd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies pending forces to rigidbody in the order they were added.
|
||||
/// </summary>
|
||||
public void Simulate()
|
||||
{
|
||||
foreach (EntryData item in _pendingForces)
|
||||
{
|
||||
AllForceData data = item.Data;
|
||||
switch (item.Type)
|
||||
{
|
||||
case ForceApplicationType.AddTorque:
|
||||
Rigidbody2D.AddTorque(data.FloatForce, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddForce:
|
||||
Rigidbody2D.AddForce(data.Vector3Force, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddRelativeForce:
|
||||
Rigidbody2D.AddRelativeForce(data.Vector3Force, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.AddForceAtPosition:
|
||||
Rigidbody2D.AddForceAtPosition(data.Vector3Force, data.Position, data.Mode);
|
||||
break;
|
||||
case ForceApplicationType.MovePosition:
|
||||
Rigidbody2D.MovePosition(data.Position);
|
||||
break;
|
||||
case ForceApplicationType.MoveRotation:
|
||||
Rigidbody2D.MoveRotation(data.Rotation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_pendingForces.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears current and pending forces for velocity and angularVelocity.
|
||||
/// </summary>
|
||||
public void ClearVelocities()
|
||||
{
|
||||
Velocity(Vector3.zero);
|
||||
AngularVelocity(0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears pending forces for velocity, or angular velocity.
|
||||
/// </summary>
|
||||
/// <param name = "nonRotational">True to clear velocities, false to clear angular velocities.</param>
|
||||
public void ClearPendingForces(bool nonRotational)
|
||||
{
|
||||
RemoveForces(nonRotational);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears pending forces for velocity and angularVelocity.
|
||||
/// </summary>
|
||||
public void ClearPendingForces()
|
||||
{
|
||||
_pendingForces.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reconciles to a state.
|
||||
/// </summary>
|
||||
public void Reconcile(PredictionRigidbody2D pr)
|
||||
{
|
||||
_pendingForces.Clear();
|
||||
if (pr._pendingForces != null)
|
||||
{
|
||||
foreach (EntryData item in pr._pendingForces)
|
||||
_pendingForces.Add(new(item));
|
||||
}
|
||||
Rigidbody2D.SetState(pr.Rigidbody2DState);
|
||||
|
||||
ResettableObjectCaches<PredictionRigidbody2D>.Store(pr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes forces from pendingForces.
|
||||
/// </summary>
|
||||
/// <param name = "nonAngular">True to remove if velocity, false if to remove angular velocity.</param>
|
||||
private void RemoveForces(bool nonAngular)
|
||||
{
|
||||
if (_pendingForces.Count > 0)
|
||||
{
|
||||
ForceApplicationType velocityApplicationTypes = ForceApplicationType.AddRelativeForce | ForceApplicationType.AddForce;
|
||||
|
||||
List<EntryData> datasToKeep = CollectionCaches<EntryData>.RetrieveList();
|
||||
foreach (EntryData item in _pendingForces)
|
||||
{
|
||||
if (VelocityApplicationTypesContains(item.Type) == !nonAngular || item.Type == ForceApplicationType.MovePosition || item.Type == ForceApplicationType.MoveRotation)
|
||||
datasToKeep.Add(item);
|
||||
}
|
||||
// Add back to _pendingForces if changed.
|
||||
if (datasToKeep.Count != _pendingForces.Count)
|
||||
{
|
||||
_pendingForces.Clear();
|
||||
|
||||
foreach (EntryData item in datasToKeep)
|
||||
_pendingForces.Add(item);
|
||||
}
|
||||
CollectionCaches<EntryData>.Store(datasToKeep);
|
||||
|
||||
bool VelocityApplicationTypesContains(ForceApplicationType apt)
|
||||
{
|
||||
return (velocityApplicationTypes & apt) == apt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetPendingForces(List<EntryData> lst) => _pendingForces = lst;
|
||||
|
||||
internal void SetReconcileData(Rigidbody2DState rs, List<EntryData> lst)
|
||||
{
|
||||
Rigidbody2DState = rs;
|
||||
_pendingForces = lst;
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
CollectionCaches<EntryData>.StoreAndDefault(ref _pendingForces);
|
||||
Rigidbody2D = null;
|
||||
}
|
||||
|
||||
public void InitializeState() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b6a1cc418198134180faa3438517b52
|
||||
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/Object/Prediction/PredictionRigidbody2D.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,98 @@
|
||||
using FishNet.Utility;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FishNet.CodeGenerating;
|
||||
using FishNet.Transporting;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)]
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
[MakePublic]
|
||||
internal struct ReplicateDataContainer<T> where T : IReplicateData, new()
|
||||
{
|
||||
#region Types
|
||||
private enum DataCachingType
|
||||
{
|
||||
Unset,
|
||||
ValueType,
|
||||
IResettableReferenceType,
|
||||
ReferenceType
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Replicate data.
|
||||
/// </summary>
|
||||
public T Data;
|
||||
/// <summary>
|
||||
/// True if the data was created locally or came through the network as created.
|
||||
/// </summary>
|
||||
public bool IsCreated;
|
||||
/// <summary>
|
||||
/// Channel the data came in on.
|
||||
/// </summary>
|
||||
public readonly Channel Channel;
|
||||
/// <summary>
|
||||
/// True if populated.
|
||||
/// </summary>
|
||||
public bool IsValid { get; private set; }
|
||||
/// <summary>
|
||||
/// How data should be cached and retrieved when not set.
|
||||
/// </summary>
|
||||
private static DataCachingType _dataCachingType = DataCachingType.Unset;
|
||||
public ReplicateDataContainer(T data, Channel channel) : this(data, channel, tick: 0, isCreated: false) { }
|
||||
public ReplicateDataContainer(T data, Channel channel, bool isCreated) : this(data, channel, tick: 0, isCreated) { }
|
||||
|
||||
public ReplicateDataContainer(T data, Channel channel, uint tick, bool isCreated = false)
|
||||
{
|
||||
Data = data;
|
||||
Channel = channel;
|
||||
IsCreated = isCreated;
|
||||
IsValid = true;
|
||||
|
||||
SetDataTick(tick);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A shortcut to calling Data.SetTick.
|
||||
/// </summary>
|
||||
public void SetDataTick(uint tick)
|
||||
{
|
||||
SetDataIfNull(ref Data);
|
||||
Data.SetTick(tick);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets data to new() if is nullable type, and is null.
|
||||
/// </summary>
|
||||
/// <param name = "data"></param>
|
||||
private void SetDataIfNull(ref T data)
|
||||
{
|
||||
// Only figure out data caching type once to save perf.
|
||||
if (_dataCachingType == DataCachingType.Unset)
|
||||
{
|
||||
if (typeof(T).IsValueType)
|
||||
_dataCachingType = DataCachingType.ValueType;
|
||||
else if (typeof(IResettable).IsAssignableFrom(typeof(T)))
|
||||
_dataCachingType = DataCachingType.IResettableReferenceType;
|
||||
else
|
||||
_dataCachingType = DataCachingType.ReferenceType;
|
||||
}
|
||||
|
||||
if (_dataCachingType != DataCachingType.ValueType && data == null)
|
||||
data = ObjectCaches<T>.Retrieve();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Data != null)
|
||||
Data.Dispose();
|
||||
|
||||
IsValid = false;
|
||||
}
|
||||
|
||||
public static ReplicateDataContainer<T> GetDefault(uint tick) => new(default, Channel.Unreliable, tick);
|
||||
public static ReplicateDataContainer<T> GetDefault() => GetDefault(tick: 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e45211dba582c146993b54bf316d8fc
|
||||
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/Object/Prediction/ReplicateDataContainer.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using FishNet.Utility;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)]
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
[Flags]
|
||||
public enum ReplicateState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The default value of this state.
|
||||
/// This value should never occur when a replicate runs.
|
||||
/// </summary>
|
||||
Invalid = 0,
|
||||
/// <summary>
|
||||
/// Server and clients use this flag.
|
||||
/// Flag will be set if data tick has run outside a reconcile, such as from user code within OnTick.
|
||||
/// </summary>
|
||||
Ticked = 1 << 0, // 1
|
||||
/// <summary>
|
||||
/// Only client will use this flag.
|
||||
/// Flag is set if data is being run during a reconcile.
|
||||
/// </summary>
|
||||
Replayed = 1 << 1, // 2
|
||||
/// <summary>
|
||||
/// Server and client use this flag.
|
||||
/// Data has been created by the server or client.
|
||||
/// This indicates that data is known and was intentionally sent.
|
||||
/// </summary>
|
||||
Created = 1 << 2 // 4
|
||||
}
|
||||
|
||||
public static class ReplicateStateExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if value is valid.
|
||||
/// This should never be false.
|
||||
/// </summary>
|
||||
public static bool IsValid(this ReplicateState value) => value != ReplicateState.Invalid;
|
||||
|
||||
/// <summary>
|
||||
/// Returns if value contains ReplicateState.Ticked.
|
||||
/// </summary>
|
||||
public static bool ContainsTicked(this ReplicateState value) => value.FastContains(ReplicateState.Ticked);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if value contains ReplicateState.Created.
|
||||
/// </summary>
|
||||
public static bool ContainsCreated(this ReplicateState value) => value.FastContains(ReplicateState.Created);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if value contains ReplicateState.Replayed.
|
||||
/// </summary>
|
||||
public static bool ContainsReplayed(this ReplicateState value) => value.FastContains(ReplicateState.Replayed);
|
||||
|
||||
[Obsolete("Use ContainsReplayed.")]
|
||||
public static bool IsReplayed(this ReplicateState value) => value.ContainsReplayed();
|
||||
|
||||
/// <summary>
|
||||
/// Returns if value is (ReplicateState.Ticked | ReplicateState.Created).
|
||||
/// </summary>
|
||||
public static bool IsTickedCreated(this ReplicateState value) => value == (ReplicateState.Ticked | ReplicateState.Created);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if value equals ReplicateState.Ticked.
|
||||
/// </summary>
|
||||
public static bool IsTickedNonCreated(this ReplicateState value) => value == ReplicateState.Ticked;
|
||||
|
||||
/// <summary>
|
||||
/// Returns if value is (ReplicateState.Replayed | ReplicateState.Ticked | ReplicateState.Created).
|
||||
/// </summary>
|
||||
public static bool IsReplayedCreated(this ReplicateState value) => value == (ReplicateState.Replayed | ReplicateState.Created);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if value is ReplicateState.Replayed without ReplicateState.Ticked nor ReplicateState.Created.
|
||||
/// </summary>
|
||||
public static bool IsFuture(this ReplicateState value) => value == ReplicateState.Replayed;
|
||||
|
||||
[Obsolete("Use ContainsCreated.")]
|
||||
public static bool IsCreated(this ReplicateState value) => value.ContainsCreated();
|
||||
|
||||
/// <summary>
|
||||
/// True if part is containined within whole.
|
||||
/// </summary>
|
||||
public static bool FastContains(this ReplicateState whole, ReplicateState part) => (whole & part) == part;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8deb74673db557489cf93509af3cb21
|
||||
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/Object/Prediction/ReplicateState.cs
|
||||
uploadId: 866910
|
||||
Reference in New Issue
Block a user