private void ApplyRigidbody()
        {
            if (_rigidbody == null)
            {
                return;
            }

            if (isOwnedLocally)
            {
                return;
            }

            // If we're using rigidbody extrapolation and we've collided with something, let the local rigidbody simulation run until we receive another packet or 500ms has passed
            if (_rigidbody != null && _extrapolation && _stopExtrapolatingAtRoomTime >= 0.0 && (realtime.room.time - _stopExtrapolatingAtRoomTime) < 0.5f)
            {
                return;
            }

            if (_rigidbody.useGravity != _model.useGravity)
            {
                _rigidbody.useGravity = _model.useGravity;
            }

            if (_rigidbody.isKinematic != _model.isKinematic)
            {
                _rigidbody.isKinematic = _model.isKinematic;
            }

            // If the model is owned by someone, we extrapolate & lerp.
            if (_model.ownerID != -1)
            {
                // Snapshot
                Vector3    position          = _syncPosition ? _model.position : _rigidbody.position;
                Quaternion rotation          = _syncRotation ? _model.rotation : _rigidbody.rotation;
                Vector3    scale             = _syncScale    ? _model.scale    :  transform.localScale;
                Vector3    velocity          = _model.velocity;
                Vector3    angularVelocity   = _model.angularVelocity;
                bool       shouldExtrapolate = _model.shouldExtrapolate;
                bool       useGravity        = _model.useGravity;
                double     timestamp         = _model.timestamp;

                float deltaTime = (float)(realtime.room.time - timestamp);

                // If the last snapshot is too old, skip extrapolation.
                if (deltaTime > 1.0f)
                {
                    shouldExtrapolate = false;
                }

                // Extrapolate
                if (shouldExtrapolate)
                {
                    // Cap extrapolation time
                    float extrapolateTimeMax = _model.isKinematic ? Time.fixedDeltaTime * 5.0f : 0.5f; // Limit to 5 frames if kinematic. 500ms if non-kinematic.
                    deltaTime = Mathf.Clamp(deltaTime, 0.0f, extrapolateTimeMax);

                    // Simulate
                    // TODO: I suspect that this function is off even though I've checked it. I know the single frame one is good, but I'm worried this one is off which is causing switching to the local simulation on bounce to not work correctly.
                    PhysXEmulation.SimulatePhysX(ref position, ref rotation, ref velocity, ref angularVelocity, useGravity, Physics.gravity, _rigidbody.drag, _rigidbody.angularDrag, _rigidbody.maxDepenetrationVelocity, _rigidbody.maxAngularVelocity, _rigidbody.constraints, deltaTime, Time.fixedDeltaTime);
                }

                // Apply
                float lerpFactor = 25.0f;
                // Set scale before position/rotation otherwise scale will get reset.
                if (_syncScale)
                {
                    transform.localScale = Vector3.Lerp(transform.localScale, scale, lerpFactor * Time.fixedDeltaTime);
                }
                if (_syncPosition)
                {
                    _rigidbody.MovePosition(Vector3.Lerp(_rigidbody.position, position, lerpFactor * Time.fixedDeltaTime));
                }
                if (_syncRotation)
                {
                    _rigidbody.MoveRotation(Quaternion.Slerp(_rigidbody.rotation, rotation, lerpFactor * Time.fixedDeltaTime));
                }
            }
            else
            {
                // If the model isn't owned by anyone, set the transform position instantly.
                if (_syncScale)
                {
                    transform.localScale = _model.scale;
                }
                if (_syncPosition)
                {
                    _rigidbody.position = _model.position;
                }
                if (_syncRotation)
                {
                    _rigidbody.rotation = _model.rotation;
                }
                if (_syncPosition)
                {
                    _rigidbody.velocity = Vector3.zero;
                }
                if (_syncRotation)
                {
                    _rigidbody.angularVelocity = Vector3.zero;
                }
            }
        }
        // Apply
        private void ApplyTransform()
        {
            if (_rigidbody != null)
            {
                return;
            }

            if (isOwnedLocally)
            {
                return;
            }

            // If the model is owned by someone, we extrapolate & lerp.
            if (_model.ownerID != -1)
            {
                // Snapshot
                Vector3    position          = _syncPosition ? _model.position : transform.localPosition;
                Quaternion rotation          = _syncRotation ? _model.rotation : transform.localRotation;
                Vector3    scale             = _syncScale    ? _model.scale    : transform.localScale;
                bool       shouldExtrapolate = _model.shouldExtrapolate;
                double     timestamp         = _model.timestamp;

                float deltaTime = (float)(realtime.room.time - timestamp);

                // If the last snapshot is too old, skip extrapolation.
                if (deltaTime > 1.0f)
                {
                    shouldExtrapolate = false;
                }

                // Extrapolate
                if (shouldExtrapolate)
                {
                    // Calculate velocity & angularVelocity
                    Vector3 velocity        = Vector3.zero;
                    Vector3 angularVelocity = Vector3.zero;

                    Vector3    previousPosition  = _model.previousPosition;
                    Quaternion previousRotation  = _model.previousRotation;
                    double     previousTimestamp = _model.previousTimestamp;

                    // Only extrapolate if we have a previous frame, otherwise we'll have no data to extrapolate with.
                    if (previousTimestamp > 0.0)
                    {
                        Vector3    positionDelta           = position - previousPosition;
                        Quaternion rotationDeltaQuaternion = Quaternion.Inverse(previousRotation) * rotation;
                        Vector3    rotationDeltaDegrees    = ReduceAngles(rotationDeltaQuaternion.eulerAngles);
                        Vector3    rotationDelta           = new Vector3(rotationDeltaDegrees.x * Mathf.Deg2Rad, rotationDeltaDegrees.y * Mathf.Deg2Rad, rotationDeltaDegrees.z * Mathf.Deg2Rad);
                        float      timeDelta = (float)(timestamp - previousTimestamp);

                        if (timeDelta > 0.0f)
                        {
                            velocity        = positionDelta / timeDelta;
                            angularVelocity = rotationDelta / timeDelta;
                        }

                        // Cap extrapolation time
                        float extrapolateTimeMax = Time.fixedDeltaTime * 10.0f; // Limit to 5 frames.
                        deltaTime = Mathf.Clamp(deltaTime, 0.0f, extrapolateTimeMax);

                        // Simulate
                        PhysXEmulation.SimulatePhysX(ref position, ref rotation, ref velocity, ref angularVelocity, false, Vector3.zero, 0.0f, 0.0f, float.PositiveInfinity, float.PositiveInfinity, RigidbodyConstraints.None, deltaTime, Time.fixedDeltaTime);
                    }
                }

                // Apply
                float lerpFactor = 25.0f;
                if (_syncPosition)
                {
                    transform.localPosition = Vector3.Lerp(transform.localPosition, position, lerpFactor * Time.deltaTime);
                }
                if (_syncRotation)
                {
                    transform.localRotation = Quaternion.Slerp(transform.localRotation, rotation, lerpFactor * Time.deltaTime);
                }
                if (_syncScale)
                {
                    transform.localScale = Vector3.Lerp(transform.localScale, scale, lerpFactor * Time.deltaTime);
                }
            }
            else
            {
                // If the model isn't owned by anyone, set the transform position instantly.
                if (_syncPosition)
                {
                    transform.localPosition = _model.position;
                }
                if (_syncRotation)
                {
                    transform.localRotation = _model.rotation;
                }
                if (_syncScale)
                {
                    transform.localScale = _model.scale;
                }
            }
        }