fix(core): Fixes a timer bug where Stop wasn't called internally (#664)

This commit is contained in:
Kamron Batman 2021-07-25 23:30:11 -07:00 committed by GitHub
parent 26592f202e
commit 06a577332d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 71 additions and 50 deletions

View file

@ -18,7 +18,7 @@ namespace Server.Tests
// Configure the world
World.Configure();
Timer.Initialize(0);
Timer.Init(0);
// Load the world
World.Load();
@ -26,7 +26,7 @@ namespace Server.Tests
public void Dispose()
{
Timer.Initialize(0);
Timer.Init(0);
}
}
}

View file

@ -23,7 +23,7 @@ namespace Server.Tests
timerTicks.ExecutedCount++;
}
Timer.Initialize(timerTicks.Ticks);
Timer.Init(timerTicks.Ticks);
var timer = Timer.DelayCall(TimeSpan.FromMilliseconds(ticks), action);
timer.Start();
@ -51,7 +51,7 @@ namespace Server.Tests
Assert.Equal(timerTicks.ExpectedTicks, timerTicks.Ticks);
}
Timer.Initialize(timerTicks.Ticks);
Timer.Init(timerTicks.Ticks);
var timer = Timer.DelayCall(TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(interval), count, action);
timer.Start();

View file

@ -467,7 +467,7 @@ namespace Server
VerifySerialization();
Timer.Initialize(TickCount);
Timer.Init(TickCount);
AssemblyHandler.Invoke("Configure");

View file

