using OWML.Common; using QSB.Utility; using QuantumUNET.Transport; using UnityEngine; namespace QSB.Syncs.Sectored.Rigidbodies { public abstract class SectoredRigidbodySync : BaseSectoredSync { public override bool ShouldReparentAttachedObject => false; public const float PositionMovedThreshold = 0.05f; public const float AngleRotatedThreshold = 0.05f; public const float VelocityChangeThreshold = 0.05f; public const float AngVelocityChangeThreshold = 0.05f; protected Vector3 _relativeVelocity; protected Vector3 _relativeAngularVelocity; protected Vector3 _prevVelocity; protected Vector3 _prevAngularVelocity; /// /// The previous position of the VISIBLE object, as if parented to the reference. /// protected Vector3 _localPrevPosition; /// /// The previous rotation of the VISIBLE object, as if parented to the reference. /// protected Quaternion _localPrevRotation; protected Vector3 _localPrevVelocity; protected Vector3 _localPrevAngularVelocity; protected abstract OWRigidbody GetRigidbody(); protected override Component SetAttachedObject() => GetRigidbody(); public override void SerializeTransform(QNetworkWriter writer, bool initialState) { base.SerializeTransform(writer, initialState); var worldPos = transform.position; var worldRot = transform.rotation; var relativeVelocity = _relativeVelocity; var relativeAngularVelocity = _relativeAngularVelocity; writer.Write(worldPos); SerializeRotation(writer, worldRot); writer.Write(relativeVelocity); writer.Write(relativeAngularVelocity); _prevPosition = worldPos; _prevRotation = worldRot; _prevVelocity = relativeVelocity; _prevAngularVelocity = relativeAngularVelocity; } public override void DeserializeTransform(QNetworkReader reader, bool initialState) { base.DeserializeTransform(reader, initialState); if (!QSBCore.WorldObjectsReady) { reader.ReadVector3(); DeserializeRotation(reader); reader.ReadVector3(); reader.ReadVector3(); return; } var pos = reader.ReadVector3(); var rot = DeserializeRotation(reader); var relativeVelocity = reader.ReadVector3(); var relativeAngularVelocity = reader.ReadVector3(); if (HasAuthority) { return; } transform.position = pos; transform.rotation = rot; _relativeVelocity = relativeVelocity; _relativeAngularVelocity = relativeAngularVelocity; if (transform.position == Vector3.zero) { DebugLog.ToConsole($"Warning - {LogName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); } } protected void SetValuesToSync() { transform.position = ReferenceTransform.EncodePos(AttachedObject.transform.position); transform.rotation = ReferenceTransform.EncodeRot(AttachedObject.transform.rotation); _relativeVelocity = ReferenceTransform.GetAttachedOWRigidbody().EncodeVel(((OWRigidbody)AttachedObject).GetVelocity(), AttachedObject.transform.position); _relativeAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().EncodeAngVel(((OWRigidbody)AttachedObject).GetAngularVelocity()); } protected override bool UpdateTransform() { if (!base.UpdateTransform()) { return false; } if (HasAuthority) { SetValuesToSync(); return true; } var targetPos = ReferenceTransform.DecodePos(transform.position); var targetRot = ReferenceTransform.DecodeRot(transform.rotation); if (targetPos == Vector3.zero || transform.position == Vector3.zero) { return false; } var positionToSet = targetPos; var rotationToSet = targetRot; if (UseInterpolation) { positionToSet = SmartSmoothDamp(AttachedObject.transform.position, targetPos); rotationToSet = QuaternionHelper.SmoothDamp(AttachedObject.transform.rotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); } var hasMoved = CustomHasMoved( transform.position, _localPrevPosition, transform.rotation, _localPrevRotation, _relativeVelocity, _localPrevVelocity, _relativeAngularVelocity, _localPrevAngularVelocity); _localPrevPosition = transform.position; _localPrevRotation = transform.rotation; _localPrevVelocity = _relativeVelocity; _localPrevAngularVelocity = _relativeAngularVelocity; if (!hasMoved) { return true; } ((OWRigidbody)AttachedObject).MoveToPosition(positionToSet); ((OWRigidbody)AttachedObject).MoveToRotation(rotationToSet); var targetVelocity = ReferenceTransform.GetAttachedOWRigidbody().DecodeVel(_relativeVelocity, targetPos); var targetAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().DecodeAngVel(_relativeAngularVelocity); ((OWRigidbody)AttachedObject).SetVelocity(targetVelocity); ((OWRigidbody)AttachedObject).SetAngularVelocity(targetAngularVelocity); return true; } public override bool HasMoved() => CustomHasMoved( transform.position, _prevPosition, transform.rotation, _prevRotation, _relativeVelocity, _prevVelocity, _relativeAngularVelocity, _prevAngularVelocity); // OPTIMIZE : optimize by using sqrMagnitude private bool CustomHasMoved( Vector3 newPosition, Vector3 prevPosition, Quaternion newRotation, Quaternion prevRotation, Vector3 newVelocity, Vector3 prevVelocity, Vector3 newAngVelocity, Vector3 prevAngVelocity) { var displacementMagnitude = (newPosition - prevPosition).magnitude; if (displacementMagnitude > PositionMovedThreshold) { return true; } if (Quaternion.Angle(newRotation, prevRotation) > AngleRotatedThreshold) { return true; } var velocityChangeMagnitude = (newVelocity - prevVelocity).magnitude; var angularVelocityChangeMagnitude = (newAngVelocity - prevAngVelocity).magnitude; if (velocityChangeMagnitude > VelocityChangeThreshold) { return true; } if (angularVelocityChangeMagnitude > AngVelocityChangeThreshold) { return true; } return false; } } }