fix: Simplifies fastwalk detection (#907)
* Uses a circular buffer for steps. * Limits to 3 steps instead of 4. * Gives a 5% buffer on the first step.
This commit is contained in:
parent
5b7b99e0de
commit
e873d2ed7e
4 changed files with 186 additions and 78 deletions
|
|
@ -9029,7 +9029,10 @@ namespace Server
|
|||
public Direction GetDirectionTo(IPoint2D p, bool run = false) =>
|
||||
p == null ? Direction.North | (run ? Direction.Running : 0) : GetDirectionTo(p.X, p.Y, run);
|
||||
|
||||
public void PublicOverheadMessage(MessageType type, int hue, bool ascii, string text, bool noLineOfSight = true)
|
||||
public void PublicOverheadMessage(
|
||||
MessageType type, int hue, bool ascii, string text, bool noLineOfSight = true,
|
||||
AccessLevel accessLevel = AccessLevel.Player
|
||||
)
|
||||
{
|
||||
if (m_Map == null)
|
||||
{
|
||||
|
|
@ -9042,7 +9045,11 @@ namespace Server
|
|||
|
||||
foreach (var state in eable)
|
||||
{
|
||||
if (state.Mobile.CanSee(this) && (noLineOfSight || state.Mobile.InLOS(this)))
|
||||
if (
|
||||
state.Mobile.AccessLevel >= accessLevel &&
|
||||
state.Mobile.CanSee(this) &&
|
||||
(noLineOfSight || state.Mobile.InLOS(this))
|
||||
)
|
||||
{
|
||||
var length = OutgoingMessagePackets.CreateMessage(
|
||||
buffer, Serial, Body, type, hue, 3, ascii, Language, Name, text
|
||||
|
|
@ -9093,7 +9100,8 @@ namespace Server
|
|||
|
||||
public void PublicOverheadMessage(
|
||||
MessageType type, int hue, int number, AffixType affixType, string affix,
|
||||
string args = "", bool noLineOfSight = false
|
||||
string args = "", bool noLineOfSight = false,
|
||||
AccessLevel accessLevel = AccessLevel.Player
|
||||
)
|
||||
{
|
||||
if (m_Map == null)
|
||||
|
|
@ -9107,7 +9115,11 @@ namespace Server
|
|||
|
||||
foreach (var state in eable)
|
||||
{
|
||||
if (state.Mobile.CanSee(this) && (noLineOfSight || state.Mobile.InLOS(this)))
|
||||
if (
|
||||
state.Mobile.AccessLevel >= accessLevel &&
|
||||
state.Mobile.CanSee(this) &&
|
||||
(noLineOfSight || state.Mobile.InLOS(this))
|
||||
)
|
||||
{
|
||||
var length = OutgoingMessagePackets.CreateMessageLocalizedAffix(
|
||||
buffer, Serial, Body, type, hue, 3, number, Name, affixType, affix, args
|
||||
|
|
|
|||
|
|
@ -19,16 +19,16 @@ namespace Server.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 int WalkFootDelay { get; set; } = 400;
|
||||
public static int RunFootDelay { get; set; } = 200;
|
||||
public static int WalkMountDelay { get; set; } = 200;
|
||||
public static int RunMountDelay { get; set; } = 100;
|
||||
|
||||
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 int MaxSteps { get; private set; } = 3;
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,80 +13,109 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using CalcMoves = Server.Movement.Movement;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public partial class NetState
|
||||
{
|
||||
public partial class NetState
|
||||
// The next step
|
||||
private int _stepIndex;
|
||||
// The last index to expire
|
||||
private int _expiredIndex;
|
||||
|
||||
private long[] _steps;
|
||||
|
||||
public bool AddStep(Direction d)
|
||||
{
|
||||
private int _stepIndex;
|
||||
private int _stepCount;
|
||||
private long _startDelay;
|
||||
private long[] _stepDelays;
|
||||
|
||||
public bool AddStep(Direction d)
|
||||
if (Mobile == null)
|
||||
{
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
_steps ??= new long[CalcMoves.MaxSteps + 1]; // Extra index as a sentinel
|
||||
var stepsLength = _steps.Length;
|
||||
|
||||
var now = Core.TickCount;
|
||||
|
||||
var lastIndex = -1;
|
||||
|
||||
// Expire old steps
|
||||
while (_expiredIndex != _stepIndex)
|
||||
{
|
||||
var step = _steps[_expiredIndex];
|
||||
|
||||
// Is the step ahead of us, or the next step rolled over and we didn't yet
|
||||
if (step > now || lastIndex > -1 && _steps[lastIndex] > step)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
lastIndex = _expiredIndex++;
|
||||
|
||||
if (_expiredIndex == stepsLength)
|
||||
{
|
||||
_expiredIndex -= stepsLength;
|
||||
}
|
||||
}
|
||||
|
||||
var stepsTaken = (_stepIndex < _expiredIndex ? _stepIndex + stepsLength : _stepIndex) - _expiredIndex;
|
||||
var maxSteps = _steps.Length - 1;
|
||||
|
||||
// Can we take a step?
|
||||
if (stepsTaken >= maxSteps)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var delay = Mobile.ComputeMovementSpeed(d);
|
||||
|
||||
var prev = _stepIndex - 1;
|
||||
if (prev < 0)
|
||||
{
|
||||
prev += stepsLength;
|
||||
}
|
||||
|
||||
// Give a 5% buffer on the first step
|
||||
_steps[_stepIndex++] = stepsTaken > 0 ? _steps[prev] + delay : now + delay * 950 / 1000;
|
||||
|
||||
if (_stepIndex == stepsLength)
|
||||
{
|
||||
_stepIndex -= stepsLength;
|
||||
}
|
||||
|
||||
// If CalcMoves.MaxSteps is modified, we need to adjust accordingly
|
||||
AdjustSteps(CalcMoves.MaxSteps);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void AdjustSteps(int maxSteps)
|
||||
{
|
||||
var stepsLength = maxSteps + 1;
|
||||
|
||||
if (_steps.Length == stepsLength)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldSteps = _steps;
|
||||
_steps = new long[stepsLength];
|
||||
|
||||
var expiredIndex = _expiredIndex;
|
||||
var newStepIndex = 0;
|
||||
while (newStepIndex < maxSteps && expiredIndex != _stepIndex)
|
||||
{
|
||||
_steps[newStepIndex++] = oldSteps[expiredIndex++];
|
||||
if (expiredIndex >= oldSteps.Length)
|
||||
{
|
||||
expiredIndex -= oldSteps.Length;
|
||||
}
|
||||
}
|
||||
|
||||
_expiredIndex = 0;
|
||||
_stepIndex = newStepIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
67
Projects/UOContent/Network/FastwalkDetection.cs
Normal file
67
Projects/UOContent/Network/FastwalkDetection.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using System.Collections.Generic;
|
||||
using Server.Commands.Generic;
|
||||
|
||||
namespace Server.Network;
|
||||
|
||||
public static class FastwalkDetection
|
||||
{
|
||||
private static readonly HashSet<Mobile> _debugFastwalk = new();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
TargetCommands.Register(new DebugFastwalk());
|
||||
EventSink.FastWalk += OnFastwalk;
|
||||
}
|
||||
|
||||
private static void OnFastwalk(FastWalkEventArgs e)
|
||||
{
|
||||
var from = e.NetState.Mobile;
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_debugFastwalk.Contains(from))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
from.PublicOverheadMessage(MessageType.Emote, from.EmoteHue, false, "Fastwalk Detected", accessLevel: AccessLevel.GameMaster);
|
||||
}
|
||||
|
||||
public class DebugFastwalk : BaseCommand
|
||||
{
|
||||
public DebugFastwalk()
|
||||
{
|
||||
AccessLevel = AccessLevel.GameMaster;
|
||||
Supports = CommandSupport.AllMobiles;
|
||||
Commands = new[] { "DebugFastwalk" };
|
||||
ObjectTypes = ObjectTypes.Mobiles;
|
||||
ListOptimized = true;
|
||||
Usage = "DebugFastwalk <on|off>";
|
||||
Description = "Enables fastwalk debug messages";
|
||||
}
|
||||
|
||||
public override void ExecuteList(CommandEventArgs e, List<object> list)
|
||||
{
|
||||
var on = e.Arguments.Length == 0 || e.GetBoolean(0);
|
||||
|
||||
foreach (var o in list)
|
||||
{
|
||||
if (o is not Mobile m)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (on)
|
||||
{
|
||||
_debugFastwalk.Add(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
_debugFastwalk.Remove(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue