[Add] FishNet
This commit is contained in:
@@ -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() { }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user