public void Handle(Stabilize message)
            {
                _node.LogMessage(message);
                _commMgr.SendAck(message, message.CorrelationId);

                var reply = new StabilizeReply(_node.Identity, message.From, message.CorrelationId)
                {
                    Predecessor = _node.Predecessor,
                    // Take the opportunity to ensure the successor table is up to date.
                    SuccessorTableEntries = _node.SuccessorTable.Entries,
                };

                _node.Log($"Sending {reply.TypeName()} Id:{reply.CorrelationId}");
                _commMgr.Send(reply);
            }
        internal void OnMakeTurn()
        {
            if (Stabilize == null)
            {
                return;
            }

            double ftime = Settings.Instance.ActorTurnStabilizeTime;

            if (ftime <= 0.0)
            {
                return;
            }

            var now = Plugin.Time;

            if (now < _LastTurnIsFromAutoTurn)
            {
                ftime *= 1.5;
            }

            var pcam = PlayerCamera.Instance;

            var pstate = pcam?.State;

            if (!(pstate is ThirdPersonState third))
            {
                return;
            }

            double x    = Math.Abs(third.XRotationFromLastResetPoint);
            var    mult = x / (Math.PI * 0.5);

            ftime *= mult;

            var ms = (long)(ftime * 1000.0);

            if (ms < 33)
            {
                return;
            }

            Stabilize.AddTweenFrom(ms, FinalResult.Transform.Position);
        }
            private void Stabilize(NodeInfo successorInfo)
            {
                // Send a stabilize message to my successor to learn it's predecessor
                //   Stabilize might need to be retried as other nodes will be joining at the same location as it is a seed node.

                var opId           = _node.Config.CorrelationFactory.GetNextCorrelation();
                var messageHandler = _node.CreateAwaitAllResponsesHandler();

                messageHandler
                .PerformAction(() =>
                {
                    var msg = new Stabilize(_node.Identity, successorInfo, opId);
                    _node.Log($"Sending {msg.TypeName()} To:{msg.To} Id:{opId}");
                    _commMgr.Send(msg);
                })
                .AndAwait(opId, (StabilizeReply reply) =>
                {
                    var thisHash        = _node.Identity.RoutingHash;
                    var predecessorHash = reply.Predecessor.RoutingHash;
                    var successorHash   = successorInfo.RoutingHash;

                    // We need to check the order of the predecessor or the successor before adopting a value
                    // as nodes are racing to join the seed node - our last query may be out of date, but not by much
                    if (thisHash.IsBetween(predecessorHash, successorHash))
                    {
                        // No ordering change. We can adopt the predecessor values and tell the successor to rectify
                        StabilizeFromSuccessor(reply);
                    }
                    else if (predecessorHash.IsBetween(thisHash, successorHash))
                    {
                        // another node beat this one to join to the successor, changing the ordering...
                        StabilizeFromPredecessor(reply);
                    }
                })
                .Run(opId);
            }
        private void DoUpdate(CameraUpdate update)
        {
            {
                var wasEnabled = IsEnabled;
                var isEnabled  = CalculateEnabled(update);

                if (wasEnabled != isEnabled)
                {
                    if (!isEnabled)
                    {
                        Stack.DisableAll(update);
                    }

                    IsEnabled = isEnabled;
                    if (isEnabled)
                    {
                        OnEnabled(update);
                    }
                    else
                    {
                        OnDisabled(update);
                    }
                }
            }

            if (IsEnabled)
            {
                OnUpdating(0);
            }

            if (IsEnabled)
            {
                if (Stabilize == null || Stabilize.ShouldRecreate(update.Target))
                {
                    Stabilize = new CameraStabilize(this, update.Target);
                }

                Stabilize?.Update(update.Target.StabilizeRootNode, update.Target.HeadNode, update);
            }

            Stack.Check(update);
            Stack.Update(update);
            update.Values.Update(Plugin.Time, IsEnabled);
            Hide.Update(update);
            {
                var isFpArms = IsEnabled && update.Values.Show1stPersonArms.CurrentValue >= 0.5;
                if (isFpArms != WasUsingFirstPersonArms)
                {
                    WasUsingFirstPersonArms = isFpArms;
                }
            }

            if (IsEnabled)
            {
                var mode      = update.Values.SkeletonMode.CurrentValue;
                var wantThird = true;
                if (mode <= -0.5)
                {
                    wantThird = !WasUsingFirstPersonArms;
                }
                else if (mode >= 0.5)
                {
                    wantThird = false;
                }
                //else wantThird = true;

                var showFirst = WasUsingFirstPersonArms;
                var showThird = !(DidCollideLastUpdate && Settings.Instance.HidePlayerWhenColliding == 2);

                UpdateSkeleton(showFirst, showThird, wantThird);
            }

            if (IsEnabled)
            {
                if (Stabilize == null || !Stabilize.Get(update.Target.StabilizeRootNode, BaseRoot.Transform))
                {
                    BaseRoot.Transform.CopyFrom(update.Target.HeadNode.WorldTransform);
                }
                BaseHead.Transform.CopyFrom(update.Target.HeadNode.WorldTransform);

                CameraResult cur = null;
                using (cur)
                {
                    {
                        var posRatio = update.Values.PositionFromHead.CurrentValue;
                        switch (posRatio)
                        {
                        case 0.0:
                            BaseResult.Transform.Position.CopyFrom(BaseRoot.Transform.Position);
                            break;

                        case 1.0:
                            BaseResult.Transform.Position.CopyFrom(BaseHead.Transform.Position);
                            break;

                        default:
                        {
                            var pos     = BaseResult.Transform.Position;
                            var rootPos = BaseRoot.Transform.Position;
                            var headPos = BaseHead.Transform.Position;

                            pos.X = (float)((headPos.X - rootPos.X) * posRatio + rootPos.X);
                            pos.Y = (float)((headPos.Y - rootPos.Y) * posRatio + rootPos.Y);
                            pos.Z = (float)((headPos.Z - rootPos.Z) * posRatio + rootPos.Z);
                            break;
                        }
                        }
                    }

                    // Calculate base rotation.
                    {
                        var rotRatio = update.Values.RotationFromHead.CurrentValue;
                        switch (rotRatio)
                        {
                        case 0.0:
                            BaseResult.Transform.Rotation.CopyFrom(BaseRoot.Transform.Rotation);
                            break;

                        case 1.0:
                            BaseResult.Transform.Rotation.CopyFrom(BaseHead.Transform.Rotation);
                            break;

                        default:
                        {
                            var rot     = BaseResult.Transform.Rotation;
                            var rootRot = BaseRoot.Transform.Rotation;
                            var headRot = BaseHead.Transform.Rotation;

                            rootRot.Interpolate(headRot, (float)rotRatio, rot);
                            break;
                        }
                        }
                    }

                    cur = BaseResult;

                    // Calculate offset based on object rotation itself.
                    {
                        var root = update.Target.RootNode;
                        if (root != null)
                        {
                            var x = Values.OffsetObjectPositionX.CurrentValue;
                            var y = Values.OffsetObjectPositionY.CurrentValue;
                            var z = Values.OffsetObjectPositionZ.CurrentValue;

                            if (x != 0.0 || y != 0.0 || z != 0.0)
                            {
                                Offset1Result.Transform.Position.CopyFrom(BaseResult.Transform.Position);
                                Offset1Result.Transform.Rotation.CopyFrom(root.WorldTransform.Rotation);
                                ApplyPositionOffset(Offset1Result.Transform, (float)x, (float)y, (float)z,
                                                    BaseResult.Transform.Position);
                            }
                        }
                    }

                    // Look down offset.
                    if (Default._look_downoffset_ratio > 0.0f || Default._look_downoffset_ratio_leftrightmove > 0.0f)
                    {
                        var ratio = Default._look_downoffset_ratio;
                        var root  = update.Target.RootNode;
                        if (root != null)
                        {
                            var x = Settings.Instance.DownOffsetX * ratio;
                            var y = Settings.Instance.DownOffsetY * ratio;
                            var z = Settings.Instance.DownOffsetZ * ratio;

                            y += Settings.Instance.TryFixLeftRightMovementClipping *
                                 Default._look_downoffset_ratio_leftrightmove;

                            if (x != 0.0f || y != 0.0f || z != 0.0f)
                            {
                                Offset1Result.Transform.Position.CopyFrom(BaseResult.Transform.Position);
                                Offset1Result.Transform.Rotation.CopyFrom(root.WorldTransform.Rotation);
                                ApplyPositionOffset(Offset1Result.Transform, x, y, z, BaseResult.Transform.Position);
                            }
                        }
                    }

                    // Calculate offset #1.
                    {
                        var rx = (float)Values.Offset1RotationX.CurrentValue;
                        var ry = (float)Values.Offset1RotationY.CurrentValue;
                        var px = (float)Values.Offset1PositionX.CurrentValue;
                        var py = (float)Values.Offset1PositionY.CurrentValue;
                        var pz = (float)Values.Offset1PositionZ.CurrentValue;

                        var hasRot = rx != 0.0f || ry != 0.0f;
                        var hasPos = px != 0.0f || py != 0.0f || pz != 0.0f;

                        if (hasRot || hasPos)
                        {
                            Offset1Result.Transform.CopyFrom(cur.Transform);
                            if (hasRot)
                            {
                                ApplyRotationOffset(Offset1Result.Transform.Rotation, rx, ry,
                                                    Offset1Result.Transform.Rotation);
                            }
                            if (hasPos)
                            {
                                ApplyPositionOffset(Offset1Result.Transform, px, py, pz, Offset1Result.Transform.Position);
                            }

                            cur = Offset1Result;
                        }
                    }

                    // Calculate input.
                    {
                        var extraX = 0.0f;
                        var extraY = 0.0f;
                        if (LastActorTurnFrames > 0)
                        {
                            LastActorTurnFrames--;
                            extraX = LastActorTurnX;
                            //extraY = this.LastActorTurnY;
                        }

                        var rx = (float)(Values.InputRotationX.CurrentValue + extraX) *
                                 (float)Values.InputRotationXMultiplier.CurrentValue;
                        var ry = (float)(Values.InputRotationY.CurrentValue + extraY) *
                                 (float)Values.InputRotationYMultiplier.CurrentValue;

                        if (rx != 0.0f || ry != 0.0f)
                        {
                            InputResult.Transform.CopyFrom(cur.Transform);
                            ApplyRotationOffset(InputResult.Transform.Rotation, rx, ry, InputResult.Transform.Rotation);

                            cur = InputResult;
                        }
                    }

                    // Calculate offset #2.
                    {
                        var rx = (float)Values.Offset2RotationX.CurrentValue;
                        var ry = (float)Values.Offset2RotationY.CurrentValue;
                        var px = (float)Values.Offset2PositionX.CurrentValue;
                        var py = (float)Values.Offset2PositionY.CurrentValue;
                        var pz = (float)Values.Offset2PositionZ.CurrentValue;

                        var hasRot = rx != 0.0f || ry != 0.0f;
                        var hasPos = px != 0.0f || py != 0.0f || pz != 0.0f;

                        if (hasRot || hasPos)
                        {
                            Offset2Result.Transform.CopyFrom(cur.Transform);
                            if (hasRot)
                            {
                                ApplyRotationOffset(Offset2Result.Transform.Rotation, rx, ry,
                                                    Offset2Result.Transform.Rotation);
                            }
                            if (hasPos)
                            {
                                ApplyPositionOffset(Offset2Result.Transform, px, py, pz, Offset2Result.Transform.Position);
                            }

                            cur = Offset2Result;
                        }
                    }

                    // Apply tween from stabilize.
                    {
                        Stabilize?.ApplyTween(cur.Transform.Position, Plugin.Time);
                    }

                    // Apply collision of camera so we don't go inside walls, this can be done within the same transform.
                    {
                        DidCollideLastUpdate = CameraCollision.Apply(update, cur.Transform, cur.Transform.Position);
                    }

                    // Calculate final result.
                    {
                        FinalResult.Transform.CopyFrom(cur.Transform);
                    }
                }

                update.GameCameraNode.LocalTransform.CopyFrom(FinalResult.Transform);
                update.GameCameraNode.Update(0.0f);

                Hide.UpdateFirstPersonSkeletonRotation(update);

                if (update.GameCameraState is ThirdPersonState third)
                {
                    third.Position.CopyFrom(update.GameCameraNode.LocalTransform.Position);
                }

                if (WasUsingFirstPersonArms)
                {
                    UpdateMagicNodePosition(update);
                }
            }
            else
            {
                DidCollideLastUpdate = false;
            }

            if (IsEnabled)
            {
                OnUpdating(1);
            }

            if (!IsEnabled && Settings.Instance.ReplaceDefaultCamera &&
                update.GameCameraState.Id == TESCameraStates.FirstPerson && IsGameCameraSwitchControlsEnabled())
            {
                update.GameCamera.EnterThirdPerson();
                SetWantState(WantStates.EnabledFromTogglePOV);
            }

            FixSensitivityMode = IsEnabled && update.GameCameraState.Id == TESCameraStates.ThirdPerson2 &&
                                 Memory.ReadUInt8(update.GameCameraState.Address + 0xDC) != 0;
        }