using FishNet.CodeGenerating; using FishNet.Managing; using FishNet.Serializing; using GameKit.Dependencies.Utilities; using System.Collections.Generic; using UnityEngine; namespace FishNet.Object.Prediction { #if !PREDICTION_1 public static class PredictionRigidbodySerializers { public static void WriteForceData(this Writer w, PredictionRigidbody.EntryData value) { PredictionRigidbody.ForceApplicationType appType = value.Type; w.WriteByte((byte)appType); switch (appType) { case PredictionRigidbody.ForceApplicationType.AddTorque: case PredictionRigidbody.ForceApplicationType.AddForce: case PredictionRigidbody.ForceApplicationType.AddRelativeTorque: case PredictionRigidbody.ForceApplicationType.AddRelativeForce: w.Write((PredictionRigidbody.ForceAndTorqueData)value.Data); break; case PredictionRigidbody.ForceApplicationType.AddExplosiveForce: w.Write((PredictionRigidbody.ExplosiveForceData)value.Data); break; case PredictionRigidbody.ForceApplicationType.AddForceAtPosition: w.Write((PredictionRigidbody.PositionForceData)value.Data); break; default: NetworkManagerExtensions.LogError($"ForceApplicationType of {appType} is not supported."); break; } } public static PredictionRigidbody.EntryData ReadForceData(this Reader r) { PredictionRigidbody.EntryData fd = new PredictionRigidbody.EntryData(); PredictionRigidbody.ForceApplicationType appType = (PredictionRigidbody.ForceApplicationType)r.ReadByte(); fd.Type = appType; switch (appType) { case PredictionRigidbody.ForceApplicationType.AddTorque: case PredictionRigidbody.ForceApplicationType.AddForce: case PredictionRigidbody.ForceApplicationType.AddRelativeTorque: case PredictionRigidbody.ForceApplicationType.AddRelativeForce: fd.Data = r.Read(); return fd; case PredictionRigidbody.ForceApplicationType.AddExplosiveForce: fd.Data = r.Read(); return fd; case PredictionRigidbody.ForceApplicationType.AddForceAtPosition: fd.Data = r.Read(); return fd; default: NetworkManagerExtensions.LogError($"ForceApplicationType of {appType} is not supported."); return fd; } } public static void WritePredictionRigidbody(this Writer w, PredictionRigidbody pr) { w.WriteList(pr.GetPendingForces()); } public static PredictionRigidbody ReadPredictionRigidbody(this Reader r) { List lst = CollectionCaches.RetrieveList(); r.ReadList(ref lst); PredictionRigidbody pr = ResettableObjectCaches.Retrieve(); pr.SetPendingForces(lst); return pr; } } [UseGlobalCustomSerializer] public class PredictionRigidbody : IResettable { #region Types. public interface IForceData { } //How the force was applied. [System.Flags] public enum ForceApplicationType : byte { AddForceAtPosition = 1, AddExplosiveForce = 2, AddForce = 4, AddRelativeForce = 8, AddTorque = 16, AddRelativeTorque = 32, } public struct ForceAndTorqueData : IForceData { public Vector3 Force; public ForceMode Mode; public ForceAndTorqueData(Vector3 force, ForceMode mode) { Force = force; Mode = mode; } } public struct PositionForceData : IForceData { public Vector3 Force; public Vector3 Position; public ForceMode Mode; public PositionForceData(Vector3 force, Vector3 position, ForceMode mode) { Force = force; Position = position; Mode = mode; } } public struct ExplosiveForceData : IForceData { public float Force; public Vector3 Position; public float Radius; public float UpwardsModifier; public ForceMode Mode; public ExplosiveForceData(float force, Vector3 position, float radius, float upwardsModifier, ForceMode mode) { Force = force; Position = position; Radius = radius; UpwardsModifier = upwardsModifier; Mode = mode; } } [UseGlobalCustomSerializer] public struct EntryData { public ForceApplicationType Type; public IForceData Data; public EntryData(ForceApplicationType type, IForceData data) { Type = type; Data = data; } public EntryData(EntryData fd) { Type = fd.Type; Data = fd.Data; } } #endregion #region Public. /// /// Rigidbody which force is applied. /// public Rigidbody Rigidbody { get; private set; } #endregion #region Private /// /// Forces waiting to be applied. /// [ExcludeSerialization] private List _pendingForces; #endregion ~PredictionRigidbody() { if (_pendingForces != null) CollectionCaches.StoreAndDefault(ref _pendingForces); Rigidbody = null; } /// /// Rigidbody which force is applied. /// /// public void Initialize(Rigidbody rb) { Rigidbody = rb; if (_pendingForces == null) _pendingForces = CollectionCaches.RetrieveList(); else _pendingForces.Clear(); } /// /// Adds Velocity force to the Rigidbody. /// public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force) { EntryData fd = new EntryData(ForceApplicationType.AddForce, new ForceAndTorqueData(force, mode)); _pendingForces.Add(fd); } public void AddRelativeForce(Vector3 force, ForceMode mode = ForceMode.Force) { EntryData fd = new EntryData(ForceApplicationType.AddRelativeForce, new ForceAndTorqueData(force, mode)); _pendingForces.Add(fd); } public void AddTorque(Vector3 force, ForceMode mode = ForceMode.Force) { EntryData fd = new EntryData(ForceApplicationType.AddTorque, new ForceAndTorqueData(force, mode)); _pendingForces.Add(fd); } public void AddRelativeTorque(Vector3 force, ForceMode mode = ForceMode.Force) { EntryData fd = new EntryData(ForceApplicationType.AddRelativeTorque, new ForceAndTorqueData(force, mode)); _pendingForces.Add(fd); } public void AddExplosiveForce(float force, Vector3 position, float radius, float upwardsModifier = 0f, ForceMode mode = ForceMode.Force) { EntryData fd = new EntryData(ForceApplicationType.AddExplosiveForce, new ExplosiveForceData(force, position, radius, upwardsModifier, mode)); _pendingForces.Add(fd); } public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode = ForceMode.Force) { EntryData fd = new EntryData(ForceApplicationType.AddForceAtPosition, new PositionForceData(force, position, mode)); _pendingForces.Add(fd); } /// /// Sets velocity while clearing pending forces. /// Simulate should still be called normally. /// public void Velocity(Vector3 force) { Rigidbody.velocity = force; RemoveForces(true); } /// /// Sets angularVelocity while clearning pending forces. /// Simulate should still be called normally. /// public void AngularVelocity(Vector3 force) { Rigidbody.angularVelocity = force; RemoveForces(false); } /// /// Applies pending forces to rigidbody in the order they were added. /// public void Simulate() { foreach (EntryData item in _pendingForces) { switch (item.Type) { case ForceApplicationType.AddTorque: ForceAndTorqueData e0 = (ForceAndTorqueData)item.Data; Rigidbody.AddTorque(e0.Force, e0.Mode); break; case ForceApplicationType.AddForce: ForceAndTorqueData e1 = (ForceAndTorqueData)item.Data; Rigidbody.AddForce(e1.Force, e1.Mode); break; case ForceApplicationType.AddRelativeTorque: ForceAndTorqueData e2 = (ForceAndTorqueData)item.Data; Rigidbody.AddRelativeTorque(e2.Force, e2.Mode); break; case ForceApplicationType.AddRelativeForce: ForceAndTorqueData e3 = (ForceAndTorqueData)item.Data; Rigidbody.AddRelativeForce(e3.Force, e3.Mode); break; case ForceApplicationType.AddExplosiveForce: ExplosiveForceData e4 = (ExplosiveForceData)item.Data; Rigidbody.AddExplosionForce(e4.Force, e4.Position, e4.Radius, e4.UpwardsModifier, e4.Mode); break; case ForceApplicationType.AddForceAtPosition: PositionForceData e5 = (PositionForceData)item.Data; Rigidbody.AddForceAtPosition(e5.Force, e5.Position, e5.Mode); break; } } _pendingForces.Clear(); } /// /// Manually clears pending forces. /// /// True to clear velocities, false to clear angular velocities. public void ClearPendingForces(bool velocity) { RemoveForces(velocity); } /// /// Clears pending velocity and angular velocity forces. /// public void ClearPendingForces() { _pendingForces.Clear(); } /// /// Reconciles to a state. /// public void Reconcile(PredictionRigidbody pr) { _pendingForces.Clear(); if (pr._pendingForces != null) { foreach (EntryData item in pr._pendingForces) _pendingForces.Add(new EntryData(item)); } ResettableObjectCaches.Store(pr); } /// /// Removes forces from pendingForces. /// /// True to remove if velocity, false if to remove angular velocity. private void RemoveForces(bool velocity) { if (_pendingForces.Count > 0) { bool shouldExist = velocity; ForceApplicationType velocityApplicationTypes = (ForceApplicationType.AddRelativeForce | ForceApplicationType.AddForce | ForceApplicationType.AddExplosiveForce); List newDatas = CollectionCaches.RetrieveList(); foreach (EntryData item in _pendingForces) { if (VelocityApplicationTypesContains(item.Type) == !velocity) newDatas.Add(item); } //Add back to _pendingForces if changed. if (newDatas.Count != _pendingForces.Count) { _pendingForces.Clear(); foreach (EntryData item in newDatas) _pendingForces.Add(item); } CollectionCaches.Store(newDatas); bool VelocityApplicationTypesContains(ForceApplicationType apt) { return (velocityApplicationTypes & apt) == apt; } } } internal List GetPendingForces() => _pendingForces; internal void SetPendingForces(List lst) => _pendingForces = lst; public void ResetState() { CollectionCaches.StoreAndDefault(ref _pendingForces); Rigidbody = null; } public void InitializeState() { } } #endif }