@ -17,8 +17,6 @@ using System;
namespace Server
{
public delegate void TimerCallback();
public delegate void TimerStateCallback<in T>(T state);
public delegate void TimerStateCallback<in T1, in T2>(T1 t1, T2 t2);
@ -32,15 +30,15 @@ namespace Server
private static string FormatDelegate(Delegate callback) =>
callback == null ? "null" : $"{callback.Method.DeclaringType?.FullName ?? ""}.{callback.Method.Name}";
public static Timer DelayCall(TimerCallback callback) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback);
public static Timer DelayCall(Action callback) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback);
public static Timer DelayCall(TimeSpan delay, TimerCallback callback) =>
public static Timer DelayCall(TimeSpan delay, Action callback) =>
DelayCall(delay, TimeSpan.Zero, 1, callback);
public static Timer DelayCall(TimeSpan delay, TimeSpan interval, TimerCallback callback) =>
public static Timer DelayCall(TimeSpan delay, TimeSpan interval, Action callback) =>
DelayCall(delay, interval, 0, callback);
public static Timer DelayCall(TimeSpan delay, TimeSpan interval, int count, TimerCallback callback)
public static Timer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback)
{
Timer t = new DelayCallTimer(delay, interval, count, callback);
t.Start();
@ -143,7 +141,7 @@ namespace Server
private class DelayCallTimer : Timer
{
public DelayCallTimer(TimeSpan delay, TimeSpan interval, int count, TimerCallback callback) : base(
public DelayCallTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) : base(
delay,
interval,
count
@ -152,7 +150,7 @@ namespace Server
Callback = callback;
}
public TimerCallback Callback { get; }
public Action Callback { get; }
protected override void OnTick()
{

View file

@ -33,7 +33,7 @@ namespace Server
private static readonly Timer[][] _rings = new Timer[_ringLayers][];
private static readonly int[] _ringIndexes = new int[_ringLayers];
public static void Initialize(long tickCount)
public static void Init(long tickCount)
{
_lastTickTurned = tickCount;
@ -107,21 +107,26 @@ namespace Server
{
var next = timer._nextTimer;
var prof = timer.GetProfile();
var finished = timer.Count != 0 && ++timer.Index >= timer.Count;
// We remove it before `OnTick()` so time references can be nulled and returned to cache safely from within OnTick.
// This can be done in OnTick by checking if Index < Count - 1 (still more iterations left)
RemoveTimer(timer);
if (finished)
{
timer.InternalStop();
}
prof?.Start();
timer.OnTick();
prof?.Finish();
if (timer.Running)
if (timer.Running && !finished)
{
RemoveTimer(timer);
if (timer.Count == 0 || ++timer.Index < timer.Count)
{
timer.Delay = timer.Interval;
timer.Next = Core.Now + timer.Interval;
AddTimer(timer, (long)timer.Delay.TotalMilliseconds);
}
timer.Delay = timer.Interval;
timer.Next = Core.Now + timer.Interval;
AddTimer(timer, (long)timer.Delay.TotalMilliseconds);
}
timer = next;
@ -166,12 +171,7 @@ namespace Server
slot -= _ringSize;
}
timer._nextTimer = _rings[i][slot];
if (timer._nextTimer != null)
{
timer._nextTimer._prevTimer = timer;
}
timer.Attach(_rings[i][slot]);
timer._remaining = remaining;
timer._ring = i;
timer._slot = (int)slot;
@ -188,22 +188,12 @@ namespace Server
private static void RemoveTimer(Timer timer)
{
if (timer._prevTimer != null)
{
timer._prevTimer._nextTimer = timer._nextTimer;
}
else
if (timer._prevTimer == null)
{
_rings[timer._ring][timer._slot] = timer._nextTimer;
}
if (timer._nextTimer != null)
{
timer._nextTimer._prevTimer = timer._prevTimer;
}
timer._nextTimer = null;
timer._prevTimer = null;
timer.Detach();
}
public static void DumpInfo(TextWriter tw)

View file

@ -15,11 +15,14 @@
using System;
using Server.Diagnostics;
using Server.Logging;
namespace Server
{
public partial class Timer
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(Timer));
// We need to know what ring/slot we are in so we can be removed if we are "head" of the link list.
private int _ring;
private int _slot;
@ -32,8 +35,11 @@ namespace Server
{
}
public Timer(TimeSpan delay, TimeSpan interval, int count = 0)
public Timer(TimeSpan delay, TimeSpan interval, int count = 0) => Init(delay, interval, count);
public void Init(TimeSpan delay, TimeSpan interval, int count)
{
Running = false;
Delay = delay;
Interval = interval;
Count = count;
@ -53,7 +59,8 @@ namespace Server
public TimeSpan Delay { get; set; }
public TimeSpan Interval { get; set; }
public int Index { get; private set; }
public int Count { get; }
public int Count { get; private set; }
public int RemainingCount => Count - Index;
public bool Running { get; private set; }
public TimerProfile GetProfile() => !Core.Profiling ? null : TimerProfile.Acquire(ToString() ?? "null");
@ -87,21 +94,50 @@ namespace Server
return this;
}
Running = false;
RemoveTimer(this);
InternalStop();
return this;
}
private void InternalStop()
{
Running = false;
var prof = GetProfile();
if (prof != null)
{
prof.Stopped++;
}
return this;
}
protected virtual void OnTick()
{
}
private void Attach(Timer timer)
{
_nextTimer = timer;
if (timer != null)
{
timer._prevTimer = this;
}
}
private void Detach()
{
if (_prevTimer != null)
{
_prevTimer._nextTimer = _nextTimer;
}
if (_nextTimer != null)
{
_nextTimer._prevTimer = _prevTimer;
}
_nextTimer = null;
_prevTimer = null;
}
}
}

View file

@ -1615,8 +1615,6 @@ namespace Server.Engines.ConPVP
private Timer m_FinishTimer;
private TimerCallback m_UnhideCallback;
public BRGame(BRController controller, DuelContext context) : base(context) => Controller = controller;
public BRController Controller { get; }
@ -1641,10 +1639,9 @@ namespace Server.Engines.ConPVP
{
if (m_Bomb != null && Controller != null)
{
m_UnhideCallback ??= UnhideBomb;
m_Bomb.Visible = false;
m_Bomb.MoveToWorld(Controller.BombHome, Controller.Map);
Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15)), m_UnhideCallback);
Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15)), UnhideBomb);
}
}