diff --git a/Projects/Server/Network/Packets/MovementPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPackets.cs similarity index 66% rename from Projects/Server/Network/Packets/MovementPackets.cs rename to Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPackets.cs index fac5b0f46..68fa0a6f6 100644 --- a/Projects/Server/Network/Packets/MovementPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPackets.cs @@ -1,18 +1,3 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: MovementPackets.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - namespace Server.Network { public sealed class SpeedControl : Packet @@ -23,7 +8,7 @@ namespace Server.Network public SpeedControl(int speedControl) : base(0xBF) { - EnsureCapacity(3); + EnsureCapacity(6); Stream.Write((short)0x26); Stream.Write((byte)speedControl); @@ -85,7 +70,7 @@ namespace Server.Network { public NullFastwalkStack() : base(0xBF) { - EnsureCapacity(256); + EnsureCapacity(29); Stream.Write((short)0x1); Stream.Write(0x0); Stream.Write(0x0); diff --git a/Projects/Server/Buffers/CircularBufferReader.cs b/Projects/Server/Buffers/CircularBufferReader.cs index 3238e8b67..b4bd8242a 100644 --- a/Projects/Server/Buffers/CircularBufferReader.cs +++ b/Projects/Server/Buffers/CircularBufferReader.cs @@ -182,6 +182,64 @@ namespace Server.Network return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadInt64() + { + long value; + + if (Position < _first.Length) + { + if (!BinaryPrimitives.TryReadInt64BigEndian(_first.Slice(Position), out value)) + { + // Not enough space. Split the spans + return ((long)ReadByte() >> 56) | + ((long)ReadByte() >> 48) | + ((long)ReadByte() >> 40) | + ((long)ReadByte() >> 32) | + ((long)ReadByte() >> 24) | + ((long)ReadByte() >> 16) | + ((long)ReadByte() >> 8) | + ReadByte(); + } + } + else if (!BinaryPrimitives.TryReadInt64BigEndian(_second.Slice(Position - _first.Length), out value)) + { + throw new OutOfMemoryException(); + } + + Position += 8; + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadUInt64() + { + ulong value; + + if (Position < _first.Length) + { + if (!BinaryPrimitives.TryReadUInt64BigEndian(_first.Slice(Position), out value)) + { + // Not enough space. Split the spans + return ((ulong)ReadByte() >> 56) | + ((ulong)ReadByte() >> 48) | + ((ulong)ReadByte() >> 40) | + ((ulong)ReadByte() >> 32) | + ((ulong)ReadByte() >> 24) | + ((ulong)ReadByte() >> 16) | + ((ulong)ReadByte() >> 8) | + ReadByte(); + } + } + else if (!BinaryPrimitives.TryReadUInt64BigEndian(_second.Slice(Position - _first.Length), out value)) + { + throw new OutOfMemoryException(); + } + + Position += 8; + return value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1) { diff --git a/Projects/Server/Buffers/CircularBufferWriter.cs b/Projects/Server/Buffers/CircularBufferWriter.cs index 7519f10cc..c80aad159 100644 --- a/Projects/Server/Buffers/CircularBufferWriter.cs +++ b/Projects/Server/Buffers/CircularBufferWriter.cs @@ -220,6 +220,76 @@ namespace System.Buffers } } + /// + /// Writes a 8-byte signed integer value to the underlying stream. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(long value) + { + if (Position < _first.Length) + { + if (!BinaryPrimitives.TryWriteInt64BigEndian(_first.Slice(Position), value)) + { + // Not enough space. Split the spans + Write((byte)(value >> 56)); + Write((byte)(value >> 48)); + Write((byte)(value >> 40)); + Write((byte)(value >> 32)); + Write((byte)(value >> 24)); + Write((byte)(value >> 16)); + Write((byte)(value >> 8)); + Write((byte)value); + } + else + { + Position += 8; + } + } + else if (BinaryPrimitives.TryWriteInt64BigEndian(_second.Slice(Position - _first.Length), value)) + { + Position += 8; + } + else + { + throw new OutOfMemoryException(); + } + } + + /// + /// Writes a 8-byte unsigned integer value to the underlying stream. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ulong value) + { + if (Position < _first.Length) + { + if (!BinaryPrimitives.TryWriteUInt64BigEndian(_first.Slice(Position), value)) + { + // Not enough space. Split the spans + Write((byte)(value >> 56)); + Write((byte)(value >> 48)); + Write((byte)(value >> 40)); + Write((byte)(value >> 32)); + Write((byte)(value >> 24)); + Write((byte)(value >> 16)); + Write((byte)(value >> 8)); + Write((byte)value); + } + else + { + Position += 8; + } + } + else if (BinaryPrimitives.TryWriteUInt64BigEndian(_second.Slice(Position - _first.Length), value)) + { + Position += 8; + } + else + { + throw new OutOfMemoryException(); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(ReadOnlySpan buffer) { diff --git a/Projects/Server/Configuration/ServerConfiguration.cs b/Projects/Server/Configuration/ServerConfiguration.cs index a4f0117fc..c37c06fff 100644 --- a/Projects/Server/Configuration/ServerConfiguration.cs +++ b/Projects/Server/Configuration/ServerConfiguration.cs @@ -45,6 +45,12 @@ namespace Server return int.TryParse(strValue, out var value) ? value : defaultValue; } + public static long GetSetting(string key, long defaultValue) + { + m_Settings.settings.TryGetValue(key, out var strValue); + return long.TryParse(strValue, out var value) ? value : defaultValue; + } + public static bool GetSetting(string key, bool defaultValue) { m_Settings.settings.TryGetValue(key, out var strValue); @@ -84,6 +90,22 @@ namespace Server return value; } + public static long GetOrUpdateSetting(string key, long defaultValue) + { + long value; + + if (m_Settings.settings.TryGetValue(key, out var strValue)) + { + value = long.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); + } + + return value; + } + public static bool GetOrUpdateSetting(string key, bool defaultValue) { bool value; diff --git a/Projects/Server/Events/FastwalkEvent.cs b/Projects/Server/Events/FastwalkEvent.cs index b659ef154..1237d0b19 100644 --- a/Projects/Server/Events/FastwalkEvent.cs +++ b/Projects/Server/Events/FastwalkEvent.cs @@ -18,22 +18,19 @@ using Server.Network; namespace Server { - public class FastWalkEventArgs : EventArgs + public class FastWalkEventArgs { - public FastWalkEventArgs(NetState state) - { - NetState = state; - Blocked = false; - } + public FastWalkEventArgs(NetState state) => NetState = state; public NetState NetState { get; } - public bool Blocked { get; set; } + public bool Blocked { get; set; } = true; } public static partial class EventSink { public static event Action FastWalk; + public static void InvokeFastWalk(FastWalkEventArgs e) => FastWalk?.Invoke(e); } } diff --git a/Projects/Server/ExpansionInfo.cs b/Projects/Server/ExpansionInfo.cs index cf4711529..9733c5eec 100644 --- a/Projects/Server/ExpansionInfo.cs +++ b/Projects/Server/ExpansionInfo.cs @@ -94,7 +94,7 @@ namespace Server Unk3 = 0x00000800, SeventhCharacterSlot = 0x00001000, Unk4 = 0x00002000, - NewMovementSystem = 0x00004000, + NewMovementSystem = 0x00004000, // Doesn't seem to be used on OSI NewFeluccaAreas = 0x00008000, ExpansionNone = ContextMenus, diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 0116bf67e..bd08e202d 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -18,6 +18,7 @@ using Server.Network; using Server.Prompts; using Server.Targeting; using Server.Utilities; +using CalcMoves = Server.Movement.Movement; namespace Server { @@ -502,7 +503,6 @@ namespace Server private Direction m_Direction; private bool m_DisplayGuildTitle; - private long m_EndQueue; private Timer m_ExpireAggrTimer; private Timer m_ExpireCombatant; private Timer m_ExpireCriminal; @@ -550,7 +550,6 @@ namespace Server */ private Item m_MountItem; - private Queue m_MoveRecords; private string m_Name; private string m_NameMod; @@ -1081,22 +1080,6 @@ namespace Server public bool Pushing { get; set; } - public static int WalkFoot { get; set; } = 400; - - public static int RunFoot { get; set; } = 200; - - public static int WalkMount { get; set; } = 200; - - public static int RunMount { get; set; } = 100; - - public static AccessLevel FwdAccessOverride { get; set; } = AccessLevel.Counselor; - - public static bool FwdEnabled { get; set; } = true; - - public static bool FwdUOTDOverride { get; set; } - - public static int FwdMaxSteps { get; set; } = 4; - public virtual bool IsDeadBondedPet => false; public ISpell Spell @@ -2861,8 +2844,6 @@ namespace Server { ns.Send(new MobileUpdateOld(this)); } - - ClearFastwalkStack(); } if (ns != null) @@ -2873,7 +2854,6 @@ namespace Server } ns.Sequence = 0; - ClearFastwalkStack(); ns.Send(MobileIncoming.Create(ns, this, this)); @@ -2897,7 +2877,6 @@ namespace Server if (ns != null) { ns.Sequence = 0; - ClearFastwalkStack(); ns.Send(MobileIncoming.Create(ns, this, this)); @@ -2997,8 +2976,6 @@ namespace Server { ns.Send(new MobileUpdateOld(this)); } - - ClearFastwalkStack(); } } else @@ -3014,7 +2991,6 @@ namespace Server } ns.Sequence = 0; - ClearFastwalkStack(); ns.Send(MobileIncoming.Create(ns, this, this)); @@ -3038,7 +3014,6 @@ namespace Server if (ns != null) { ns.Sequence = 0; - ClearFastwalkStack(); ns.Send(MobileIncoming.Create(ns, this, this)); @@ -3228,8 +3203,6 @@ namespace Server { ourState.Send(new MobileUpdateOld(m)); } - - ClearFastwalkStack(); } if (sendIncoming) @@ -4512,17 +4485,188 @@ namespace Server return true; } - public virtual void ClearFastwalkStack() + public virtual bool CheckMovement(Direction d, out int newZ) => Movement.Movement.CheckMovement(this, d, out newZ); + + private bool CanMove(Direction d, Point3D oldLocation, ref Point3D newLocation) { - if (m_MoveRecords != null && m_MoveRecords.Count > 0) + if (m_Spell?.OnCasterMoving(d) == false) { - m_MoveRecords.Clear(); + return false; } - m_EndQueue = Core.TickCount; - } + if (m_Paralyzed || m_Frozen) + { + SendLocalizedMessage(500111); // You are frozen and can not move. - public virtual bool CheckMovement(Direction d, out int newZ) => Movement.Movement.CheckMovement(this, d, out newZ); + return false; + } + + if (!CheckMovement(d, out var newZ)) + { + return false; + } + + int x = oldLocation.m_X, y = oldLocation.m_Y; + int oldX = x, oldY = y; + var oldZ = oldLocation.m_Z; + + switch (d & Direction.Mask) + { + case Direction.North: + --y; + break; + case Direction.Right: + ++x; + --y; + break; + case Direction.East: + ++x; + break; + case Direction.Down: + ++x; + ++y; + break; + case Direction.South: + ++y; + break; + case Direction.Left: + --x; + ++y; + break; + case Direction.West: + --x; + break; + case Direction.Up: + --x; + --y; + break; + } + + newLocation.m_X = x; + newLocation.m_Y = y; + newLocation.m_Z = newZ; + + Pushing = false; + + var map = m_Map; + + if (map == null || !Region.CanMove(this, d, newLocation, oldLocation, map)) + { + return false; + } + + var oldSector = map.GetSector(oldX, oldY); + var newSector = map.GetSector(x, y); + + if (oldSector != newSector) + { + for (var i = 0; i < oldSector.Mobiles.Count; ++i) + { + var m = oldSector.Mobiles[i]; + + if (m != this && m.X == oldX && m.Y == oldY && m.Z + 15 > oldZ && oldZ + 15 > m.Z && + !m.OnMoveOff(this)) + { + return false; + } + } + + for (var i = 0; i < oldSector.Items.Count; ++i) + { + var item = oldSector.Items[i]; + + if (item.AtWorldPoint(oldX, oldY) && + (item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) && + !item.OnMoveOff(this)) + { + return false; + } + } + + for (var i = 0; i < newSector.Mobiles.Count; ++i) + { + var m = newSector.Mobiles[i]; + + if (m.X == x && m.Y == y && m.Z + 15 > newZ && newZ + 15 > m.Z && !m.OnMoveOver(this)) + { + return false; + } + } + + for (var i = 0; i < newSector.Items.Count; ++i) + { + var item = newSector.Items[i]; + + if (item.AtWorldPoint(x, y) && + (item.Z == newZ || item.Z + item.ItemData.Height > newZ && newZ + 15 > item.Z) && + !item.OnMoveOver(this)) + { + return false; + } + } + } + else + { + for (var i = 0; i < oldSector.Mobiles.Count; ++i) + { + var m = oldSector.Mobiles[i]; + + if (m != this && m.X == oldX && m.Y == oldY && m.Z + 15 > oldZ && oldZ + 15 > m.Z && + !m.OnMoveOff(this)) + { + return false; + } + + if (m.X == x && m.Y == y && m.Z + 15 > newZ && newZ + 15 > m.Z && !m.OnMoveOver(this)) + { + return false; + } + } + + for (var i = 0; i < oldSector.Items.Count; ++i) + { + var item = oldSector.Items[i]; + + if (item.AtWorldPoint(oldX, oldY) && + (item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) && + !item.OnMoveOff(this)) + { + return false; + } + + if (item.AtWorldPoint(x, y) && + (item.Z == newZ || item.Z + item.ItemData.Height > newZ && newZ + 15 > item.Z) && + !item.OnMoveOver(this)) + { + return false; + } + } + } + + if (!InternalOnMove(d)) + { + return false; + } + + if ( + CalcMoves.EnableFastwalkPrevention && + AccessLevel < CalcMoves.FastwalkExemptionLevel && + m_NetState?.AddStep(d) == false + ) + { + var fw = new FastWalkEventArgs(m_NetState); + EventSink.InvokeFastWalk(fw); + + if (fw.Blocked) + { + return false; + } + } + + LastMoveTime = Core.TickCount; + + return true; + } public virtual bool Move(Direction d) { @@ -4538,227 +4682,13 @@ namespace Server box.Close(); } - var newLocation = m_Location; - var oldLocation = newLocation; + var oldLocation = m_Location; + Point3D newLocation = oldLocation; if ((m_Direction & Direction.Mask) == (d & Direction.Mask)) { // We are actually moving (not just a direction change) - - if (m_Spell?.OnCasterMoving(d) == false) - { - return false; - } - - if (m_Paralyzed || m_Frozen) - { - SendLocalizedMessage(500111); // You are frozen and can not move. - - return false; - } - - if (CheckMovement(d, out var newZ)) - { - int x = oldLocation.m_X, y = oldLocation.m_Y; - int oldX = x, oldY = y; - var oldZ = oldLocation.m_Z; - - switch (d & Direction.Mask) - { - case Direction.North: - --y; - break; - case Direction.Right: - ++x; - --y; - break; - case Direction.East: - ++x; - break; - case Direction.Down: - ++x; - ++y; - break; - case Direction.South: - ++y; - break; - case Direction.Left: - --x; - ++y; - break; - case Direction.West: - --x; - break; - case Direction.Up: - --x; - --y; - break; - } - - newLocation.m_X = x; - newLocation.m_Y = y; - newLocation.m_Z = newZ; - - Pushing = false; - - var map = m_Map; - - if (map != null) - { - var oldSector = map.GetSector(oldX, oldY); - var newSector = map.GetSector(x, y); - - if (oldSector != newSector) - { - for (var i = 0; i < oldSector.Mobiles.Count; ++i) - { - var m = oldSector.Mobiles[i]; - - if (m != this && m.X == oldX && m.Y == oldY && m.Z + 15 > oldZ && oldZ + 15 > m.Z && - !m.OnMoveOff(this)) - { - return false; - } - } - - for (var i = 0; i < oldSector.Items.Count; ++i) - { - var item = oldSector.Items[i]; - - if (item.AtWorldPoint(oldX, oldY) && - (item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) && - !item.OnMoveOff(this)) - { - return false; - } - } - - for (var i = 0; i < newSector.Mobiles.Count; ++i) - { - var m = newSector.Mobiles[i]; - - if (m.X == x && m.Y == y && m.Z + 15 > newZ && newZ + 15 > m.Z && !m.OnMoveOver(this)) - { - return false; - } - } - - for (var i = 0; i < newSector.Items.Count; ++i) - { - var item = newSector.Items[i]; - - if (item.AtWorldPoint(x, y) && - (item.Z == newZ || item.Z + item.ItemData.Height > newZ && newZ + 15 > item.Z) && - !item.OnMoveOver(this)) - { - return false; - } - } - } - else - { - for (var i = 0; i < oldSector.Mobiles.Count; ++i) - { - var m = oldSector.Mobiles[i]; - - if (m != this && m.X == oldX && m.Y == oldY && m.Z + 15 > oldZ && oldZ + 15 > m.Z && - !m.OnMoveOff(this)) - { - return false; - } - - if (m.X == x && m.Y == y && m.Z + 15 > newZ && newZ + 15 > m.Z && !m.OnMoveOver(this)) - { - return false; - } - } - - for (var i = 0; i < oldSector.Items.Count; ++i) - { - var item = oldSector.Items[i]; - - if (item.AtWorldPoint(oldX, oldY) && - (item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) && - !item.OnMoveOff(this)) - { - return false; - } - - if (item.AtWorldPoint(x, y) && - (item.Z == newZ || item.Z + item.ItemData.Height > newZ && newZ + 15 > item.Z) && - !item.OnMoveOver(this)) - { - return false; - } - } - } - - if (!Region.CanMove(this, d, newLocation, oldLocation, m_Map)) - { - return false; - } - } - else - { - return false; - } - - if (!InternalOnMove(d)) - { - return false; - } - - if (FwdEnabled && m_NetState != null && m_AccessLevel < FwdAccessOverride && - (!FwdUOTDOverride || !m_NetState.IsUOTDClient)) - { - m_MoveRecords ??= new Queue(6); - - while (m_MoveRecords.Count > 0) - { - var r = m_MoveRecords.Peek(); - - if (r.Expired()) - { - m_MoveRecords.Dequeue(); - } - else - { - break; - } - } - - if (m_MoveRecords.Count >= FwdMaxSteps) - { - var fw = new FastWalkEventArgs(m_NetState); - EventSink.InvokeFastWalk(fw); - - if (fw.Blocked) - { - return false; - } - } - - var delay = ComputeMovementSpeed(d); - - long end; - - if (m_MoveRecords.Count > 0) - { - end = m_EndQueue + delay; - } - else - { - end = Core.TickCount + delay; - } - - m_MoveRecords.Enqueue(MovementRecord.NewInstance(end)); - - m_EndQueue = end; - } - - LastMoveTime = Core.TickCount; - } - else + if (!CanMove(d, oldLocation, ref newLocation)) { return false; } @@ -4766,12 +4696,7 @@ namespace Server DisruptiveAction(); } - m_NetState?.Send( - MovementAck.Instantiate( - m_NetState.Sequence, - this - ) - ); + m_NetState?.SendMovementAck(m_NetState.Sequence, this); SetLocation(newLocation, false); SetDirection(d); @@ -4886,22 +4811,14 @@ namespace Server public int ComputeMovementSpeed() => ComputeMovementSpeed(Direction, false); - public int ComputeMovementSpeed(Direction dir) => ComputeMovementSpeed(dir, true); - - public virtual int ComputeMovementSpeed(Direction dir, bool checkTurning) + public virtual int ComputeMovementSpeed(Direction dir, bool checkTurning = true) { - int delay; - if (Mounted) { - delay = (dir & Direction.Running) != 0 ? RunMount : WalkMount; - } - else - { - delay = (dir & Direction.Running) != 0 ? RunFoot : WalkFoot; + return (dir & Direction.Running) != 0 ? CalcMoves.RunMountDelay : CalcMoves.WalkMountDelay; } - return delay; + return (dir & Direction.Running) != 0 ? CalcMoves.RunFootDelay : CalcMoves.WalkFootDelay; } /// @@ -7856,8 +7773,6 @@ namespace Server { m_NetState.Send(new MobileUpdateOld(this)); } - - ClearFastwalkStack(); } var map = m_Map; diff --git a/Projects/Server/Mobiles/Movement.cs b/Projects/Server/Mobiles/Movement.cs index b61076f2e..e62ddb8cb 100644 --- a/Projects/Server/Mobiles/Movement.cs +++ b/Projects/Server/Mobiles/Movement.cs @@ -1,8 +1,48 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Movement.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + namespace Server.Movement { public static class Movement { + // Movement implementation algorithm public static IMovementImpl Impl { get; set; } + public static int WalkFootDelay { get; set; } = 440; + public static int RunFootDelay { get; set; } = 220; + public static int WalkMountDelay { get; set; } = 220; + public static int RunMountDelay { get; set; } = 110; + + public static bool EnableFastwalkPrevention { get; set; } = true; + public static AccessLevel FastwalkExemptionLevel { get; set; } = AccessLevel.Counselor; + + // If this is changed during runtime, then the steps array needs resizing. + public static int MaxSteps { get; private set; } = 4; + + public static void Configure() + { + EnableFastwalkPrevention = ServerConfiguration.GetOrUpdateSetting("movement.enableFastWalkPrevention", EnableFastwalkPrevention); + MaxSteps = ServerConfiguration.GetOrUpdateSetting("movement.maxSteps", MaxSteps); + FastwalkExemptionLevel = ServerConfiguration.GetOrUpdateSetting("movement.fastwalkExemptionLevel", FastwalkExemptionLevel); + WalkFootDelay = ServerConfiguration.GetOrUpdateSetting("movement.delay.walkFoot", WalkFootDelay); + RunFootDelay = ServerConfiguration.GetOrUpdateSetting("movement.delay.runFoot", RunFootDelay); + WalkMountDelay = ServerConfiguration.GetOrUpdateSetting("movement.delay.walkMount", WalkMountDelay); + RunMountDelay = ServerConfiguration.GetOrUpdateSetting("movement.delay.runMount", RunMountDelay); + + // Testing + FastwalkExemptionLevel = (AccessLevel)((int)AccessLevel.Owner + 1); + } public static bool CheckMovement(Mobile m, Direction d, out int newZ) { diff --git a/Projects/Server/Mobiles/MovementRecord.cs b/Projects/Server/Mobiles/MovementRecord.cs deleted file mode 100644 index cdbee21fb..000000000 --- a/Projects/Server/Mobiles/MovementRecord.cs +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: MovementRecord.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; - -namespace Server.Mobiles -{ - public class MovementRecord - { - private static readonly Queue m_InstancePool = new Queue(); - private long m_End; - - private MovementRecord(long end) => m_End = end; - - public static MovementRecord NewInstance(long end) - { - MovementRecord r; - - if (m_InstancePool.Count > 0) - { - r = m_InstancePool.Dequeue(); - - r.m_End = end; - } - else - { - r = new MovementRecord(end); - } - - return r; - } - - public bool Expired() - { - var v = Core.TickCount - m_End >= 0; - - if (v) - { - m_InstancePool.Enqueue(this); - } - - return v; - } - } -} diff --git a/Projects/Server/Network/NetState/NetState.Fastwalk.cs b/Projects/Server/Network/NetState/NetState.Fastwalk.cs new file mode 100644 index 000000000..eb6934aaf --- /dev/null +++ b/Projects/Server/Network/NetState/NetState.Fastwalk.cs @@ -0,0 +1,92 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: NetState.Fastwalk.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using CalcMoves = Server.Movement.Movement; + +namespace Server.Network +{ + public partial class NetState + { + private int _stepIndex; + private int _stepCount; + private long _startDelay; + private long[] _stepDelays; + + public bool AddStep(Direction d) + { + if (Mobile == null) + { + return false; + } + + var maxSteps = CalcMoves.MaxSteps; + + _stepDelays ??= new long[maxSteps]; + var length = _stepDelays.Length; + + var index = _stepIndex - _stepCount; + if (index < 0) + { + index += length; + } + + var now = Core.TickCount; + var last = _startDelay; + + // Discard old steps by decrementing the step counter + while (index != _stepIndex || _stepCount >= maxSteps) + { + var step = _stepDelays[index++]; + if (now - last < step) + { + break; + } + + last += step; + _stepCount--; + if (index >= length) + { + index = 0; + } + } + + _startDelay = last; + + // If we are out of steps, fail + if (_stepCount >= maxSteps) + { + return false; + } + + var delay = Mobile.ComputeMovementSpeed(d); + + // Add the delay + _stepDelays[_stepIndex++] = delay; + + if (_stepIndex >= length) + { + _stepIndex = 0; + } + + if (_stepCount == 0) + { + _startDelay = now; + } + _stepCount++; + + return true; + } + } +} diff --git a/Projects/Server/Network/Packets/IncomingAccountPackets.cs b/Projects/Server/Network/Packets/IncomingAccountPackets.cs index b251a23cd..0819dcafd 100644 --- a/Projects/Server/Network/Packets/IncomingAccountPackets.cs +++ b/Projects/Server/Network/Packets/IncomingAccountPackets.cs @@ -392,8 +392,6 @@ namespace Server.Network state.SendMapChange(m.Map); EventSink.InvokeLogin(m); - - m.ClearFastwalkStack(); } private static int GenerateAuthID(this NetState state) diff --git a/Projects/Server/Network/Packets/IncomingMovementPackets.cs b/Projects/Server/Network/Packets/IncomingMovementPackets.cs new file mode 100644 index 000000000..c0a3c326c --- /dev/null +++ b/Projects/Server/Network/Packets/IncomingMovementPackets.cs @@ -0,0 +1,120 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: IncomingMovementPackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public static class IncomingMovementPackets + { + public static void Configure() + { + IncomingPackets.Register(0x02, 7, true, MovementReq); + IncomingPackets.Register(0xF0, 0, true, NewMovementReq); + IncomingPackets.Register(0xF1, 9, true, TimeSyncReq); + } + + public static void NewMovementReq(NetState ns, CircularBufferReader reader) + { + var from = ns.Mobile; + + if (from == null) + { + return; + } + + var steps = reader.ReadByte(); + for (int i = 0; i < steps; i++) + { + var t1 = reader.ReadUInt64(); // start time? + var t2 = reader.ReadUInt64(); // end time? + int seq = reader.ReadByte(); + var dir = (Direction)reader.ReadByte(); + var mode = reader.ReadInt32(); // 1 = walk, 2 = run + if (mode == 2) + { + dir |= Direction.Running; + } + + // Location + reader.ReadInt32(); // x + reader.ReadInt32(); // y + reader.ReadInt32(); // z + + if (ns.Sequence == 0 && seq != 0 || !from.Move(dir)) + { + ns.SendMovementRej(seq, from); + ns.Sequence = 0; + } + else + { + ++seq; + + if (seq == 256) + { + seq = 1; + } + + ns.Sequence = seq; + } + } + } + + public static void TimeSyncReq(NetState ns, CircularBufferReader reader) + { + reader.ReadUInt64(); // Client Time? + + ns.SendTimeSyncResponse(); + } + + public static void MovementReq(NetState state, CircularBufferReader reader) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var dir = (Direction)reader.ReadByte(); + int seq = reader.ReadByte(); + var key = reader.ReadUInt32(); + + if (key != 0) + { + state.WriteConsole("OldMove w/ Key {0}", key); + } + else + { + state.WriteConsole("Old Movement!"); + } + + if (state.Sequence == 0 && seq != 0 || !from.Move(dir)) + { + state.SendMovementRej(seq, from); + state.Sequence = 0; + } + else + { + ++seq; + + if (seq == 256) + { + seq = 1; + } + + state.Sequence = seq; + } + } + } +} diff --git a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs index 8fef5bcb7..4cebd2176 100644 --- a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs +++ b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs @@ -23,7 +23,6 @@ namespace Server.Network public static void Configure() { IncomingPackets.Register(0x01, 5, false, Disconnect); - IncomingPackets.Register(0x02, 7, true, MovementReq); IncomingPackets.Register(0x05, 5, true, AttackReq); IncomingPackets.Register(0x12, 0, true, TextCommand); IncomingPackets.Register(0x22, 3, true, Resynchronize); @@ -490,8 +489,6 @@ namespace Server.Network from.SendEverything(); state.Sequence = 0; - - from.ClearFastwalkStack(); } public static void PingReq(NetState state, CircularBufferReader reader) @@ -504,40 +501,6 @@ namespace Server.Network state.Send(ChangeUpdateRange.Instantiate(18)); } - // TODO: Change to OSI fastwalk stack - public static void MovementReq(NetState state, CircularBufferReader reader) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - var dir = (Direction)reader.ReadByte(); - int seq = reader.ReadByte(); - var key = reader.ReadInt32(); // Fastwalk stack key - - if (state.Sequence == 0 && seq != 0 || !from.Move(dir)) - { - state.Send(new MovementRej(seq, from)); - state.Sequence = 0; - - from.ClearFastwalkStack(); - } - else - { - ++seq; - - if (seq == 256) - { - seq = 1; - } - - state.Sequence = seq; - } - } - public static void MobileQuery(NetState state, CircularBufferReader reader) { var from = state.Mobile; diff --git a/Projects/Server/Network/Packets/OutgoingMovementPackets.cs b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs new file mode 100644 index 000000000..6418a2485 --- /dev/null +++ b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs @@ -0,0 +1,145 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: OutgoingMovementPackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Buffers; + +namespace Server.Network +{ + public enum SpeedControlSetting + { + Disable, + Mount, + Walk + } + + public static class OutgoingMovementPackets + { + public static void SendSpeedControl(this NetState ns, SpeedControlSetting speedControl) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)06); + writer.Write((ushort)0x26); // Subpacket + writer.Write((byte)speedControl); + + ns.Send(ref buffer, writer.Position); + } + + public static void SendMovePlayer(this NetState ns, Direction d) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + buffer[0] = 0x97; // Packet ID + buffer[1] = (byte)d; + + ns.Send(ref buffer, 2); + } + + public static void SendMovementAck(this NetState ns, int seq, Mobile m) => + ns.SendMovementAck(seq, Notoriety.Compute(m, m)); + + public static void SendMovementAck(this NetState ns, int seq, int noto) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + buffer[0] = 0x22; // Packet ID + buffer[1] = (byte)seq; + buffer[2] = (byte)noto; + + ns.Send(ref buffer, 3); + } + + public static void SendMovementRej(this NetState ns, int seq, Mobile m) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0x21); // Packet ID + writer.Write((byte)seq); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((byte)m.Direction); + writer.Write((sbyte)m.Z); + + ns.Send(ref buffer, 8); + } + + public static void SendInitialFastwalkStack(this NetState ns, uint[] keys) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)0x1); // Subpacket + writer.Write(keys[0]); + writer.Write(keys[1]); + writer.Write(keys[2]); + writer.Write(keys[3]); + writer.Write(keys[4]); + writer.Write(keys[5]); + + ns.Send(ref buffer, writer.Position); + } + + public static void SendFastwalkStackKey(this NetState ns, uint key = 0) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)0x2); // Subpacket + writer.Write(key); + + ns.Send(ref buffer, writer.Position); + } + + public static void SendTimeSyncResponse(this NetState ns) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xF2); // Packet ID + + writer.Write(Core.TickCount); // ?? + writer.Write(Core.TickCount); // ?? + writer.Write(Core.TickCount); // ?? + + ns.Send(ref buffer, writer.Position); + } + } +} diff --git a/Projects/Server/Network/Pipe.cs b/Projects/Server/Network/Pipe.cs index 067cc6836..478853fd4 100644 --- a/Projects/Server/Network/Pipe.cs +++ b/Projects/Server/Network/Pipe.cs @@ -59,7 +59,7 @@ namespace Server.Network return; } - for (int i = 0; i < Buffer.Length; i++) + for (int i = 0; i < 2; i++) { var buffer = Buffer[i]; var sz = Math.Min(remaining, buffer.Count); diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index ed7fdd0b1..c2f9aeead 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -1185,6 +1185,23 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool RandomBool() => RandomSources.Source.NextBool(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RandomMinMax(uint min, uint max) + { + if (min > max) + { + var copy = min; + min = max; + max = copy; + } + else if (min == max) + { + return min; + } + + return min + RandomSources.Source.Next(max - min + 1); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int RandomMinMax(int min, int max) { diff --git a/Projects/UOContent/Commands/Handlers.cs b/Projects/UOContent/Commands/Handlers.cs index 65c228b4f..469a45a1f 100644 --- a/Projects/UOContent/Commands/Handlers.cs +++ b/Projects/UOContent/Commands/Handlers.cs @@ -85,12 +85,12 @@ namespace Server.Commands { if (e.Length == 1 && !e.GetBoolean(0)) { - from.Send(SpeedControl.Disable); + from.NetState.SendSpeedControl(SpeedControlSetting.Disable); from.SendMessage("Speed boost has been disabled."); } else { - from.Send(SpeedControl.MountSpeed); + from.NetState.SendSpeedControl(SpeedControlSetting.Mount); from.SendMessage("Speed boost has been enabled."); } } diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs index 86dbcbf1a..4a8c962e4 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs @@ -79,9 +79,8 @@ namespace Server.Items else if (Core.SE && Utility.RandomDouble() > .20 && (from.Direction & Direction.Running) != 0 && Core.TickCount - from.LastMoveTime < from.ComputeMovementSpeed(from.Direction)) { - from.SendLocalizedMessage( - 1063305 - ); // Didn't your parents ever tell you not to run with scissors in your hand?! + // Didn't your parents ever tell you not to run with scissors in your hand?! + from.SendLocalizedMessage(1063305); } else if (targeted is Item item && !item.Movable) { diff --git a/Projects/UOContent/Misc/Fastwalk.cs b/Projects/UOContent/Misc/Fastwalk.cs deleted file mode 100644 index 125ad25c2..000000000 --- a/Projects/UOContent/Misc/Fastwalk.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Server.Misc -{ - // This fastwalk detection is no longer required - // As of B36 PlayerMobile implements movement packet throttling which more reliably controls movement speeds - public static class Fastwalk - { - private static readonly int MaxSteps = 4; // Maximum number of queued steps until fastwalk is detected - private static readonly bool Enabled = false; // Is fastwalk detection enabled? - private static readonly bool UOTDOverride = false; // Should UO:TD clients not be checked for fastwalk? - - private static readonly AccessLevel - AccessOverride = AccessLevel.GameMaster; // Anyone with this or higher access level is not checked for fastwalk - - public static void Initialize() - { - Mobile.FwdMaxSteps = MaxSteps; - Mobile.FwdEnabled = Enabled; - Mobile.FwdUOTDOverride = UOTDOverride; - Mobile.FwdAccessOverride = AccessOverride; - - if (Enabled) - { - EventSink.FastWalk += OnFastWalk; - } - } - - public static void OnFastWalk(FastWalkEventArgs e) - { - e.Blocked = true; // disallow this fastwalk - var state = e.NetState; - state.WriteConsole("Fast movement detected (name={0})", state.Mobile.Name); - } - } -} diff --git a/Projects/UOContent/Misc/ProtocolExtensions.cs b/Projects/UOContent/Misc/ProtocolExtensions.cs index 66e823e53..c1d50741f 100644 --- a/Projects/UOContent/Misc/ProtocolExtensions.cs +++ b/Projects/UOContent/Misc/ProtocolExtensions.cs @@ -8,7 +8,7 @@ namespace Server.Misc public static void Initialize() { - IncomingPackets.Register(0xF0, 0, false, DecodeBundledPacket); + // IncomingPackets.Register(0xF0, 0, false, DecodeBundledPacket); } public static void Register(int packetID, bool ingame, OnPacketReceive onReceive) diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs index 6231b2339..581a8b1d2 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs @@ -221,7 +221,7 @@ namespace Server.Mobiles { if (to.Alive && to.Player && !UnderCacophonicAttack(to)) { - to.Send(SpeedControl.WalkSpeed); + to.NetState.SendSpeedControl(SpeedControlSetting.Walk); to.SendLocalizedMessage(1072069); // A cacophonic sound lambastes you, suppressing your ability to move. to.PlaySound(0x584); @@ -233,7 +233,7 @@ namespace Server.Mobiles public virtual void CacophonicEnd(Mobile from) { m_Table.Remove(from); - from.Send(SpeedControl.Disable); + from.NetState.SendSpeedControl(SpeedControlSetting.Disable); } public static bool UnderCacophonicAttack(Mobile from) => m_Table.Contains(from); diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index f78ce3e40..87f992a25 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -34,6 +34,7 @@ using Server.Spells.Spellweaving; using Server.Targeting; using Server.Utilities; using BaseQuestGump = Server.Engines.MLQuests.Gumps.BaseQuestGump; +using CalcMoves = Server.Movement.Movement; using QuestOfferGump = Server.Engines.MLQuests.Gumps.QuestOfferGump; using RankDefinition = Server.Guilds.RankDefinition; @@ -96,9 +97,6 @@ namespace Server.Mobiles { private static bool m_NoRecursion; - private static readonly bool FastwalkPrevention = true; // Is fastwalk prevention enabled? - private static readonly int FastwalkThreshold = 400; // Fastwalk prevention will become active after 0.4 seconds - private static readonly Point3D[] m_TrammelDeathDestinations = { new Point3D(1481, 1612, 20), @@ -166,7 +164,6 @@ namespace Server.Mobiles private RankDefinition m_GuildRank; private int m_HairModID = -1, m_HairModHue; - private bool m_HasMoved; public DateTime m_hontime; @@ -188,7 +185,7 @@ namespace Server.Mobiles private DateTime m_NextJustAward; - private long m_NextMovementTime; + private int m_NextMovementTime; private int m_NextProtectionCheck = 10; private DateTime m_NextSmithBulkOrder; private DateTime m_NextTailorBulkOrder; @@ -715,8 +712,6 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public SolenFriendship SolenFriendship { get; set; } - public virtual bool UsesFastwalkPrevention => AccessLevel < AccessLevel.Counselor; - public Type EnemyOfOneType { get => m_EnemyOfOneType; @@ -1031,11 +1026,6 @@ namespace Server.Mobiles public static void Initialize() { - if (FastwalkPrevention) - { - IncomingPackets.RegisterThrottler(0x02, MovementThrottle_Callback); - } - EventSink.Login += OnLogin; EventSink.Logout += OnLogout; EventSink.Connected += EventSink_Connected; @@ -4264,59 +4254,30 @@ namespace Server.Mobiles SentHonorContext?.Cancel(); } - public override int ComputeMovementSpeed(Direction dir, bool checkTurning) + public override int ComputeMovementSpeed(Direction dir, bool checkTurning = true) { if (checkTurning && (dir & Direction.Mask) != (Direction & Direction.Mask)) { - return RunMount; // We are NOT actually moving (just a direction change) + return CalcMoves.RunMountDelay; // We are NOT actually moving (just a direction change) } var context = TransformationSpellHelper.GetContext(this); if (context?.Type == typeof(ReaperFormSpell)) { - return WalkFoot; + return CalcMoves.WalkFootDelay; } var running = (dir & Direction.Running) != 0; var onHorse = Mount != null; - var animalContext = AnimalForm.GetContext(this); - - if (onHorse || animalContext?.SpeedBoost == true) + if (onHorse || AnimalForm.GetContext(this)?.SpeedBoost == true) { - return running ? RunMount : WalkMount; + return running ? CalcMoves.RunMountDelay : CalcMoves.WalkMountDelay; } - return running ? RunFoot : WalkFoot; - } - - public static TimeSpan MovementThrottle_Callback(NetState ns) - { - if (!(ns.Mobile is PlayerMobile pm) || !pm.UsesFastwalkPrevention) - { - return TimeSpan.Zero; - } - - if (!pm.m_HasMoved) - { - // has not yet moved - pm.m_NextMovementTime = Core.TickCount; - pm.m_HasMoved = true; - return TimeSpan.Zero; - } - - var ts = pm.m_NextMovementTime - Core.TickCount; - - if (ts < 0) - { - // been a while since we've last moved - pm.m_NextMovementTime = Core.TickCount; - return TimeSpan.Zero; - } - - return ts < FastwalkThreshold ? TimeSpan.Zero : TimeSpan.FromTicks(ts); + return running ? CalcMoves.RunFootDelay : CalcMoves.WalkFootDelay; } private void DeltaEnemies(Type oldType, Type newType) diff --git a/Projects/UOContent/Regions/TwistedWealdDesertRegion.cs b/Projects/UOContent/Regions/TwistedWealdDesertRegion.cs index 72187b4af..5eef97f8a 100644 --- a/Projects/UOContent/Regions/TwistedWealdDesertRegion.cs +++ b/Projects/UOContent/Regions/TwistedWealdDesertRegion.cs @@ -23,7 +23,7 @@ namespace Server.Regions if (ns != null && !TransformationSpellHelper.UnderTransformation(m, typeof(AnimalForm)) && m.AccessLevel == AccessLevel.Player) { - ns.Send(SpeedControl.WalkSpeed); + ns.SendSpeedControl(SpeedControlSetting.Walk); } } @@ -32,7 +32,7 @@ namespace Server.Regions var ns = m.NetState; if (ns != null && !TransformationSpellHelper.UnderTransformation(m, typeof(AnimalForm))) { - ns.Send(SpeedControl.Disable); + ns.SendSpeedControl(SpeedControlSetting.Disable); } } @@ -40,7 +40,7 @@ namespace Server.Regions { if (m.Region.IsPartOf() && m.AccessLevel == AccessLevel.Player) { - m.NetState.Send(SpeedControl.WalkSpeed); + m.NetState.SendSpeedControl(SpeedControlSetting.Walk); } } } diff --git a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs index 7ded9b015..ecb54d3b4 100644 --- a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs +++ b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs @@ -72,7 +72,7 @@ namespace Server.Spells.Ninjitsu { if (GetContext(m)?.SpeedBoost == true) { - m.Send(SpeedControl.MountSpeed); + m.NetState.SendSpeedControl(SpeedControlSetting.Mount); } } @@ -243,7 +243,7 @@ namespace Server.Spells.Ninjitsu if (entry.SpeedBoost) { - m.Send(SpeedControl.MountSpeed); + m.NetState.SendSpeedControl(SpeedControlSetting.Mount); } SkillMod mod = null; @@ -296,7 +296,7 @@ namespace Server.Spells.Ninjitsu if (context.SpeedBoost) { - m.Send(SpeedControl.Disable); + m.NetState.SendSpeedControl(SpeedControlSetting.Disable); } var mod = context.Mod; diff --git a/Projects/UOContent/Spells/Spellweaving/ReaperForm.cs b/Projects/UOContent/Spells/Spellweaving/ReaperForm.cs index b7b70e973..3c94e2233 100644 --- a/Projects/UOContent/Spells/Spellweaving/ReaperForm.cs +++ b/Projects/UOContent/Spells/Spellweaving/ReaperForm.cs @@ -38,7 +38,7 @@ namespace Server.Spells.Spellweaving if (context?.Type == typeof(ReaperFormSpell)) { - m.Send(SpeedControl.WalkSpeed); + m.NetState.SendSpeedControl(SpeedControlSetting.Walk); } } @@ -46,12 +46,12 @@ namespace Server.Spells.Spellweaving { m.PlaySound(0x1BA); - m.Send(SpeedControl.WalkSpeed); + m.NetState.SendSpeedControl(SpeedControlSetting.Walk); } public override void RemoveEffect(Mobile m) { - m.Send(SpeedControl.Disable); + m.NetState.SendSpeedControl(SpeedControlSetting.Disable); } } }