diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 8c677d1c3..2d805ab91 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -26,7 +26,7 @@ jobs: - name: Setup .NET 5 uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.301 + dotnet-version: 5.0.302 - name: Build run: ./publish.cmd - name: Test diff --git a/Projects/Benchmarks/Benchmarks.csproj b/Projects/Benchmarks/Benchmarks.csproj index e0ab71eea..cd7d4a26c 100644 --- a/Projects/Benchmarks/Benchmarks.csproj +++ b/Projects/Benchmarks/Benchmarks.csproj @@ -9,7 +9,7 @@ true - + diff --git a/Projects/Server.Tests/Fixtures/ServerFixture.cs b/Projects/Server.Tests/Fixtures/ServerFixture.cs index f286d1051..fa4669c32 100644 --- a/Projects/Server.Tests/Fixtures/ServerFixture.cs +++ b/Projects/Server.Tests/Fixtures/ServerFixture.cs @@ -7,6 +7,8 @@ namespace Server.Tests // Global setup static ServerFixture() { + Core.LoopContext = new EventLoopContext(); + Core.Expansion = Expansion.EJ; // Load Configurations diff --git a/Projects/Server.Tests/Tests/Timer/TimerTests.cs b/Projects/Server.Tests/Tests/Timer/TimerTests.cs index d7a005626..30fdbcd7a 100644 --- a/Projects/Server.Tests/Tests/Timer/TimerTests.cs +++ b/Projects/Server.Tests/Tests/Timer/TimerTests.cs @@ -25,8 +25,7 @@ namespace Server.Tests Timer.Init(timerTicks.Ticks); - var timer = Timer.DelayCall(TimeSpan.FromMilliseconds(ticks), action); - timer.Start(); + Timer.StartTimer(TimeSpan.FromMilliseconds(ticks), action); var tickCount = expectedTicks / 8; @@ -53,8 +52,7 @@ namespace Server.Tests Timer.Init(timerTicks.Ticks); - var timer = Timer.DelayCall(TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(interval), count, action); - timer.Start(); + Timer.StartTimer(TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(interval), count, action); var tickCount = (expectedDelayTicks + (expectedIntervalTicks * count - 1)) / 8; diff --git a/Projects/Server/EventLoopTasks.cs b/Projects/Server/EventLoopTasks.cs index a96d7aa53..3ead229cb 100644 --- a/Projects/Server/EventLoopTasks.cs +++ b/Projects/Server/EventLoopTasks.cs @@ -32,6 +32,8 @@ namespace Server public override SynchronizationContext CreateCopy() => new EventLoopContext(); + public void Post(Action d) => _queue.Enqueue(d); + public override void Post(SendOrPostCallback d, object state) => _queue.Enqueue(() => d(state)); public override void Send(SendOrPostCallback d, object state) diff --git a/Projects/Server/IEntity.cs b/Projects/Server/IEntity.cs index 827eb28ae..4dbb61470 100644 --- a/Projects/Server/IEntity.cs +++ b/Projects/Server/IEntity.cs @@ -108,7 +108,7 @@ namespace Server public void Deserialize(IGenericReader reader) { // Should not actually be saved - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); } public void Serialize(IGenericWriter writer) diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index cffee465a..c54d6ae40 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -3027,7 +3027,7 @@ namespace Server if (HeldBy != null) { - Timer.DelayCall(FixHolding_Sandbox); + Timer.StartTimer(FixHolding_Sandbox); } // if (version < 9) diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 6944fc26d..01e46e8eb 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -47,7 +47,7 @@ namespace Server private static int _itemCount; private static int _mobileCount; - private static EventLoopContext _eventLoopContext; + public static EventLoopContext LoopContext { get; set; } private static readonly Type[] _serialTypeArray = { typeof(Serial) }; @@ -366,9 +366,9 @@ namespace Server AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; - _eventLoopContext = new EventLoopContext(); + LoopContext = new EventLoopContext(); - SynchronizationContext.SetSynchronizationContext(_eventLoopContext); + SynchronizationContext.SetSynchronizationContext(LoopContext); foreach (var a in args) { @@ -505,7 +505,9 @@ namespace Server events += NetState.Slice(); // Execute captured post-await methods (like Timer.Pause) - events += _eventLoopContext.ExecuteTasks(); + events += LoopContext.ExecuteTasks(); + + Timer.CheckTimerPool(); // Check for pool depletion so we can async refill it. _tickCount = 0; _now = DateTime.MinValue; diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 0e79e03ba..aee6309fa 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -454,7 +454,7 @@ namespace Server private List _actions; private AccessLevel m_AccessLevel; - private Timer m_AutoManifestTimer; + private TimerExecutionToken _autoManifestTimerToken; private Container m_Backpack; @@ -465,7 +465,7 @@ namespace Server private int m_ChangingCombatant; private Mobile m_Combatant; - private Timer m_CombatTimer; + private TimerExecutionToken _combatTimerToken; private ContextMenu m_ContextMenu; private bool m_Criminal; @@ -473,14 +473,15 @@ namespace Server private Direction m_Direction; private bool m_DisplayGuildTitle; - private Timer m_ExpireAggrTimer; - private Timer m_ExpireCombatant; + private TimerExecutionToken _expireAggrTimerToken; + private TimerExecutionToken _expireCombatantTimerToken; + private TimerExecutionToken _expireCriminalTimerToken; private FacialHairInfo m_FacialHair; private int m_Fame, m_Karma; private bool m_Female, m_Warmode, m_Hidden, m_Blessed, m_Flying; private int m_Followers, m_FollowersMax; private bool m_Frozen; - private Timer m_FrozenTimer; + private TimerExecutionToken _frozenTimerToken; private BaseGuild m_Guild; private string m_GuildTitle; @@ -498,7 +499,7 @@ namespace Server private string m_Language; private int m_LightLevel; private Point3D m_Location; - private Timer m_LogoutTimer; + private TimerExecutionToken _logoutTimerToken; private Timer m_ManaTimer, m_HitsTimer, m_StamTimer; private Map m_Map; @@ -525,7 +526,7 @@ namespace Server private NetState m_NetState; private DateTime m_NextWarmodeChange; private bool m_Paralyzed; - private Timer m_ParaTimer; + private TimerExecutionToken _paraTimerToken; private bool m_Player; private Poison m_Poison; private Prompt m_Prompt; @@ -546,7 +547,7 @@ namespace Server private int m_VirtualArmor; private int m_VirtualArmorMod; private int m_WarmodeChanges; - private WarmodeTimer m_WarmodeTimer; + private bool _warmodeSpamValue; private IWeapon m_Weapon; private bool m_YellowHealthbar; @@ -768,12 +769,7 @@ namespace Server Delta(MobileDelta.Flags); SendLocalizedMessage(m_Paralyzed ? 502381 : 502382); - - if (m_ParaTimer != null) - { - m_ParaTimer.Stop(); - m_ParaTimer = null; - } + _paraTimerToken.Cancel(); } } } @@ -794,12 +790,7 @@ namespace Server { m_Frozen = value; Delta(MobileDelta.Flags); - - if (m_FrozenTimer != null) - { - m_FrozenTimer.Stop(); - m_FrozenTimer = null; - } + _frozenTimerToken.Cancel(); } } } @@ -880,7 +871,11 @@ namespace Server public bool ChangingCombatant => m_ChangingCombatant > 0; - private void ExpireCombatant() => Combatant = null; + private void ExpireCombatant() + { + Combatant = null; + _expireCombatantTimerToken.Cancel(); + } /// /// Overridable. Gets or sets which Mobile that this Mobile is currently engaged in combat with. @@ -915,20 +910,14 @@ namespace Server if (m_Combatant == null) { m_NetState.SendChangeCombatant(Serial.Zero); - m_ExpireCombatant?.Stop(); - m_CombatTimer?.Stop(); - - m_ExpireCombatant = null; - m_CombatTimer = null; + _expireCombatantTimerToken.Cancel(); + _combatTimerToken.Cancel(); } else { m_NetState.SendChangeCombatant(m_Combatant.Serial); - m_ExpireCombatant ??= Timer.DelayCall(ExpireCombatantDelay, ExpireCombatant); - m_ExpireCombatant.Start(); - - m_CombatTimer ??= new CombatTimer(this); - m_CombatTimer.Start(); + Timer.StartTimer(ExpireCombatantDelay, ExpireCombatant, out _expireCombatantTimerToken); + Timer.StartTimer(TimeSpan.FromSeconds(0.01), 0, CheckCombatTime, out _combatTimerToken); if (CanBeHarmful(m_Combatant, false)) { @@ -943,6 +932,40 @@ namespace Server } } + private void CheckCombatTime() + { + if (Core.TickCount - NextCombatTime < 0) + { + return; + } + + var combatant = Combatant; + + // If no combatant, wrong map, one of us is a ghost, or cannot see, or deleted, then stop combat + if (combatant?.Deleted != false || Deleted || combatant.m_Map != m_Map || + !combatant.Alive || !Alive || !CanSee(combatant) || combatant.IsDeadBondedPet || + IsDeadBondedPet) + { + Combatant = null; + return; + } + + var weapon = Weapon; + + if (!InRange(combatant, weapon.MaxRange)) + { + return; + } + + if (InLOS(combatant)) + { + weapon.OnBeforeSwing(this, combatant); + RevealingAction(); + NextCombatTime = + Core.TickCount + (int)weapon.OnSwing(this, combatant).TotalMilliseconds; + } + } + [CommandProperty(AccessLevel.GameMaster)] public int TotalGold => GetTotal(TotalType.Gold); @@ -1347,11 +1370,7 @@ namespace Server if (m_Warmode != value) { - if (m_AutoManifestTimer != null) - { - m_AutoManifestTimer.Stop(); - m_AutoManifestTimer = null; - } + _autoManifestTimerToken.Cancel(); m_Warmode = value; Delta(MobileDelta.Flags); @@ -1439,6 +1458,7 @@ namespace Server } m_NetState = value; + _logoutTimerToken.Cancel(); if (m_NetState == null) { @@ -1446,27 +1466,13 @@ namespace Server EventSink.InvokeDisconnected(this); // Disconnected, start the logout timer - if (m_LogoutTimer == null) - { - m_LogoutTimer = Timer.DelayCall(GetLogoutDelay(), Logout); - } - else - { - m_LogoutTimer.Stop(); - m_LogoutTimer.Delay = GetLogoutDelay(); - m_LogoutTimer.Start(); - } + Timer.StartTimer(GetLogoutDelay(), Logout, out _logoutTimerToken); } else { OnConnected(); EventSink.InvokeConnected(this); - // Connected, stop the logout timer and if needed, move to the world - m_LogoutTimer?.Stop(); - - m_LogoutTimer = null; - if (m_Map == Map.Internal && LogoutMap != null) { Map = LogoutMap; @@ -1494,7 +1500,7 @@ namespace Server } } - Timer.DelayCall(item.Delete); + Timer.StartTimer(item.Delete); } } @@ -1852,8 +1858,6 @@ namespace Server } } - public Timer ExpireCriminalTimer { get; set; } - [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] public virtual bool Criminal { @@ -1867,23 +1871,11 @@ namespace Server InvalidateProperties(); } + _expireCriminalTimerToken.Cancel(); + if (m_Criminal) { - if (ExpireCriminalTimer == null) - { - ExpireCriminalTimer = Timer.DelayCall(ExpireCriminalDelay, ExpireCriminal); - } - else - { - ExpireCriminalTimer.Stop(); - } - - ExpireCriminalTimer.Start(); - } - else if (ExpireCriminalTimer != null) - { - ExpireCriminalTimer.Stop(); - ExpireCriminalTimer = null; + Timer.StartTimer(ExpireCriminalDelay, ExpireCriminal, out _expireCriminalTimerToken); } } } @@ -3545,10 +3537,9 @@ namespace Server { StopAggrExpire(); } - else if (m_ExpireAggrTimer == null) + else if (!_expireAggrTimerToken.Running) { - m_ExpireAggrTimer = Timer.DelayCall(ExpireAggressorsDelay, ExpireAggressorsDelay, ExpireAggr); - m_ExpireAggrTimer.Start(); + Timer.StartTimer(ExpireAggressorsDelay, ExpireAggressorsDelay, ExpireAggr, out _expireAggrTimerToken); } } @@ -3566,8 +3557,7 @@ namespace Server private void StopAggrExpire() { - m_ExpireAggrTimer?.Stop(); - m_ExpireAggrTimer = null; + _expireAggrTimerToken.Cancel(); } private void CheckAggrExpire() @@ -3712,9 +3702,10 @@ namespace Server public void DelayChangeWarmode(bool value) { - if (m_WarmodeTimer != null) + if (m_WarmodeChanges > WarmodeCatchCount) { - m_WarmodeTimer.Value = value; + _warmodeSpamValue = value; + m_WarmodeChanges++; return; } @@ -3731,21 +3722,21 @@ namespace Server m_WarmodeChanges = 1; m_NextWarmodeChange = now + WarmodeSpamCatch; } - else if (m_WarmodeChanges == WarmodeCatchCount) + else if (m_WarmodeChanges++ == WarmodeCatchCount) { - m_WarmodeTimer = new WarmodeTimer(this, value); - m_WarmodeTimer.Start(); - + Timer.StartTimer(WarmodeSpamDelay, WarmodeSpamTimeout); return; } - else - { - ++m_WarmodeChanges; - } Warmode = value; } + private void WarmodeSpamTimeout() + { + Warmode = _warmodeSpamValue; + m_WarmodeChanges = 0; + } + public bool InLOS(Mobile target) => !Deleted && m_Map != null && (target == this || m_AccessLevel > AccessLevel.Player || m_Map.LineOfSight(this, target)); @@ -3808,9 +3799,7 @@ namespace Server if (!m_Paralyzed) { Paralyzed = true; - - m_ParaTimer = Timer.DelayCall(duration, ExpireParalyzed); - m_ParaTimer.Start(); + Timer.StartTimer(duration, ExpireParalyzed, out _paraTimerToken); } } @@ -3824,9 +3813,7 @@ namespace Server if (!m_Frozen) { Frozen = true; - - m_FrozenTimer = Timer.DelayCall(duration, ExpireFrozen); - m_FrozenTimer.Start(); + Timer.StartTimer(duration, ExpireFrozen, out _frozenTimerToken); } } @@ -3938,16 +3925,8 @@ namespace Server if (Combatant == aggressor) { - if (m_ExpireCombatant == null) - { - m_ExpireCombatant = Timer.DelayCall(ExpireCombatantDelay, ExpireCombatant); - } - else - { - m_ExpireCombatant.Stop(); - } - - m_ExpireCombatant.Start(); + _expireCombatantTimerToken.Cancel(); + Timer.StartTimer(ExpireCombatantDelay, ExpireCombatant, out _expireCombatantTimerToken); } var addAggressor = true; @@ -4787,14 +4766,13 @@ namespace Server m_HitsTimer?.Stop(); m_StamTimer?.Stop(); m_ManaTimer?.Stop(); - m_CombatTimer?.Stop(); - m_ExpireCombatant?.Stop(); - m_LogoutTimer?.Stop(); - ExpireCriminalTimer?.Stop(); - m_WarmodeTimer?.Stop(); - m_ParaTimer?.Stop(); - m_FrozenTimer?.Stop(); - m_AutoManifestTimer?.Stop(); + _combatTimerToken.Cancel(); + _expireCombatantTimerToken.Cancel(); + _logoutTimerToken.Cancel(); + _expireCriminalTimerToken.Cancel(); + _paraTimerToken.Cancel(); + _frozenTimerToken.Cancel(); + _autoManifestTimerToken.Cancel(); } public virtual bool AllowSkillUse(SkillName name) => true; @@ -4864,15 +4842,11 @@ namespace Server if (Paralyzed) { Paralyzed = false; - - m_ParaTimer?.Stop(); } if (Frozen) { Frozen = false; - - m_FrozenTimer?.Stop(); } var content = new List(); @@ -5570,17 +5544,8 @@ namespace Server public virtual void Manifest(TimeSpan delay) { Warmode = true; - - if (m_AutoManifestTimer == null) - { - m_AutoManifestTimer = Timer.DelayCall(delay, AutoManifest); - } - else - { - m_AutoManifestTimer.Stop(); - } - - m_AutoManifestTimer.Start(); + _autoManifestTimerToken.Cancel(); + Timer.StartTimer(delay, AutoManifest, out _autoManifestTimerToken); } public virtual bool CheckSpeechManifest() @@ -5592,7 +5557,7 @@ namespace Server var delay = AutoManifestTimeout; - if (delay > TimeSpan.Zero && (!Warmode || m_AutoManifestTimer != null)) + if (delay > TimeSpan.Zero && (!Warmode || _autoManifestTimerToken.Running)) { Manifest(delay); return true; @@ -6548,8 +6513,7 @@ namespace Server if (m_Criminal) { - ExpireCriminalTimer ??= Timer.DelayCall(ExpireCriminalDelay, ExpireCriminal); - ExpireCriminalTimer.Start(); + Timer.StartTimer(ExpireCriminalDelay, ExpireCriminal, out _expireCriminalTimerToken); } if (ShouldCheckStatTimers) @@ -8186,27 +8150,6 @@ namespace Server { } - private class WarmodeTimer : Timer - { - private Mobile m_Mobile; - - public WarmodeTimer(Mobile m, bool value) : base(WarmodeSpamDelay) - { - m_Mobile = m; - Value = value; - } - - public bool Value{ get; set; } - - protected override void OnTick() - { - m_Mobile.Warmode = Value; - m_Mobile.m_WarmodeChanges = 0; - - m_Mobile.m_WarmodeTimer = null; - } - } - public static TimeSpan GetHitsRegenRate(Mobile m) => HitsRegenRateHandler?.Invoke(m) ?? DefaultHitsRate; public static TimeSpan GetStamRegenRate(Mobile m) => StamRegenRateHandler?.Invoke(m) ?? DefaultStamRate; @@ -8588,16 +8531,8 @@ namespace Server Combatant = target; } - if (m_ExpireCombatant == null) - { - m_ExpireCombatant = Timer.DelayCall(ExpireCombatantDelay, ExpireCombatant); - } - else - { - m_ExpireCombatant.Stop(); - } - - m_ExpireCombatant.Start(); + _expireCombatantTimerToken.Cancel(); + Timer.StartTimer(ExpireCombatantDelay, ExpireCombatant, out _expireCombatantTimerToken); } public virtual bool HarmfulCheck(Mobile target) @@ -9446,48 +9381,6 @@ namespace Server } } - private class CombatTimer : Timer - { - private readonly Mobile m_Mobile; - - public CombatTimer(Mobile m) : base(TimeSpan.FromSeconds(0.0), TimeSpan.FromSeconds(0.01)) => - m_Mobile = m; - - protected override void OnTick() - { - if (Core.TickCount - m_Mobile.NextCombatTime < 0) - { - return; - } - - var combatant = m_Mobile.Combatant; - - // If no combatant, wrong map, one of us is a ghost, or cannot see, or deleted, then stop combat - if (combatant?.Deleted != false || m_Mobile.Deleted || combatant.m_Map != m_Mobile.m_Map || - !combatant.Alive || !m_Mobile.Alive || !m_Mobile.CanSee(combatant) || combatant.IsDeadBondedPet || - m_Mobile.IsDeadBondedPet) - { - m_Mobile.Combatant = null; - return; - } - - var weapon = m_Mobile.Weapon; - - if (!m_Mobile.InRange(combatant, weapon.MaxRange)) - { - return; - } - - if (m_Mobile.InLOS(combatant)) - { - weapon.OnBeforeSwing(m_Mobile, combatant); - m_Mobile.RevealingAction(); - m_Mobile.NextCombatTime = - Core.TickCount + (int)weapon.OnSwing(m_Mobile, combatant).TotalMilliseconds; - } - } - } - private void ExpireCriminal() { Criminal = false; diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index 84ce948be..1d3e1b364 100644 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -124,7 +124,7 @@ namespace Server.Network public static void Initialize() { - Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); + Timer.StartTimer(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); } public NetState(ISocket connection) diff --git a/Projects/Server/SecureTrade.cs b/Projects/Server/SecureTrade.cs index 12a582c2e..032931cea 100644 --- a/Projects/Server/SecureTrade.cs +++ b/Projects/Server/SecureTrade.cs @@ -138,8 +138,8 @@ namespace Server ns?.RemoveTrade(this); - Timer.DelayCall(From.Dispose); - Timer.DelayCall(To.Dispose); + Timer.StartTimer(From.Dispose); + Timer.StartTimer(To.Dispose); } public void UpdateFromCurrency() diff --git a/Projects/Server/Timer/Timer.DelayCall.cs b/Projects/Server/Timer/Timer.DelayCall.cs index 2d9f43859..2c156a744 100644 --- a/Projects/Server/Timer/Timer.DelayCall.cs +++ b/Projects/Server/Timer/Timer.DelayCall.cs @@ -14,252 +14,222 @@ *************************************************************************/ using System; +#if DEBUG_TIMERS +using System.Collections.Generic; +#endif +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; namespace Server { - public delegate void TimerStateCallback(T state); - - public delegate void TimerStateCallback(T1 t1, T2 t2); - - public delegate void TimerStateCallback(T1 t1, T2 t2, T3 t3); - - public delegate void TimerStateCallback(T1 t1, T2 t2, T3 t3, T4 t4); - public partial class Timer { private static string FormatDelegate(Delegate callback) => callback == null ? "null" : $"{callback.Method.DeclaringType?.FullName ?? ""}.{callback.Method.Name}"; - public static Timer DelayCall(Action callback) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(Action callback) => DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback); - public static Timer DelayCall(TimeSpan delay, Action callback) => - DelayCall(delay, TimeSpan.Zero, 1, callback); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan delay, Action callback) => DelayCall(delay, TimeSpan.Zero, 1, callback); - public static Timer DelayCall(TimeSpan delay, TimeSpan interval, Action callback) => + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, Action callback) => DelayCall(delay, interval, 0, callback); - public static Timer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan interval, int count, Action callback) => + DelayCall(TimeSpan.Zero, interval, count, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback) { - Timer t = new DelayCallTimer(delay, interval, count, callback); + DelayCallTimer t = new DelayCallTimer(delay, interval, count, callback); t.Start(); +#if DEBUG_TIMERS + t._allowFinalization = true; +#endif return t; } - public static Timer DelayCall(TimerStateCallback callback, T state) => - DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, state); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(Action callback) => StartTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback); - public static Timer DelayCall(TimeSpan delay, TimerStateCallback callback, T state) => - DelayCall(delay, TimeSpan.Zero, 1, callback, state); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, Action callback) => StartTimer(delay, TimeSpan.Zero, 1, callback); - public static Timer DelayCall(TimeSpan delay, TimeSpan interval, TimerStateCallback callback, T state) => - DelayCall(delay, interval, 0, callback, state); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback) => + StartTimer(delay, interval, 0, callback); - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, - T state - ) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan interval, int count, Action callback) => + StartTimer(TimeSpan.Zero, interval, count, callback); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) { - Timer t = new DelayStateCallTimer(delay, interval, count, callback, state); + DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); + t._selfReturn = true; t.Start(); - return t; +#if DEBUG_TIMERS + DelayCallTimer._stackTraces[t.GetHashCode()] = new StackTrace().ToString(); +#endif } - public static Timer DelayCall(TimerStateCallback callback, T1 t1, T2 t2) => - DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(Action callback, out TimerExecutionToken token) => + StartTimer(TimeSpan.Zero, TimeSpan.Zero, 1, callback, out token); - public static Timer DelayCall(TimeSpan delay, TimerStateCallback callback, T1 t1, T2 t2) => - DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, Action callback, out TimerExecutionToken token) => + StartTimer(delay, TimeSpan.Zero, 1, callback, out token); - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, TimerStateCallback callback, - T1 t1, T2 t2 - ) => DelayCall(delay, interval, 0, callback, t1, t2); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback, out TimerExecutionToken token) => + StartTimer(delay, interval, 0, callback, out token); - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, - T1 t1, T2 t2 - ) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan interval, int count, Action callback, out TimerExecutionToken token) => + StartTimer(TimeSpan.Zero, interval, count, callback, out token); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, out TimerExecutionToken token) { - Timer t = new DelayStateCallTimer(delay, interval, count, callback, t1, t2); + DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); t.Start(); - return t; +#if DEBUG_TIMERS + DelayCallTimer._stackTraces[t.GetHashCode()] = new StackTrace().ToString(); +#endif + token = new TimerExecutionToken(t); } - public static Timer DelayCall(TimerStateCallback callback, T1 t1, T2 t2, T3 t3) => - DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer Pause(TimeSpan ms) => new(ms); - public static Timer DelayCall( - TimeSpan delay, TimerStateCallback callback, T1 t1, T2 t2, T3 t3 - ) => - DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2, t3); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DelayCallTimer Pause(int ms) => Pause(TimeSpan.FromMilliseconds(ms)); - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, TimerStateCallback callback, - T1 t1, T2 t2, T3 t3 - ) => DelayCall(delay, interval, 0, callback, t1, t2, t3); - - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, int count, - TimerStateCallback callback, T1 t1, T2 t2, T3 t3 - ) + public sealed class DelayCallTimer : Timer, INotifyCompletion { - Timer t = new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3); - t.Start(); + internal bool _selfReturn; +#if DEBUG_TIMERS + internal bool _allowFinalization; +#endif + private Action _continuation; + private bool _complete; - return t; - } - - public static Timer DelayCall( - TimerStateCallback callback, T1 t1, T2 t2, T3 t3, T4 t4 - ) => - DelayCall(TimeSpan.Zero, TimeSpan.Zero, 1, callback, t1, t2, t3, t4); - - public static Timer DelayCall( - TimeSpan delay, TimerStateCallback callback, - T1 t1, T2 t2, T3 t3, T4 t4 - ) => DelayCall(delay, TimeSpan.Zero, 1, callback, t1, t2, t3, t4); - - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, - TimerStateCallback callback, T1 t1, T2 t2, T3 t3, T4 t4 - ) => - DelayCall(delay, interval, 0, callback, t1, t2, t3, t4); - - public static Timer DelayCall( - TimeSpan delay, TimeSpan interval, int count, - TimerStateCallback callback, T1 t1, T2 t2, T3 t3, T4 t4 - ) - { - Timer t = new DelayStateCallTimer(delay, interval, count, callback, t1, t2, t3, t4); - t.Start(); - - return t; - } - - private class DelayCallTimer : Timer - { - public DelayCallTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) : base( + internal DelayCallTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) : base( delay, interval, count - ) - { - Callback = callback; - } + ) => + _continuation = callback; - public Action Callback { get; } + internal DelayCallTimer(TimeSpan delay) : base(delay) + { +#if DEBUG_TIMERS + t._allowFinalization = true; +#endif + Start(); + } protected override void OnTick() { - Callback?.Invoke(); + _complete = true; + _continuation?.Invoke(); } - public override string ToString() => $"DelayCallTimer[{FormatDelegate(Callback)}]"; - } - - private class DelayStateCallTimer : Timer - { - private readonly T m_State; - - public DelayStateCallTimer(TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, T state) - : base(delay, interval, count) + public override void Stop() { - Callback = callback; - m_State = state; + base.Stop(); + + if (_selfReturn) + { + Return(); + } } - public TimerStateCallback Callback { get; } - - protected override void OnTick() + internal void Return() { - Callback?.Invoke(m_State); + if (Running) + { + logger.Error($"Timer is returned while still running! {new StackTrace()}"); + return; + } + + Version++; // Increment the version so if this is called from OnTick() and another timer is started, we don't have a problem + + if (_poolCount >= _poolCapacity) + { +#if DEBUG_TIMERS + logger.Warning($"DelayCallTimer pool reached maximum of {_poolSize} timers"); + _allowFinalization = true; + _stackTraces.Remove(GetHashCode()); +#endif + return; + } + + _continuation = null; + ReturnToPool(1, this, this); } - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; - } - - private class DelayStateCallTimer : Timer - { - private readonly T1 m_T1; - private readonly T2 m_T2; - - public DelayStateCallTimer( - TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, - T1 t1, T2 t2 - ) : base(delay, interval, count) + public static DelayCallTimer GetTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) { - Callback = callback; - m_T1 = t1; - m_T2 = t2; + if (_poolHead != null) + { + _poolCount--; +#if DEBUG_TIMERS + logger.Information($"Pool count changed: {_poolCount} ({_poolCapacity})"); +#endif + + var timer = GetFromPool(); + + timer.Init(delay, interval, count); + timer._continuation = callback; + timer._selfReturn = false; +#if DEBUG_TIMERS + timer._allowFinalization = false; +#endif + + return timer; + } + + _timerPoolDepletionAmount++; + +#if DEBUG_TIMERS + logger.Warning($"Timer pool depleted and timer was allocated.\n{new StackTrace()}); +#endif + return new DelayCallTimer(delay, interval, count, callback); } - public TimerStateCallback Callback { get; } + public override string ToString() => $"DelayCallTimer[{FormatDelegate(_continuation)}]"; - protected override void OnTick() +#if DEBUG_TIMERS + internal static Dictionary _stackTraces = new(); + + ~DelayCallTimer() { - Callback?.Invoke(m_T1, m_T2); + if (!_allowFinalization) + { + logger.Warning($"Pooled timer was not returned to the pool.\n{_stackTraces[GetHashCode()]}"); + } } +#endif - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; - } + public DelayCallTimer GetAwaiter() => this; - private class DelayStateCallTimer : Timer - { - private readonly T1 m_T1; - private readonly T2 m_T2; - private readonly T3 m_T3; + public bool IsCompleted => _complete; - public DelayStateCallTimer( - TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, - T1 t1, T2 t2, T3 t3 - ) : base(delay, interval, count) + public void OnCompleted(Action continuation) => _continuation = continuation; + + public void GetResult() { - Callback = callback; - m_T1 = t1; - m_T2 = t2; - m_T3 = t3; } - - public TimerStateCallback Callback { get; } - - protected override void OnTick() - { - Callback?.Invoke(m_T1, m_T2, m_T3); - } - - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; - } - - private class DelayStateCallTimer : Timer - { - private readonly T1 m_T1; - private readonly T2 m_T2; - private readonly T3 m_T3; - private readonly T4 m_T4; - - public DelayStateCallTimer( - TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, - T1 t1, T2 t2, T3 t3, T4 t4 - ) : base(delay, interval, count) - { - Callback = callback; - m_T1 = t1; - m_T2 = t2; - m_T3 = t3; - m_T4 = t4; - } - - public TimerStateCallback Callback { get; } - - protected override void OnTick() - { - Callback?.Invoke(m_T1, m_T2, m_T3, m_T4); - } - - public override string ToString() => $"DelayStateCall[{FormatDelegate(Callback)}]"; } } } diff --git a/Projects/Server/Timer/Timer.Pause.cs b/Projects/Server/Timer/Timer.Pause.cs deleted file mode 100644 index d0854f7dd..000000000 --- a/Projects/Server/Timer/Timer.Pause.cs +++ /dev/null @@ -1,53 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2021 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Timer.Pause.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; -using System.Runtime.CompilerServices; - -namespace Server -{ - public partial class Timer - { - public class DelayTaskTimer : Timer, INotifyCompletion - { - private Action _continuation; - private bool _complete; - - internal DelayTaskTimer(TimeSpan delay) : base(delay) => Start(); - - protected override void OnTick() - { - _complete = true; - _continuation?.Invoke(); - } - - public DelayTaskTimer GetAwaiter() => this; - - public bool IsCompleted => _complete; - - public void OnCompleted(Action continuation) => _continuation = continuation; - - public void GetResult() - { - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayTaskTimer Pause(TimeSpan ms) => new(ms); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DelayTaskTimer Pause(int ms) => Pause(TimeSpan.FromMilliseconds(ms)); - } -} diff --git a/Projects/Server/Timer/Timer.Pool.cs b/Projects/Server/Timer/Timer.Pool.cs new file mode 100644 index 000000000..2776610e6 --- /dev/null +++ b/Projects/Server/Timer/Timer.Pool.cs @@ -0,0 +1,128 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Timer.Pool.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; +using System.Threading; + +namespace Server +{ + public partial class Timer + { + private const int _timerPoolDepletionThreshold = 128; // Maximum timers allocated in a single tick before we force adjust + private static int _timerPoolDepletionAmount; // Amount the pool has been depleted by + private static int _maxPoolCapacity; + private static int _poolCapacity; + private static int _poolCount; + private static DelayCallTimer _poolHead; + + public static void CheckTimerPool() + { + // Anything less than this threshold and we are ok with the number of allocations. + if (_timerPoolDepletionAmount < _timerPoolDepletionThreshold) + { + _timerPoolDepletionAmount = 0; + return; + } + + var growthFactor = Math.DivRem(_timerPoolDepletionAmount, _poolCapacity, out var rem); + var amountToGrow = _poolCapacity * (growthFactor + (rem > 0 ? 1 : 0)); + var amountToRefill = Math.Min(_maxPoolCapacity, amountToGrow); + + var maximumHit = amountToGrow > amountToRefill ? " Maximum pool size has been reached." : ""; + + logger.Warning($"Timer pool depleted by {_timerPoolDepletionAmount}. Refilling with {amountToRefill}.{maximumHit}"); + RefillPoolAsync(amountToRefill); + _timerPoolDepletionAmount = 0; + } + + public static void ConfigureTimerPool() + { + _poolCapacity = ServerConfiguration.GetOrUpdateSetting("timer.intialPoolCapacity", 1024); + _maxPoolCapacity = ServerConfiguration.GetOrUpdateSetting("timer.maxPoolCapacity", _poolCapacity * 16); + + RefillPool(_poolCapacity, out var head, out var tail); + ReturnToPool(_poolCapacity, head, tail); + } + + private static void ReturnToPool(int amount, DelayCallTimer head, DelayCallTimer tail) + { + tail.Attach(_poolHead); + _poolHead = head; + _poolCount += amount; +#if DEBUG_TIMERS + logger.Information($"Pool count changed: {_poolCount} ({_poolCapacity})"); +#endif + } + + private static DelayCallTimer GetFromPool() + { + var timer = _poolHead; + _poolHead = _poolHead._nextTimer as DelayCallTimer; + timer.Detach(); + return timer; + } + + internal static void RefillPool(int amount, out DelayCallTimer head, out DelayCallTimer tail) + { +#if DEBUG_TIMERS + logger.Information($"Filling pool with {amount} timers."); +#endif + + head = null; + tail = null; + + for (var i = 0; i < amount; i++) + { + var timer = new DelayCallTimer(TimeSpan.Zero, TimeSpan.Zero, 0, null); + timer.Attach(head); + + if (i == 0) + { + tail = timer; + } + + head = timer; + } + } + + internal static void RefillPoolAsync(int amountToRefill) + { + ThreadPool.UnsafeQueueUserWorkItem( + static amount => + { + RefillPool(amount, out var head, out var tail); + + // Run this on the core thread + Core.LoopContext.Post( + state => + { + if (state == null) + { + return; + } + + var (listHead, listTail) = ((DelayCallTimer, DelayCallTimer))state; + ReturnToPool(amount, listHead, listTail); + _poolCapacity = amount; + }, + (head, tail) + ); + }, + amountToRefill, + false + ); + } + } +} diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index ec4beef46..db3076f19 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -113,20 +113,25 @@ namespace Server // This can be done in OnTick by checking if Index < Count - 1 (still more iterations left) RemoveTimer(timer); - if (finished) - { - timer.InternalStop(); - } + var version = timer.Version; prof?.Start(); timer.OnTick(); prof?.Finish(); - if (timer.Running && !finished) + // If the timer has not been stopped, and it has not been altered (shared timers) + if (timer.Running && timer.Version == version) { - timer.Delay = timer.Interval; - timer.Next = Core.Now + timer.Interval; - AddTimer(timer, (long)timer.Delay.TotalMilliseconds); + if (finished) + { + timer.Stop(); + } + else + { + timer.Delay = timer.Interval; + timer.Next = Core.Now + timer.Interval; + AddTimer(timer, (long)timer.Delay.TotalMilliseconds); + } } timer = next; @@ -200,8 +205,8 @@ namespace Server { var now = DateTime.UtcNow; - tw.WriteLine("Date: {0}", now); - tw.WriteLine(); + tw.WriteLine("Date: {0}\n", now); + tw.WriteLine("Pool - Count: {0}; Size {1}\n", _poolCount - _timerPoolDepletionAmount, _poolCapacity); var total = 0.0; var hash = new Dictionary(); @@ -221,9 +226,11 @@ namespace Server } } + tw.WriteLine("Timers:"); + foreach (var (name, count) in hash.OrderByDescending(o => o.Value)) { - tw.WriteLine($"Type: {name}; Count: {count}; Percent: {count / total}%"); + tw.WriteLine($"- Type: {name}; Count: {count}; Percent: {count / total}%"); } tw.WriteLine(); diff --git a/Projects/Server/Timer/Timer.cs b/Projects/Server/Timer/Timer.cs index e2dcb562f..abe3aa509 100644 --- a/Projects/Server/Timer/Timer.cs +++ b/Projects/Server/Timer/Timer.cs @@ -21,12 +21,16 @@ namespace Server { public partial class Timer { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(Timer)); + protected internal static readonly ILogger logger = LogFactory.GetLogger(typeof(Timer)); + + public static void Configure() + { + ConfigureTimerPool(); + } // 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; - private long _remaining; private Timer _nextTimer; private Timer _prevTimer; @@ -37,10 +41,11 @@ namespace Server public Timer(TimeSpan delay, TimeSpan interval, int count = 0) => Init(delay, interval, count); - public void Init(TimeSpan delay, TimeSpan interval, int count) + protected void Init(TimeSpan delay, TimeSpan interval, int count) { Running = false; Delay = delay; + Index = 0; Interval = interval; Count = count; _nextTimer = null; @@ -55,6 +60,8 @@ namespace Server } } + protected int Version { get; set; } // Used to determine if a timer was altered and we should abandon it. + public DateTime Next { get; private set; } public TimeSpan Delay { get; set; } public TimeSpan Interval { get; set; } @@ -87,21 +94,14 @@ namespace Server return this; } - public Timer Stop() + public virtual void Stop() { if (!Running) { - return this; + return; } RemoveTimer(this); - InternalStop(); - - return this; - } - - private void InternalStop() - { Running = false; var prof = GetProfile(); diff --git a/Projects/Server/Timer/TimerExecutionToken.cs b/Projects/Server/Timer/TimerExecutionToken.cs new file mode 100644 index 000000000..9535e726e --- /dev/null +++ b/Projects/Server/Timer/TimerExecutionToken.cs @@ -0,0 +1,60 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: TimerExecutionToken.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; +using System.Runtime.CompilerServices; + +namespace Server +{ + public struct TimerExecutionToken + { + private Timer.DelayCallTimer _timer; + + internal TimerExecutionToken(Timer.DelayCallTimer timer) => _timer = timer; + + public bool Running + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.Running == true; + } + + public int Index + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.Index ?? 0; + } + + public int RemainingCount + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.RemainingCount ?? 0; + } + + public DateTime Next + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _timer?.Next ?? DateTime.MinValue; + } + + public void Cancel() + { + _timer?.Stop(); + _timer?.Return(); + _timer = null; + + this = default; + } + } +} diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index f4bb661fa..1e03d7228 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -419,7 +419,7 @@ namespace Server m_DiskWriteHandle.Set(); - Timer.DelayCall(FinishWorldSave); + Timer.StartTimer(FinishWorldSave); } private static void ProcessDecay() diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index fe9de870b..e6d11f918 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -405,7 +405,7 @@ namespace Server.Accounting _totalGameTime = reader.ReadTimeSpan(); - Timer.DelayCall(AfterDeserialization); + Timer.StartTimer(AfterDeserialization); } /// diff --git a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs index 45ac2414c..95c9b9d95 100644 --- a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs @@ -128,7 +128,7 @@ namespace Server.Engines.BulkOrders RequireExceptional = reader.ReadBool(); Material = (BulkMaterialType)reader.ReadInt(); - Timer.DelayCall(AfterDeserialization); + Timer.StartTimer(AfterDeserialization); } } } diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs index 7805abf3f..ab21f89bc 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs @@ -42,9 +42,10 @@ namespace Server.Engines.CannedEvil //Goes back each level, below level 0 and it goes off! - private Timer m_Timer; + private TimerExecutionToken _timerToken; private IdolOfTheChampion m_Idol; + private TimerExecutionToken _restartTimerToken; public virtual string BroadcastMessage => "The Champion has sensed your presence! Beware its wrath!"; public virtual bool ProximitySpawn => false; @@ -58,8 +59,6 @@ namespace Server.Engines.CannedEvil public Dictionary DamageEntries { get; private set; } - public Timer RestartTimer { get; set; } - [CommandProperty(AccessLevel.GameMaster)] public bool ConfinedRoaming { get; set; } @@ -92,7 +91,7 @@ namespace Server.Engines.CannedEvil RestartDelay = TimeSpan.FromMinutes(30.0); DamageEntries = new Dictionary(); - Timer.DelayCall(TimeSpan.Zero, SetInitialSpawnArea); + Timer.StartTimer(TimeSpan.Zero, SetInitialSpawnArea); } public void SetInitialSpawnArea() @@ -303,13 +302,10 @@ namespace Server.Engines.CannedEvil HasBeenAdvanced = false; m_MaxLevel = 16 + Utility.Random(3); - m_Timer?.Stop(); + _timerToken.Cancel(); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnSlice, out _timerToken); - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnSlice); - m_Timer.Start(); - - RestartTimer?.Stop(); - RestartTimer = null; + _restartTimerToken.Cancel(); if (m_Altar != null) { @@ -336,12 +332,8 @@ namespace Server.Engines.CannedEvil HasBeenAdvanced = false; m_MaxLevel = 0; - m_Timer?.Stop(); - - m_Timer = null; - - RestartTimer?.Stop(); - RestartTimer = null; + _timerToken.Cancel(); + _restartTimerToken.Cancel(); if (m_Altar != null) { @@ -363,17 +355,14 @@ namespace Server.Engines.CannedEvil NextProximityTime = Core.Now + TimeSpan.FromHours(6.0); } - Timer.DelayCall(TimeSpan.FromMinutes(10.0), ExpireCreatures); + Timer.StartTimer(TimeSpan.FromMinutes(10.0), ExpireCreatures); } public void BeginRestart(TimeSpan ts) { - RestartTimer?.Stop(); - RestartTime = Core.Now + ts; - - RestartTimer = Timer.DelayCall(ts, EndRestart); - RestartTimer.Start(); + _restartTimerToken.Cancel(); + Timer.StartTimer(ts, EndRestart, out _restartTimerToken); } public void EndRestart() @@ -1314,12 +1303,15 @@ namespace Server.Engines.CannedEvil writer.Write(Champion); writer.Write(RestartDelay); - writer.Write(RestartTimer != null); - - if (RestartTimer != null) + if (_restartTimerToken.Running) { + writer.Write(true); writer.WriteDeltaTime(RestartTime); } + else + { + writer.Write(false); + } } public override void Deserialize(IGenericReader reader) @@ -1456,7 +1448,7 @@ namespace Server.Engines.CannedEvil } } - Timer.DelayCall(TimeSpan.Zero, UpdateRegion); + Timer.StartTimer(TimeSpan.Zero, UpdateRegion); } } diff --git a/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs b/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs index 0c65a1a21..50e6d845f 100644 --- a/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs +++ b/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs @@ -98,7 +98,7 @@ namespace Server.Engines.ConPVP AddButton(314, 173, 247, 248, 1); - Timer.DelayCall(TimeSpan.FromSeconds(15.0), AutoReject); + Timer.StartTimer(TimeSpan.FromSeconds(15.0), AutoReject); } public string Center(string text) => $"
{text}
"; diff --git a/Projects/UOContent/Engines/ConPVP/Arena.cs b/Projects/UOContent/Engines/ConPVP/Arena.cs index c22cffc22..6b099c578 100644 --- a/Projects/UOContent/Engines/ConPVP/Arena.cs +++ b/Projects/UOContent/Engines/ConPVP/Arena.cs @@ -333,12 +333,12 @@ namespace Server.Engines.ConPVP if (IsOccupied) { - Timer.DelayCall(TimeSpan.FromSeconds(2.0), Evict); + Timer.StartTimer(TimeSpan.FromSeconds(2.0), Evict); } if (m_Tournament != null) { - Timer.DelayCall(AttachToTournament_Sandbox); + Timer.StartTimer(AttachToTournament_Sandbox); } } diff --git a/Projects/UOContent/Engines/ConPVP/DuelContext.cs b/Projects/UOContent/Engines/ConPVP/DuelContext.cs index 27f72d4a6..162d8fa4f 100644 --- a/Projects/UOContent/Engines/ConPVP/DuelContext.cs +++ b/Projects/UOContent/Engines/ConPVP/DuelContext.cs @@ -30,9 +30,10 @@ namespace Server.Engines.ConPVP private readonly List m_Walls = new(); - private Timer m_AutoTieTimer; - - private Timer m_Countdown; + private TimerExecutionToken _autoTieTimerToken; + private TimerExecutionToken _countdownTimerToken; + private TimerExecutionToken _SdWarnTimerToken; + private TimerExecutionToken _SdActivateTimerToken; public EventGame m_EventGame; private Map m_GateFacet; @@ -42,7 +43,6 @@ namespace Server.Engines.ConPVP public Arena m_OverrideArena; - private Timer m_SDWarnTimer, m_SDActivateTimer; public Tournament m_Tournament; private bool m_Yielding; @@ -109,7 +109,7 @@ namespace Server.Engines.ConPVP public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) { - Timer.DelayCall(ts, DelayBounce_Callback, mob, corpse); + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); } public static bool AllowSpecialMove(Mobile from, string name, SpecialMove move) => @@ -766,7 +766,7 @@ namespace Server.Engines.ConPVP return; } - EndAutoTie(); + _autoTieTimerToken.Cancel(); StopSDTimers(); Finished = true; @@ -844,7 +844,7 @@ namespace Server.Engines.ConPVP m_EventGame?.OnStop(); - Timer.DelayCall(TimeSpan.FromSeconds(9.0), UnregisterRematch); + Timer.StartTimer(TimeSpan.FromSeconds(9.0), UnregisterRematch); } public void Award(Mobile us, Mobile them, bool won) @@ -1057,23 +1057,20 @@ namespace Server.Engines.ConPVP public void StartCountdown(int count, CountdownCallback cb) { - cb(count); - m_Countdown = Timer.DelayCall( - TimeSpan.FromSeconds(1.0), + Timer.StartTimer( TimeSpan.FromSeconds(1.0), count, - () => Countdown_Callback(--count, cb) + () => Countdown_Callback(cb), + out _countdownTimerToken ); } - public void StopCountdown() - { - m_Countdown?.Stop(); - m_Countdown = null; - } + public void StopCountdown() => _countdownTimerToken.Cancel(); - private void Countdown_Callback(int count, CountdownCallback cb) + private void Countdown_Callback(CountdownCallback cb) { + var count = _countdownTimerToken.RemainingCount; + if (count == 0) { StopCountdown(); @@ -1084,24 +1081,17 @@ namespace Server.Engines.ConPVP public void StopSDTimers() { - m_SDWarnTimer?.Stop(); - - m_SDWarnTimer = null; - - m_SDActivateTimer?.Stop(); - - m_SDActivateTimer = null; + _SdWarnTimerToken.Cancel(); + _SdActivateTimerToken.Cancel(); } public void StartSuddenDeath(TimeSpan timeUntilActive) { - m_SDWarnTimer?.Stop(); + _SdWarnTimerToken.Cancel(); + Timer.StartTimer(TimeSpan.FromMinutes(timeUntilActive.TotalMinutes * 0.9), WarnSuddenDeath, out _SdWarnTimerToken); - m_SDWarnTimer = Timer.DelayCall(TimeSpan.FromMinutes(timeUntilActive.TotalMinutes * 0.9), WarnSuddenDeath); - - m_SDActivateTimer?.Stop(); - - m_SDActivateTimer = Timer.DelayCall(timeUntilActive, ActivateSuddenDeath); + _SdActivateTimerToken.Cancel(); + Timer.StartTimer(timeUntilActive, ActivateSuddenDeath, out _SdActivateTimerToken); } public void WarnSuddenDeath() @@ -1127,9 +1117,7 @@ namespace Server.Engines.ConPVP m_Tournament?.Alert(Arena, "Sudden death will be active soon!"); - m_SDWarnTimer?.Stop(); - - m_SDWarnTimer = null; + _SdWarnTimerToken.Cancel(); } public static bool CheckSuddenDeath(Mobile mob) => mob is PlayerMobile pm && pm.DuelPlayer?.Eliminated == false && @@ -1163,32 +1151,22 @@ namespace Server.Engines.ConPVP IsSuddenDeath = true; - m_SDActivateTimer?.Stop(); - - m_SDActivateTimer = null; + _SdActivateTimerToken.Cancel(); } public void BeginAutoTie() { - m_AutoTieTimer?.Stop(); - var ts = m_Tournament == null || m_Tournament.TourneyType == TourneyType.Standard ? AutoTieDelay : TimeSpan.FromMinutes(90.0); - m_AutoTieTimer = Timer.DelayCall(ts, InvokeAutoTie); - } - - public void EndAutoTie() - { - m_AutoTieTimer?.Stop(); - - m_AutoTieTimer = null; + _autoTieTimerToken.Cancel(); + Timer.StartTimer(ts, InvokeAutoTie, out _autoTieTimerToken); } public void InvokeAutoTie() { - m_AutoTieTimer = null; + _autoTieTimerToken.Cancel(); if (!Started || Finished) { @@ -1258,7 +1236,7 @@ namespace Server.Engines.ConPVP m_Tournament?.HandleTie(Arena, m_Match, remaining); - Timer.DelayCall(TimeSpan.FromSeconds(10.0), Unregister); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), Unregister); } public static void Initialize() @@ -1640,9 +1618,7 @@ namespace Server.Engines.ConPVP } else { - pm.DuelContext.m_Countdown?.Stop(); - pm.DuelContext.m_Countdown = null; - + pm.DuelContext.StopCountdown(); pm.DuelContext.StartedReadyCountdown = false; p.Broadcast(0x22, null, "{0} has yielded.", "You have yielded."); @@ -2777,7 +2753,7 @@ namespace Server.Engines.ConPVP TitleColor = 0x7800; TitleNumber = 1062051; // Gate Warning - Timer.DelayCall(TimeSpan.FromSeconds(10.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), Delete); } public ArenaMoongate(Serial serial) : base(serial) diff --git a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs index cd2814ee8..6d4b26976 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs @@ -71,7 +71,7 @@ namespace Server.Engines.ConPVP } } - Timer.DelayCall(Delete); // delete this after the world loads + Timer.StartTimer(Delete); // delete this after the world loads } public override void Serialize(IGenericWriter writer) @@ -243,7 +243,7 @@ namespace Server.Engines.ConPVP m_Path.Clear(); m_PathIdx = 0; - Timer.DelayCall(TimeSpan.FromSeconds(0.05), ContinueFlight); + Timer.StartTimer(TimeSpan.FromSeconds(0.05), ContinueFlight); } private bool CheckCatch(Mobile m, Point3D myLoc) @@ -640,7 +640,7 @@ namespace Server.Engines.ConPVP DoAnim(GetWorldLocation(), m_Path[m_PathIdx - 1], Map); } - Timer.DelayCall(TimeSpan.FromSeconds(0.1), ContinueFlight); + Timer.StartTimer(TimeSpan.FromSeconds(0.1), ContinueFlight); } else { @@ -864,7 +864,7 @@ namespace Server.Engines.ConPVP // has to be delayed in case some other target canceled us... if (m_Resend) { - Timer.DelayCall(ResendBombTarget); + Timer.StartTimer(ResendBombTarget); } } @@ -1613,24 +1613,13 @@ namespace Server.Engines.ConPVP { private BRBomb m_Bomb; - private Timer m_FinishTimer; + private TimerExecutionToken _finishTimerToken; public BRGame(BRController controller, DuelContext context) : base(context) => Controller = controller; public BRController Controller { get; } - public Map Facet - { - get - { - if (m_Context.Arena != null) - { - return m_Context.Arena.Facet; - } - - return Controller.Map; - } - } + public Map Facet => m_Context.Arena != null ? m_Context.Arena.Facet : Controller.Map; public override bool CantDoAnything(Mobile mob) => mob.Backpack?.FindItemByType() != null && GetTeamInfo(mob) != null; @@ -1641,7 +1630,7 @@ namespace Server.Engines.ConPVP { m_Bomb.Visible = false; m_Bomb.MoveToWorld(Controller.BombHome, Controller.Map); - Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15)), UnhideBomb); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15)), UnhideBomb); } } @@ -1724,7 +1713,7 @@ namespace Server.Engines.ConPVP public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) { - Timer.DelayCall(ts, DelayBounce_Callback, mob, corpse); + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); } private void DelayBounce_Callback(Mobile mob, Container corpse) @@ -1821,12 +1810,12 @@ namespace Server.Engines.ConPVP ); } - m_FinishTimer?.Stop(); + _finishTimerToken.Cancel(); m_Bomb = new BRBomb(this); ReturnBomb(); - m_FinishTimer = Timer.DelayCall(Controller.Duration, Finish_Callback); + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); } private void Finish_Callback() @@ -2047,8 +2036,7 @@ namespace Server.Engines.ConPVP ApplyHues(m_Context.Participants[i], -1); } - m_FinishTimer?.Stop(); - m_FinishTimer = null; + _finishTimerToken.Cancel(); } } } diff --git a/Projects/UOContent/Engines/ConPVP/Games/CTF.cs b/Projects/UOContent/Engines/ConPVP/Games/CTF.cs index 089e03690..0dfbbbbdd 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/CTF.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/CTF.cs @@ -252,7 +252,7 @@ namespace Server.Engines.ConPVP public Mobile m_Returner; public DateTime m_ReturnTime; - private Timer m_ReturnTimer; + private TimerExecutionToken _returnTimerToken; public CTFTeamInfo m_TeamInfo; [Constructible] @@ -370,18 +370,11 @@ namespace Server.Engines.ConPVP } } - private void StopCountdown() - { - m_ReturnTimer?.Stop(); - - m_ReturnTimer = null; - } - private void BeginCountdown(int returnCount) { - StopCountdown(); + _returnTimerToken.Cancel(); - m_ReturnTimer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Countdown_OnTick); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Countdown_OnTick, out _returnTimerToken); m_ReturnCount = returnCount; } @@ -518,7 +511,7 @@ namespace Server.Engines.ConPVP public void SendHome() { - StopCountdown(); + _returnTimerToken.Cancel(); if (m_TeamInfo == null) { @@ -897,7 +890,7 @@ namespace Server.Engines.ConPVP public sealed class CTFGame : EventGame { - private Timer m_FinishTimer; + private TimerExecutionToken _finishTimerToken; public CTFGame(CTFController controller, DuelContext context) : base(context) => Controller = controller; @@ -1004,7 +997,7 @@ namespace Server.Engines.ConPVP public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) { - Timer.DelayCall(ts, DelayBounce_Callback, mob, corpse); + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); } private void DelayBounce_Callback(Mobile mob, Container corpse) @@ -1135,9 +1128,8 @@ namespace Server.Engines.ConPVP ApplyHues(m_Context.Participants[i], Controller.TeamInfo[i % 8].Color); } - m_FinishTimer?.Stop(); - - m_FinishTimer = Timer.DelayCall(Controller.Duration, Finish_Callback); + _finishTimerToken.Cancel(); + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); } private void Finish_Callback() @@ -1364,9 +1356,7 @@ namespace Server.Engines.ConPVP ApplyHues(m_Context.Participants[i], -1); } - m_FinishTimer?.Stop(); - - m_FinishTimer = null; + _finishTimerToken.Cancel(); } } } diff --git a/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs b/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs index 0083e29d7..e248a8487 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs @@ -495,10 +495,9 @@ namespace Server.Engines.ConPVP private int m_CapStage; private bool m_Capturable = true; - private Timer m_CaptureTimer; - - private Timer m_FinishTimer; - private Timer m_UncaptureTimer; + private TimerExecutionToken _captureTimerToken; + private TimerExecutionToken _finishTimerToken; + private TimerExecutionToken _uncaptureTimerToken; public DDGame(DDController controller, DuelContext context) : base(context) => Controller = controller; @@ -587,7 +586,7 @@ namespace Server.Engines.ConPVP public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) { - Timer.DelayCall(ts, DelayBounce_Callback, mob, corpse); + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); } private void DelayBounce_Callback(Mobile mob, Container corpse) @@ -668,17 +667,8 @@ namespace Server.Engines.ConPVP { m_Capturable = true; - if (m_CaptureTimer != null) - { - m_CaptureTimer.Stop(); - m_CaptureTimer = null; - } - - if (m_UncaptureTimer != null) - { - m_UncaptureTimer.Stop(); - m_UncaptureTimer = null; - } + _captureTimerToken.Cancel(); + _uncaptureTimerToken.Cancel(); for (var i = 0; i < Controller.TeamInfo.Length; ++i) { @@ -706,8 +696,8 @@ namespace Server.Engines.ConPVP ); } - m_FinishTimer?.Stop(); - m_FinishTimer = Timer.DelayCall(Controller.Duration, Finish_Callback); + _finishTimerToken.Cancel(); + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); } private void Finish_Callback() @@ -933,25 +923,15 @@ namespace Server.Engines.ConPVP m_Capturable = false; - if (m_CaptureTimer != null) - { - m_CaptureTimer.Stop(); - m_CaptureTimer = null; - } - - if (m_UncaptureTimer != null) - { - m_UncaptureTimer.Stop(); - m_UncaptureTimer = null; - } + _captureTimerToken.Cancel(); + _uncaptureTimerToken.Cancel(); for (var i = 0; i < m_Context.Participants.Count; ++i) { ApplyHues(m_Context.Participants[i], -1); } - m_FinishTimer?.Stop(); - m_FinishTimer = null; + _finishTimerToken.Cancel(); } public void Dominate(DDWayPoint point, Mobile from, DDTeamInfo team) @@ -975,14 +955,13 @@ namespace Server.Engines.ConPVP Controller.PointA?.SetNonCaptureHue(); Controller.PointB?.SetNonCaptureHue(); - m_CaptureTimer?.Stop(); - m_CaptureTimer = null; + _captureTimerToken.Cancel(); } if (!wasDom && isDom) { m_CapStage = 0; - m_CaptureTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1.0), CaptureTick); + Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1.0), CaptureTick, out _captureTimerToken); } } @@ -993,8 +972,7 @@ namespace Server.Engines.ConPVP if (team == null) { m_Capturable = true; - m_CaptureTimer?.Stop(); - m_CaptureTimer = null; + _captureTimerToken.Cancel(); return; } @@ -1014,8 +992,7 @@ namespace Server.Engines.ConPVP m_Capturable = false; m_CapStage = 0; - m_CaptureTimer.Stop(); - m_CaptureTimer = null; + _captureTimerToken.Cancel(); if (Controller.PointA != null) { @@ -1029,8 +1006,7 @@ namespace Server.Engines.ConPVP Controller.PointB.SetUncapturableHue(); } - m_UncaptureTimer = Timer.DelayCall(TimeSpan.FromSeconds(30.0), UncaptureTick); - m_UncaptureTimer.Start(); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), UncaptureTick, out _uncaptureTimerToken); } } @@ -1038,17 +1014,8 @@ namespace Server.Engines.ConPVP { m_Capturable = true; - if (m_CaptureTimer != null) - { - m_CaptureTimer.Stop(); - m_CaptureTimer = null; - } - - if (m_UncaptureTimer != null) - { - m_UncaptureTimer.Stop(); - m_UncaptureTimer = null; - } + _captureTimerToken.Cancel(); + _uncaptureTimerToken.Cancel(); if (Controller.PointA != null) { diff --git a/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs b/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs index fbfbfe879..6d95d1630 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs @@ -25,8 +25,7 @@ namespace Server.Engines.ConPVP Name = "the hill"; } - public HillOfTheKing(Serial s) - : base(s) + public HillOfTheKing(Serial s) : base(s) { } @@ -49,18 +48,7 @@ namespace Server.Engines.ConPVP [CommandProperty(AccessLevel.GameMaster)] public int ScoreInterval { get; set; } - public int CapturesSoFar - { - get - { - if (m_KingTimer != null) - { - return m_KingTimer.Captures; - } - - return 0; - } - } + public int CapturesSoFar => m_KingTimer?.Captures ?? 0; public override void Deserialize(IGenericReader reader) { @@ -864,7 +852,7 @@ namespace Server.Engines.ConPVP public sealed class KHGame : EventGame { - private Timer m_FinishTimer; + private TimerExecutionToken _finishTimerToken; public KHGame(KHController controller, DuelContext context) : base(context) => Controller = controller; @@ -969,7 +957,7 @@ namespace Server.Engines.ConPVP public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) { - Timer.DelayCall(ts, DelayBounce_Callback, mob, corpse); + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); } private void DelayBounce_Callback(Mobile mob, Container corpse) @@ -994,7 +982,7 @@ namespace Server.Engines.ConPVP if (corpse?.Deleted == false) { - Timer.DelayCall(TimeSpan.FromSeconds(30), corpse.Delete); + Timer.StartTimer(TimeSpan.FromSeconds(30), corpse.Delete); } } @@ -1067,8 +1055,6 @@ namespace Server.Engines.ConPVP ); } - m_FinishTimer?.Stop(); - for (var i = 0; i < Controller.Hills.Length; i++) { if (Controller.Hills[i] != null) @@ -1085,7 +1071,8 @@ namespace Server.Engines.ConPVP } } - m_FinishTimer = Timer.DelayCall(Controller.Duration, Finish_Callback); + _finishTimerToken.Cancel(); + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); } private void Finish_Callback() @@ -1310,8 +1297,7 @@ namespace Server.Engines.ConPVP ApplyHues(m_Context.Participants[i], -1); } - m_FinishTimer?.Stop(); - m_FinishTimer = null; + _finishTimerToken.Cancel(); } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs index 89ecc6415..d05be1836 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs @@ -244,7 +244,7 @@ namespace Server.Engines.ConPVP y -= 3; AddButton(314, y, 247, 248, 1); - Timer.DelayCall(TimeSpan.FromSeconds(15.0), AutoReject); + Timer.StartTimer(TimeSpan.FromSeconds(15.0), AutoReject); } public string Center(string text) => $"
{text}
"; diff --git a/Projects/UOContent/Engines/ConPVP/Tournament.cs b/Projects/UOContent/Engines/ConPVP/Tournament.cs index 5d32b5a0a..aa1571140 100644 --- a/Projects/UOContent/Engines/ConPVP/Tournament.cs +++ b/Projects/UOContent/Engines/ConPVP/Tournament.cs @@ -107,7 +107,7 @@ namespace Server.Engines.ConPVP } } - Timer.DelayCall(SliceInterval, SliceInterval, Slice); + Timer.StartTimer(SliceInterval, SliceInterval, Slice); } public Tournament() @@ -122,7 +122,7 @@ namespace Server.Engines.ConPVP Arenas = new List(); SignupPeriod = TimeSpan.FromMinutes(10.0); - Timer.DelayCall(SliceInterval, SliceInterval, Slice); + Timer.StartTimer(SliceInterval, SliceInterval, Slice); } public bool IsNotoRestricted => TourneyType != TourneyType.Standard; @@ -992,18 +992,19 @@ namespace Server.Engines.ConPVP public void Alert(Arena arena, params string[] alerts) { - if (arena?.Announcer != null) + if (arena?.Announcer == null) { - for (var j = 0; j < alerts.Length; ++j) - { - Timer.DelayCall( - TimeSpan.FromSeconds(Math.Max(j - 0.5, 0.0)), - (announcer, alert) => announcer.PublicOverheadMessage(MessageType.Regular, 0x35, false, alert), - arena.Announcer, - alerts[j] - ); - } + return; } + + var count = 0; + + Timer.StartTimer(TimeSpan.FromSeconds(0.5), alerts.Length, + () => + { + arena.Announcer.PublicOverheadMessage(MessageType.Regular, 0x35, false, alerts[count++]); + } + ); } } } diff --git a/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs b/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs index 8cc0d1c05..c2e20b6dd 100644 --- a/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs +++ b/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs @@ -10,7 +10,7 @@ namespace Server.Engines.ConPVP [Constructible] public TournamentRegistrar() { - Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); } public TournamentRegistrar(Serial serial) : base(serial) @@ -70,7 +70,7 @@ namespace Server.Engines.ConPVP m.NetState ); m.BeginAction(this); - Timer.DelayCall(TimeSpan.FromSeconds(10.0), ReleaseLock_Callback, m); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), () => ReleaseLock_Callback(m)); } } @@ -103,7 +103,7 @@ namespace Server.Engines.ConPVP } } - Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); } } } diff --git a/Projects/UOContent/Engines/Craft/Core/Repair.cs b/Projects/UOContent/Engines/Craft/Core/Repair.cs index ba392e9ea..a967556f2 100644 --- a/Projects/UOContent/Engines/Craft/Core/Repair.cs +++ b/Projects/UOContent/Engines/Craft/Core/Repair.cs @@ -262,7 +262,7 @@ namespace Server.Engines.Craft toDelete = true; from.BeginAction(); - Timer.DelayCall(TimeSpan.FromSeconds(12.0), from.EndAction); + Timer.StartTimer(TimeSpan.FromSeconds(12.0), from.EndAction); } else { diff --git a/Projects/UOContent/Engines/Doom/GauntletSpawner.cs b/Projects/UOContent/Engines/Doom/GauntletSpawner.cs index 5ccec19dd..705a9ca67 100644 --- a/Projects/UOContent/Engines/Doom/GauntletSpawner.cs +++ b/Projects/UOContent/Engines/Doom/GauntletSpawner.cs @@ -22,7 +22,7 @@ namespace Server.Engines.Doom private GauntletSpawnerState m_State; - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public GauntletSpawner(string typeName = null) : base(0x36FE) @@ -136,7 +136,7 @@ namespace Server.Engines.Doom CreateRegion(); FullSpawn(); - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Slice); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Slice, out _timerToken); } else { @@ -176,8 +176,7 @@ namespace Server.Engines.Doom ClearTraps(); DestroyRegion(); - m_Timer?.Stop(); - m_Timer = null; + _timerToken.Cancel(); } public override void OnDelete() diff --git a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs index 32745c925..5ca64e0cb 100644 --- a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs +++ b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs @@ -96,7 +96,7 @@ namespace Server.Engines.Doom private static readonly int[] ms2 = { 0x13F, 0x14B }; private static readonly int[] cs1 = { 0x244 }; private static readonly int[] exp = { 0x307 }; - private Timer l_Timer; + private TimerExecutionToken _resetTimerToken; private LampRoomBox m_Box; private Region m_LampRoom; @@ -331,15 +331,8 @@ namespace Server.Engines.Doom public virtual void KillTimers() { - if (l_Timer?.Running == true) - { - l_Timer.Stop(); - } - - if (m_Timer?.Running == true) - { - m_Timer.Stop(); - } + _resetTimerToken.Cancel(); + m_Timer?.Stop(); } public virtual void RemoveSuccessful() @@ -357,7 +350,7 @@ namespace Server.Engines.Doom if ((TheirKey = (ushort)(code | (TheirKey <<= 4))) < 0x0FFF) { - l_Timer = Timer.DelayCall(TimeSpan.FromSeconds(30.0), ResetPuzzle); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), ResetPuzzle, out _resetTimerToken); return; } diff --git a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs index c10c70888..b6545d5b9 100644 --- a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs +++ b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs @@ -38,7 +38,7 @@ namespace Server.Engines.Doom m_Wanderer = new WandererOfTheVoid(); m_Wanderer.MoveToWorld(LeverPuzzleController.lr_Enter, Map.Malas); m_Wanderer.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1060002); // I am the guardian of... - Timer.DelayCall(TimeSpan.FromSeconds(5.0), CallBackMessage); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), CallBackMessage); } } diff --git a/Projects/UOContent/Engines/Ethics/Core/Ethic.cs b/Projects/UOContent/Engines/Ethics/Core/Ethic.cs index 01f2cd8fb..e14ea51bf 100644 --- a/Projects/UOContent/Engines/Ethics/Core/Ethic.cs +++ b/Projects/UOContent/Engines/Ethics/Core/Ethic.cs @@ -268,7 +268,7 @@ namespace Server.Ethics if (pl.Mobile != null) { - Timer.DelayCall(pl.CheckAttach); + Timer.StartTimer(pl.CheckAttach); } } diff --git a/Projects/UOContent/Engines/Factions/Core/Election.cs b/Projects/UOContent/Engines/Factions/Core/Election.cs index 12927be3a..977e3d5df 100644 --- a/Projects/UOContent/Engines/Factions/Core/Election.cs +++ b/Projects/UOContent/Engines/Factions/Core/Election.cs @@ -13,7 +13,7 @@ namespace Server.Factions public static readonly TimeSpan CampaignPeriod = TimeSpan.FromDays(1.0); public static readonly TimeSpan VotingPeriod = TimeSpan.FromDays(3.0); - private Timer m_Timer; + private TimerExecutionToken _timerToken; public Election(Faction faction) { @@ -109,7 +109,7 @@ namespace Server.Factions public void StartTimer() { - m_Timer = Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Slice); + Timer.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Slice, out _timerToken); } public void Serialize(IGenericWriter writer) @@ -274,9 +274,7 @@ namespace Server.Factions { if (Faction.Election != this) { - m_Timer?.Stop(); - m_Timer = null; - + _timerToken.Cancel(); return; } diff --git a/Projects/UOContent/Engines/Factions/Core/Faction.cs b/Projects/UOContent/Engines/Factions/Core/Faction.cs index c3e331c4b..dce3f15db 100644 --- a/Projects/UOContent/Engines/Factions/Core/Faction.cs +++ b/Projects/UOContent/Engines/Factions/Core/Faction.cs @@ -621,9 +621,9 @@ namespace Server.Factions EventSink.Login += EventSink_Login; EventSink.Logout += EventSink_Logout; - Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(10.0), HandleAtrophy); + Timer.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(10.0), HandleAtrophy); - Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), ProcessTick); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), ProcessTick); CommandSystem.Register("FactionElection", AccessLevel.GameMaster, FactionElection_OnCommand); CommandSystem.Register("FactionCommander", AccessLevel.Administrator, FactionCommander_OnCommand); @@ -1332,7 +1332,7 @@ namespace Server.Factions } } - context.m_Timer = Timer.DelayCall(SkillLossPeriod, ClearSkillLoss_Event, mob); + Timer.StartTimer(SkillLossPeriod, () => ClearSkillLoss_Event(mob), out context._timerToken); } private static void ClearSkillLoss_Event(Mobile mob) => ClearSkillLoss(mob); @@ -1353,7 +1353,7 @@ namespace Server.Factions mob.RemoveSkillMod(mods[i]); } - context.m_Timer.Stop(); + context._timerToken.Cancel(); return true; } @@ -1373,7 +1373,7 @@ namespace Server.Factions private class SkillLossContext { public List m_Mods; - public Timer m_Timer; + public TimerExecutionToken _timerToken; } } diff --git a/Projects/UOContent/Engines/Factions/Core/FactionState.cs b/Projects/UOContent/Engines/Factions/Core/FactionState.cs index 88d892b42..d7b588b0b 100644 --- a/Projects/UOContent/Engines/Factions/Core/FactionState.cs +++ b/Projects/UOContent/Engines/Factions/Core/FactionState.cs @@ -124,7 +124,7 @@ namespace Server.Factions { var factionItem = new FactionItem(reader, m_Faction); - Timer.DelayCall(factionItem.CheckAttach); // sandbox attachment + Timer.StartTimer(factionItem.CheckAttach); // sandbox attachment } } diff --git a/Projects/UOContent/Engines/Factions/Core/Keywords.cs b/Projects/UOContent/Engines/Factions/Core/Keywords.cs index c73ed31a3..a4f533955 100644 --- a/Projects/UOContent/Engines/Factions/Core/Keywords.cs +++ b/Projects/UOContent/Engines/Factions/Core/Keywords.cs @@ -176,7 +176,7 @@ namespace Server.Factions if (pl != null) { - Timer.DelayCall(ShowScore_Sandbox, pl); + Timer.StartTimer(() => ShowScore_Sandbox(pl)); } break; diff --git a/Projects/UOContent/Engines/Factions/Core/Town.cs b/Projects/UOContent/Engines/Factions/Core/Town.cs index c4a86e7e7..e62aaf451 100644 --- a/Projects/UOContent/Engines/Factions/Core/Town.cs +++ b/Projects/UOContent/Engines/Factions/Core/Town.cs @@ -12,7 +12,7 @@ namespace Server.Factions public static readonly TimeSpan TaxChangePeriod = TimeSpan.FromHours(12.0); public static readonly TimeSpan IncomePeriod = TimeSpan.FromDays(1.0); - private Timer m_IncomeTimer; + private Timer _incomeTimer; private TownState m_State; public Town() @@ -222,16 +222,12 @@ namespace Server.Factions public void StartIncomeTimer() { - m_IncomeTimer?.Stop(); - - m_IncomeTimer = Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckIncome); + _incomeTimer ??= Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckIncome); } - public void StopIncomeTimer() + public void Delete() { - m_IncomeTimer?.Stop(); - - m_IncomeTimer = null; + _incomeTimer?.Stop(); } public void CheckIncome() diff --git a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/ClarityPotion.cs b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/ClarityPotion.cs index 217f7e7a3..9429c20a1 100644 --- a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/ClarityPotion.cs +++ b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/ClarityPotion.cs @@ -43,7 +43,7 @@ namespace Server from.PlaySound(0x1EE); from.AddStatMod(new StatMod(StatType.Int, "clarity-potion", amount, TimeSpan.FromMinutes(time))); - Timer.DelayCall(TimeSpan.FromMinutes(time), from.EndAction); + Timer.StartTimer(TimeSpan.FromMinutes(time), from.EndAction); return true; } diff --git a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/PowerFactionItem.cs b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/PowerFactionItem.cs index 20e20e3fd..8262e07c2 100644 --- a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/PowerFactionItem.cs +++ b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/PowerFactionItem.cs @@ -109,7 +109,7 @@ namespace Server "The object vanishes from your hands as you touch it." ); - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(1.0), () => from.LocalOverheadMessage( MessageType.Regular, @@ -119,7 +119,7 @@ namespace Server ) ); - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(4.0), () => { from.LocalOverheadMessage(MessageType.Regular, 2118, false, "Your skin begins to burn."); } ); diff --git a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs index 93c0e6f20..fef72f966 100644 --- a/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs +++ b/Projects/UOContent/Engines/Factions/Items/Power Faction Items/StormsEye.cs @@ -60,7 +60,7 @@ namespace Server Hue - 1 ); - Timer.DelayCall(TimeSpan.FromSeconds(0.5), OnDelay, from, stormsEye, origin, facet); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), () => OnDelay(from, stormsEye, origin, facet)); }, this ); @@ -85,7 +85,7 @@ namespace Server 2 ); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), OnHit, from, origin, facet); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => OnHit(from, origin, facet)); } private static void OnHit(Mobile from, Point3D origin, Map facet) @@ -165,7 +165,7 @@ namespace Server 100 ); - Timer.DelayCall(TimeSpan.FromSeconds(0.50), mob.PlaySound, 0x1FB); + Timer.StartTimer(TimeSpan.FromSeconds(0.50), () => mob.PlaySound(0x1FB)); } } diff --git a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs index 7a2cf78d4..80dee9efc 100644 --- a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs +++ b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs @@ -15,7 +15,7 @@ namespace Server.Factions public abstract class BaseFactionTrap : BaseTrap { - private Timer m_Concealing; + private TimerExecutionToken _concealingTimerToken; public BaseFactionTrap(Faction f, Mobile m, int itemID) : base(itemID) { @@ -52,18 +52,7 @@ namespace Server.Factions public virtual TimeSpan ConcealPeriod => TimeSpan.FromMinutes(1.0); - public virtual TimeSpan DecayPeriod - { - get - { - if (Core.AOS) - { - return TimeSpan.FromDays(1.0); - } - - return TimeSpan.MaxValue; // no decay - } - } + public virtual TimeSpan DecayPeriod => Core.AOS ? TimeSpan.FromDays(1.0) : TimeSpan.MaxValue; public override void OnTrigger(Mobile from) { @@ -206,7 +195,7 @@ namespace Server.Factions if (TimeOfPlacement + decayPeriod < Core.Now) { - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); return true; } @@ -215,16 +204,13 @@ namespace Server.Factions public virtual void BeginConceal() { - m_Concealing?.Stop(); - - m_Concealing = Timer.DelayCall(ConcealPeriod, Conceal); + _concealingTimerToken.Cancel(); + Timer.StartTimer(ConcealPeriod, Conceal, out _concealingTimerToken); } public virtual void Conceal() { - m_Concealing?.Stop(); - - m_Concealing = null; + _concealingTimerToken.Cancel(); if (!Deleted) { diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs index 6344a7f95..aa7716781 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs @@ -468,7 +468,7 @@ namespace Server.Factions m_Town = Town.ReadReference(reader); Orders = new Orders(this, reader); - Timer.DelayCall(Register); + Timer.StartTimer(Register); } } diff --git a/Projects/UOContent/Engines/Harvest/Fishing.cs b/Projects/UOContent/Engines/Harvest/Fishing.cs index 12deb2fcd..d9bf10cc2 100644 --- a/Projects/UOContent/Engines/Harvest/Fishing.cs +++ b/Projects/UOContent/Engines/Harvest/Fishing.cs @@ -487,7 +487,7 @@ namespace Server.Engines.Harvest if (GetHarvestDetails(from, tool, toHarvest, out _, out var map, out var loc)) { - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(1.5), () => { diff --git a/Projects/UOContent/Engines/Khaldun/RaisableItem.cs b/Projects/UOContent/Engines/Khaldun/RaisableItem.cs index 542e9bb3b..c27326b28 100644 --- a/Projects/UOContent/Engines/Khaldun/RaisableItem.cs +++ b/Projects/UOContent/Engines/Khaldun/RaisableItem.cs @@ -153,8 +153,7 @@ namespace Server.Items var delay = m_CloseTime - Core.Now; - static void start(Timer timer) => timer.Start(); - DelayCall(delay, start, this); + StartTimer(delay, () => Start()); return; } diff --git a/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs b/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs index b539aa53b..12642adcc 100644 --- a/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs +++ b/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs @@ -224,7 +224,7 @@ namespace Server.Items var version = reader.ReadEncodedInt(); - Timer.DelayCall(Refresh); + Timer.StartTimer(Refresh); } } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs index 7cdd95d4e..4bd855321 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs @@ -104,7 +104,7 @@ namespace Server.Engines.MLQuests.Gumps { if (m_Pending.TryGetValue(ns, out var state)) { - state.m_Timeout.Stop(); + state._timeoutToken.Cancel(); m_Pending.Remove(ns); } @@ -273,13 +273,13 @@ namespace Server.Engines.MLQuests.Gumps public readonly IRaceChanger m_Owner; public readonly Race m_TargetRace; - public readonly Timer m_Timeout; + public TimerExecutionToken _timeoutToken; public RaceChangeState(IRaceChanger owner, NetState ns, Race targetRace) { m_Owner = owner; m_TargetRace = targetRace; - m_Timeout = Timer.DelayCall(m_TimeoutDelay, Timeout, ns); + Timer.StartTimer(m_TimeoutDelay, () => Timeout(ns), out _timeoutToken); } } } diff --git a/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs b/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs index 87e50b87e..49b63857b 100644 --- a/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs +++ b/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs @@ -20,7 +20,7 @@ namespace Server.Engines.MLQuests private MLQuestInstanceFlags m_Flags; private IQuestGiver m_Quester; - private Timer m_Timer; + private TimerExecutionToken _timerToken; public MLQuestInstance(MLQuest quest, IQuestGiver quester, PlayerMobile player) { @@ -52,7 +52,7 @@ namespace Server.Engines.MLQuests if (timed) { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), Slice); + Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), Slice, out _timerToken); } } @@ -476,13 +476,7 @@ namespace Server.Engines.MLQuests private void StopTimer() { - if (m_Timer == null) - { - return; - } - - m_Timer.Stop(); - m_Timer = null; + _timerToken.Cancel(); } public void OnQuesterDeleted() diff --git a/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs b/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs index 872bbdf2a..87f69716a 100644 --- a/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs +++ b/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs @@ -76,7 +76,7 @@ namespace Server.Engines.MLQuests.Mobiles if (from.CanBeginAction(this)) { from.BeginAction(this); - Timer.DelayCall(m_ShoutCooldown, EndLock, from); + Timer.StartTimer(m_ShoutCooldown, () => EndLock(from)); } MLQuestSystem.TurnToFace(this, from); diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs index f1a0f9aa9..489182da0 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs @@ -98,14 +98,14 @@ namespace Server.Engines.MLQuests.Objectives private readonly BaseCreature m_Escort; private readonly EscortObjective m_Objective; private DateTime m_LastSeenEscorter; - private Timer m_Timer; + private TimerExecutionToken _timerToken; public EscortObjectiveInstance(EscortObjective objective, MLQuestInstance instance) : base(instance, objective) { m_Objective = objective; HasCompleted = false; - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), CheckDestination); + Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), CheckDestination, out _timerToken); m_LastSeenEscorter = Core.Now; m_Escort = instance.Quester as BaseCreature; @@ -183,11 +183,7 @@ namespace Server.Engines.MLQuests.Objectives private void StopTimer() { - if (m_Timer != null) - { - m_Timer.Stop(); - m_Timer = null; - } + _timerToken.Cancel(); } public static void BeginFollow(BaseCreature quester, PlayerMobile pm) diff --git a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs index d1ff86b0d..97774aaa1 100644 --- a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs +++ b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs @@ -8,7 +8,7 @@ namespace Server.Engines.Quests { private int m_Charges; - private Timer m_PlayTimer; + private TimerExecutionToken _timerToken; [Constructible] public HornOfRetreat() : base(0xFC4) @@ -62,7 +62,7 @@ namespace Server.Engines.Quests { from.SendLocalizedMessage(1076154); // You can only use this in Trammel and Malas. } - else if (m_PlayTimer != null) + else if (_timerToken.Running) { SendLocalizedMessageTo(from, 1042144); // This is currently in use. } @@ -74,7 +74,7 @@ namespace Server.Engines.Quests --Charges; - m_PlayTimer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), PlayTimer_Callback, from); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => PlayTimer_Callback(from), out _timerToken); } else { @@ -89,7 +89,7 @@ namespace Server.Engines.Quests public virtual void PlayTimer_Callback(Mobile from) { - m_PlayTimer = null; + _timerToken.Cancel(); var gate = new HornOfRetreatMoongate(DestLoc, DestMap, from, Hue); @@ -146,7 +146,7 @@ namespace Server.Engines.Quests Dispellable = false; - Timer.DelayCall(TimeSpan.FromSeconds(10.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), Delete); } public HornOfRetreatMoongate(Serial serial) : base(serial) diff --git a/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs b/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs index d3c6e7d0e..3ea47a4c3 100644 --- a/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs +++ b/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs @@ -38,7 +38,7 @@ namespace Server.Engines.Quests typeof(TerribleHatchlingsQuest) }; - private Timer m_Timer; + private TimerExecutionToken _timerToken; public QuestSystem(PlayerMobile from) { @@ -69,19 +69,18 @@ namespace Server.Engines.Quests public virtual void StartTimer() { - if (m_Timer != null) + if (_timerToken.Running) { return; } - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), Slice); + // TODO: Find out if this can go on forever. We should not allow timers to leak if this is the case. + Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), Slice, out _timerToken); } public virtual void StopTimer() { - m_Timer?.Stop(); - - m_Timer = null; + _timerToken.Cancel(); } public virtual void Slice() @@ -344,36 +343,38 @@ namespace Server.Engines.Quests { StopTimer(); - if (From.Quest == this) + if (From.Quest != this) { - From.Quest = null; + return; + } - var restartDelay = RestartDelay; + From.Quest = null; - if (completed && restartDelay > TimeSpan.Zero || !completed && restartDelay == TimeSpan.MaxValue) + var restartDelay = RestartDelay; + + if (completed && restartDelay > TimeSpan.Zero || !completed && restartDelay == TimeSpan.MaxValue) + { + From.DoneQuests ??= new List(); + + var found = false; + + var ourQuestType = GetType(); + + for (var i = 0; i < From.DoneQuests.Count; ++i) { - From.DoneQuests ??= new List(); + var restartInfo = From.DoneQuests[i]; - var found = false; - - var ourQuestType = GetType(); - - for (var i = 0; i < From.DoneQuests.Count; ++i) + if (restartInfo.QuestType == ourQuestType) { - var restartInfo = From.DoneQuests[i]; - - if (restartInfo.QuestType == ourQuestType) - { - restartInfo.Reset(restartDelay); - found = true; - break; - } + restartInfo.Reset(restartDelay); + found = true; + break; } + } - if (!found) - { - From.DoneQuests.Add(new QuestRestartInfo(ourQuestType, restartDelay)); - } + if (!found) + { + From.DoneQuests.Add(new QuestRestartInfo(ourQuestType, restartDelay)); } } } diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs index b23921f0a..cfa1cabea 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs @@ -44,7 +44,7 @@ namespace Server.Engines.Quests.Necro Maabus = new Maabus { Location = SpawnLocation, Map = Map }; Maabus.Direction = Maabus.GetDirectionTo(caller); - Timer.DelayCall(TimeSpan.FromSeconds(7.5), BeginSleep); + Timer.StartTimer(TimeSpan.FromSeconds(7.5), BeginSleep); } public void BeginSleep() @@ -56,7 +56,7 @@ namespace Server.Engines.Quests.Necro Effects.PlaySound(Maabus.Location, Maabus.Map, 0x48E); - Timer.DelayCall(TimeSpan.FromSeconds(2.5), Sleep); + Timer.StartTimer(TimeSpan.FromSeconds(2.5), Sleep); } public void Sleep() diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs index 78ef2f36f..a4f020cb7 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs @@ -91,7 +91,7 @@ namespace Server.Engines.Quests.Necro m_ToDelete = true; - Timer.DelayCall(TimeSpan.FromSeconds(5.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), Delete); } else if (m_Necromancer.Map != Map || GetDistanceToSqrt(m_Necromancer) > RangePerception + 1) { @@ -239,7 +239,7 @@ namespace Server.Engines.Quests.Necro Hue = 0x482; Light = LightType.Circle300; - Timer.DelayCall(TimeSpan.FromSeconds(10.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), Delete); } public SummonedPaladinMoongate(Serial serial) : base(serial) diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs index 14ac82e76..eed0b50a5 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs @@ -82,7 +82,7 @@ namespace Server.Engines.Quests.Samurai } } - Timer.DelayCall(TimeSpan.FromMinutes(2.0), GenerateTreasure); + Timer.StartTimer(TimeSpan.FromMinutes(2.0), GenerateTreasure); } public override void Serialize(IGenericWriter writer) @@ -98,7 +98,7 @@ namespace Server.Engines.Quests.Samurai var version = reader.ReadEncodedInt(); - Timer.DelayCall(GenerateTreasure); + Timer.StartTimer(GenerateTreasure); } } } diff --git a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs index 2fb58578b..db9219214 100644 --- a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs @@ -39,16 +39,14 @@ namespace Server.Engines.Quests.Naturalist if (m_CurrentNest.Special) { - from.SendLocalizedMessage( - 1054057 - ); // You complete your examination of this bizarre Egg Nest. The Naturalist will undoubtedly be quite interested in these notes! + // You complete your examination of this bizarre Egg Nest. The Naturalist will undoubtedly be quite interested in these notes! + from.SendLocalizedMessage(1054057); StudiedSpecialNest = true; } else { - from.SendLocalizedMessage( - 1054054 - ); // You have completed your study of this Solen Egg Nest. You put your notes away. + // You have completed your study of this Solen Egg Nest. You put your notes away. + from.SendLocalizedMessage(1054054); CurProgress++; } } @@ -56,9 +54,8 @@ namespace Server.Engines.Quests.Naturalist { if (!nest.Special) { - from.SendLocalizedMessage( - 1054058 - ); // You begin recording your completed notes on a bit of parchment. + // You begin recording your completed notes on a bit of parchment. + from.SendLocalizedMessage(1054058); } m_StudyState = StudyState.SecondStep; @@ -69,9 +66,8 @@ namespace Server.Engines.Quests.Naturalist { if (m_StudyState != StudyState.Inactive) { - from.SendLocalizedMessage( - 1054046 - ); // You abandon your study of the Solen Egg Nest without gathering the needed information. + // You abandon your study of the Solen Egg Nest without gathering the needed information. + from.SendLocalizedMessage(1054046); } m_CurrentNest = null; @@ -90,9 +86,8 @@ namespace Server.Engines.Quests.Naturalist { m_StudyState = StudyState.Inactive; - from.SendLocalizedMessage( - 1054047 - ); // You glance at the Egg Nest, realizing you've already studied this one. + // You glance at the Egg Nest, realizing you've already studied this one. + from.SendLocalizedMessage(1054047); } else { @@ -100,15 +95,13 @@ namespace Server.Engines.Quests.Naturalist if (nest.Special) { - from.SendLocalizedMessage( - 1054056 - ); // You notice something very odd about this Solen Egg Nest. You begin taking notes. + // You notice something very odd about this Solen Egg Nest. You begin taking notes. + from.SendLocalizedMessage(105405); } else { - from.SendLocalizedMessage( - 1054045 - ); // You begin studying the Solen Egg Nest to gather information. + // You begin studying the Solen Egg Nest to gather information. + from.SendLocalizedMessage(1054045); } if (from.Female) diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Items/BellOfTheDead.cs b/Projects/UOContent/Engines/Quests/The Summoning/Items/BellOfTheDead.cs index d0b27d40e..c2f638961 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Items/BellOfTheDead.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Items/BellOfTheDead.cs @@ -61,7 +61,7 @@ namespace Server.Engines.Quests.Doom Effects.PlaySound(GetWorldLocation(), Map, 0x100); - Timer.DelayCall(TimeSpan.FromSeconds(8.0), EndSummon, from); + Timer.StartTimer(TimeSpan.FromSeconds(8.0), () => EndSummon(from)); } } diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs b/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs index 24a63f417..de59ed9f0 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs @@ -56,7 +56,7 @@ namespace Server.Engines.Quests.Doom return; } - Timer.DelayCall(TimeSpan.FromSeconds(4.0), EndGiveWarning); + Timer.StartTimer(TimeSpan.FromSeconds(4.0), EndGiveWarning); } public virtual void EndGiveWarning() @@ -84,12 +84,12 @@ namespace Server.Engines.Quests.Doom return; } - Timer.DelayCall(TimeSpan.FromSeconds(30.0), EndSummonDragon); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), EndSummonDragon); } public virtual void BeginRemove(TimeSpan delay) { - Timer.DelayCall(delay, EndRemove); + Timer.StartTimer(delay, EndRemove); } public virtual void EndRemove() diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs index 6be2a30d1..819439ced 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs @@ -94,7 +94,7 @@ namespace Server.Engines.Quests.Hag { PublicOverheadMessage(MessageType.Regular, 0x3B2, 500849); // *hic* - Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(60, 180)), Heave); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(60, 180)), Heave); } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs index f076ac917..59858d6fe 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs @@ -58,7 +58,7 @@ namespace Server.Engines.Quests.Hag // * You see a strange imp stealing a scrap of paper from the bloodied corpse * Corpse.SendLocalizedMessageTo(player, 1055049); - Timer.DelayCall(TimeSpan.FromSeconds(3.0), DeleteImp, imp); + Timer.StartTimer(TimeSpan.FromSeconds(3.0), () => DeleteImp(imp)); } private void DeleteImp(Mobile m) @@ -221,7 +221,7 @@ namespace Server.Engines.Quests.Hag imp.Direction = imp.GetDirectionTo(from); - Timer.DelayCall(TimeSpan.FromSeconds(3.0), DeleteImp, imp); + Timer.StartTimer(TimeSpan.FromSeconds(3.0), () => DeleteImp(imp)); } private void DeleteImp(object imp) diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs index f05fd9666..9a5326f54 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -247,7 +247,7 @@ namespace Server.Mobiles if (m_SculptedBy == null || Map == Map.Internal) // Remove preview statues { - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs index 9209c47dd..1d9414d9e 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs @@ -101,7 +101,7 @@ namespace Server.Items if (m_Statue?.SculptedBy == null || Map == Map.Internal) { - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); } } diff --git a/Projects/UOContent/Engines/Virtues/Honor.cs b/Projects/UOContent/Engines/Virtues/Honor.cs index c22b46493..77b0040c7 100644 --- a/Projects/UOContent/Engines/Virtues/Honor.cs +++ b/Projects/UOContent/Engines/Virtues/Honor.cs @@ -88,7 +88,7 @@ namespace Server pm.HonorActive = true; pm.SendLocalizedMessage(1063235); // You embrace your honor - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(duration), () => { @@ -227,7 +227,7 @@ namespace Server m_Timer.Start(); source.m_hontime = Core.Now + TimeSpan.FromMinutes(40); - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromMinutes(40), () => { diff --git a/Projects/UOContent/Engines/Virtues/Justice.cs b/Projects/UOContent/Engines/Virtues/Justice.cs index 1b5c7f7a8..7d6b6a900 100644 --- a/Projects/UOContent/Engines/Virtues/Justice.cs +++ b/Projects/UOContent/Engines/Virtues/Justice.cs @@ -171,7 +171,7 @@ namespace Server if (protector.BeginAction()) { - Timer.DelayCall(TimeSpan.FromMinutes(15.0), protector.EndAction); + Timer.StartTimer(TimeSpan.FromMinutes(15.0), protector.EndAction); } } diff --git a/Projects/UOContent/Engines/Virtues/Sacrifice.cs b/Projects/UOContent/Engines/Virtues/Sacrifice.cs index 146fcdf83..9ce3c9be8 100644 --- a/Projects/UOContent/Engines/Virtues/Sacrifice.cs +++ b/Projects/UOContent/Engines/Virtues/Sacrifice.cs @@ -156,7 +156,7 @@ namespace Server from.SendLocalizedMessage(1052010); // You have set the creature free. - Timer.DelayCall(TimeSpan.FromSeconds(1.0), targ.Delete); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), targ.Delete); pm.LastSacrificeGain = Core.Now; diff --git a/Projects/UOContent/Gumps/ReportMurderer.cs b/Projects/UOContent/Gumps/ReportMurderer.cs index 35fb28781..1a96ca70f 100644 --- a/Projects/UOContent/Gumps/ReportMurderer.cs +++ b/Projects/UOContent/Gumps/ReportMurderer.cs @@ -146,7 +146,7 @@ namespace Server.Gumps if (Core.SE) { from.RecentlyReported.Add(killer); - Timer.DelayCall(TimeSpan.FromMinutes(10), ReportedListExpiry_Callback, from, killer); + Timer.StartTimer(TimeSpan.FromMinutes(10), () => ReportedListExpiry_Callback(from, killer)); } if (killer is PlayerMobile pk) diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs b/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs index 4ea5d3753..2657e6876 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Events.Halloween; using Server.Items; using Server.Mobiles; @@ -64,7 +65,7 @@ namespace Server.Engines.Events { target.SolidHueOverride = Utility.RandomMinMax(2501, 2644); - Timer.DelayCall(TimeSpan.FromSeconds(10), RemoveHueMod, target); + Timer.StartTimer(TimeSpan.FromSeconds(10), () => RemoveHueMod(target)); } } @@ -113,7 +114,7 @@ namespace Server.Engines.Events twin.MoveToWorld(m_From.Map.CanSpawnMobile(point) ? point : m_From.Location, m_From.Map); - Timer.DelayCall(TimeSpan.FromSeconds(5), DeleteTwin, twin); + Timer.StartTimer(TimeSpan.FromSeconds(5), () => DeleteTwin(twin)); } } @@ -134,6 +135,7 @@ namespace Server.Engines.Events return loc; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool CheckMobile(Mobile mobile) => mobile?.Map != null && !mobile.Deleted && mobile.Alive && mobile.Map != Map.Internal; @@ -214,15 +216,15 @@ namespace Server.Engines.Events if (action == 0) { - Timer.DelayCall(OneSecond, OneSecond, 10, Bleeding, from); + Timer.StartTimer(OneSecond, OneSecond, 10, () => Bleeding(from)); } else if (action == 1) { - Timer.DelayCall(TimeSpan.FromSeconds(2), SolidHueMobile, from); + Timer.StartTimer(TimeSpan.FromSeconds(2), () => SolidHueMobile(from)); } else { - Timer.DelayCall(TimeSpan.FromSeconds(2), MakeTwin, from); + Timer.StartTimer(TimeSpan.FromSeconds(2), () => MakeTwin(from)); } } } @@ -283,7 +285,7 @@ namespace Server.Engines.Events m_From = from; Name = $"{from.Name}\'s Naughty Twin"; - Timer.DelayCall(TrickOrTreat.OneSecond, StealCandyOrGate, m_From); + Timer.StartTimer(TrickOrTreat.OneSecond, () => StealCandyOrGate(m_From)); } } diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs index 99fc5be2b..cf00181eb 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs @@ -7,7 +7,7 @@ namespace Server.Engines.Events { public static class PumpkinPatchSpawner { - private static Timer m_Timer; + private static Timer _timer; private static readonly Rectangle2D[] m_PumpkinFields = { @@ -25,9 +25,10 @@ namespace Server.Engines.Events { var now = Core.Now; + // TODO: World timer to turn these on/off if (now >= HolidaySettings.StartHalloween && now <= HolidaySettings.FinishHalloween) { - m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(30), 0, PumpkinPatchSpawnerCallback); + _timer = Timer.DelayCall(TimeSpan.FromSeconds(30), 0, PumpkinPatchSpawnerCallback); } } @@ -35,6 +36,12 @@ namespace Server.Engines.Events { AddPumpkin(Map.Felucca); AddPumpkin(Map.Trammel); + + if (Core.Now > HolidaySettings.FinishHalloween) + { + _timer.Stop(); + _timer = null; + } } private static void AddPumpkin(Map map) diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs b/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs index 814eb1a5b..a281b34f4 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2011/Mobiles/PumpkinHead.cs @@ -92,16 +92,12 @@ namespace Server.Mobiles } } - public override Item NewHarmfulItem() - { - Item bad = new AcidSlime(TimeSpan.FromSeconds(10), 25, 30); - - bad.Name = "gooey nasty pumpkin hummus"; - - bad.Hue = 144; - - return bad; - } + public override Item NewHarmfulItem() => + new PoolOfAcid(TimeSpan.FromSeconds(10), 25, 30) + { + Name = "gooey nasty pumpkin hummus", + Hue = 144 + }; public override void OnDamage(int amount, Mobile from, bool willKill) { diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs b/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs index 629a486f6..42ef293f5 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs @@ -8,15 +8,15 @@ namespace Server.Engines.Events { public static class HalloweenHauntings { - private static Timer m_Timer; - private static Timer m_ClearTimer; + private static Timer _timer; + private static Timer _clearTimer; private static int m_TotalZombieLimit; private static int m_DeathQueueLimit; private static int m_QueueDelaySeconds; private static int m_QueueClearIntervalSeconds; - private static List m_DeathQueue; + private static HashSet _deathQueue; private static readonly Rectangle2D[] m_Cemetaries = { @@ -39,7 +39,7 @@ namespace Server.Engines.Events new(5224, 3655, 5, 14) // T2A }; - public static Dictionary ReAnimated { get; set; } + internal static Dictionary _reAnimated; public static void Initialize() { @@ -52,14 +52,13 @@ namespace Server.Engines.Events var tick = TimeSpan.FromSeconds(m_QueueDelaySeconds); var clear = TimeSpan.FromSeconds(m_QueueClearIntervalSeconds); - ReAnimated = new Dictionary(); - m_DeathQueue = new List(); + _reAnimated = new Dictionary(); + _deathQueue = new HashSet(); if (today >= HolidaySettings.StartHalloween && today <= HolidaySettings.FinishHalloween) { - m_Timer = Timer.DelayCall(tick, tick, Timer_Callback); - - m_ClearTimer = Timer.DelayCall(clear, clear, Clear_Callback); + _timer = Timer.DelayCall(tick, 0, Timer_Callback); + _clearTimer = Timer.DelayCall(clear, 0, Clear_Callback); EventSink.PlayerDeath += EventSink_PlayerDeath; } @@ -67,65 +66,69 @@ namespace Server.Engines.Events public static void EventSink_PlayerDeath(Mobile m) { - if (m is PlayerMobile pm && !pm.Deleted && m_Timer.Running && !m_DeathQueue.Contains(pm) && - m_DeathQueue.Count < m_DeathQueueLimit) + if (m is PlayerMobile { Deleted: false } pm && + _timer.Running && !_deathQueue.Contains(pm) && _deathQueue.Count < m_DeathQueueLimit) { - m_DeathQueue.Add(pm); + _deathQueue.Add(pm); } } private static void Clear_Callback() { - ReAnimated.Clear(); - - m_DeathQueue.Clear(); - - if (Core.Now <= HolidaySettings.FinishHalloween) + if (Core.Now > HolidaySettings.FinishHalloween) { - m_ClearTimer.Stop(); + _clearTimer.Stop(); + _clearTimer = null; + _reAnimated = null; + _deathQueue = null; + return; } + + _reAnimated.Clear(); + _deathQueue.Clear(); } private static void Timer_Callback() { + + if (Core.Now > HolidaySettings.FinishHalloween) + { + _timer.Stop(); + _timer = null; + return; + } + PlayerMobile player = null; - if (Core.Now <= HolidaySettings.FinishHalloween) + foreach (var entry in _deathQueue) { - for (var index = 0; m_DeathQueue.Count > 0 && index < m_DeathQueue.Count; index++) + if (!_reAnimated.ContainsKey(entry)) { - var entry = m_DeathQueue[index]; - - if (!ReAnimated.ContainsKey(entry)) - { - player = entry; - break; - } - } - - if (player?.Deleted == false && ReAnimated.Count < m_TotalZombieLimit) - { - var map = Utility.RandomBool() ? Map.Trammel : Map.Felucca; - - var home = GetRandomPointInRect(m_Cemetaries.RandomElement(), map); - - if (map.CanSpawnMobile(home)) - { - var zombieskel = new ZombieSkeleton(player); - - ReAnimated.Add(player, zombieskel); - zombieskel.Home = home; - zombieskel.RangeHome = 10; - - zombieskel.MoveToWorld(home, map); - - m_DeathQueue.Remove(player); - } + player = entry; + break; } } - else + + if (player?.Deleted != false || _reAnimated.Count >= m_TotalZombieLimit) { - m_Timer.Stop(); + return; + } + + var map = Utility.RandomBool() ? Map.Trammel : Map.Felucca; + + var home = GetRandomPointInRect(m_Cemetaries.RandomElement(), map); + + if (map.CanSpawnMobile(home)) + { + var zombieskel = new ZombieSkeleton(player); + + _reAnimated.Add(player, zombieskel); + zombieskel.Home = home; + zombieskel.RangeHome = 10; + + zombieskel.MoveToWorld(home, map); + + _deathQueue.Remove(player); } } @@ -248,7 +251,7 @@ namespace Server.Engines.Events { if (_deadPlayer?.Deleted == false) { - HalloweenHauntings.ReAnimated?.Remove(_deadPlayer); + HalloweenHauntings._reAnimated?.Remove(_deadPlayer); } } } diff --git a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs index 2ac8be893..2ecbe52a9 100644 --- a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs @@ -26,7 +26,6 @@ namespace Server.Items }; private int m_Flour; - private Timer m_Timer; [Constructible] public FlourMillEastAddon() @@ -49,7 +48,7 @@ namespace Server.Items public bool IsFull => m_Flour >= MaxFlour; [CommandProperty(AccessLevel.GameMaster)] - public bool IsWorking => m_Timer != null; + public bool IsWorking { get; private set; } [CommandProperty(AccessLevel.GameMaster)] public int MaxFlour => 2; @@ -72,17 +71,14 @@ namespace Server.Items return; } - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), FinishWorking_Callback, from); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => FinishWorking_Callback(from)); + IsWorking = true; UpdateStage(); } private void FinishWorking_Callback(Mobile from) { - if (m_Timer != null) - { - m_Timer.Stop(); - m_Timer = null; - } + IsWorking = false; if (from?.Deleted == false && !Deleted && IsFull) { diff --git a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs index 898f9ee53..d098aed2c 100644 --- a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs @@ -13,7 +13,6 @@ namespace Server.Items }; private int m_Flour; - private Timer m_Timer; [Constructible] public FlourMillSouthAddon() @@ -36,7 +35,7 @@ namespace Server.Items public bool IsFull => m_Flour >= MaxFlour; [CommandProperty(AccessLevel.GameMaster)] - public bool IsWorking => m_Timer != null; + public bool IsWorking { get; private set; } [CommandProperty(AccessLevel.GameMaster)] public int MaxFlour => 2; @@ -59,17 +58,14 @@ namespace Server.Items return; } - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), FinishWorking_Callback, from); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => FinishWorking_Callback(from)); + IsWorking = true; UpdateStage(); } private void FinishWorking_Callback(Mobile from) { - if (m_Timer != null) - { - m_Timer.Stop(); - m_Timer = null; - } + IsWorking = false; if (from?.Deleted == false && !Deleted && IsFull) { diff --git a/Projects/UOContent/Items/Addons/JackOLantern.cs b/Projects/UOContent/Items/Addons/JackOLantern.cs index d3a72ea3c..8e68ccbfa 100644 --- a/Projects/UOContent/Items/Addons/JackOLantern.cs +++ b/Projects/UOContent/Items/Addons/JackOLantern.cs @@ -60,7 +60,7 @@ namespace Server.Items if (version <= 1) { - Timer.DelayCall(Fix, version); + Timer.StartTimer(() => Fix(version)); } } diff --git a/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs b/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs index 44fdbc2ac..0f66aed1d 100644 --- a/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs +++ b/Projects/UOContent/Items/Addons/RejuvinationAnkhs.cs @@ -38,7 +38,7 @@ namespace Server.Items SendLocalizedMessageTo(from, 500803); // You feel as though you've slept for days! } - Timer.DelayCall(TimeSpan.FromHours(2.0), ReleaseUseLock_Callback, from, random); + Timer.StartTimer(TimeSpan.FromHours(2.0), () => ReleaseUseLock_Callback(from, random)); } } diff --git a/Projects/UOContent/Items/Aquarium/Aquarium.cs b/Projects/UOContent/Items/Aquarium/Aquarium.cs index 4a90b68ad..db6c3fa9c 100644 --- a/Projects/UOContent/Items/Aquarium/Aquarium.cs +++ b/Projects/UOContent/Items/Aquarium/Aquarium.cs @@ -33,7 +33,7 @@ namespace Server.Items private bool m_RewardAvailable; // evaluate timer - private Timer m_Timer; + private Timer _timer; // vacation info private int m_VacationLeft; @@ -68,7 +68,7 @@ namespace Server.Items Events = new List(); - m_Timer = Timer.DelayCall(EvaluationInterval, EvaluationInterval, Evaluate); + _timer = Timer.DelayCall(EvaluationInterval, EvaluationInterval, Evaluate); } public Aquarium(Serial serial) : base(serial) @@ -76,7 +76,6 @@ namespace Server.Items } // items info - [CommandProperty(AccessLevel.GameMaster)] public int LiveCreatures { get; private set; } @@ -193,11 +192,8 @@ namespace Server.Items public override void OnDelete() { - if (m_Timer != null) - { - m_Timer.Stop(); - m_Timer = null; - } + _timer.Stop(); + _timer = null; } public override void OnDoubleClick(Mobile from) @@ -529,14 +525,7 @@ namespace Server.Items writer.Write(3); // Version // version 1 - if (m_Timer != null) - { - writer.Write(m_Timer.Next); - } - else - { - writer.Write(Core.Now + EvaluationInterval); - } + writer.Write(_timer?.Running == true ? _timer.Next : Core.Now + EvaluationInterval); // version 0 writer.Write(LiveCreatures); @@ -574,7 +563,7 @@ namespace Server.Items next = Core.Now; } - m_Timer = Timer.DelayCall(next - Core.Now, EvaluationInterval, Evaluate); + _timer = Timer.DelayCall(next - Core.Now, EvaluationInterval, Evaluate); goto case 0; } diff --git a/Projects/UOContent/Items/Aquarium/BaseFish.cs b/Projects/UOContent/Items/Aquarium/BaseFish.cs index be75c513c..f8e55d856 100644 --- a/Projects/UOContent/Items/Aquarium/BaseFish.cs +++ b/Projects/UOContent/Items/Aquarium/BaseFish.cs @@ -6,7 +6,7 @@ namespace Server.Items { private static readonly TimeSpan DeathDelay = TimeSpan.FromMinutes(5); - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public BaseFish(int itemID) : base(itemID) @@ -23,19 +23,15 @@ namespace Server.Items public virtual void StartTimer() { - m_Timer?.Stop(); - - m_Timer = Timer.DelayCall(DeathDelay, Kill); + _timerToken.Cancel(); + Timer.StartTimer(DeathDelay, Kill, out _timerToken); InvalidateProperties(); } public virtual void StopTimer() { - m_Timer?.Stop(); - - m_Timer = null; - + _timerToken.Cancel(); InvalidateProperties(); } @@ -74,7 +70,7 @@ namespace Server.Items list.Add(GetDescription()); - if (!Dead && m_Timer != null) + if (!Dead && _timerToken.Running) { list.Add(1074507); // Gasping for air } diff --git a/Projects/UOContent/Items/Containers/FillableContainers.cs b/Projects/UOContent/Items/Containers/FillableContainers.cs index eb5d34982..9f8a5ca58 100644 --- a/Projects/UOContent/Items/Containers/FillableContainers.cs +++ b/Projects/UOContent/Items/Containers/FillableContainers.cs @@ -9,11 +9,9 @@ namespace Server.Items protected FillableContent m_Content; protected DateTime m_NextRespawnTime; - protected Timer m_RespawnTimer; + protected TimerExecutionToken _respawnTimerToken; - public FillableContainer(int itemID) - : base(itemID) => - Movable = false; + public FillableContainer(int itemID) : base(itemID) => Movable = false; public FillableContainer(Serial serial) : base(serial) @@ -98,11 +96,7 @@ namespace Server.Items { base.OnAfterDelete(); - if (m_RespawnTimer != null) - { - m_RespawnTimer.Stop(); - m_RespawnTimer = null; - } + _respawnTimerToken.Cancel(); } public int GetItemsCount() @@ -124,29 +118,24 @@ namespace Server.Items if (canSpawn) { - if (m_RespawnTimer == null) + if (!_respawnTimerToken.Running) { var mins = Utility.RandomMinMax(MinRespawnMinutes, MaxRespawnMinutes); var delay = TimeSpan.FromMinutes(mins); m_NextRespawnTime = Core.Now + delay; - m_RespawnTimer = Timer.DelayCall(delay, Respawn); + Timer.StartTimer(delay, Respawn, out _respawnTimerToken); } } - else if (m_RespawnTimer != null) + else if (_respawnTimerToken.Running) { - m_RespawnTimer.Stop(); - m_RespawnTimer = null; + _respawnTimerToken.Cancel(); } } public void Respawn() { - if (m_RespawnTimer != null) - { - m_RespawnTimer.Stop(); - m_RespawnTimer = null; - } + _respawnTimerToken.Cancel(); if (m_Content == null || Deleted) { @@ -249,7 +238,7 @@ namespace Server.Items writer.Write((int)ContentType); - if (m_RespawnTimer != null) + if (_respawnTimerToken.Running) { writer.Write(true); writer.WriteDeltaTime(m_NextRespawnTime); @@ -280,7 +269,7 @@ namespace Server.Items m_NextRespawnTime = reader.ReadDeltaTime(); var delay = m_NextRespawnTime - Core.Now; - m_RespawnTimer = Timer.DelayCall(delay > TimeSpan.Zero ? delay : TimeSpan.Zero, Respawn); + Timer.StartTimer(delay, Respawn, out _respawnTimerToken); } else { @@ -341,7 +330,7 @@ namespace Server.Items if (version == 0 && m_Content == null) { - Timer.DelayCall(AcquireContent); + Timer.StartTimer(AcquireContent); } } } diff --git a/Projects/UOContent/Items/Containers/Strongbox.cs b/Projects/UOContent/Items/Containers/Strongbox.cs index ca0c2eaea..a1d6b8ca8 100644 --- a/Projects/UOContent/Items/Containers/Strongbox.cs +++ b/Projects/UOContent/Items/Containers/Strongbox.cs @@ -77,7 +77,7 @@ namespace Server.Items } } - Timer.DelayCall(TimeSpan.FromSeconds(1.0), Validate); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), Validate); } private void Validate() diff --git a/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs b/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs index ad4143ca4..8865c48c8 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/StealableArtifactsSpawner.cs @@ -9,7 +9,7 @@ namespace Server.Items private static Type[] m_TypesOfEntries; private StealableInstance[] m_Artifacts; - private Timer m_RespawnTimer; + private Timer _respawnTimer; private Dictionary m_Table; private StealableArtifactsSpawner() : base(1) @@ -24,7 +24,7 @@ namespace Server.Items m_Artifacts[i] = new StealableInstance(Entries[i]); } - m_RespawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); + _respawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); } public StealableArtifactsSpawner(Serial serial) : base(serial) => Instance = this; @@ -244,11 +244,8 @@ namespace Server.Items { base.OnDelete(); - if (m_RespawnTimer != null) - { - m_RespawnTimer.Stop(); - m_RespawnTimer = null; - } + _respawnTimer.Stop(); + _respawnTimer = null; foreach (var si in m_Artifacts) { @@ -316,7 +313,7 @@ namespace Server.Items m_Artifacts[i] = new StealableInstance(Entries[i]); } - m_RespawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); + _respawnTimer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMinutes(15.0), CheckRespawn); } public class StealableEntry diff --git a/Projects/UOContent/Items/Farming/FarmableCrop.cs b/Projects/UOContent/Items/Farming/FarmableCrop.cs index 20fb3efb1..5ec0340f6 100644 --- a/Projects/UOContent/Items/Farming/FarmableCrop.cs +++ b/Projects/UOContent/Items/Farming/FarmableCrop.cs @@ -48,7 +48,7 @@ namespace Server.Items Unlink(); - Timer.DelayCall(TimeSpan.FromMinutes(5.0), Delete); + Timer.StartTimer(TimeSpan.FromMinutes(5.0), Delete); } public void Unlink() diff --git a/Projects/UOContent/Items/Guilds/Guildstone.cs b/Projects/UOContent/Items/Guilds/Guildstone.cs index 6786b3718..8191d8505 100644 --- a/Projects/UOContent/Items/Guilds/Guildstone.cs +++ b/Projects/UOContent/Items/Guilds/Guildstone.cs @@ -158,7 +158,7 @@ namespace Server.Items if (Guild.NewGuildSystem && m_BeforeChangeover) { - Timer.DelayCall(AddToHouse); + Timer.StartTimer(AddToHouse); } if (!Guild.NewGuildSystem && Guild == null) diff --git a/Projects/UOContent/Items/Maps/TreasureMap.cs b/Projects/UOContent/Items/Maps/TreasureMap.cs index d5fea55a7..63e6a2dfe 100644 --- a/Projects/UOContent/Items/Maps/TreasureMap.cs +++ b/Projects/UOContent/Items/Maps/TreasureMap.cs @@ -1023,7 +1023,7 @@ namespace Server.Items { Movable = false; - Timer.DelayCall(TimeSpan.FromMinutes(2.0), Delete); + Timer.StartTimer(TimeSpan.FromMinutes(2.0), Delete); } public TreasureChestDirt(Serial serial) : base(serial) diff --git a/Projects/UOContent/Items/Misc/AcidSlime.cs b/Projects/UOContent/Items/Misc/AcidSlime.cs deleted file mode 100644 index a141827cb..000000000 --- a/Projects/UOContent/Items/Misc/AcidSlime.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Mobiles; - -namespace Server.Items -{ - public class AcidSlime : Item - { - private readonly DateTime m_Created; - private readonly TimeSpan m_Duration; - private readonly int m_MaxDamage; - private readonly int m_MinDamage; - private readonly Timer m_Timer; - private bool m_Drying; - - [Constructible] - public AcidSlime() : this(TimeSpan.FromSeconds(10.0), 5, 10) - { - } - - [Constructible] - public AcidSlime(TimeSpan duration, int minDamage, int maxDamage) - : base(0x122A) - { - Hue = 0x3F; - Movable = false; - m_MinDamage = minDamage; - m_MaxDamage = maxDamage; - m_Created = Core.Now; - m_Duration = duration; - m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick); - } - - public AcidSlime(Serial serial) : base(serial) - { - } - - public override string DefaultName => "slime"; - - public override void OnAfterDelete() - { - m_Timer?.Stop(); - } - - private void OnTick() - { - var now = Core.Now; - var age = now - m_Created; - - if (age > m_Duration) - { - Delete(); - } - else - { - if (!m_Drying && age > m_Duration - age) - { - m_Drying = true; - ItemID = 0x122B; - } - - var toDamage = new List(); - - foreach (var m in GetMobilesInRange(0)) - { - if (m.Alive && !m.IsDeadBondedPet && (!(m is BaseCreature bc) || bc.Controlled || bc.Summoned)) - { - toDamage.Add(m); - } - } - - for (var i = 0; i < toDamage.Count; i++) - { - Damage(toDamage[i]); - } - } - } - - public override bool OnMoveOver(Mobile m) - { - Damage(m); - return true; - } - - public void Damage(Mobile m) - { - var damage = Utility.RandomMinMax(m_MinDamage, m_MaxDamage); - if (Core.AOS) - { - AOS.Damage(m, damage, 0, 0, 0, 100, 0); - } - else - { - m.Damage(damage); - } - } - - public override void Serialize(IGenericWriter writer) - { - } - - public override void Deserialize(IGenericReader reader) - { - } - } -} diff --git a/Projects/UOContent/Items/Misc/Bola.cs b/Projects/UOContent/Items/Misc/Bola.cs index 53aebb572..67bac32f3 100644 --- a/Projects/UOContent/Items/Misc/Bola.cs +++ b/Projects/UOContent/Items/Misc/Bola.cs @@ -104,7 +104,7 @@ namespace Server.Items to.Damage(1); - Timer.DelayCall(TimeSpan.FromSeconds(2.0), from.EndAction); + Timer.StartTimer(TimeSpan.FromSeconds(2.0), from.EndAction); } private static bool HasFreeHands(Mobile from) @@ -213,7 +213,7 @@ namespace Server.Items from.Animate(11, 5, 1, true, false, 0); from.MovingEffect(to, 0x26AC, 10, 0, false, false); - Timer.DelayCall(TimeSpan.FromSeconds(0.5), FinishThrow, from, to); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), () => FinishThrow(from, to)); } else { diff --git a/Projects/UOContent/Items/Misc/DeceitBrazier.cs b/Projects/UOContent/Items/Misc/DeceitBrazier.cs index 0cc30f4c7..999bf8106 100644 --- a/Projects/UOContent/Items/Misc/DeceitBrazier.cs +++ b/Projects/UOContent/Items/Misc/DeceitBrazier.cs @@ -7,7 +7,7 @@ namespace Server.Items { public class DeceitBrazier : Item { - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public DeceitBrazier() : base(0xE31) @@ -95,22 +95,21 @@ namespace Server.Items PublicOverheadMessage( MessageType.Regular, 0x3B2, - 500761 - ); // Heed this warning well, and use this brazier at your own peril. + 500761 // Heed this warning well, and use this brazier at your own peril. + ); + + _timerToken.Cancel(); } public override void OnMovement(Mobile m, Point3D oldLocation) { - if (NextSpawn < Core.Now) // means we haven't spawned anything if the next spawn is below + // means we haven't spawned anything if the next spawn is below + if (NextSpawn < Core.Now && + Utility.InRange(m.Location, Location, 1) && + !Utility.InRange(oldLocation, Location, 1) && + m.Player && !(m.AccessLevel > AccessLevel.Player || m.Hidden) && !_timerToken.Running) { - if (Utility.InRange(m.Location, Location, 1) && !Utility.InRange(oldLocation, Location, 1) && m.Player && - !(m.AccessLevel > AccessLevel.Player || m.Hidden)) - { - if (m_Timer?.Running != true) - { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(2), HeedWarning); - } - } + Timer.StartTimer(TimeSpan.FromSeconds(2), HeedWarning, out _timerToken); } base.OnMovement(m, oldLocation); @@ -180,7 +179,7 @@ namespace Server.Items DoEffect(spawnLoc, map); - Timer.DelayCall(TimeSpan.FromSeconds(1), SummonCreatureToWorld, bc, spawnLoc, map); + Timer.StartTimer(TimeSpan.FromSeconds(1), () => SummonCreatureToWorld(bc, spawnLoc, map)); NextSpawn = Core.Now + NextSpawnDelay; } diff --git a/Projects/UOContent/Items/Misc/EffectController.cs b/Projects/UOContent/Items/Misc/EffectController.cs index a8358b1ee..9b0fe536f 100644 --- a/Projects/UOContent/Items/Misc/EffectController.cs +++ b/Projects/UOContent/Items/Misc/EffectController.cs @@ -286,24 +286,24 @@ namespace Server.Items return; } - if (trigger is Mobile mobile && mobile.Hidden && mobile.AccessLevel > AccessLevel.Player) + if (trigger is Mobile { Hidden: true } mobile && mobile.AccessLevel > AccessLevel.Player) { return; } if (SoundID > 0) { - Timer.DelayCall(SoundDelay, PlaySound, trigger); + Timer.StartTimer(SoundDelay, () => PlaySound(trigger)); } if (Sequence != null) { - Timer.DelayCall(TriggerDelay, Sequence.DoEffect, trigger); + Timer.StartTimer(TriggerDelay, () => Sequence.DoEffect(trigger)); } if (EffectType != ECEffectType.None) { - Timer.DelayCall(EffectDelay, InternalDoEffect, trigger); + Timer.StartTimer(EffectDelay, () => InternalDoEffect(trigger)); } } diff --git a/Projects/UOContent/Items/Misc/Firebomb.cs b/Projects/UOContent/Items/Misc/Firebomb.cs index 5d2a29093..60433972e 100644 --- a/Projects/UOContent/Items/Misc/Firebomb.cs +++ b/Projects/UOContent/Items/Misc/Firebomb.cs @@ -11,7 +11,7 @@ namespace Server.Items { private Mobile m_LitBy; private int m_Ticks; - private Timer m_Timer; + private TimerExecutionToken _timerToken; private List m_Users; [Constructible] @@ -55,15 +55,15 @@ namespace Server.Items return; } - if (m_Timer == null) + if (_timerToken.Running) { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnFirebombTimerTick); - m_LitBy = from; - from.SendLocalizedMessage(1060582); // You light the firebomb. Throw it now! + from.SendLocalizedMessage(1060581); // You've already lit it! Better throw it now! } else { - from.SendLocalizedMessage(1060581); // You've already lit it! Better throw it now! + Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnFirebombTimerTick, out _timerToken); + m_LitBy = from; + from.SendLocalizedMessage(1060582); // You light the firebomb. Throw it now! } m_Users ??= new List(); @@ -80,7 +80,7 @@ namespace Server.Items { if (Deleted) { - m_Timer.Stop(); + _timerToken.Cancel(); return; } @@ -158,7 +158,7 @@ namespace Server.Items new FirebombField(m_LitBy, toDamage).MoveToWorld(Location, Map); } - m_Timer.Stop(); + _timerToken.Cancel(); Delete(); break; } @@ -172,33 +172,35 @@ namespace Server.Items return; } - if (!(obj is IPoint3D p)) + if (obj is not IPoint3D p) { return; } SpellHelper.GetSurfaceTop(ref p); + var loc = new Point3D(p); + var map = Map; from.RevealingAction(); - var to = p as IEntity ?? new Entity(Serial.Zero, new Point3D(p), Map); + var to = p as IEntity ?? new Entity(Serial.Zero, loc, map); Effects.SendMovingEffect(from, to, ItemID, 7, 0, false, false, Hue); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), FirebombReposition_OnTick, p, Map); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), + () => + { + if (Deleted) + { + return; + } + + MoveToWorld(loc, map); + } + ); Internalize(); } - private void FirebombReposition_OnTick(IPoint3D p, Map map) - { - if (Deleted) - { - return; - } - - MoveToWorld(new Point3D(p), map); - } - private class ThrowTarget : Target { public ThrowTarget(Firebomb bomb) @@ -219,7 +221,7 @@ namespace Server.Items private readonly List m_Burning; private readonly DateTime m_Expire; private readonly Mobile m_LitBy; - private readonly Timer m_Timer; + private TimerExecutionToken _timerToken; public FirebombField(Mobile litBy, List toDamage) : base(0x376A) { @@ -227,7 +229,7 @@ namespace Server.Items m_LitBy = litBy; m_Expire = Core.Now + TimeSpan.FromSeconds(10); m_Burning = toDamage; - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnFirebombFieldTimerTick); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnFirebombFieldTimerTick, out _timerToken); } public FirebombField(Serial serial) : base(serial) @@ -266,7 +268,7 @@ namespace Server.Items { if (Deleted) { - m_Timer.Stop(); + _timerToken.Cancel(); return; } @@ -297,7 +299,7 @@ namespace Server.Items if (Core.Now >= m_Expire) { - m_Timer.Stop(); + _timerToken.Cancel(); Delete(); } } diff --git a/Projects/UOContent/Items/Misc/Guillotine.cs b/Projects/UOContent/Items/Misc/Guillotine.cs index ab08c3c91..b1195cb88 100644 --- a/Projects/UOContent/Items/Misc/Guillotine.cs +++ b/Projects/UOContent/Items/Misc/Guillotine.cs @@ -37,10 +37,10 @@ namespace Server.Items Effects.PlaySound(GetWorldLocation(), Map, 0x387); - Timer.DelayCall(TimeSpan.FromSeconds(0.25), Down1); - Timer.DelayCall(TimeSpan.FromSeconds(0.50), Down2); + Timer.StartTimer(TimeSpan.FromSeconds(0.25), Down1); + Timer.StartTimer(TimeSpan.FromSeconds(0.50), Down2); - Timer.DelayCall(TimeSpan.FromSeconds(5.00), BackUp); + Timer.StartTimer(TimeSpan.FromSeconds(5.00), BackUp); m_NextUse = Core.Now + TimeSpan.FromSeconds(10.0); } diff --git a/Projects/UOContent/Items/Misc/MorphItem.cs b/Projects/UOContent/Items/Misc/MorphItem.cs index 6d04586f3..cc522080b 100644 --- a/Projects/UOContent/Items/Misc/MorphItem.cs +++ b/Projects/UOContent/Items/Misc/MorphItem.cs @@ -139,7 +139,7 @@ namespace Server.Items } } - Timer.DelayCall(Refresh); + Timer.StartTimer(Refresh); } } } diff --git a/Projects/UOContent/Items/Misc/PoolOfAcid.cs b/Projects/UOContent/Items/Misc/PoolOfAcid.cs index b343459e5..c5ceb1d96 100644 --- a/Projects/UOContent/Items/Misc/PoolOfAcid.cs +++ b/Projects/UOContent/Items/Misc/PoolOfAcid.cs @@ -4,13 +4,14 @@ using Server.Mobiles; namespace Server.Items { + [TypeAlias("Server.Items.AcidSlime")] public class PoolOfAcid : Item { private readonly DateTime m_Created; private readonly TimeSpan m_Duration; private readonly int m_MaxDamage; private readonly int m_MinDamage; - private readonly Timer m_Timer; + private TimerExecutionToken _timerToken; private bool m_Drying; [Constructible] @@ -30,7 +31,7 @@ namespace Server.Items m_Created = Core.Now; m_Duration = duration; - m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick); + Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick, out _timerToken); } public PoolOfAcid(Serial serial) : base(serial) @@ -39,9 +40,9 @@ namespace Server.Items public override string DefaultName => "a pool of acid"; - public override void OnAfterDelete() + public override void OnDelete() { - m_Timer?.Stop(); + _timerToken.Cancel(); } private void OnTick() diff --git a/Projects/UOContent/Items/Misc/Teleporter.cs b/Projects/UOContent/Items/Misc/Teleporter.cs index 7336aa897..88b689899 100644 --- a/Projects/UOContent/Items/Misc/Teleporter.cs +++ b/Projects/UOContent/Items/Misc/Teleporter.cs @@ -234,7 +234,7 @@ namespace Server.Items } else { - Timer.DelayCall(m_Delay, DoTeleport, m); + Timer.StartTimer(m_Delay, () => DoTeleport(m)); } } @@ -454,7 +454,7 @@ namespace Server.Items ); } - Timer.DelayCall(TimeSpan.FromSeconds(5.0), m.EndAction, this); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => m.EndAction(this)); } return false; @@ -671,7 +671,7 @@ namespace Server.Items public class WaitTeleporter : KeywordTeleporter { - private static Dictionary m_Table; + private static Dictionary m_Table = new Dictionary(); [Constructible] public WaitTeleporter() @@ -700,8 +700,6 @@ namespace Server.Items public static void Initialize() { - m_Table = new Dictionary(); - EventSink.Logout += EventSink_Logout; } @@ -709,7 +707,7 @@ namespace Server.Items { if (from != null && m_Table.Remove(from, out var info)) { - info.Timer.Stop(); + info.TimerToken.Cancel(); } } @@ -731,11 +729,6 @@ namespace Server.Items return $"{s} second{(s == 1 ? "" : "s")}"; } - private void EndLock(Mobile m) - { - m.EndAction(this); - } - public override void StartTeleport(Mobile m) { if (m_Table.TryGetValue(m, out var info)) @@ -755,16 +748,16 @@ namespace Server.Items if (ShowTimeRemaining) { - m.SendMessage("Time remaining: {0}", FormatTime(info.Timer.Next - Core.Now)); + m.SendMessage("Time remaining: {0}", FormatTime(info.TimerToken.Next - Core.Now)); } - Timer.DelayCall(TimeSpan.FromSeconds(5), EndLock, m); + Timer.StartTimer(TimeSpan.FromSeconds(5), () => m.EndAction(this)); } return; } - info.Timer.Stop(); + info.TimerToken.Cancel(); } if (StartMessage != null) @@ -782,7 +775,8 @@ namespace Server.Items } else { - m_Table[m] = new TeleportingInfo(this, Timer.DelayCall(Delay, DoTeleport, m)); + Timer.StartTimer(Delay, () => DoTeleport(m), out var timerToken); + m_Table[m] = new TeleportingInfo(this, timerToken); } } @@ -821,21 +815,21 @@ namespace Server.Items private class TeleportingInfo { - public TeleportingInfo(WaitTeleporter tele, Timer t) + public TeleportingInfo(WaitTeleporter tele, TimerExecutionToken token) { Teleporter = tele; - Timer = t; + TimerToken = token; } public WaitTeleporter Teleporter { get; } - public Timer Timer { get; } + public TimerExecutionToken TimerToken { get; } } } public class TimeoutTeleporter : Teleporter { - private Dictionary m_Teleporting; + private Dictionary m_Teleporting; [Constructible] public TimeoutTeleporter() : this(new Point3D(0, 0, 0)) @@ -845,7 +839,7 @@ namespace Server.Items [Constructible] public TimeoutTeleporter(Point3D pointDest, Map mapDest = null, bool creatures = false) : base(pointDest, mapDest, creatures) => - m_Teleporting = new Dictionary(); + m_Teleporting = new Dictionary(); public TimeoutTeleporter(Serial serial) : base(serial) @@ -862,19 +856,16 @@ namespace Server.Items private void StartTimer(Mobile m, TimeSpan delay) { - if (m_Teleporting.TryGetValue(m, out var t)) - { - t.Stop(); - } - - m_Teleporting[m] = Timer.DelayCall(delay, StartTeleport, m); + StopTimer(m); + Timer.StartTimer(delay, () => StartTeleport(m), out var timerToken); + m_Teleporting[m] = timerToken; } public void StopTimer(Mobile m) { if (m_Teleporting.Remove(m, out var t)) { - t.Stop(); + t.Cancel(); } } @@ -923,7 +914,7 @@ namespace Server.Items var version = reader.ReadInt(); TimeoutDelay = reader.ReadTimeSpan(); - m_Teleporting = new Dictionary(); + m_Teleporting = new Dictionary(); var count = reader.ReadInt(); diff --git a/Projects/UOContent/Items/Misc/WarningItem.cs b/Projects/UOContent/Items/Misc/WarningItem.cs index ebf899930..8d7c9e8ab 100644 --- a/Projects/UOContent/Items/Misc/WarningItem.cs +++ b/Projects/UOContent/Items/Misc/WarningItem.cs @@ -129,7 +129,7 @@ namespace Server.Items } } - Timer.DelayCall(StopBroadcasting); + Timer.StartTimer(StopBroadcasting); } private void StopBroadcasting() diff --git a/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs b/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs index 2385ab2b1..53bc96d55 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs @@ -74,7 +74,7 @@ namespace Server.Items private class LogoutGump : Gump { private readonly Bedroll m_Bedroll; - private readonly Timer m_CloseTimer; + private TimerExecutionToken _closeTimerToken; private readonly CampfireEntry m_Entry; @@ -83,7 +83,7 @@ namespace Server.Items m_Entry = entry; m_Bedroll = bedroll; - m_CloseTimer = Timer.DelayCall(TimeSpan.FromSeconds(10.0), CloseGump); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), CloseGump, out _closeTimerToken); AddBackground(0, 0, 400, 350, 0xA28); @@ -108,7 +108,7 @@ namespace Server.Items { var pm = m_Entry.Player; - m_CloseTimer.Stop(); + _closeTimerToken.Cancel(); if (Campfire.GetEntry(pm) != m_Entry) { diff --git a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs index a9fb1ab6b..9b67bffe1 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs @@ -20,7 +20,7 @@ namespace Server.Items private readonly List m_Entries; - private readonly Timer m_Timer; + private TimerExecutionToken _timerToken; public Campfire() : base(0xDE3) { @@ -30,7 +30,7 @@ namespace Server.Items m_Entries = new List(); Created = Core.Now; - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnTick); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnTick, out _timerToken); } public Campfire(Serial serial) : base(serial) @@ -161,7 +161,7 @@ namespace Server.Items public override void OnAfterDelete() { - m_Timer?.Stop(); + _timerToken.Cancel(); ClearEntries(); } diff --git a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs index 3679c5876..6a6a1dab4 100644 --- a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs +++ b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs @@ -307,7 +307,7 @@ namespace Server.Items } } - Timer.DelayCall(FixMovingCrate); + Timer.StartTimer(FixMovingCrate); } private void FixMovingCrate() diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs index 75b238abe..741864073 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs @@ -181,7 +181,7 @@ namespace Server.Items var index = 0; - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.25), 14, diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs index 79019ba69..3f352c762 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs @@ -94,7 +94,7 @@ namespace Server.Items if (version < 1) { - Timer.DelayCall(UpdateWeight); + Timer.StartTimer(UpdateWeight); } } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/Conflagration Potions/BaseConflagrationPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/Conflagration Potions/BaseConflagrationPotion.cs index 17bc2cef0..b18cd8da9 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/Conflagration Potions/BaseConflagrationPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/Conflagration Potions/BaseConflagrationPotion.cs @@ -7,7 +7,7 @@ namespace Server.Items { public abstract class BaseConflagrationPotion : BasePotion { - private static readonly Dictionary m_Delay = new(); + private static readonly Dictionary m_Delay = new(); private readonly List m_Users = new(); public BaseConflagrationPotion(PotionEffect effect) : base(0xF06, effect) => Hue = 0x489; @@ -107,8 +107,10 @@ namespace Server.Items public static void AddDelay(Mobile m) { m_Delay.TryGetValue(m, out var timer); - timer?.Stop(); - m_Delay[m] = Timer.DelayCall(TimeSpan.FromSeconds(30), EndDelay, m); + timer.Cancel(); + + Timer.StartTimer(TimeSpan.FromSeconds(30), () => EndDelay(m), out timer); + m_Delay[m] = timer; } public static int GetDelay(Mobile m) @@ -125,7 +127,7 @@ namespace Server.Items { if (m_Delay.Remove(m, out var timer)) { - timer.Stop(); + timer.Cancel(); } } @@ -142,7 +144,7 @@ namespace Server.Items return; } - if (!(targeted is IPoint3D p) || from.Map == null) + if (targeted is not IPoint3D p || from.Map == null) { return; } @@ -151,6 +153,8 @@ namespace Server.Items AddDelay(from); SpellHelper.GetSurfaceTop(ref p); + var loc = new Point3D(p); + var map = from.Map; from.RevealingAction(); @@ -162,11 +166,11 @@ namespace Server.Items } else { - to = new Entity(Serial.Zero, new Point3D(p), from.Map); + to = new Entity(Serial.Zero, loc, map); } Effects.SendMovingEffect(from, to, 0xF0D, 7, 0, false, false, Potion.Hue); - Timer.DelayCall(TimeSpan.FromSeconds(1.5), Potion.Explode, from, new Point3D(p), from.Map); + Timer.StartTimer(TimeSpan.FromSeconds(1.5), () => Potion.Explode(from, loc, map)); } } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/Confusion Blast Potions/BaseConfusionBlastPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/Confusion Blast Potions/BaseConfusionBlastPotion.cs index a73fde919..63909ee5c 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/Confusion Blast Potions/BaseConfusionBlastPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/Confusion Blast Potions/BaseConfusionBlastPotion.cs @@ -9,7 +9,7 @@ namespace Server.Items { public abstract class BaseConfusionBlastPotion : BasePotion { - private static readonly Dictionary m_Delay = new(); + private static readonly Dictionary m_Delay = new(); private readonly List m_Users = new(); public BaseConfusionBlastPotion(PotionEffect effect) : base(0xF06, effect) => Hue = 0x48D; @@ -93,7 +93,7 @@ namespace Server.Items Geometry.Circle2D(loc, map, Radius, BlastEffect, 270, 90); - Timer.DelayCall(TimeSpan.FromSeconds(0.3), CircleEffect2, loc, map); + Timer.StartTimer(TimeSpan.FromSeconds(0.3), () => CircleEffect2(loc, map)); foreach (var mobile in map.GetMobilesInRange(loc, Radius)) { @@ -125,8 +125,10 @@ namespace Server.Items public static void AddDelay(Mobile m) { m_Delay.TryGetValue(m, out var timer); - timer?.Stop(); - m_Delay[m] = Timer.DelayCall(TimeSpan.FromSeconds(60), EndDelay, m); + timer.Cancel(); + + Timer.StartTimer(TimeSpan.FromSeconds(60), () => EndDelay(m), out timer); + m_Delay[m] = timer; } public static int GetDelay(Mobile m) @@ -143,7 +145,7 @@ namespace Server.Items { if (m_Delay.Remove(m, out var timer)) { - timer.Stop(); + timer.Cancel(); } } @@ -169,6 +171,8 @@ namespace Server.Items AddDelay(from); SpellHelper.GetSurfaceTop(ref p); + var loc = new Point3D(p); + var map = from.Map; from.RevealingAction(); @@ -180,11 +184,11 @@ namespace Server.Items } else { - to = new Entity(Serial.Zero, new Point3D(p), from.Map); + to = new Entity(Serial.Zero, loc, map); } Effects.SendMovingEffect(from, to, 0xF0D, 7, 0, false, false, Potion.Hue); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), Potion.Explode, from, new Point3D(p), from.Map); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => Potion.Explode(from, loc, map)); } } } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs index e951800b4..3ada8a14a 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs @@ -15,7 +15,7 @@ namespace Server.Items private static readonly bool InstantExplosion = false; // Should explosion potions explode on impact? private static readonly bool RelativeLocation = false; // Is the explosion target location relative for mobiles? - private Timer m_Timer; + private TimerExecutionToken _timerToken; public BaseExplosionPotion(PotionEffect effect) : base(0xF0D, effect) { @@ -93,7 +93,7 @@ namespace Server.Items from.Target = new ThrowTarget(this); - if (m_Timer == null) + if (!_timerToken.Running) { from.SendLocalizedMessage(500236); // You should throw it now! @@ -101,21 +101,25 @@ namespace Server.Items if (Core.ML) { - m_Timer = Timer.DelayCall( + // 3.6 seconds explosion delay + Timer.StartTimer( TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.25), - 5, - () => Detonate_OnTick(from, timer--) - ); // 3.6 seconds explosion delay + 5, // TODO: Should this be 4? + () => Detonate_OnTick(from, timer--), + out _timerToken + ); } else { - m_Timer = Timer.DelayCall( + // 2.6 seconds explosion delay + Timer.StartTimer( TimeSpan.FromSeconds(0.75), TimeSpan.FromSeconds(1.0), 4, - () => Detonate_OnTick(from, timer--) - ); // 2.6 seconds explosion delay + () => Detonate_OnTick(from, timer--), + out _timerToken + ); } } } @@ -150,7 +154,7 @@ namespace Server.Items } Explode(from, true, loc, map); - m_Timer = null; + _timerToken.Cancel(); } else { @@ -282,7 +286,7 @@ namespace Server.Items return; } - if (!(targeted is IPoint3D p)) + if (targeted is not IPoint3D p) { return; } @@ -295,16 +299,17 @@ namespace Server.Items } SpellHelper.GetSurfaceTop(ref p); + var loc = new Point3D(p); from.RevealingAction(); - IEntity to = new Entity(Serial.Zero, new Point3D(p), map); + IEntity to = new Entity(Serial.Zero, loc, map); if (p is Mobile m) { if (!RelativeLocation) // explosion location = current mob location. { - p = m.Location; + loc = m.Location; } else { @@ -320,7 +325,7 @@ namespace Server.Items } Potion.Internalize(); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), Potion.Reposition_OnTick, from, new Point3D(p), map); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => Potion.Reposition_OnTick(from, loc, map)); } } } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs index 61f311d8d..dc3601c93 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs @@ -65,7 +65,7 @@ namespace Server.Items Consume(); } - Timer.DelayCall(TimeSpan.FromSeconds(Delay), from.EndAction); + Timer.StartTimer(TimeSpan.FromSeconds(Delay), from.EndAction); } else { diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs index 593d094db..7c16e3243 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs @@ -5,7 +5,7 @@ namespace Server.Items { public class InvisibilityPotion : BasePotion { - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); [Constructible] public InvisibilityPotion() : base(0xF0A, PotionEffect.Invisibility) => Hue = 0x48D; @@ -31,7 +31,8 @@ namespace Server.Items } Consume(); - m_Table[from] = Timer.DelayCall(TimeSpan.FromSeconds(2), Hide, from); + Timer.StartTimer(TimeSpan.FromSeconds(2), () => Hide(from), out var timerToken); + m_Table[from] = timerToken; PlayDrinkEffect(from); } @@ -53,7 +54,7 @@ namespace Server.Items RemoveTimer(m); - Timer.DelayCall(TimeSpan.FromSeconds(30), EndHide, m); + Timer.StartTimer(TimeSpan.FromSeconds(30), () => EndHide(m)); } public static void EndHide(Mobile m) @@ -73,7 +74,7 @@ namespace Server.Items m.SendLocalizedMessage(1073187); // The invisibility effect is interrupted. } - timer.Stop(); + timer.Cancel(); } } diff --git a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs index 38692b80a..6f35e8b82 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs @@ -66,7 +66,13 @@ namespace Server.Items } from.BeginAction(); - Timer.DelayCall(Core.AOS ? TimeSpan.FromSeconds(6.0) : TimeSpan.FromSeconds(12.0), EndAction, from); + Timer.StartTimer(Core.AOS ? TimeSpan.FromSeconds(6.0) : TimeSpan.FromSeconds(12.0), + () => + { + from.EndAction(); + from.SendLocalizedMessage(1049621); // You catch your breath. + } + ); var music = from.Skills.Musicianship.Fixed; @@ -199,12 +205,6 @@ namespace Server.Items } } - private static void EndAction(Mobile m) - { - m?.EndAction(); - m?.SendLocalizedMessage(1049621); // You catch your breath. - } - public override void Serialize(IGenericWriter writer) { base.Serialize(writer); diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs index 07b28289d..52fc69dd1 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs @@ -403,7 +403,7 @@ namespace Server.Items if (m_UsesRemaining != oldUses) { - Timer.DelayCall(InvalidateProperties); + Timer.StartTimer(InvalidateProperties); } } @@ -567,8 +567,8 @@ namespace Server.Items { SetInstrument(from, this); - // Delay of 7 second before being able to play another instrument again - new InternalTimer(from).Start(); + // Delay of 6 second before being able to play another instrument again + Timer.StartTimer(TimeSpan.FromSeconds(6), from.EndAction); if (CheckMusicianship(from)) { @@ -601,20 +601,5 @@ namespace Server.Items { from.PlaySound(FailureSound); } - - private class InternalTimer : Timer - { - private readonly Mobile m_From; - - public InternalTimer(Mobile from) : base(TimeSpan.FromSeconds(6.0)) - { - m_From = from; - } - - protected override void OnTick() - { - m_From.EndAction(); - } - } } } diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs index 0285dd08b..aeca39b5d 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs @@ -69,10 +69,10 @@ namespace Server.Items if (CombatCheck(from, target)) { - Timer.DelayCall(TimeSpan.FromSeconds(1.0), OnHit, from, target, weapon); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => OnHit(from, target, weapon)); } - Timer.DelayCall(TimeSpan.FromSeconds(2.5), ResetUsing, from); + Timer.StartTimer(TimeSpan.FromSeconds(2.5), () => from.NinjaWepCooldown = false); } else { @@ -81,11 +81,6 @@ namespace Server.Items } } - private static void ResetUsing(PlayerMobile from) - { - from.NinjaWepCooldown = false; - } - private static void Unload(Mobile from, INinjaWeapon weapon) { if (weapon.UsesRemaining > 0) diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs index 2155fe21c..794d5f582 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs @@ -99,7 +99,7 @@ namespace Server.Items private int m_Count; private int m_ItemID; - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public DawnsMusicBox() : base(0x2AF9) @@ -214,7 +214,7 @@ namespace Server.Items public void PlayMusic(Mobile m, MusicName music) { - if (m_Timer?.Running == true) + if (_timerToken.Running) { EndMusic(m); } @@ -224,16 +224,12 @@ namespace Server.Items } m.NetState.SendPlayMusic(music); - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 4, Animate); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 4, Animate, out _timerToken); } public void EndMusic(Mobile m) { - if (m_Timer?.Running == true) - { - m_Timer.Stop(); - } - + _timerToken.Cancel(); m.NetState.SendStopMusic(); if (m_Count > 0) diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs index bd451b669..a6e2d7e86 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs @@ -47,7 +47,7 @@ namespace Server.Items { private int m_Charges; - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public FountainOfLife(int charges = 10) @@ -55,7 +55,7 @@ namespace Server.Items { m_Charges = charges; - m_Timer = Timer.DelayCall(RechargeTime, RechargeTime, Recharge); + Timer.StartTimer(RechargeTime, RechargeTime, Recharge, out _timerToken); } public FountainOfLife(Serial serial) @@ -130,7 +130,7 @@ namespace Server.Items public override void OnDelete() { - m_Timer?.Stop(); + _timerToken.Cancel(); base.OnDelete(); } @@ -142,7 +142,7 @@ namespace Server.Items writer.WriteEncodedInt(0); // version writer.Write(m_Charges); - writer.Write(m_Timer.Next); + writer.Write(_timerToken.Next); } public override void Deserialize(IGenericReader reader) @@ -159,11 +159,11 @@ namespace Server.Items if (next < now) { - m_Timer = Timer.DelayCall(RechargeTime, Recharge); + Timer.StartTimer(RechargeTime, Recharge, out _timerToken); } else { - m_Timer = Timer.DelayCall(next - now, RechargeTime, Recharge); + Timer.StartTimer(next - now, RechargeTime, Recharge, out _timerToken); } } diff --git a/Projects/UOContent/Items/Special/Evil Home Decor Collection/CreepyPortrait.cs b/Projects/UOContent/Items/Special/Evil Home Decor Collection/CreepyPortrait.cs index 75db1575f..a8f1a198e 100644 --- a/Projects/UOContent/Items/Special/Evil Home Decor Collection/CreepyPortrait.cs +++ b/Projects/UOContent/Items/Special/Evil Home Decor Collection/CreepyPortrait.cs @@ -38,7 +38,7 @@ namespace Server.Items if (ItemID == 0x2A69 || ItemID == 0x2A6D) { Up(); - Timer.DelayCall(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 2, Up); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 2, Up); } } else if (Utility.InRange(old, Location, 2) && !Utility.InRange(m.Location, Location, 2)) @@ -46,7 +46,7 @@ namespace Server.Items if (ItemID == 0x2A6C || ItemID == 0x2A70) { Down(); - Timer.DelayCall(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 2, Down); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 2, Down); } } } diff --git a/Projects/UOContent/Items/Special/Evil Home Decor Collection/DisturbingPortrait.cs b/Projects/UOContent/Items/Special/Evil Home Decor Collection/DisturbingPortrait.cs index 461b043b9..a300366c4 100644 --- a/Projects/UOContent/Items/Special/Evil Home Decor Collection/DisturbingPortrait.cs +++ b/Projects/UOContent/Items/Special/Evil Home Decor Collection/DisturbingPortrait.cs @@ -6,13 +6,12 @@ namespace Server.Items [Flippable(0x2A5D, 0x2A61)] public class DisturbingPortraitComponent : AddonComponent { - private Timer m_Timer; + private TimerExecutionToken _timerToken; - public DisturbingPortraitComponent() : base(0x2A5D) => m_Timer = Timer.DelayCall( - TimeSpan.FromMinutes(3), - TimeSpan.FromMinutes(3), - Change - ); + public DisturbingPortraitComponent() : base(0x2A5D) + { + Timer.StartTimer(TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3), Change, out _timerToken); + } public DisturbingPortraitComponent(Serial serial) : base(serial) { @@ -36,10 +35,7 @@ namespace Server.Items { base.OnAfterDelete(); - if (m_Timer?.Running == true) - { - m_Timer.Stop(); - } + _timerToken.Cancel(); } public override void Serialize(IGenericWriter writer) @@ -55,7 +51,7 @@ namespace Server.Items var version = reader.ReadEncodedInt(); - m_Timer = Timer.DelayCall(TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3), Change); + Timer.StartTimer(TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3), Change, out _timerToken); } private void Change() diff --git a/Projects/UOContent/Items/Special/Evil Home Decor Collection/SacrificialAltar.cs b/Projects/UOContent/Items/Special/Evil Home Decor Collection/SacrificialAltar.cs index 2852c68ad..74eb5b57d 100644 --- a/Projects/UOContent/Items/Special/Evil Home Decor Collection/SacrificialAltar.cs +++ b/Projects/UOContent/Items/Special/Evil Home Decor Collection/SacrificialAltar.cs @@ -5,7 +5,7 @@ namespace Server.Items [FlippableAddon(Direction.South, Direction.East)] public class SacrificialAltarAddon : BaseAddonContainer { - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public SacrificialAltarAddon() : base(0x2A9B) @@ -25,6 +25,12 @@ namespace Server.Items public override int DefaultGumpID => 0x107; public override int DefaultDropSound => 0x42; + private void StartTimer() + { + _timerToken.Cancel(); + Timer.StartTimer(TimeSpan.FromMinutes(3), Empty, out _timerToken); + } + public override bool OnDragDrop(Mobile from, Item dropped) { if (!base.OnDragDrop(from, dropped)) @@ -41,9 +47,7 @@ namespace Server.Items { SendLocalizedMessageTo(from, 1010442); // The item will be deleted in three minutes - m_Timer?.Stop(); - - m_Timer = Timer.DelayCall(TimeSpan.FromMinutes(3), Empty); + StartTimer(); } return true; @@ -65,9 +69,7 @@ namespace Server.Items { SendLocalizedMessageTo(from, 1010442); // The item will be deleted in three minutes - m_Timer?.Stop(); - - m_Timer = Timer.DelayCall(TimeSpan.FromMinutes(3), Empty); + StartTimer(); } return true; @@ -88,7 +90,7 @@ namespace Server.Items if (Items.Count > 0) { - m_Timer = Timer.DelayCall(TimeSpan.FromMinutes(3), Empty); + StartTimer(); } } @@ -131,9 +133,7 @@ namespace Server.Items } } - m_Timer?.Stop(); - - m_Timer = null; + _timerToken.Cancel(); } } diff --git a/Projects/UOContent/Items/Special/Evil Home Decor Collection/UnsettlingPortrait.cs b/Projects/UOContent/Items/Special/Evil Home Decor Collection/UnsettlingPortrait.cs index ee92194eb..be797ef0b 100644 --- a/Projects/UOContent/Items/Special/Evil Home Decor Collection/UnsettlingPortrait.cs +++ b/Projects/UOContent/Items/Special/Evil Home Decor Collection/UnsettlingPortrait.cs @@ -6,13 +6,17 @@ namespace Server.Items [Flippable(0x2A65, 0x2A67)] public class UnsettlingPortraitComponent : AddonComponent { - private Timer m_Timer; + private TimerExecutionToken _timerToken; - public UnsettlingPortraitComponent() : base(0x2A65) => m_Timer = Timer.DelayCall( - TimeSpan.FromMinutes(3), - TimeSpan.FromMinutes(3), - ChangeDirection - ); + public UnsettlingPortraitComponent() : base(0x2A65) + { + Timer.StartTimer( + TimeSpan.FromMinutes(3), + TimeSpan.FromMinutes(3), + ChangeDirection, + out _timerToken + ); + } public UnsettlingPortraitComponent(Serial serial) : base(serial) { @@ -36,7 +40,7 @@ namespace Server.Items { base.OnAfterDelete(); - m_Timer?.Stop(); + _timerToken.Cancel(); } public override void Serialize(IGenericWriter writer) @@ -52,7 +56,7 @@ namespace Server.Items var version = reader.ReadEncodedInt(); - m_Timer = Timer.DelayCall(TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3), ChangeDirection); + Timer.StartTimer(TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3), ChangeDirection, out _timerToken); } private void ChangeDirection() diff --git a/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs b/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs index 46759ca65..0ef623565 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs @@ -9,7 +9,7 @@ namespace Server.Items public BaseFruitTreeAddon() { - Timer.DelayCall(TimeSpan.FromMinutes(5), Respawn); + Timer.StartTimer(TimeSpan.FromMinutes(5), Respawn); } public BaseFruitTreeAddon(Serial serial) : base(serial) @@ -48,7 +48,7 @@ namespace Server.Items { if (--m_Fruits == 0) { - Timer.DelayCall(TimeSpan.FromMinutes(30), Respawn); + Timer.StartTimer(TimeSpan.FromMinutes(30), Respawn); } from.SendLocalizedMessage(501016); // You pick some fruit and put it in your backpack. diff --git a/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs b/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs index 13d90f03c..9612bb558 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs @@ -54,15 +54,15 @@ namespace Server.Items { from.Location = Location; - Timer.DelayCall(TimeSpan.FromSeconds(0.5), Activate, c, from); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), () => Activate(c, from)); } else { from.LocalOverheadMessage( MessageType.Regular, 0, - 501777 - ); // Hmm... you suspect that if you used this again, it might hurt. + 501777 // Hmm... you suspect that if you used this again, it might hurt. + ); } } else @@ -135,27 +135,19 @@ namespace Server.Items ); // Hmm... you suspect that if you used this again, it might hurt. SpellHelper.Damage(TimeSpan.Zero, from, Utility.Dice(2, 10, 5)); - Timer.DelayCall(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 2, Deactivate, c); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 2, () => Deactivate(c)); } private void Deactivate(AddonComponent c) { - if (c.ItemID == 0x1269) + c.ItemID = c.ItemID switch { - c.ItemID = 0x1260; - } - else if (c.ItemID == 0x1260) - { - c.ItemID = 0x125E; - } - else if (c.ItemID == 0x1247) - { - c.ItemID = 0x1246; - } - else if (c.ItemID == 0x1246) - { - c.ItemID = 0x1230; - } + 0x1269 => 0x1260, + 0x1260 => 0x125E, + 0x1247 => 0x1246, + 0x1246 => 0x1230, + _ => c.ItemID + }; } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs b/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs index ff57499dd..258ef6c45 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs @@ -26,15 +26,15 @@ namespace Server.Items from.Location = Location; c.ItemID = 0x124A; - Timer.DelayCall(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 3, Activate, c, from); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 3, () => Activate(c, from)); } else { from.LocalOverheadMessage( MessageType.Regular, 0, - 501777 - ); // Hmm... you suspect that if you used this again, it might hurt. + 501777 // Hmm... you suspect that if you used this again, it might hurt. + ); } } else @@ -98,12 +98,7 @@ namespace Server.Items ); // Hmm... you suspect that if you used this again, it might hurt. SpellHelper.Damage(TimeSpan.Zero, from, Utility.Dice(2, 10, 5)); - Timer.DelayCall(TimeSpan.FromSeconds(1), Deactivate, c); - } - - private void Deactivate(AddonComponent c) - { - c.ItemID = 0x1249; + Timer.StartTimer(TimeSpan.FromSeconds(1), () => c.ItemID = 0x1249); } } diff --git a/Projects/UOContent/Items/Special/Holiday/Christmas/HolidayTree.cs b/Projects/UOContent/Items/Special/Holiday/Christmas/HolidayTree.cs index 5619af911..86979127f 100644 --- a/Projects/UOContent/Items/Special/Holiday/Christmas/HolidayTree.cs +++ b/Projects/UOContent/Items/Special/Holiday/Christmas/HolidayTree.cs @@ -175,7 +175,7 @@ namespace Server.Items } } - Timer.DelayCall(ValidatePlacement); + Timer.StartTimer(ValidatePlacement); } public void ValidatePlacement() diff --git a/Projects/UOContent/Items/Special/Holiday/IcyPatch.cs b/Projects/UOContent/Items/Special/Holiday/IcyPatch.cs index 62039334c..b03224ef3 100644 --- a/Projects/UOContent/Items/Special/Holiday/IcyPatch.cs +++ b/Projects/UOContent/Items/Special/Holiday/IcyPatch.cs @@ -52,7 +52,7 @@ namespace Server.Items if (freeze) { m.Frozen = true; - Timer.DelayCall(TimeSpan.FromSeconds(message == 1095162 ? 2.0 : 1.25), EndFall_Callback, m); + Timer.StartTimer(TimeSpan.FromSeconds(message == 1095162 ? 2.0 : 1.25), () => m.Frozen = false); } m.SendLocalizedMessage(message); @@ -68,10 +68,11 @@ namespace Server.Items } var p = new Point3D(Location); + var map = Map; - if (SpellHelper.FindValidSpawnLocation(Map, ref p, true)) + if (SpellHelper.FindValidSpawnLocation(map, ref p, true)) { - Timer.DelayCall(TimeSpan.FromSeconds(0), m.MoveToWorld, p, m.Map); + Timer.StartTimer(TimeSpan.FromSeconds(0), () => m.MoveToWorld(p, map)); } action = 21 + Utility.Random(2); @@ -85,25 +86,20 @@ namespace Server.Items if (action > 0) { - Timer.DelayCall(TimeSpan.FromSeconds(0.4), BeginFall_Callback, m, action, sound); + Timer.StartTimer(TimeSpan.FromSeconds(0.4), + () => + { + if (!m.Mounted) + { + m.Animate(action, 1, 1, false, true, 0); + } + + m.PlaySound(sound); + } + ); } } - private static void BeginFall_Callback(Mobile m, int action, int sound) - { - if (!m.Mounted) - { - m.Animate(action, 1, 1, false, true, 0); - } - - m.PlaySound(sound); - } - - private static void EndFall_Callback(Mobile m) - { - m.Frozen = false; - } - public override void Serialize(IGenericWriter writer) { base.Serialize(writer); diff --git a/Projects/UOContent/Items/Special/Holiday/Wreath.cs b/Projects/UOContent/Items/Special/Holiday/Wreath.cs index 12bb95082..ebc3cad6c 100644 --- a/Projects/UOContent/Items/Special/Holiday/Wreath.cs +++ b/Projects/UOContent/Items/Special/Holiday/Wreath.cs @@ -77,7 +77,7 @@ namespace Server.Items var version = reader.ReadInt(); - Timer.DelayCall(FixMovingCrate); + Timer.StartTimer(FixMovingCrate); } private void FixMovingCrate() diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs index 8e6f99915..c3a4d51ef 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs @@ -275,7 +275,7 @@ namespace Server.Items } } - Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckEnd_OnTick); + Timer.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckEnd_OnTick); } public bool ValidLocation() => diff --git a/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs b/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs index d6f5c8cb0..a451dfbe5 100644 --- a/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs +++ b/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs @@ -71,7 +71,7 @@ namespace Server.Mobiles if (version < 1) { - Timer.DelayCall(TimeSpan.FromSeconds(0), OnAfterDeserialize_Callback); + Timer.StartTimer(TimeSpan.FromSeconds(0), OnAfterDeserialize_Callback); } } } diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBlood.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBlood.cs index 8a504850c..bece494b2 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBlood.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBlood.cs @@ -5,14 +5,15 @@ namespace Server.Items { public class PlagueBeastBlood : PlagueBeastComponent { - private readonly Timer m_Timer; - - public PlagueBeastBlood() : base(0x122C, 0) => m_Timer = Timer.DelayCall( - TimeSpan.FromSeconds(1.5), - TimeSpan.FromSeconds(1.5), - 3, - Hemorrhage - ); + public PlagueBeastBlood() : base(0x122C, 0) + { + Timer.StartTimer( + TimeSpan.FromSeconds(1.5), + TimeSpan.FromSeconds(1.5), + 3, + Hemorrhage + ); + } public PlagueBeastBlood(Serial serial) : base(serial) { @@ -22,69 +23,57 @@ namespace Server.Items public bool Starting => ItemID == 0x122C; - public override void OnAfterDelete() - { - if (m_Timer?.Running == true) - { - m_Timer.Stop(); - } - } - public override bool OnBandage(Mobile from) { - if (IsAccessibleTo(from) && !Patched) + if (!IsAccessibleTo(from) || Patched) { - if (m_Timer?.Running == true) - { - m_Timer.Stop(); - } - - if (Starting) - { - X += 2; - Y -= 9; - - if (Organ is PlagueBeastRubbleOrgan) - { - Y -= 5; - } - else if (Organ is PlagueBeastBackupOrgan) - { - X += 7; - } - } - else - { - X -= 4; - Y -= 2; - } - - ItemID = 0x1765; - - var pack = Owner?.Backpack; - - if (pack != null) - { - for (var i = 0; i < pack.Items.Count; i++) - { - if (pack.Items[i] is PlagueBeastMainOrgan main && main.Complete) - { - main.FinishOpening(from); - } - } - } - - PublicOverheadMessage(MessageType.Regular, 0x3B2, 1071916); // * You patch up the wound with a bandage * - - return true; + return false; } - return false; + if (Starting) + { + X += 2; + Y -= 9; + + switch (Organ) + { + case PlagueBeastRubbleOrgan: + Y -= 5; + break; + case PlagueBeastBackupOrgan: + X += 7; + break; + } + } + else + { + X -= 4; + Y -= 2; + } + + ItemID = 0x1765; + + var pack = Owner?.Backpack; + + if (pack != null) + { + for (var i = 0; i < pack.Items.Count; i++) + { + if (pack.Items[i] is PlagueBeastMainOrgan main && main.Complete) + { + main.FinishOpening(from); + } + } + } + + PublicOverheadMessage(MessageType.Regular, 0x3B2, 1071916); // * You patch up the wound with a bandage * + + return true; } private void Hemorrhage() { - if (Patched) + if (Deleted || Patched) { return; } diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs index 623575f59..66683f9d8 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs @@ -41,7 +41,13 @@ namespace Server.Items if (owner != null) { - Timer.DelayCall(TimeSpan.FromSeconds(1), KillParent, owner); + Timer.StartTimer(TimeSpan.FromSeconds(1), + () => + { + owner.Unfreeze(); + owner.Kill(); + } + ); } return true; @@ -50,12 +56,6 @@ namespace Server.Items return false; } - private void KillParent(PlagueBeastLord parent) - { - parent.Unfreeze(); - parent.Kill(); - } - public override void Serialize(IGenericWriter writer) { base.Serialize(writer); diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastOrgans.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastOrgans.cs index 1ad40358a..df540cfbf 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastOrgans.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastOrgans.cs @@ -6,7 +6,7 @@ namespace Server.Items { public class PlagueBeastOrgan : PlagueBeastInnard { - private Timer m_Timer; + private bool _opening; public PlagueBeastOrgan(int itemID = 1, int hue = 0) : base(itemID, hue) { @@ -15,7 +15,7 @@ namespace Server.Items Movable = false; Visible = itemID <= 1; - Timer.DelayCall(Initialize); + Timer.StartTimer(Initialize); } public PlagueBeastOrgan(Serial serial) : base(serial) @@ -52,9 +52,20 @@ namespace Server.Items { if (IsCuttable && IsAccessibleTo(from)) { - if (!Opened && m_Timer == null) + if (!Opened && !_opening) { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(3), FinishOpening, from); + _opening = true; + void Open() + { + _opening = false; + if (!Deleted) + { + FinishOpening(from); + } + } + + Timer.StartTimer(TimeSpan.FromSeconds(3), Open); + scissors.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1071897); // You carefully cut into the organ. return true; } @@ -65,14 +76,6 @@ namespace Server.Items return false; } - public override void OnAfterDelete() - { - if (m_Timer?.Running == true) - { - m_Timer.Stop(); - } - } - public virtual bool OnLifted(Mobile from, PlagueBeastComponent c) => c.IsGland || c.IsBrain; public virtual bool OnDropped(Mobile from, Item item, PlagueBeastComponent to) => false; @@ -80,7 +83,6 @@ namespace Server.Items public virtual void FinishOpening(Mobile from) { Opened = true; - Owner?.PlaySound(0x50); } @@ -177,8 +179,8 @@ namespace Server.Items with.PublicOverheadMessage( MessageType.Regular, 0x3B2, - 1071896 - ); // This is too crude an implement for such a procedure. + 1071896 // This is too crude an implement for such a procedure. + ); } } @@ -314,8 +316,8 @@ namespace Server.Items from.LocalOverheadMessage( MessageType.Regular, 0x3B2, - 1071901 - ); // * As you cut the vein, a cloud of poison is expelled from the plague beast's organ, and the plague beast dissolves into a puddle of goo * + 1071901 // * As you cut the vein, a cloud of poison is expelled from the plague beast's organ, and the plague beast dissolves into a puddle of goo * + ); from.ApplyPoison(from, Poison.Greater); from.PlaySound(0x22F); @@ -378,8 +380,8 @@ namespace Server.Items with.PublicOverheadMessage( MessageType.Regular, 0x3B2, - 1071896 - ); // This is too crude an implement for such a procedure. + 1071896 // This is too crude an implement for such a procedure. + ); } } @@ -405,7 +407,7 @@ namespace Server.Items if (to.Hue == 0x1 && m_Gland == null && item is PlagueBeastGland) { m_Gland = item; - Timer.DelayCall(TimeSpan.FromSeconds(3), FinishHealing); + Timer.StartTimer(TimeSpan.FromSeconds(3), FinishHealing); from.SendAsciiMessage(0x3B2, "* You place the healthy gland inside the organ sac *"); item.Movable = false; @@ -435,7 +437,7 @@ namespace Server.Items Components[i].Hue = 0x6; } - Timer.DelayCall(TimeSpan.FromSeconds(2), OpenOrgan); + Timer.StartTimer(TimeSpan.FromSeconds(2), OpenOrgan); } public void OpenOrgan() @@ -524,8 +526,8 @@ namespace Server.Items from.LocalOverheadMessage( MessageType.Regular, 0x34, - 1071913 - ); // You place the organ in the fleshy receptacle near the core. + 1071913 // You place the organ in the fleshy receptacle near the core. + ); if (Owner != null) { @@ -536,8 +538,8 @@ namespace Server.Items from.LocalOverheadMessage( MessageType.Regular, 0x34, - 1071922 - ); // The plague beast is still bleeding from open wounds. You must seal any bleeding wounds before the core will open! + 1071922 // The plague beast is still bleeding from open wounds. You must seal any bleeding wounds before the core will open! + ); return true; } } diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastVein.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastVein.cs index f6439a1a2..f70ab310c 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastVein.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastVein.cs @@ -5,7 +5,7 @@ namespace Server.Items { public class PlagueBeastVein : PlagueBeastComponent { - private Timer m_Timer; + private bool _cutting; public PlagueBeastVein(int itemID, int hue) : base(itemID, hue) => Cut = false; @@ -19,14 +19,15 @@ namespace Server.Items { if (IsAccessibleTo(from)) { - if (!Cut && m_Timer == null) + if (!Cut && !_cutting) { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(3), CuttingDone, from); + _cutting = true; + Timer.StartTimer(TimeSpan.FromSeconds(3), () => CuttingDone(from)); scissors.PublicOverheadMessage( MessageType.Regular, 0x3B2, - 1071899 - ); // You begin cutting through the vein. + 1071899 // You begin cutting through the vein. + ); return true; } @@ -36,26 +37,12 @@ namespace Server.Items return false; } - public override void OnAfterDelete() - { - if (m_Timer?.Running == true) - { - m_Timer.Stop(); - } - } - private void CuttingDone(Mobile from) { + _cutting = false; Cut = true; - if (ItemID == 0x1B1C) - { - ItemID = 0x1B1B; - } - else - { - ItemID = 0x1B1C; - } + ItemID = ItemID == 0x1B1C ? 0x1B1B : 0x1B1C; Owner?.PlaySound(0x199); diff --git a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs index 8b656de51..d6b5453c6 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofAlacrity.cs @@ -97,7 +97,7 @@ namespace Server.Items Effects.SendTargetParticles(from, 0x373A, 35, 45, 0x00, 0x00, 9502, (EffectLayer)255, 0x100); pm.AcceleratedStart = Core.Now + TimeSpan.FromMinutes(15); - Timer.DelayCall(TimeSpan.FromMinutes(15), Expire_Callback, from); + Timer.StartTimer(TimeSpan.FromMinutes(15), () => Expire_Callback(from)); pm.AcceleratedSkill = Skill; diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs index 759b2d8df..0cb2917e8 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs @@ -16,8 +16,6 @@ namespace Server.Items public class MiningCart : BaseAddon, IRewardItem { - private Timer m_Timer; - [Constructible] public MiningCart(MiningCartType type) { @@ -67,7 +65,7 @@ namespace Server.Items break; } - m_Timer = Timer.DelayCall(TimeSpan.FromDays(1), TimeSpan.FromDays(1), GiveResources); + _lastResourceTime = Core.Now; } public MiningCart(Serial serial) : base(serial) @@ -78,42 +76,68 @@ namespace Server.Items { get { - var deed = new MiningCartDeed(); - deed.IsRewardItem = IsRewardItem; - deed.Gems = Gems; - deed.Ore = Ore; - - return deed; + GiveResources(); + return new MiningCartDeed { IsRewardItem = IsRewardItem, Gems = _gems, Ore = _ore }; } } [CommandProperty(AccessLevel.GameMaster)] public MiningCartType CartType { get; private set; } - [CommandProperty(AccessLevel.GameMaster)] - public int Gems { get; set; } + private int _gems; [CommandProperty(AccessLevel.GameMaster)] - public int Ore { get; set; } + public int Gems + { + get + { + GiveResources(); + return _gems; + } + set => _gems = value; + } + + private int _ore; + + [CommandProperty(AccessLevel.GameMaster)] + public int Ore + { + get + { + GiveResources(); + return _ore; + } + set => _ore = value; + } [CommandProperty(AccessLevel.GameMaster)] public bool IsRewardItem { get; set; } private void GiveResources() { + var amount = (Core.Now - _lastResourceTime).Days; + if (amount <= 0) + { + return; + } + switch (CartType) { case MiningCartType.OreSouth: case MiningCartType.OreEast: - Ore = Math.Min(100, Ore + 10); + _ore = Math.Min(100, _ore + amount * 10); break; case MiningCartType.GemSouth: case MiningCartType.GemEast: - Gems = Math.Min(50, Gems + 5); + _gems = Math.Min(50, _gems + amount * 5); break; } + + _lastResourceTime += TimeSpan.FromDays(amount); } + private DateTime _lastResourceTime; + public override void OnComponentUsed(AddonComponent c, Mobile from) { var house = BaseHouse.FindHouseAt(this); @@ -140,100 +164,105 @@ namespace Server.Items if (!from.InRange(GetWorldLocation(), 2) || !from.InLOS(this) || !(from.Z - Z > -3 && from.Z - Z < 3)) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + return; } - else if (house?.HasSecureAccess(from, SecureLevel.Friends) == true) - { - switch (CartType) - { - case MiningCartType.OreSouth: - case MiningCartType.OreEast: - if (Ore > 0) - { - var ingots = Utility.Random(9) switch - { - 0 => (Item)new IronIngot(), - 1 => new DullCopperIngot(), - 2 => new ShadowIronIngot(), - 3 => new CopperIngot(), - 4 => new BronzeIngot(), - 5 => new GoldIngot(), - 6 => new AgapiteIngot(), - 7 => new VeriteIngot(), - 8 => new ValoriteIngot(), - _ => null - }; - var amount = Math.Min(10, Ore); - // ReSharper disable once PossibleNullReferenceException - ingots.Amount = amount; - - if (!from.PlaceInBackpack(ingots)) - { - ingots.Delete(); - from.SendLocalizedMessage(1078837); // Your backpack is full! Please make room and try again. - } - else - { - PublicOverheadMessage(MessageType.Regular, 0, 1094724, amount.ToString()); // Ore: ~1_COUNT~ - Ore -= amount; - } - } - else - { - from.SendLocalizedMessage(1094725); // There are no more resources available at this time. - } - - break; - case MiningCartType.GemSouth: - case MiningCartType.GemEast: - if (Gems > 0) - { - Item gems = Utility.Random(15) switch - { - 0 => new Amber(), - 1 => new Amethyst(), - 2 => new Citrine(), - 3 => new Diamond(), - 4 => new Emerald(), - 5 => new Ruby(), - 6 => new Sapphire(), - 7 => new StarSapphire(), - 8 => new Tourmaline(), - - // Mondain's Legacy gems - 9 => new PerfectEmerald(), - 10 => new DarkSapphire(), - 11 => new Turquoise(), - 12 => new EcruCitrine(), - 13 => new FireRuby(), - _ => new BlueDiamond() // 14 - }; - - var amount = Math.Min(5, Gems); - gems.Amount = amount; - - if (!from.PlaceInBackpack(gems)) - { - gems.Delete(); - from.SendLocalizedMessage(1078837); // Your backpack is full! Please make room and try again. - } - else - { - PublicOverheadMessage(MessageType.Regular, 0, 1094723, amount.ToString()); // Gems: ~1_COUNT~ - Gems -= amount; - } - } - else - { - from.SendLocalizedMessage(1094725); // There are no more resources available at this time. - } - - break; - } - } - else + if (house?.HasSecureAccess(from, SecureLevel.Friends) != true) { from.SendLocalizedMessage(1061637); // You are not allowed to access this. + return; + } + + GiveResources(); + + switch (CartType) + { + case MiningCartType.OreSouth: + case MiningCartType.OreEast: + if (_ore > 0) + { + var ingots = Utility.Random(9) switch + { + 0 => (Item)new IronIngot(), + 1 => new DullCopperIngot(), + 2 => new ShadowIronIngot(), + 3 => new CopperIngot(), + 4 => new BronzeIngot(), + 5 => new GoldIngot(), + 6 => new AgapiteIngot(), + 7 => new VeriteIngot(), + _ => new ValoriteIngot(), + }; + + var amount = Math.Min(10, _ore); + ingots.Amount = amount; + + if (!from.PlaceInBackpack(ingots)) + { + ingots.Delete(); + from.SendLocalizedMessage( + 1078837 + ); // Your backpack is full! Please make room and try again. + } + else + { + PublicOverheadMessage(MessageType.Regular, 0, 1094724, amount.ToString()); // Ore: ~1_COUNT~ + _ore -= amount; + } + } + else + { + from.SendLocalizedMessage(1094725); // There are no more resources available at this time. + } + + break; + case MiningCartType.GemSouth: + case MiningCartType.GemEast: + if (_gems > 0) + { + Item gems = Utility.Random(15) switch + { + 0 => new Amber(), + 1 => new Amethyst(), + 2 => new Citrine(), + 3 => new Diamond(), + 4 => new Emerald(), + 5 => new Ruby(), + 6 => new Sapphire(), + 7 => new StarSapphire(), + 8 => new Tourmaline(), + + // Mondain's Legacy gems + 9 => new PerfectEmerald(), + 10 => new DarkSapphire(), + 11 => new Turquoise(), + 12 => new EcruCitrine(), + 13 => new FireRuby(), + _ => new BlueDiamond() // 14 + }; + + var amount = Math.Min(5, _gems); + gems.Amount = amount; + + if (!from.PlaceInBackpack(gems)) + { + gems.Delete(); + from.SendLocalizedMessage( + 1078837 // Your backpack is full! Please make room and try again. + ); + } + else + { + PublicOverheadMessage(MessageType.Regular, 0, 1094723, amount.ToString()); // Gems: ~1_COUNT~ + _gems -= amount; + } + } + else + { + from.SendLocalizedMessage(1094725); // There are no more resources available at this time. + } + + break; } } @@ -241,22 +270,15 @@ namespace Server.Items { base.Serialize(writer); - writer.WriteEncodedInt(1); // version + writer.WriteEncodedInt(2); // version writer.Write((int)CartType); writer.Write(IsRewardItem); - writer.Write(Gems); - writer.Write(Ore); + writer.Write(_gems); + writer.Write(_ore); - if (m_Timer != null) - { - writer.Write(m_Timer.Next); - } - else - { - writer.Write(Core.Now + TimeSpan.FromDays(1)); - } + writer.Write(_lastResourceTime); } public override void Deserialize(IGenericReader reader) @@ -267,24 +289,20 @@ namespace Server.Items switch (version) { + case 2: case 1: CartType = (MiningCartType)reader.ReadInt(); goto case 0; case 0: IsRewardItem = reader.ReadBool(); - Gems = reader.ReadInt(); - Ore = reader.ReadInt(); + _gems = reader.ReadInt(); + _ore = reader.ReadInt(); - var next = reader.ReadDateTime(); - - if (next < Core.Now) - { - next = Core.Now; - } - - m_Timer = Timer.DelayCall(next - Core.Now, TimeSpan.FromDays(1), GiveResources); + _lastResourceTime = reader.ReadDateTime() - (version < 2 ? TimeSpan.FromDays(1.0) : TimeSpan.Zero); break; } + + GiveResources(); } } @@ -303,18 +321,8 @@ namespace Server.Items public override int LabelNumber => 1080385; // deed for a mining cart decoration - public override BaseAddon Addon - { - get - { - var addon = new MiningCart(m_CartType); - addon.IsRewardItem = m_IsRewardItem; - addon.Gems = Gems; - addon.Ore = Ore; - - return addon; - } - } + public override BaseAddon Addon => + new MiningCart(m_CartType) { IsRewardItem = m_IsRewardItem, Gems = Gems, Ore = Ore }; [CommandProperty(AccessLevel.GameMaster)] public int Gems { get; set; } diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs index 195f87c13..c9c32ede4 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs @@ -9,17 +9,15 @@ namespace Server.Items public class TreeStump : BaseAddon, IRewardItem { private bool m_IsRewardItem; - private int m_Logs; - - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public TreeStump(int itemID) { AddComponent(new AddonComponent(itemID), 0, 0, 0); - m_Timer = Timer.DelayCall(TimeSpan.FromDays(1), TimeSpan.FromDays(1), GiveLogs); + Timer.StartTimer(TimeSpan.FromDays(1), TimeSpan.FromDays(1), GiveLogs, out _timerToken); } public TreeStump(Serial serial) : base(serial) @@ -65,6 +63,11 @@ namespace Server.Items m_Logs = Math.Min(100, m_Logs + 10); } + public override void OnAfterDelete() + { + _timerToken.Cancel(); + } + public override void OnComponentUsed(AddonComponent c, Mobile from) { var house = BaseHouse.FindHouseAt(this); @@ -104,12 +107,10 @@ namespace Server.Items 3 => new YewLog(), 4 => new HeartwoodLog(), 5 => new BloodwoodLog(), - 6 => new FrostwoodLog(), - _ => null + _ => new FrostwoodLog() }; var amount = Math.Min(10, m_Logs); - // ReSharper disable once PossibleNullReferenceException logs.Amount = amount; if (!from.PlaceInBackpack(logs)) @@ -143,9 +144,9 @@ namespace Server.Items writer.Write(m_IsRewardItem); writer.Write(m_Logs); - if (m_Timer != null) + if (_timerToken.Running) { - writer.Write(m_Timer.Next); + writer.Write(_timerToken.Next); } else { @@ -169,7 +170,7 @@ namespace Server.Items next = Core.Now; } - m_Timer = Timer.DelayCall(next - Core.Now, TimeSpan.FromDays(1), GiveLogs); + Timer.StartTimer(next - Core.Now, TimeSpan.FromDays(1), GiveLogs, out _timerToken); } } diff --git a/Projects/UOContent/Items/Talismans/BaseTalisman.cs b/Projects/UOContent/Items/Talismans/BaseTalisman.cs index d57ece6f4..f429e2e21 100644 --- a/Projects/UOContent/Items/Talismans/BaseTalisman.cs +++ b/Projects/UOContent/Items/Talismans/BaseTalisman.cs @@ -144,7 +144,7 @@ namespace Server.Items private TalismanAttribute m_Summoner; - private Timer m_Timer; + private TimerExecutionToken _timerToken; public BaseTalisman() : this(GetRandomItemID()) @@ -1026,20 +1026,25 @@ namespace Server.Items public virtual void StartTimer() { - if (m_Timer?.Running != true) + if (!_timerToken.Running) { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), Slice); + Timer.StartTimer(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), Slice, out _timerToken); } } public virtual void StopTimer() { - m_Timer?.Stop(); - m_Timer = null; + _timerToken.Cancel(); } public virtual void Slice() { + if (Deleted) + { + StopTimer(); + return; + } + if (m_ChargeTime - 10 > 0) { m_ChargeTime -= 10; @@ -1197,9 +1202,9 @@ namespace Server.Items if (m_Talisman.ChargeTime > 0) { from.SendLocalizedMessage( - 1074882, + 1074882, // You must wait ~1_val~ seconds for this to recharge. m_Talisman.ChargeTime.ToString() - ); // You must wait ~1_val~ seconds for this to recharge. + ); return; } diff --git a/Projects/UOContent/Items/Talismans/TalismanSummons.cs b/Projects/UOContent/Items/Talismans/TalismanSummons.cs index 4a3ff4601..e74dd8f4f 100644 --- a/Projects/UOContent/Items/Talismans/TalismanSummons.cs +++ b/Projects/UOContent/Items/Talismans/TalismanSummons.cs @@ -571,7 +571,7 @@ namespace Server.Mobiles Hue = 0x480; BaseSoundID = 0xC9; - Timer.DelayCall(TimeSpan.FromMinutes(30.0), BeginTunnel); + Timer.StartTimer(TimeSpan.FromMinutes(30.0), BeginTunnel); } public SummonedVorpalBunny(Serial serial) : base(serial) @@ -593,7 +593,7 @@ namespace Server.Mobiles Say("* The bunny begins to dig a tunnel back to its underground lair *"); PlaySound(0x247); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), Delete); } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs b/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs index f42533659..d432154e2 100644 --- a/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs +++ b/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs @@ -7,7 +7,7 @@ namespace Server.Items public class FlameSpurtTrap : BaseTrap { private Item m_Spurt; - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public FlameSpurtTrap() : base(0x1B71) => Visible = false; @@ -18,14 +18,15 @@ namespace Server.Items public virtual void StartTimer() { - m_Timer ??= Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Refresh); + if (!_timerToken.Running) + { + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Refresh, out _timerToken); + } } public virtual void StopTimer() { - m_Timer?.Stop(); - - m_Timer = null; + _timerToken.Cancel(); } public virtual void CheckTimer() diff --git a/Projects/UOContent/Items/Traps/MushroomTrap.cs b/Projects/UOContent/Items/Traps/MushroomTrap.cs index 9b089da55..62f1c15b1 100644 --- a/Projects/UOContent/Items/Traps/MushroomTrap.cs +++ b/Projects/UOContent/Items/Traps/MushroomTrap.cs @@ -32,7 +32,7 @@ namespace Server.Items SpellHelper.Damage(TimeSpan.FromSeconds(0.5), from, from, Utility.Dice(2, 4, 0)); - Timer.DelayCall(TimeSpan.FromSeconds(2.0), OnMushroomReset); + Timer.StartTimer(TimeSpan.FromSeconds(2.0), OnMushroomReset); } public virtual void OnMushroomReset() diff --git a/Projects/UOContent/Items/Traps/SpikeTrap.cs b/Projects/UOContent/Items/Traps/SpikeTrap.cs index d3c6c8624..d8405f373 100644 --- a/Projects/UOContent/Items/Traps/SpikeTrap.cs +++ b/Projects/UOContent/Items/Traps/SpikeTrap.cs @@ -118,7 +118,7 @@ namespace Server.Items } } - Timer.DelayCall(TimeSpan.FromSeconds(1.0), OnSpikeExtended); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), OnSpikeExtended); from.LocalOverheadMessage(MessageType.Regular, 0x22, 500852); // You stepped onto a spike trap! } @@ -126,7 +126,7 @@ namespace Server.Items public virtual void OnSpikeExtended() { Extended = true; - Timer.DelayCall(TimeSpan.FromSeconds(5.0), OnSpikeRetracted); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), OnSpikeRetracted); } public virtual void OnSpikeRetracted() diff --git a/Projects/UOContent/Items/Traps/StoneFaceTrap.cs b/Projects/UOContent/Items/Traps/StoneFaceTrap.cs index 3bbe5e0b3..8c454901c 100644 --- a/Projects/UOContent/Items/Traps/StoneFaceTrap.cs +++ b/Projects/UOContent/Items/Traps/StoneFaceTrap.cs @@ -100,8 +100,8 @@ namespace Server.Items Breathing = true; - Timer.DelayCall(TimeSpan.FromSeconds(2.0), FinishBreath); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), TriggerDamage); + Timer.StartTimer(TimeSpan.FromSeconds(2.0), FinishBreath); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TriggerDamage); } public virtual void FinishBreath() diff --git a/Projects/UOContent/Items/Wands/BaseWand.cs b/Projects/UOContent/Items/Wands/BaseWand.cs index 305556d6d..935eb7aec 100644 --- a/Projects/UOContent/Items/Wands/BaseWand.cs +++ b/Projects/UOContent/Items/Wands/BaseWand.cs @@ -104,12 +104,17 @@ namespace Server.Items public virtual void ApplyDelayTo(Mobile from) { from.BeginAction(); - Timer.DelayCall(GetUseDelay, ReleaseWandLock_Callback, from); + Timer.StartTimer(GetUseDelay, + () => + { + from.EndAction(); + ReleaseWandLock_Callback(from); + } + ); } public virtual void ReleaseWandLock_Callback(Mobile state) { - state.EndAction(); } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs index e38229d50..174951364 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs @@ -52,7 +52,7 @@ namespace Server.Items attacker.AddResistanceMod(mod); info = new DefenseMasteryInfo(attacker, 80 - modifier, mod); - info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(3.0), EndDefense, info); + Timer.StartTimer(TimeSpan.FromSeconds(3.0), () => EndDefense(info), out info._timerToken); _table[attacker] = info; @@ -77,7 +77,7 @@ namespace Server.Items info.m_From.RemoveResistanceMod(info.m_Mod); } - info.m_Timer?.Stop(); + info._timerToken.Cancel(); // No message is sent to the player. @@ -91,7 +91,7 @@ namespace Server.Items public readonly int m_DamageMalus; public readonly Mobile m_From; public readonly ResistanceMod m_Mod; - public Timer m_Timer; + public TimerExecutionToken _timerToken; public DefenseMasteryInfo(Mobile from, int damageMalus, ResistanceMod mod) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs index 974a49f56..a523b7a1b 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs @@ -81,7 +81,7 @@ namespace Server.Items attacker.PlaySound(0x2A1); } - Timer.DelayCall(TimeSpan.FromSeconds(2.0), RepeatEffect, attacker); + Timer.StartTimer(TimeSpan.FromSeconds(2.0), () => RepeatEffect(attacker)); } private void RepeatEffect(Mobile attacker) diff --git a/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs index c8cfb2114..5c98873f1 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs @@ -13,7 +13,7 @@ namespace Server.Items public static readonly TimeSpan PlayerDuration = TimeSpan.FromSeconds(6.0); public static readonly TimeSpan NPCDuration = TimeSpan.FromSeconds(12.0); - private static readonly Dictionary _table = new(); + private static readonly Dictionary _table = new(); public override int BaseMana => 30; @@ -41,26 +41,26 @@ namespace Server.Items public static bool IsWounded(Mobile m) => _table.ContainsKey(m); + private static void StopTimer(Mobile m) + { + if (_table.Remove(m, out var timerToken)) + { + timerToken.Cancel(); + } + } + public static void BeginWound(Mobile m, TimeSpan duration) { - if (_table.TryGetValue(m, out var timer)) - { - timer?.Stop(); - } - - _table[m] = timer = Timer.DelayCall(duration, EndWound, m); - timer.Start(); + StopTimer(m); + Timer.StartTimer(duration, () => EndWound(m), out var timerToken); + _table[m] = timerToken; m.YellowHealthbar = true; } public static void EndWound(Mobile m) { - if (_table.Remove(m, out var timer)) - { - timer.Stop(); - } - + StopTimer(m); m.YellowHealthbar = false; m.SendLocalizedMessage(1060208); // You are no longer mortally wounded. } diff --git a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs index 7c5f28e45..9c8184ebd 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs @@ -13,7 +13,7 @@ namespace Server.Items public static readonly TimeSpan FreezeDelayDuration = TimeSpan.FromSeconds(8.0); - private static readonly Dictionary _table = new(); + private static readonly Dictionary _table = new(); public override int BaseMana => 30; @@ -84,20 +84,16 @@ namespace Server.Items public static void BeginImmunity(Mobile m, TimeSpan duration) { - if (_table.TryGetValue(m, out var timer)) - { - timer?.Stop(); - } - - _table[m] = timer = Timer.DelayCall(duration, EndImmunity, m); - timer.Start(); + EndImmunity(m); + Timer.StartTimer(duration, () => EndImmunity(m), out var timerToken); + _table[m] = timerToken; } public static void EndImmunity(Mobile m) { - if (_table.Remove(m, out var timer)) + if (_table.Remove(m, out var timerToken)) { - timer?.Stop(); + timerToken.Cancel(); } } } diff --git a/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs b/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs index 46ce6cb10..672c97f6a 100644 --- a/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs +++ b/Projects/UOContent/Items/Weapons/Maces/FireworksWand.cs @@ -83,7 +83,7 @@ namespace Server.Items 0 ); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), FinishLaunch, endLoc, map); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => FinishLaunch(endLoc, map)); } private static void FinishLaunch(Point3D endLoc, Map map) diff --git a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs index bec5b60df..9ec94b1f6 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs @@ -9,7 +9,7 @@ namespace Server.Items { private bool m_Balanced; - private Timer m_RecoveryTimer; // so we don't start too many timers + private TimerExecutionToken _recoveryTimerToken; private int m_Velocity; public BaseRanged(int itemID) : base(itemID) @@ -154,11 +154,16 @@ namespace Server.Items if (!pm.Warmode) { - m_RecoveryTimer ??= Timer.DelayCall(TimeSpan.FromSeconds(10), pm.RecoverAmmo); - - if (!m_RecoveryTimer.Running) + if (!_recoveryTimerToken.Running) { - m_RecoveryTimer.Start(); + Timer.StartTimer(TimeSpan.FromSeconds(10), + () => + { + _recoveryTimerToken.Cancel(); + pm.RecoverAmmo(); + }, + out _recoveryTimerToken + ); } } } diff --git a/Projects/UOContent/Misc/AOS.cs b/Projects/UOContent/Misc/AOS.cs index 80c2d81c8..b4ceaa3db 100644 --- a/Projects/UOContent/Misc/AOS.cs +++ b/Projects/UOContent/Misc/AOS.cs @@ -1230,30 +1230,14 @@ namespace Server } } - if (!m.CanBeginAction() && m.Skills.Magery.Value < 66.1) + if (m.Skills.Magery.Value < 66.1) { - m.BodyMod = 0; - m.HueMod = -1; - m.NameMod = null; - m.EndAction(); - BaseArmor.ValidateMobile(m); - BaseClothing.ValidateMobile(m); + PolymorphSpell.EndPolymorph(m); } - if (!m.CanBeginAction() && m.Skills.Magery.Value < 38.1) + if (m.Skills.Magery.Value < 38.1) { - if (m is PlayerMobile mobile) - { - mobile.SetHairMods(-1, -1); - } - - m.BodyMod = 0; - m.HueMod = -1; - m.NameMod = null; - m.EndAction(); - BaseArmor.ValidateMobile(m); - BaseClothing.ValidateMobile(m); - BuffInfo.RemoveBuff(m, BuffIcon.Incognito); + IncognitoSpell.EndIncognito(m); } } } diff --git a/Projects/UOContent/Misc/AutoRestart.cs b/Projects/UOContent/Misc/AutoRestart.cs index 6c46c4d99..db881e4b7 100644 --- a/Projects/UOContent/Misc/AutoRestart.cs +++ b/Projects/UOContent/Misc/AutoRestart.cs @@ -100,14 +100,14 @@ namespace Server.Misc if (WarningDelay > TimeSpan.Zero) { Warning_Callback(); - DelayCall(WarningDelay, WarningDelay, Warning_Callback); + StartTimer(WarningDelay, WarningDelay, Warning_Callback); } AutoSave.Save(); Restarting = true; - DelayCall(RestartDelay, Restart_Callback); + StartTimer(RestartDelay, Restart_Callback); } } } diff --git a/Projects/UOContent/Misc/AutoSave.cs b/Projects/UOContent/Misc/AutoSave.cs index 1e35d43d9..b35774716 100644 --- a/Projects/UOContent/Misc/AutoSave.cs +++ b/Projects/UOContent/Misc/AutoSave.cs @@ -85,7 +85,7 @@ namespace Server.Misc World.Broadcast(0x35, true, "The world will save in {0} second{1}.", s, s != 1 ? "s" : ""); } - DelayCall(Warning, Save); + StartTimer(Warning, Save); } } diff --git a/Projects/UOContent/Misc/BuffIcons.cs b/Projects/UOContent/Misc/BuffIcons.cs index 16423d25d..7008ed1cd 100644 --- a/Projects/UOContent/Misc/BuffIcons.cs +++ b/Projects/UOContent/Misc/BuffIcons.cs @@ -7,6 +7,8 @@ namespace Server { public class BuffInfo { + private TimerExecutionToken _timerToken; + public BuffInfo(BuffIcon iconID, int titleCliloc) : this(iconID, titleCliloc, titleCliloc + 1) { @@ -31,7 +33,7 @@ namespace Server TimeLength = length; TimeStart = Core.TickCount; - Timer = Timer.DelayCall(length, RemoveBuff, m, this); + Timer.StartTimer(length, () => RemoveBuff(m, this), out _timerToken); } public BuffInfo(BuffIcon iconID, int titleCliloc, TextDefinition args) @@ -103,7 +105,7 @@ namespace Server public long TimeStart { get; } - public Timer Timer { get; } + public TimerExecutionToken TimerToken => _timerToken; public bool RetainThroughDeath { get; } @@ -126,7 +128,7 @@ namespace Server { if (ns.Mobile is PlayerMobile pm) { - Timer.DelayCall(pm.ResendBuffs); + Timer.StartTimer(pm.ResendBuffs); } } diff --git a/Projects/UOContent/Misc/CharacterCreation.cs b/Projects/UOContent/Misc/CharacterCreation.cs index 3847439a5..70f72ccbe 100644 --- a/Projects/UOContent/Misc/CharacterCreation.cs +++ b/Projects/UOContent/Misc/CharacterCreation.cs @@ -750,7 +750,7 @@ namespace Server.Misc * without an AOS client. You are now being taken to the city of * Haven on the Trammel facet. */ - Timer.DelayCall(BadStartMessageDelay, from => from.SendLocalizedMessage(1062205), m); + Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1062205)); break; } @@ -774,7 +774,7 @@ namespace Server.Misc * without an SE client. You are now being taken to the city of * Haven on the Trammel facet. */ - Timer.DelayCall(BadStartMessageDelay, from => from.SendLocalizedMessage(1063487), m); + Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1063487)); break; } @@ -794,7 +794,7 @@ namespace Server.Misc * without an SE client. You are now being taken to the city of * Haven on the Trammel facet. */ - Timer.DelayCall(BadStartMessageDelay, from => from.SendLocalizedMessage(1063487), m); + Timer.StartTimer(BadStartMessageDelay, () => m.SendLocalizedMessage(1063487)); break; } diff --git a/Projects/UOContent/Misc/Cleanup.cs b/Projects/UOContent/Misc/Cleanup.cs index 8feb365ab..4f49bf30f 100644 --- a/Projects/UOContent/Misc/Cleanup.cs +++ b/Projects/UOContent/Misc/Cleanup.cs @@ -12,7 +12,7 @@ namespace Server.Misc public static void Initialize() { - Timer.DelayCall(TimeSpan.FromSeconds(2.5), Run); + Timer.StartTimer(TimeSpan.FromSeconds(2.5), Run); } public static void Run() diff --git a/Projects/UOContent/Misc/ClientVerification.cs b/Projects/UOContent/Misc/ClientVerification.cs index c81d219a9..cf3c92e72 100644 --- a/Projects/UOContent/Misc/ClientVerification.cs +++ b/Projects/UOContent/Misc/ClientVerification.cs @@ -138,7 +138,7 @@ namespace Server.Misc state.Mobile.SendMessage(0x22, kickMessage); state.Mobile.SendMessage(0x22, "You will be disconnected in {0} seconds.", KickDelay.TotalSeconds); - Timer.DelayCall(KickDelay, OnKick, state); + Timer.StartTimer(KickDelay, () => OnKick(state)); } else if (Required != null && version < Required) { @@ -190,7 +190,7 @@ namespace Server.Misc ); } - Timer.DelayCall(TimeSpan.FromMinutes(Utility.Random(5, 15)), SendAnnoyGump, from); + Timer.StartTimer(TimeSpan.FromMinutes(Utility.Random(5, 15)), () => SendAnnoyGump(from)); } private static void SendAnnoyGump(Mobile m) diff --git a/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs b/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs index 344b5346c..6edd5a673 100644 --- a/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs +++ b/Projects/UOContent/Misc/Gifts/Winter2004/Mistletoe.cs @@ -77,7 +77,7 @@ namespace Server.Items var version = reader.ReadInt(); - Timer.DelayCall(FixMovingCrate); + Timer.StartTimer(FixMovingCrate); } private void FixMovingCrate() diff --git a/Projects/UOContent/Misc/Guild.cs b/Projects/UOContent/Misc/Guild.cs index 908e6361d..48ac54ca4 100644 --- a/Projects/UOContent/Misc/Guild.cs +++ b/Projects/UOContent/Misc/Guild.cs @@ -1337,7 +1337,7 @@ namespace Server.Guilds AcceptedWars ??= new List(); PendingWars ??= new List(); - Timer.DelayCall(VerifyGuild_Callback); + Timer.StartTimer(VerifyGuild_Callback); } private void VerifyGuild_Callback() diff --git a/Projects/UOContent/Misc/ShardPoller.cs b/Projects/UOContent/Misc/ShardPoller.cs index 5644aa213..eaa3d892c 100644 --- a/Projects/UOContent/Misc/ShardPoller.cs +++ b/Projects/UOContent/Misc/ShardPoller.cs @@ -145,7 +145,7 @@ namespace Server.Misc return; } - Timer.DelayCall(TimeSpan.FromSeconds(1.0), EventSink_Login_Callback, m); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => EventSink_Login_Callback(m)); } private static void EventSink_Login_Callback(Mobile from) @@ -522,14 +522,9 @@ namespace Server.Misc if (shardPoller != null) { - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(1.0), - data => - { - var (mobile, poller, polls) = data; - m_From.SendGump(new ShardPollGump(mobile, poller, false, polls)); - }, - (m_From, shardPoller, m_Polls) + () => m_From.SendGump(new ShardPollGump(m_From, shardPoller, false, m_Polls)) ); } } diff --git a/Projects/UOContent/Misc/Weather.cs b/Projects/UOContent/Misc/Weather.cs index f02977ff2..d868a1d56 100644 --- a/Projects/UOContent/Misc/Weather.cs +++ b/Projects/UOContent/Misc/Weather.cs @@ -29,7 +29,7 @@ namespace Server.Misc list?.Add(this); - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds((0.2 + Utility.RandomDouble() * 0.8) * interval.TotalSeconds), interval, OnTick diff --git a/Projects/UOContent/Mobiles/AI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI.cs index e24dcc4d3..0cff0afdc 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI.cs @@ -2688,7 +2688,7 @@ namespace Server.Mobiles !m_Mobile.InRange(spawner.HomeLocation, spawner.HomeRange) )) { - Timer.DelayCall(ReturnToHome); + Timer.StartTimer(ReturnToHome); } } } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs index 25cb49cb8..5aac32a1f 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Hiryu.cs @@ -214,7 +214,7 @@ namespace Server.Mobiles if (version <= 1) { - Timer.DelayCall(Fix, version); + Timer.StartTimer(() => Fix(version)); } if (version < 2) diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs b/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs index 54a9c1f21..dafed75de 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/LesserHiryu.cs @@ -216,7 +216,7 @@ namespace Server.Mobiles if (version <= 1) { - Timer.DelayCall(Fix, version); + Timer.StartTimer(() => Fix(version)); } if (version < 2) diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index 0de22b4b1..952fe37a1 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -286,7 +286,7 @@ namespace Server.Mobiles private int m_FireResistance; private bool m_HasGeneratedLoot; // have we generated our loot yet? - private Timer m_HealTimer; + private TimerExecutionToken _healTimerToken; private Point3D m_Home; // The home position of the creature, used by some AI @@ -1138,7 +1138,7 @@ namespace Server.Mobiles public virtual double HealOwnerInterval => 30.0; public virtual bool HealOwnerFully => false; - public bool IsHealing => m_HealTimer != null; + public bool IsHealing => _healTimerToken.Running; public virtual bool HasAura => false; public virtual TimeSpan AuraInterval => TimeSpan.FromSeconds(5); @@ -1466,10 +1466,7 @@ namespace Server.Mobiles c?.Slip(); } - if (Confidence.IsRegenerating(this)) - { - Confidence.StopRegenerating(this); - } + Confidence.StopRegenerating(this); WeightOverloading.FatigueOnDamage(this, amount); @@ -1491,7 +1488,7 @@ namespace Server.Mobiles } else if (from is PlayerMobile mobile) { - Timer.DelayCall(TimeSpan.FromSeconds(10), mobile.RecoverAmmo); + Timer.StartTimer(TimeSpan.FromSeconds(10), mobile.RecoverAmmo); } base.OnDamage(amount, from, willKill); @@ -2208,7 +2205,7 @@ namespace Server.Mobiles public override void RevealingAction() { - InvisibilitySpell.RemoveTimer(this); + InvisibilitySpell.StopTimer(this); base.RevealingAction(); } @@ -2804,10 +2801,10 @@ namespace Server.Mobiles Say(1013037 + Utility.Random(16)); guardedRegion.CallGuards(Location); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), ReleaseGuardLock); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), ReleaseGuardLock); m_NoDupeGuards = m; - Timer.DelayCall(ReleaseGuardDupeLock); + Timer.StartTimer(ReleaseGuardDupeLock); } } @@ -3585,8 +3582,8 @@ namespace Server.Mobiles m_NextRummageTime = tc + (int)TimeSpan.FromMinutes(delay).TotalMilliseconds; } - if (CanBreath && tc - m_NextBreathTime >= 0 - ) // tested: controlled dragons do breath fire, what about summoned skeletal dragons? + // tested: controlled dragons do breath fire, what about summoned skeletal dragons? + if (CanBreath && tc - m_NextBreathTime >= 0) { var target = Combatant; @@ -3890,7 +3887,7 @@ namespace Server.Mobiles { if (!Deleted && ReturnsToHome && IsSpawnerBound() && !InRange(Home, RangeHome + 5)) { - Timer.DelayCall(TimeSpan.FromSeconds(Utility.Random(45) + 15), GoHome_Callback); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.Random(45) + 15), GoHome_Callback); m_ReturnQueued = true; } @@ -4000,7 +3997,7 @@ namespace Server.Mobiles Direction = GetDirectionTo(target); - Timer.DelayCall(TimeSpan.FromSeconds(BreathEffectDelay), BreathEffect_Callback, target); + Timer.StartTimer(TimeSpan.FromSeconds(BreathEffectDelay), () => BreathEffect_Callback(target)); } public virtual void BreathStallMovement() @@ -4031,7 +4028,7 @@ namespace Server.Mobiles BreathPlayEffectSound(); BreathPlayEffect(target); - Timer.DelayCall(TimeSpan.FromSeconds(BreathDamageDelay), BreathDamage_Callback, target); + Timer.StartTimer(TimeSpan.FromSeconds(BreathDamageDelay), () => BreathDamage_Callback(target)); } public virtual void BreathPlayEffectSound() @@ -5397,7 +5394,7 @@ namespace Server.Mobiles var seconds = (onSelf ? HealDelay : HealOwnerDelay) + (patient.Alive ? 0.0 : 5.0); - m_HealTimer = Timer.DelayCall(TimeSpan.FromSeconds(seconds), Heal, patient); + Timer.StartTimer(TimeSpan.FromSeconds(seconds), () => Heal(patient), out _healTimerToken); } public virtual void Heal(Mobile patient) @@ -5479,9 +5476,7 @@ namespace Server.Mobiles public virtual void StopHeal() { - m_HealTimer?.Stop(); - - m_HealTimer = null; + _healTimerToken.Cancel(); } public virtual void HealEffect(Mobile patient) diff --git a/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs b/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs index 3f4f5514f..6ad276cc2 100644 --- a/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs +++ b/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs @@ -58,7 +58,7 @@ namespace Server.Mobiles FixedEffect(0x37C4, 1, 12, 1109, 6); PlaySound(0x1D3); - Timer.DelayCall(TimeSpan.FromSeconds(0.5), Flare); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), Flare); } private void Flare() diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs b/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs index 3886782ca..caa5c72e5 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/DemonKnight.cs @@ -246,7 +246,7 @@ namespace Server.Mobiles if (Utility.RandomDouble() < 0.05) { - Timer.DelayCall(TimeSpan.FromSeconds(1.0), CreateBones_Callback, from); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => CreateBones_Callback(from)); } m_InHere = false; diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs b/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs index 1d83a13e2..232be7b9f 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/ShadowKnight.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles { private bool m_HasTeleportedAway; - private Timer m_SoundTimer; + private TimerExecutionToken _soundTimerToken; [Constructible] public ShadowKnight() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) @@ -111,15 +111,13 @@ namespace Server.Mobiles else { Frozen = false; - - m_SoundTimer?.Stop(); - - m_SoundTimer = null; + _soundTimerToken.Cancel(); } } public override void OnThink() { + // TODO: Can the shadow knight teleport twice? What if it heals completely and enough time has passed? if (!m_HasTeleportedAway && Hits < HitsMax / 2) { var map = Map; @@ -168,10 +166,11 @@ namespace Server.Mobiles Effects.PlaySound(to, map, 0x1FE); m_HasTeleportedAway = true; - m_SoundTimer = Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(2.5), - SendTrackingSound + SendTrackingSound, + out _soundTimerToken ); Frozen = true; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs index fba6f279a..79a0a4b31 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs @@ -131,7 +131,7 @@ namespace Server.Mobiles if (defender.Alive) { defender.Frozen = true; - Timer.DelayCall(TimeSpan.FromSeconds(5.0), Recover_Callback, defender); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => Recover_Callback(defender)); } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs index 50b08d09a..d7ad5c249 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs @@ -165,7 +165,7 @@ namespace Server.Mobiles } } - Timer.DelayCall(TimeSpan.FromSeconds(1.0), EndSavageDance); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), EndSavageDance); } } diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs index bac1a1ab6..483d3a274 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs @@ -128,7 +128,7 @@ namespace Server.Mobiles if (defender.Alive) { defender.Frozen = true; - Timer.DelayCall(TimeSpan.FromSeconds(5.0), Recover_Callback, defender); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => Recover_Callback(defender)); } } } diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs index 8f5405bf5..eff006e42 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs @@ -156,14 +156,13 @@ namespace Server.Mobiles toBuff.FixedParticles(0x375A, 10, 15, 5017, EffectLayer.Waist); toBuff.PlaySound(0x1EE); + var maxHits = toBuff.HitsMaxSeed; + var rawStr = toBuff.RawStr; + var rawDex = toBuff.RawDex; - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(20.0), - Unbuff, - toBuff, - toBuff.HitsMaxSeed, - toBuff.RawStr, - toBuff.RawDex + () => Unbuff(toBuff, maxHits, rawStr, rawDex) ); } } diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs index ddf4808d0..feb53f272 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs @@ -78,7 +78,7 @@ namespace Server.Mobiles Say(true, "Beware, mortals! You have provoked my wrath!"); FixedParticles(0x376A, 10, 10, 9537, 33, 0, EffectLayer.Waist); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), DoAreaLeech_Finish); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), DoAreaLeech_Finish); } private void DoAreaLeech_Finish() @@ -129,7 +129,7 @@ namespace Server.Mobiles { Say(true, message); - Timer.DelayCall(TimeSpan.FromSeconds(0.5), DoFocusedLeech_Stage1, combatant); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), () => DoFocusedLeech_Stage1(combatant)); } private void DoFocusedLeech_Stage1(Mobile combatant) @@ -140,7 +140,7 @@ namespace Server.Mobiles MovingParticles(combatant, 0x0001, 1, 0, false, true, 1108, 0, 9533, 9534, 0, (EffectLayer)255, 0); PlaySound(0x1FB); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), DoFocusedLeech_Stage2, combatant); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => DoFocusedLeech_Stage2(combatant)); } } diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs index c3dfab7d3..3d171a090 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerMage.cs @@ -7,7 +7,7 @@ namespace Server.Mobiles { public class MeerMage : BaseCreature { - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); private DateTime m_NextAbilityTime; @@ -135,14 +135,17 @@ namespace Server.Mobiles } else if (combatant.Player) { - var count = 0; - Say(true, "I call a plague of insects to sting your flesh!"); - m_Table[combatant] = Timer.DelayCall( + + var count = 0; + Timer.StartTimer( TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(7.0), - () => DoEffect(combatant, count++) + () => DoEffect(combatant, count++), + out var timerToken ); + + m_Table[combatant] = timerToken; } } } @@ -166,51 +169,49 @@ namespace Server.Mobiles ); } - timer.Stop(); + timer.Cancel(); } } - public void DoEffect(Mobile m, int count) + private void DoEffect(Mobile m, int count) { if (!m.Alive) { StopEffect(m, false); + return; } - else + + if (m.FindItemOnLayer(Layer.TwoHanded) is Torch { Burning: true }) { - if (m.FindItemOnLayer(Layer.TwoHanded) is Torch torch && torch.Burning) - { - StopEffect(m, true); - } - else - { - if (count % 4 == 0) - { - m.LocalOverheadMessage( - MessageType.Emote, - m.SpeechHue, - true, - "* The swarm of insects bites and stings your flesh! *" - ); - m.NonlocalOverheadMessage( - MessageType.Emote, - m.SpeechHue, - true, - $"* {m.Name} is stung by a swarm of insects *" - ); - } + StopEffect(m, true); + return; + } - m.FixedParticles(0x91C, 10, 180, 9539, EffectLayer.Waist); - m.PlaySound(0x00E); - m.PlaySound(0x1BC); + if (count % 4 == 0) + { + m.LocalOverheadMessage( + MessageType.Emote, + m.SpeechHue, + true, + "* The swarm of insects bites and stings your flesh! *" + ); + m.NonlocalOverheadMessage( + MessageType.Emote, + m.SpeechHue, + true, + $"* {m.Name} is stung by a swarm of insects *" + ); + } - AOS.Damage(m, this, Utility.RandomMinMax(30, 40) - (Core.AOS ? 0 : 10), 100, 0, 0, 0, 0); + m.FixedParticles(0x91C, 10, 180, 9539, EffectLayer.Waist); + m.PlaySound(0x00E); + m.PlaySound(0x1BC); - if (!m.Alive) - { - StopEffect(m, false); - } - } + AOS.Damage(m, this, Utility.RandomMinMax(30, 40) - (Core.AOS ? 0 : 10), 100, 0, 0, 0, 0); + + if (!m.Alive) + { + StopEffect(m, false); } } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs b/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs index deb5ff324..cb578515f 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Animal/Ferret.cs @@ -82,12 +82,12 @@ namespace Server.Mobiles if (to != null && Utility.RandomBool()) { - Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 8)), to.Talk); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 8)), to.Talk); } m_CanTalk = false; - Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(20, 30)), ResetCanTalk); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(20, 30)), ResetCanTalk); } } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs index 1b807a9be..b200c08ef 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Ilhenir.cs @@ -226,16 +226,16 @@ namespace Server.Mobiles to.PlaySound(0x584); m_Table.Add(to); - Timer.DelayCall(TimeSpan.FromSeconds(30), CacophonicEnd, to); + Timer.StartTimer(TimeSpan.FromSeconds(30), + () => + { + m_Table.Remove(to); + to.NetState.SendSpeedControl(SpeedControlSetting.Disable); + } + ); } } - public virtual void CacophonicEnd(Mobile from) - { - m_Table.Remove(from); - from.NetState.SendSpeedControl(SpeedControlSetting.Disable); - } - public static bool UnderCacophonicAttack(Mobile from) => m_Table.Contains(from); public virtual void DropOoze() @@ -299,7 +299,7 @@ namespace Server.Mobiles public class StainedOoze : Item { private int m_Ticks; - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public StainedOoze(bool corrosive = false) : base(0x122A) @@ -308,7 +308,7 @@ namespace Server.Mobiles Hue = 0x95; Corrosive = corrosive; - m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick); + Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick, out _timerToken); m_Ticks = 0; } @@ -322,11 +322,7 @@ namespace Server.Mobiles public override void OnAfterDelete() { - if (m_Timer != null) - { - m_Timer.Stop(); - m_Timer = null; - } + _timerToken.Cancel(); } private void OnTick() @@ -391,8 +387,8 @@ namespace Server.Mobiles m.LocalOverheadMessage( MessageType.Regular, 0x21, - 1072070 - ); // The infernal ooze scorches you, setting you and your equipment ablaze! + 1072070 // The infernal ooze scorches you, setting you and your equipment ablaze! + ); return; } } @@ -417,7 +413,7 @@ namespace Server.Mobiles Corrosive = reader.ReadBool(); - m_Timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick); + Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1), OnTick, out _timerToken); m_Ticks = ItemID == 0x122A ? 0 : 30; } } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs b/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs index d278bb322..62995b8ff 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Special/Meraktus.cs @@ -51,7 +51,7 @@ namespace Server.Mobiles PackTalismans(5); } - Timer.DelayCall(TimeSpan.FromSeconds(1), SpawnTormented); + Timer.StartTimer(TimeSpan.FromSeconds(1), SpawnTormented); } public Meraktus(Serial serial) : base(serial) diff --git a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs index 8e3178483..c386b5cae 100644 --- a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs +++ b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs @@ -64,7 +64,7 @@ namespace Server.Mobiles public virtual void DelayBeginTunnel() { - Timer.DelayCall(TimeSpan.FromMinutes(3.0), BeginTunnel); + Timer.StartTimer(TimeSpan.FromMinutes(3.0), BeginTunnel); } public virtual void BeginTunnel() @@ -80,7 +80,7 @@ namespace Server.Mobiles Say("* The bunny begins to dig a tunnel back to its underground lair *"); PlaySound(0x247); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), Delete); } public override int GetAttackSound() => 0xC9; @@ -112,7 +112,7 @@ namespace Server.Mobiles Movable = false; Hue = 1; - Timer.DelayCall(TimeSpan.FromSeconds(40.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(40.0), Delete); } public BunnyHole(Serial serial) : base(serial) diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs index 3ffa0917a..46f1a0535 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/Golem.cs @@ -194,7 +194,7 @@ namespace Server.Mobiles if (defender.Alive) { defender.Frozen = true; - Timer.DelayCall(TimeSpan.FromSeconds(5.0), Recover_Callback, defender); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => Recover_Callback(defender)); } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs index 057c108d6..a96c23de2 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeastLord.cs @@ -104,19 +104,19 @@ namespace Server.Mobiles PrivateOverheadMessage( MessageType.Regular, 0x3B2, - 1071919, + 1071919, // * ~1_VAL~ slices through the plague beast's amorphous tissue * from.Name, m.NetState - ); // * ~1_VAL~ slices through the plague beast's amorphous tissue * + ); } } from.LocalOverheadMessage( MessageType.Regular, 0x21, - 1071904 - ); // * You slice through the plague beast's amorphous tissue * - Timer.DelayCall(pack.Open, from); + 1071904 // * You slice through the plague beast's amorphous tissue * + ); + Timer.StartTimer(() => pack.Open(from)); } } @@ -127,8 +127,8 @@ namespace Server.Mobiles scissors.PublicOverheadMessage( MessageType.Regular, 0x3B2, - 1071918 - ); // You can't cut through the plague beast's amorphous skin with scissors! + 1071918 // You can't cut through the plague beast's amorphous skin with scissors! + ); } return false; @@ -231,7 +231,7 @@ namespace Server.Mobiles m_Timer = new DecayTimer(this); m_Timer.Start(); - Timer.DelayCall(BroadcastMessage); + Timer.StartTimer(BroadcastMessage); } private void BroadcastMessage() diff --git a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs index b509e7dbc..2da33812e 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles { private static readonly Dictionary m_Table = new(); - private Timer m_DisguiseTimer; + private TimerExecutionToken _disguiseTimerToken; [Constructible] public BakeKitsune() : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) @@ -76,9 +76,9 @@ namespace Server.Mobiles public override void OnCombatantChange() { - if (Combatant == null && !IsBodyMod && !Controlled && m_DisguiseTimer == null && Utility.RandomBool()) + if (Combatant == null && !IsBodyMod && !Controlled && !_disguiseTimerToken.Running && Utility.RandomBool()) { - m_DisguiseTimer = Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(15, 30)), Disguise); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(15, 30)), Disguise, out _disguiseTimerToken); } } @@ -151,7 +151,7 @@ namespace Server.Mobiles SetResistance(ResistanceType.Energy, 40, 60); } - Timer.DelayCall(RemoveDisguise); + Timer.StartTimer(RemoveDisguise); } public void Disguise() @@ -202,7 +202,8 @@ namespace Server.Mobiles AddItem(new Robe(Utility.RandomNondyedHue())); - m_DisguiseTimer = Timer.DelayCall(TimeSpan.FromSeconds(75), RemoveDisguise); + _disguiseTimerToken.Cancel(); + Timer.StartTimer(TimeSpan.FromSeconds(75), RemoveDisguise, out _disguiseTimerToken); } public void RemoveDisguise() @@ -224,7 +225,7 @@ namespace Server.Mobiles DeleteItemOnLayer(Layer.OuterTorso); DeleteItemOnLayer(Layer.Shoes); - m_DisguiseTimer = null; + _disguiseTimerToken.Cancel(); } public void DeleteItemOnLayer(Layer layer) diff --git a/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs b/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs index 721937a1e..5abb5deb5 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/Kappa.cs @@ -165,7 +165,10 @@ namespace Server.Mobiles base.OnDamage(amount, from, willKill); } - public override Item NewHarmfulItem() => new AcidSlime(TimeSpan.FromSeconds(10), 5, 10); + public override Item NewHarmfulItem() => new PoolOfAcid(TimeSpan.FromSeconds(10), 5, 10) + { + Name = "slime" + }; public override void Serialize(IGenericWriter writer) { diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index a6a97e5ec..00be9495d 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Accounting; using Server.ContextMenus; using Server.Engines.BulkOrders; @@ -147,6 +148,7 @@ namespace Server.Mobiles private List m_AllFollowers; private int m_BeardModID = -1, m_BeardModHue; + // TODO: Pool BuffInfo objects private Dictionary m_BuffTable; private DuelPlayer m_DuelPlayer; @@ -983,7 +985,7 @@ namespace Server.Mobiles if (Core.SE) { - Timer.DelayCall(CheckPets); + Timer.StartTimer(CheckPets); } } @@ -1094,7 +1096,8 @@ namespace Server.Mobiles } } - private static bool CheckBlock(MountBlock block) => block?.m_Timer.Running == true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool CheckBlock(MountBlock block) => block?._timerToken.Running == true; public void SetMountBlock(BlockMountType type, TimeSpan duration, bool dismount) { @@ -1110,7 +1113,7 @@ namespace Server.Mobiles } } - if (m_MountBlock?.m_Timer.Running != true || m_MountBlock.m_Timer.Next < Core.Now + duration) + if (!CheckBlock(m_MountBlock) || m_MountBlock._timerToken.Next < Core.Now + duration) { m_MountBlock = new MountBlock(duration, type, this); } @@ -1257,7 +1260,7 @@ namespace Server.Mobiles if (from.NetState != null) { - Timer.DelayCall(TimeSpan.FromSeconds(1.0), from.NetState.Disconnect, "Server is locked down"); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => from.NetState.Disconnect("Server is locked down")); } } else if (from.AccessLevel >= AccessLevel.Administrator) @@ -1293,7 +1296,7 @@ namespace Server.Mobiles } m_NoDeltaRecursion = true; - Timer.DelayCall(ValidateEquipment_Sandbox); + Timer.StartTimer(ValidateEquipment_Sandbox); } private void ValidateEquipment_Sandbox() @@ -1548,7 +1551,7 @@ namespace Server.Mobiles DisguiseTimers.StartTimer(m); - Timer.DelayCall(SpecialMove.ClearAllMoves, m); + Timer.StartTimer(() => SpecialMove.ClearAllMoves(m)); } private static void EventSink_Disconnected(Mobile from) @@ -1604,7 +1607,7 @@ namespace Server.Mobiles return; } - InvisibilitySpell.RemoveTimer(this); + InvisibilitySpell.StopTimer(this); base.RevealingAction(); @@ -2391,10 +2394,7 @@ namespace Server.Mobiles c?.Slip(); } - if (Confidence.IsRegenerating(this)) - { - Confidence.StopRegenerating(this); - } + Confidence.StopRegenerating(this); WeightOverloading.FatigueOnDamage(this, amount); @@ -2403,7 +2403,7 @@ namespace Server.Mobiles if (willKill && from is PlayerMobile mobile) { - Timer.DelayCall(TimeSpan.FromSeconds(10), mobile.RecoverAmmo); + Timer.StartTimer(TimeSpan.FromSeconds(10), mobile.RecoverAmmo); } base.OnDamage(amount, from, willKill); @@ -2430,7 +2430,7 @@ namespace Server.Mobiles { if (!Warmode) { - Timer.DelayCall(TimeSpan.FromSeconds(10), RecoverAmmo); + Timer.StartTimer(TimeSpan.FromSeconds(10), RecoverAmmo); } } @@ -2695,7 +2695,7 @@ namespace Server.Mobiles { if (YoungDeathTeleport()) { - Timer.DelayCall(TimeSpan.FromSeconds(2.5), SendYoungDeathNotice); + Timer.StartTimer(TimeSpan.FromSeconds(2.5), SendYoungDeathNotice); } } @@ -3669,7 +3669,7 @@ namespace Server.Mobiles if (pet.Map != Map) { pet.PlaySound(pet.GetAngerSound()); - Timer.DelayCall(pet.Delete); + Timer.StartTimer(pet.Delete); } continue; @@ -4600,10 +4600,7 @@ namespace Server.Mobiles return; } - if (info.Timer?.Running == true) - { - info.Timer.Stop(); - } + info.TimerToken.Cancel(); if (NetState?.BuffIcon == true) { @@ -4635,14 +4632,14 @@ namespace Server.Mobiles private class MountBlock { - public readonly Timer m_Timer; + public TimerExecutionToken _timerToken; public readonly BlockMountType m_Type; public MountBlock(TimeSpan duration, BlockMountType type, Mobile mobile) { m_Type = type; - m_Timer = Timer.DelayCall(duration, RemoveBlock, mobile); + Timer.StartTimer(duration, () => RemoveBlock(mobile), out _timerToken); } private void RemoveBlock(Mobile mobile) @@ -4651,6 +4648,8 @@ namespace Server.Mobiles { pm.m_MountBlock = null; } + + _timerToken.Cancel(); } } diff --git a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs index e1db5c9c5..b6e60df77 100644 --- a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs +++ b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs @@ -430,7 +430,7 @@ namespace Server.Mobiles SetControlMaster(null); EscortTable.Remove(master); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), Delete); return null; } diff --git a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs index 09399e9fd..34e7814ed 100644 --- a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs +++ b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs @@ -20,7 +20,7 @@ namespace Server.Mobiles { private static GlobalTownCrierEntryList m_Instance; - public static GlobalTownCrierEntryList Instance => m_Instance ?? (m_Instance = new GlobalTownCrierEntryList()); + public static GlobalTownCrierEntryList Instance => m_Instance ??= new GlobalTownCrierEntryList(); public bool IsEmpty => Entries == null || Entries.Count == 0; @@ -299,8 +299,8 @@ namespace Server.Mobiles public class TownCrier : Mobile, ITownCrierEntryList { - private Timer m_AutoShoutTimer; - private Timer m_NewsTimer; + private Timer _autoShoutTimer; + private Timer _newsTimer; [Constructible] public TownCrier() @@ -386,14 +386,9 @@ namespace Server.Mobiles } } - if (Entries == null || Entries.Count == 0) - { - return GlobalTownCrierEntryList.Instance.GetRandomEntry(); - } - var entry = GlobalTownCrierEntryList.Instance.GetRandomEntry(); - return entry ?? (Utility.RandomBool() ? Entries.RandomElement() : null); + return entry ?? (Entries?.Count > 0 && Utility.RandomBool() ? Entries.RandomElement() : null); } public TownCrierEntry AddEntry(string[] lines, TimeSpan duration) @@ -404,7 +399,7 @@ namespace Server.Mobiles Entries.Add(tce); - m_AutoShoutTimer ??= Timer.DelayCall(TimeSpan.FromSeconds(5.0), TimeSpan.FromMinutes(1.0), AutoShout_Callback); + ForceBeginAutoShout(); return tce; } @@ -425,15 +420,14 @@ namespace Server.Mobiles if (Entries == null && GlobalTownCrierEntryList.Instance.IsEmpty) { - m_AutoShoutTimer?.Stop(); - - m_AutoShoutTimer = null; + _autoShoutTimer.Stop(); + _autoShoutTimer = null; } } public void ForceBeginAutoShout() { - m_AutoShoutTimer ??= Timer.DelayCall(TimeSpan.FromSeconds(5.0), TimeSpan.FromMinutes(1.0), AutoShout_Callback); + _autoShoutTimer ??= Timer.DelayCall(TimeSpan.FromSeconds(5.0), TimeSpan.FromMinutes(1.0), AutoShout_Callback); } private void AutoShout_Callback() @@ -442,28 +436,29 @@ namespace Server.Mobiles if (tce == null) { - m_AutoShoutTimer?.Stop(); - - m_AutoShoutTimer = null; + _autoShoutTimer.Stop(); + _autoShoutTimer = null; } - else if (m_NewsTimer == null) + else if (_newsTimer == null) { - m_NewsTimer = Timer.DelayCall( + _newsTimer = Timer.DelayCall( TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(3.0), - () => ShoutNews_Callback(tce, 0) + tce.Lines.Length, + () => ShoutNews_Callback(tce) ); PublicOverheadMessage(MessageType.Regular, 0x3B2, 502976); // Hear ye! Hear ye! } } - private void ShoutNews_Callback(TownCrierEntry tce, int index) + private void ShoutNews_Callback(TownCrierEntry tce) { - if (index < 0 || index >= tce.Lines.Length) + var index = _newsTimer.Index; + if (index >= tce.Lines.Length) { - m_NewsTimer?.Stop(); - m_NewsTimer = null; + _newsTimer.Stop(); + _newsTimer = null; } else { @@ -483,11 +478,11 @@ namespace Server.Mobiles } } - public override bool HandlesOnSpeech(Mobile from) => m_NewsTimer == null && from.Alive && InRange(from, 12); + public override bool HandlesOnSpeech(Mobile from) => _newsTimer == null && from.Alive && InRange(from, 12); public override void OnSpeech(SpeechEventArgs e) { - if (m_NewsTimer == null && e.HasKeyword(0x30) && e.Mobile.Alive && InRange(e.Mobile, 12)) // *news* + if (_newsTimer == null && e.HasKeyword(0x30) && e.Mobile.Alive && InRange(e.Mobile, 12)) // *news* { Direction = GetDirectionTo(e.Mobile); @@ -499,10 +494,11 @@ namespace Server.Mobiles } else { - m_NewsTimer = Timer.DelayCall( + _newsTimer = Timer.DelayCall( TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(3.0), - () => ShoutNews_Callback(tce, 0) + tce.Lines.Length, + () => ShoutNews_Callback(tce) ); PublicOverheadMessage(MessageType.Regular, 0x3B2, 502978); // Some of the latest news! diff --git a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs index 669722ad7..313d24fe2 100644 --- a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs @@ -1361,7 +1361,7 @@ namespace Server.Mobiles IsParagon = false; } - Timer.DelayCall(CheckMorph); + Timer.StartTimer(CheckMorph); } public override void AddCustomContextEntries(Mobile from, List list) diff --git a/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs b/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs index eb39fc5dd..7537ac39a 100644 --- a/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs +++ b/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs @@ -263,7 +263,14 @@ namespace Server.Mobiles m_Mobiles = new List(); // This cannot be null in case it is referenced before disposing - Timer.DelayCall(DeleteEntities, entities); + Timer.StartTimer(() => + { + foreach (var entity in entities) + { + entity.Delete(); + } + } + ); m_Table = new Dictionary(); @@ -276,14 +283,6 @@ namespace Server.Mobiles Delete(); } } - - private static void DeleteEntities(List entities) - { - foreach (var entity in entities) - { - entity.Delete(); - } - } } } } diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs b/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs index 9051cdc36..bed4072bb 100644 --- a/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs +++ b/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs @@ -144,7 +144,7 @@ namespace Server.Mobiles private readonly List m_SBInfos = new(); private BaseHouse m_House; - private Timer m_NewsTimer; + private Timer _newsTimer; public PlayerBarkeeper(Mobile owner, BaseHouse house) : base("the barkeeper") { @@ -222,12 +222,13 @@ namespace Server.Mobiles public override bool HandlesOnSpeech(Mobile from) => InRange(from, 3) || base.HandlesOnSpeech(from); - private void ShoutNews_Callback(TownCrierEntry tce, int index) + private void ShoutNews_Callback(TownCrierEntry tce) { - if (index < 0 || index >= tce.Lines.Length) + var index = _newsTimer.Index; + if (index >= tce.Lines.Length) { - m_NewsTimer?.Stop(); - m_NewsTimer = null; + _newsTimer.Stop(); + _newsTimer = null; } else { @@ -265,7 +266,7 @@ namespace Server.Mobiles if (!e.Handled && InRange(e.Mobile, 3)) { - if (m_NewsTimer == null && e.HasKeyword(0x30)) // *news* + if (_newsTimer == null && e.HasKeyword(0x30)) // *news* { var tce = GlobalTownCrierEntryList.Instance.GetRandomEntry(); @@ -275,11 +276,11 @@ namespace Server.Mobiles } else { - var index = 0; - m_NewsTimer = Timer.DelayCall( + _newsTimer = Timer.DelayCall( TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(3.0), - () => ShoutNews_Callback(tce, index) + tce.Lines.Length, + () => ShoutNews_Callback(tce) ); PublicOverheadMessage(MessageType.Regular, 0x3B2, 502978); // Some of the latest news! @@ -607,7 +608,7 @@ namespace Server.Mobiles if (version < 1) { - Timer.DelayCall(UpgradeFromVersion0); + Timer.StartTimer(UpgradeFromVersion0); } } diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs index f8b9879cc..11cffccc3 100644 --- a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs @@ -473,11 +473,11 @@ namespace Server.Mobiles if (version < 1) { m_ShopName = "Shop Not Yet Named"; - Timer.DelayCall(UpgradeFromVersion0, newVendorSystemActivated); + Timer.StartTimer(() => UpgradeFromVersion0(newVendorSystemActivated)); } else { - Timer.DelayCall(FixDresswear); + Timer.StartTimer(FixDresswear); } NextPayTime = Core.Now + PayTimer.GetInterval(); @@ -917,7 +917,7 @@ namespace Server.Mobiles { if (GetVendorItem(item) == null) { - Timer.DelayCall(OnItemGiven, from, item); + Timer.StartTimer(() => OnItemGiven(from, item)); } return true; @@ -1694,7 +1694,7 @@ namespace Server.Mobiles Vendor = (PlayerVendor)reader.ReadEntity(); - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); } private class ExpireTimer : Timer diff --git a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs index 3ca3ec6c0..d4d5ce4bd 100644 --- a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs +++ b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs @@ -41,7 +41,7 @@ namespace Server.Mobiles if (Items.Count == 0 && Gold == 0) { - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); } else { diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs index d8491528f..eed92df2d 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs @@ -67,11 +67,11 @@ namespace Server.Multis private DateTime m_DecayTime; private Direction m_Facing; - private Timer m_MoveTimer; + private TimerExecutionToken _moveTimerToken; private string m_ShipName; - private Timer m_TurnTimer; + private TimerExecutionToken _turnTimerToken; public BaseBoat() : base(0x0) { @@ -125,7 +125,7 @@ namespace Server.Multis public Direction Moving { get; set; } [CommandProperty(AccessLevel.GameMaster)] - public bool IsMoving => m_MoveTimer != null; + public bool IsMoving => _moveTimerToken.Running; [CommandProperty(AccessLevel.GameMaster)] public int Speed { get; set; } @@ -453,10 +453,8 @@ namespace Server.Multis Hold?.Delete(); PPlank?.Delete(); SPlank?.Delete(); - m_TurnTimer?.Stop(); - m_TurnTimer = null; - m_MoveTimer?.Stop(); - m_MoveTimer = null; + _turnTimerToken.Cancel(); + _moveTimerToken.Cancel(); Boats.Remove(this); } @@ -1087,10 +1085,8 @@ namespace Server.Multis Speed = FastSpeed; Order = single ? BoatOrder.Single : BoatOrder.Course; - m_MoveTimer?.Stop(); - - m_MoveTimer = new MoveTimer(this, FastInterval, false); - m_MoveTimer.Start(); + _moveTimerToken.Cancel(); + Timer.StartTimer(FastInterval, FastInterval, StopBoat, out _moveTimerToken); if (message) { @@ -1100,6 +1096,14 @@ namespace Server.Multis return true; } + private void StopBoat() + { + if (!DoMovement(true)) + { + StopMove(false); + } + } + public override void OnSpeech(SpeechEventArgs e) { if (CheckDecay()) @@ -1267,16 +1271,13 @@ namespace Server.Multis return false; } - if (m_MoveTimer != null && Order != BoatOrder.Move) + if (Order != BoatOrder.Move) { - m_MoveTimer.Stop(); - m_MoveTimer = null; + _moveTimerToken.Cancel(); } - m_TurnTimer?.Stop(); - - m_TurnTimer = Timer.DelayCall(TimeSpan.FromMilliseconds(500), Turn, offset); - m_TurnTimer.Start(); + _turnTimerToken.Cancel(); + Timer.StartTimer(TimeSpan.FromMilliseconds(500), () => Turn(offset), out _turnTimerToken); if (message) { @@ -1286,15 +1287,9 @@ namespace Server.Multis return true; } - public void Turn(int offset) => Turn(offset, true); - - public bool Turn(int offset, bool message) + public bool Turn(int offset, bool message = true) { - if (m_TurnTimer != null) - { - m_TurnTimer.Stop(); - m_TurnTimer = null; - } + _turnTimerToken.Cancel(); if (CheckDecay()) { @@ -1346,10 +1341,8 @@ namespace Server.Multis m_ClientSpeed = clientSpeed; Order = BoatOrder.Move; - m_MoveTimer?.Stop(); - - m_MoveTimer = new MoveTimer(this, interval, single); - m_MoveTimer.Start(); + _moveTimerToken.Cancel(); + Timer.StartTimer(interval, StopBoat, out _moveTimerToken); return true; } @@ -1361,7 +1354,7 @@ namespace Server.Multis return false; } - if (m_MoveTimer == null) + if (!_moveTimerToken.Running) { if (message) { @@ -1374,8 +1367,7 @@ namespace Server.Multis Moving = Direction.North; Speed = 0; m_ClientSpeed = 0; - m_MoveTimer.Stop(); - m_MoveTimer = null; + _moveTimerToken.Cancel(); if (message) { @@ -1624,12 +1616,12 @@ namespace Server.Multis if (dir == Left || dir == BackwardLeft || dir == Backward) { - return Turn(-2, true); + return Turn(-2); } if (dir == Right || dir == BackwardRight) { - return Turn(2, true); + return Turn(2); } speed = Math.Min(Speed, maxSpeed); @@ -2077,24 +2069,6 @@ namespace Server.Multis } } - private class MoveTimer : Timer - { - private readonly BaseBoat m_Boat; - - public MoveTimer(BaseBoat boat, TimeSpan interval, bool single) : base(interval, interval, single ? 1 : 0) - { - m_Boat = boat; - } - - protected override void OnTick() - { - if (!m_Boat.DoMovement(true)) - { - m_Boat.StopMove(false); - } - } - } - /* * OSI sends the 0xF7 packet instead, holding 0xF3 packets * for every entity on the boat. Though, the regular 0xF3 diff --git a/Projects/UOContent/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs index 66dc066e6..e5f0cbdc9 100644 --- a/Projects/UOContent/Multis/Camps/BaseCamp.cs +++ b/Projects/UOContent/Multis/Camps/BaseCamp.cs @@ -9,7 +9,7 @@ namespace Server.Multis { private TimeSpan m_DecayDelay; private DateTime m_DecayTime; - private Timer m_DecayTimer; + private TimerExecutionToken _decayTimerToken; private List m_Items; private List m_Mobiles; @@ -20,7 +20,7 @@ namespace Server.Multis m_DecayDelay = TimeSpan.FromMinutes(30.0); RefreshDecay(true); - Timer.DelayCall(CheckAddComponents); + Timer.StartTimer(CheckAddComponents); } public BaseCamp(Serial serial) : base(serial) @@ -62,14 +62,13 @@ namespace Server.Multis return; } - m_DecayTimer?.Stop(); - if (setDecayTime) { m_DecayTime = Core.Now + DecayDelay; } - m_DecayTimer = Timer.DelayCall(DecayDelay, Delete); + _decayTimerToken.Cancel(); + Timer.StartTimer(DecayDelay, Delete, out _decayTimerToken); } public virtual void AddItem(Item item, int xOffset, int yOffset, int zOffset) diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index c235327f5..39dcc26d3 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -592,7 +592,7 @@ namespace Server.Multis { if (!Deleted && DecayLevel == DecayLevel.Collapsed) { - Timer.DelayCall(Decay_Sandbox); + Timer.StartTimer(Decay_Sandbox); return true; } @@ -1197,7 +1197,7 @@ namespace Server.Multis LockedDownFlag = 1; SecureFlag = 2; - Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Decay_OnTick); + Timer.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Decay_OnTick); } public virtual int GetAosCurLockdowns() @@ -2957,7 +2957,7 @@ namespace Server.Multis if (child.Decays && !child.IsLockedDown && !child.IsSecure && child.LastMoved + child.DecayTime <= Core.Now) { - Timer.DelayCall(child.Delete); + Timer.StartTimer(child.Delete); } } } @@ -3191,7 +3191,7 @@ namespace Server.Multis if (version < 10) { - Timer.DelayCall(FixLockdowns_Sandbox); + Timer.StartTimer(FixLockdowns_Sandbox); } if (version < 11) @@ -3215,12 +3215,12 @@ namespace Server.Multis { if (RelocatedEntities.Count > 0) { - Timer.DelayCall(RestoreRelocatedEntities); + Timer.StartTimer(RestoreRelocatedEntities); } if (m_Owner == null && Friends.Count == 0 && CoOwners.Count == 0) { - Timer.DelayCall(TimeSpan.FromSeconds(10.0), Delete); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), Delete); } } } @@ -3269,7 +3269,7 @@ namespace Server.Multis if (trans == null && house.CoOwners.Count == 0) { - Timer.DelayCall(house.Delete); + Timer.StartTimer(house.Delete); } else { @@ -4362,7 +4362,7 @@ namespace Server.Multis m_RegionOwner = regionowner; - Timer.DelayCall(house.RestrictedPlacingTime, Unregister); + Timer.StartTimer(house.RestrictedPlacingTime, Unregister); } public override bool AllowHousing(Mobile from, Point3D p) => diff --git a/Projects/UOContent/Multis/Houses/MovingCrate.cs b/Projects/UOContent/Multis/Houses/MovingCrate.cs index 2a14a239c..6ef37c309 100644 --- a/Projects/UOContent/Multis/Houses/MovingCrate.cs +++ b/Projects/UOContent/Multis/Houses/MovingCrate.cs @@ -253,11 +253,11 @@ namespace Server.Multis if (House != null) { House.MovingCrate = this; - Timer.DelayCall(Hide); + Timer.StartTimer(Hide); } else { - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); } if (version == 0) diff --git a/Projects/UOContent/Multis/Houses/PreviewHouse.cs b/Projects/UOContent/Multis/Houses/PreviewHouse.cs index 39c69c0ea..29123d788 100644 --- a/Projects/UOContent/Multis/Houses/PreviewHouse.cs +++ b/Projects/UOContent/Multis/Houses/PreviewHouse.cs @@ -126,7 +126,7 @@ namespace Server.Multis } } - Timer.DelayCall(Delete); + Timer.StartTimer(Delete); } private class DecayTimer : Timer diff --git a/Projects/UOContent/Skills/AnimalTaming.cs b/Projects/UOContent/Skills/AnimalTaming.cs index 2c9ebc06d..15f976454 100644 --- a/Projects/UOContent/Skills/AnimalTaming.cs +++ b/Projects/UOContent/Skills/AnimalTaming.cs @@ -262,7 +262,7 @@ namespace Server.SkillHandlers if (creature.BardPacified && Utility.RandomDouble() > .24) { - Timer.DelayCall(TimeSpan.FromSeconds(2.0), Pacify, creature); + Timer.StartTimer(TimeSpan.FromSeconds(2.0), () => Pacify(creature)); } else { diff --git a/Projects/UOContent/Skills/Discordance.cs b/Projects/UOContent/Skills/Discordance.cs index 1b00dc94c..b4feb9288 100644 --- a/Projects/UOContent/Skills/Discordance.cs +++ b/Projects/UOContent/Skills/Discordance.cs @@ -68,7 +68,7 @@ namespace Server.SkillHandlers if (ends && info.m_Ending && info.m_EndTime < Core.Now) { - info.m_Timer?.Stop(); + info._timerToken.Cancel(); info.Clear(); m_Table.Remove(targ); @@ -98,7 +98,7 @@ namespace Server.SkillHandlers public readonly List m_Mods; public bool m_Ending; public DateTime m_EndTime; - public Timer m_Timer; + public TimerExecutionToken _timerToken; public DiscordanceInfo(Mobile from, Mobile creature, int effect, List mods) { @@ -289,11 +289,11 @@ namespace Server.SkillHandlers } var info = new DiscordanceInfo(from, targ, effect.Abs(), mods); - info.m_Timer = Timer.DelayCall( + Timer.StartTimer( TimeSpan.Zero, TimeSpan.FromSeconds(1.25), - ProcessDiscordance, - info + () => ProcessDiscordance(info), + out info._timerToken ); m_Table[targ] = info; diff --git a/Projects/UOContent/Special Systems/Engines/GiftGiving.cs b/Projects/UOContent/Special Systems/Engines/GiftGiving.cs index 92a9e938f..14634063b 100644 --- a/Projects/UOContent/Special Systems/Engines/GiftGiving.cs +++ b/Projects/UOContent/Special Systems/Engines/GiftGiving.cs @@ -69,7 +69,7 @@ namespace Server.Misc public virtual void DelayGiveGift(TimeSpan delay, Mobile mob) { - Timer.DelayCall(delay, GiveGift, mob); + Timer.StartTimer(delay, () => GiveGift(mob)); } public virtual GiftResult GiveGift(Mobile mob, Item item) diff --git a/Projects/UOContent/Spells/Bushido/Confidence.cs b/Projects/UOContent/Spells/Bushido/Confidence.cs index eeba44bc2..a846088f1 100644 --- a/Projects/UOContent/Spells/Bushido/Confidence.cs +++ b/Projects/UOContent/Spells/Bushido/Confidence.cs @@ -12,8 +12,8 @@ namespace Server.Spells.Bushido 9002 ); - private static readonly Dictionary m_Table = new(); - private static readonly Dictionary m_RegenTable = new(); + private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_RegenTable = new(); public Confidence(Mobile caster, Item scroll) : base(caster, scroll, m_Info) { @@ -53,82 +53,72 @@ namespace Server.Spells.Bushido public static void BeginConfidence(Mobile m) { - m_Table.TryGetValue(m, out var timer); - timer?.Stop(); - m_Table[m] = timer = new InternalTimer(m); + StopConfidenceTimer(m); - timer.Start(); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), + () => + { + EndConfidence(m); + m.SendLocalizedMessage(1063116); // Your confidence wanes. + }, + out var timerToken + ); + + m_Table[m] = timerToken; + } + + private static bool StopConfidenceTimer(Mobile m) + { + if (m_Table.Remove(m, out var timerToken)) + { + timerToken.Cancel(); + return true; + } + + return false; } public static void EndConfidence(Mobile m) { - if (m_Table.Remove(m, out var timer)) + if (StopConfidenceTimer(m)) { - timer.Stop(); + OnEffectEnd(m, typeof(Confidence)); } - - OnEffectEnd(m, typeof(Confidence)); } public static bool IsRegenerating(Mobile m) => m_RegenTable.ContainsKey(m); + // TODO: Move this to a central regeneration so it actually works properly public static void BeginRegenerating(Mobile m) { - m_RegenTable.TryGetValue(m, out var timer); - timer?.Stop(); + StopRegenerating(m); - m_RegenTable[m] = timer = new RegenTimer(m); + // RunUO says this goes for 5 seconds, but UOGuide says 4 seconds during normal regeneration + var hits = (15 + m.Skills.Bushido.Fixed * m.Skills.Bushido.Fixed / 57600) / 4; - timer.Start(); + TimerExecutionToken timerToken = default; + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), 4, + () => + { + // ReSharper disable once AccessToModifiedClosure + if (timerToken.RemainingCount == 0) + { + StopRegenerating(m); + } + + m.Hits += hits; + }, + out timerToken + ); + + m_RegenTable[m] = timerToken; } public static void StopRegenerating(Mobile m) { if (m_RegenTable.Remove(m, out var timer)) { - timer.Stop(); - } - } - - private class InternalTimer : Timer - { - private readonly Mobile m_Mobile; - - public InternalTimer(Mobile m) : base(TimeSpan.FromSeconds(15.0)) - { - m_Mobile = m; - } - - protected override void OnTick() - { - EndConfidence(m_Mobile); - m_Mobile.SendLocalizedMessage(1063116); // Your confidence wanes. - } - } - - private class RegenTimer : Timer - { - private readonly int m_Hits; - private readonly Mobile m_Mobile; - private int m_Ticks; - - public RegenTimer(Mobile m) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) - { - m_Mobile = m; - m_Hits = 15 + m.Skills.Bushido.Fixed * m.Skills.Bushido.Fixed / 57600; - } - - protected override void OnTick() - { - ++m_Ticks; - - if (m_Ticks >= 5) - { - m_Mobile.Hits += m_Hits - m_Hits * 4 / 5; - StopRegenerating(m_Mobile); - } - - m_Mobile.Hits += m_Hits / 5; + timer.Cancel(); } } } diff --git a/Projects/UOContent/Spells/Bushido/CounterAttack.cs b/Projects/UOContent/Spells/Bushido/CounterAttack.cs index a4db6ff37..04eb1ebcb 100644 --- a/Projects/UOContent/Spells/Bushido/CounterAttack.cs +++ b/Projects/UOContent/Spells/Bushido/CounterAttack.cs @@ -13,7 +13,7 @@ namespace Server.Spells.Bushido 9002 ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public CounterAttack(Mobile caster, Item scroll) : base(caster, scroll, m_Info) { @@ -73,28 +73,39 @@ namespace Server.Spells.Bushido public static bool IsCountering(Mobile m) => m_Table.ContainsKey(m); + private static bool StopCounterTimer(Mobile m) + { + if (m_Table.Remove(m, out var timerToken)) + { + timerToken.Cancel(); + return true; + } + + return false; + } + public static void StartCountering(Mobile m) { - m_Table.TryGetValue(m, out var timer); - timer?.Stop(); + StopCounterTimer(m); - m_Table[m] = Timer.DelayCall(TimeSpan.FromSeconds(30.0), EndCountering, m); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), + () => + { + StopCountering(m); + m.SendLocalizedMessage(1063119); // You return to your normal stance. + }, + out var timerToken + ); + + m_Table[m] = timerToken; } public static void StopCountering(Mobile m) { - if (m_Table.Remove(m, out var timer)) + if (StopCounterTimer(m)) { - timer.Stop(); + OnEffectEnd(m, typeof(CounterAttack)); } - - OnEffectEnd(m, typeof(CounterAttack)); - } - - private static void EndCountering(Mobile m) - { - StopCountering(m); - m.SendLocalizedMessage(1063119); // You return to your normal stance. } } } diff --git a/Projects/UOContent/Spells/Bushido/Evasion.cs b/Projects/UOContent/Spells/Bushido/Evasion.cs index 0220fc9d8..6c1c37b5c 100644 --- a/Projects/UOContent/Spells/Bushido/Evasion.cs +++ b/Projects/UOContent/Spells/Bushido/Evasion.cs @@ -13,7 +13,7 @@ namespace Server.Spells.Bushido 9002 ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public Evasion(Mobile caster, Item scroll) : base(caster, scroll, m_Info) @@ -133,7 +133,7 @@ namespace Server.Spells.Bushido BeginEvasion(Caster); Caster.BeginAction(); - Timer.DelayCall(TimeSpan.FromSeconds(20.0), Caster.EndAction); + Timer.StartTimer(TimeSpan.FromSeconds(20.0), Caster.EndAction); } FinishSequence(); @@ -206,37 +206,36 @@ namespace Server.Spells.Bushido public static void BeginEvasion(Mobile m) { - m_Table.TryGetValue(m, out var timer); - timer?.Stop(); + StopEvasionTimer(m); - m_Table[m] = timer = new InternalTimer(m, GetEvadeDuration(m)); - timer.Start(); + Timer.StartTimer(GetEvadeDuration(m), + () => + { + EndEvasion(m); + m.SendLocalizedMessage(1063121); // You no longer feel that you could deflect any attack. + }, + out var timerToken + ); + + m_Table[m] = timerToken; + } + + private static bool StopEvasionTimer(Mobile m) + { + if (m_Table.Remove(m, out var timer)) + { + timer.Cancel(); + return true; + } + + return false; } public static void EndEvasion(Mobile m) { - if (m_Table.Remove(m, out var timer)) + if (StopEvasionTimer(m)) { - timer.Stop(); - } - - OnEffectEnd(m, typeof(Evasion)); - } - - private class InternalTimer : Timer - { - private readonly Mobile m_Mobile; - - public InternalTimer(Mobile m, TimeSpan delay) - : base(delay) - { - m_Mobile = m; - } - - protected override void OnTick() - { - EndEvasion(m_Mobile); - m_Mobile.SendLocalizedMessage(1063121); // You no longer feel that you could deflect any attack. + OnEffectEnd(m, typeof(Evasion)); } } } diff --git a/Projects/UOContent/Spells/Bushido/HonorableExecution.cs b/Projects/UOContent/Spells/Bushido/HonorableExecution.cs index e5ec28dbc..0df746862 100644 --- a/Projects/UOContent/Spells/Bushido/HonorableExecution.cs +++ b/Projects/UOContent/Spells/Bushido/HonorableExecution.cs @@ -27,6 +27,8 @@ namespace Server.Spells.Bushido ClearCurrentMove(attacker); RemovePenalty(attacker); + HonorableExecutionTimer timer; + if (!defender.Alive) { attacker.FixedParticles(0x373A, 1, 17, 0x7E2, EffectLayer.Waist); @@ -38,7 +40,7 @@ namespace Server.Spells.Bushido var swingBonus = Math.Max(1, (int)(bushido / 720.0)); - m_Table[attacker] = new HonorableExecutionTimer(attacker, swingBonus); + timer = new HonorableExecutionTimer(attacker, swingBonus); } else { @@ -58,9 +60,12 @@ namespace Server.Spells.Bushido mods.Add(new DefaultSkillMod(SkillName.MagicResist, true, -resSpells)); } - m_Table[attacker] = new HonorableExecutionTimer(attacker, mods); + timer = new HonorableExecutionTimer(attacker, mods); } + m_Table[attacker] = timer; + timer.Start(); + attacker.Delta(MobileDelta.WeaponDamage); CheckGain(attacker); } diff --git a/Projects/UOContent/Spells/Bushido/SamuraiSpell.cs b/Projects/UOContent/Spells/Bushido/SamuraiSpell.cs index a9e3056a1..e0fd4a198 100644 --- a/Projects/UOContent/Spells/Bushido/SamuraiSpell.cs +++ b/Projects/UOContent/Spells/Bushido/SamuraiSpell.cs @@ -47,18 +47,18 @@ namespace Server.Spells.Bushido { var args = $"{RequiredSkill:0.#}\t{CastSkill.ToString()}\t "; Caster.SendLocalizedMessage( - 1063013, + 1063013, // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. args - ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. + ); return false; } if (Caster.Mana < mana) { Caster.SendLocalizedMessage( - 1060174, + 1060174, // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. mana.ToString() - ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + ); return false; } @@ -72,18 +72,18 @@ namespace Server.Spells.Bushido if (Caster.Skills[CastSkill].Value < RequiredSkill) { Caster.SendLocalizedMessage( - 1070768, + 1070768, // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! RequiredSkill.ToString("F1") - ); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + ); return false; } if (Caster.Mana < mana) { Caster.SendLocalizedMessage( - 1060174, + 1060174, // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. mana.ToString() - ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + ); return false; } @@ -107,20 +107,9 @@ namespace Server.Spells.Bushido public virtual void OnCastSuccessful(Mobile caster) { - if (Evasion.IsEvading(caster)) - { - Evasion.EndEvasion(caster); - } - - if (Confidence.IsConfident(caster)) - { - Confidence.EndConfidence(caster); - } - - if (CounterAttack.IsCountering(caster)) - { - CounterAttack.StopCountering(caster); - } + Evasion.EndEvasion(caster); + Confidence.EndConfidence(caster); + CounterAttack.StopCountering(caster); var spellID = SpellRegistry.GetRegistryNumber(this); diff --git a/Projects/UOContent/Spells/Chivalry/DivineFury.cs b/Projects/UOContent/Spells/Chivalry/DivineFury.cs index 7916a94ac..c29ac7959 100644 --- a/Projects/UOContent/Spells/Chivalry/DivineFury.cs +++ b/Projects/UOContent/Spells/Chivalry/DivineFury.cs @@ -12,7 +12,7 @@ namespace Server.Spells.Chivalry 9002 ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public DivineFurySpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) { @@ -37,12 +37,17 @@ namespace Server.Spells.Chivalry Caster.Stam = Caster.StamMax; - m_Table.TryGetValue(Caster, out var timer); - timer?.Stop(); + RemoveTimer(Caster); var delay = Math.Clamp(ComputePowerValue(10), 7, 24); - m_Table[Caster] = Timer.DelayCall(TimeSpan.FromSeconds(delay), Expire_Callback, Caster); + Timer.StartTimer(TimeSpan.FromSeconds(delay), + () => { StopDivineFury(Caster); }, + out var timerToken + ); + + m_Table[Caster] = timerToken; + Caster.Delta(MobileDelta.WeaponDamage); BuffInfo.AddBuff( @@ -54,14 +59,21 @@ namespace Server.Spells.Chivalry FinishSequence(); } - public static bool UnderEffect(Mobile m) => m_Table.ContainsKey(m); - - private static void Expire_Callback(Mobile m) + private static void RemoveTimer(Mobile m) { - m_Table.Remove(m); + if (m_Table.Remove(m, out var timerToken)) + { + timerToken.Cancel(); + } + } + public static void StopDivineFury(Mobile m) + { + RemoveTimer(m); m.Delta(MobileDelta.WeaponDamage); m.PlaySound(0xF8); } + + public static bool UnderEffect(Mobile m) => m_Table.ContainsKey(m); } } diff --git a/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs b/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs index abc69aad3..5e58b04cc 100644 --- a/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs +++ b/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs @@ -13,7 +13,7 @@ namespace Server.Spells.Chivalry 9002 ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public EnemyOfOneSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) { @@ -36,12 +36,13 @@ namespace Server.Spells.Chivalry Caster.FixedParticles(0x375A, 1, 30, 9966, 33, 2, EffectLayer.Head); Caster.FixedParticles(0x37B9, 1, 30, 9502, 43, 3, EffectLayer.Head); - m_Table.TryGetValue(Caster, out var timer); - timer?.Stop(); + RemoveTimer(Caster); var delay = Math.Clamp(ComputePowerValue(1) / 60.0, 1.5, 3.5); - m_Table[Caster] = Timer.DelayCall(TimeSpan.FromMinutes(delay), Expire_Callback, Caster); + Timer.StartTimer(TimeSpan.FromMinutes(delay), () => Expire_Callback(Caster), out var timerToken); + + m_Table[Caster] = timerToken; if (Caster is PlayerMobile mobile) { @@ -58,9 +59,17 @@ namespace Server.Spells.Chivalry FinishSequence(); } + private static void RemoveTimer(Mobile m) + { + if (m_Table.Remove(m, out var timerToken)) + { + timerToken.Cancel(); + } + } + private static void Expire_Callback(Mobile m) { - m_Table.Remove(m); + RemoveTimer(m); m.PlaySound(0x1F8); diff --git a/Projects/UOContent/Spells/Fifth/Incognito.cs b/Projects/UOContent/Spells/Fifth/Incognito.cs index ed67b9efa..a0eb9b041 100644 --- a/Projects/UOContent/Spells/Fifth/Incognito.cs +++ b/Projects/UOContent/Spells/Fifth/Incognito.cs @@ -19,7 +19,7 @@ namespace Server.Spells.Fifth Reagent.Nightshade ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public IncognitoSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) { @@ -111,7 +111,8 @@ namespace Server.Spells.Fifth var timeVal = Math.Min(6 * Caster.Skills.Magery.Fixed / 50 + 1, 144); var length = TimeSpan.FromSeconds(timeVal); - m_Table[Caster] = Timer.DelayCall(length, EndIncognito, Caster); + Timer.StartTimer(length, () => EndIncognito(Caster), out var timerToken); + m_Table[Caster] = timerToken; BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.Incognito, 1075819, length, Caster)); } @@ -126,16 +127,15 @@ namespace Server.Spells.Fifth public static void StopTimer(Mobile m) { - if (m_Table.Remove(m, out var t)) + if (m_Table.Remove(m, out var timerToken)) { - t.Stop(); - + timerToken.Cancel(); } - + BuffInfo.RemoveBuff(m, BuffIcon.Incognito); } - private static void EndIncognito(Mobile m) + public static void EndIncognito(Mobile m) { if (m.CanBeginAction()) { @@ -148,9 +148,10 @@ namespace Server.Spells.Fifth m.HueMod = -1; m.NameMod = null; m.EndAction(); - BaseArmor.ValidateMobile(m); BaseClothing.ValidateMobile(m); + + StopTimer(m); } } } diff --git a/Projects/UOContent/Spells/Fifth/MindBlast.cs b/Projects/UOContent/Spells/Fifth/MindBlast.cs index f7daa19d7..cc19f0743 100644 --- a/Projects/UOContent/Spells/Fifth/MindBlast.cs +++ b/Projects/UOContent/Spells/Fifth/MindBlast.cs @@ -51,13 +51,10 @@ namespace Server.Spells.Fifth var damage = Math.Min((int)((Caster.Skills.Magery.Value + Caster.Int) / 5), 60); - Timer.DelayCall( - TimeSpan.FromSeconds(1.0), - AosDelay_Callback, - Caster, - target, - m, - damage + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => + { + AosDelay_Callback(Caster, target, m, damage); + } ); } } diff --git a/Projects/UOContent/Spells/Fourth/Curse.cs b/Projects/UOContent/Spells/Fourth/Curse.cs index d27ebc975..73bff2c02 100644 --- a/Projects/UOContent/Spells/Fourth/Curse.cs +++ b/Projects/UOContent/Spells/Fourth/Curse.cs @@ -51,7 +51,7 @@ namespace Server.Spells.Fourth { var duration = SpellHelper.GetDuration(Caster, m); m_UnderEffect.Add(m); - Timer.DelayCall(duration, RemoveEffect, m); + Timer.StartTimer(duration, () => RemoveEffect(m)); m.UpdateResistances(); } diff --git a/Projects/UOContent/Spells/Fourth/ManaDrain.cs b/Projects/UOContent/Spells/Fourth/ManaDrain.cs index ce484bf50..342b4aed4 100644 --- a/Projects/UOContent/Spells/Fourth/ManaDrain.cs +++ b/Projects/UOContent/Spells/Fourth/ManaDrain.cs @@ -62,7 +62,7 @@ namespace Server.Spells.Fourth m.Mana -= toDrain; m_Table.Add(m); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), AosDelay_Callback, m, toDrain); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), () => AosDelay_Callback(m, toDrain)); } } else diff --git a/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs b/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs index 09838188f..408ce92c1 100644 --- a/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs @@ -46,7 +46,7 @@ namespace Server.Spells.Mysticism Caster.MovingParticles(m, 0x407A, 7, 0, false, true, 0, 0, 0xBBE, 0xFA6, 0xFFFF, 0); Caster.PlaySound(0x2EE); - Timer.DelayCall(TimeSpan.FromSeconds(1.0), Damage, m); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), () => Damage(m)); } FinishSequence(); diff --git a/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs b/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs index 002823b6b..d4e14cfe5 100644 --- a/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs +++ b/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs @@ -191,7 +191,7 @@ namespace Server.Spells.Necromancy 0 ); - Timer.DelayCall( + Timer.StartTimer( TimeSpan.FromSeconds(2.0), () => SummonDelay_Callback(Caster, c, p, map, group) ); @@ -279,10 +279,10 @@ namespace Server.Spells.Necromancy if (list.Count > 3) { - Timer.DelayCall(list[0].Kill); + Timer.StartTimer(list[0].Kill); } - Timer.DelayCall(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0), Summoned_Damage, summoned); + Timer.StartTimer(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0), () => Summoned_Damage(summoned)); } private static void Summoned_Damage(Mobile mob) diff --git a/Projects/UOContent/Spells/Necromancy/EvilOmen.cs b/Projects/UOContent/Spells/Necromancy/EvilOmen.cs index 89b509949..789110ffe 100644 --- a/Projects/UOContent/Spells/Necromancy/EvilOmen.cs +++ b/Projects/UOContent/Spells/Necromancy/EvilOmen.cs @@ -66,7 +66,7 @@ namespace Server.Spells.Necromancy var duration = TimeSpan.FromSeconds(Caster.Skills.SpiritSpeak.Value / 12 + 1.0); - Timer.DelayCall(duration, mob => TryEndEffect(mob), m); + Timer.StartTimer(duration, () => TryEndEffect(m)); HarmfulSpell(m); diff --git a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs index 1e8b50f94..78a16e8bf 100644 --- a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs +++ b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs @@ -486,9 +486,9 @@ namespace Server.Spells.Ninjitsu if (mana > m_Caster.Mana) { m_Caster.SendLocalizedMessage( - 1060174, + 1060174, // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. mana.ToString() - ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + ); } else if (m_Caster is PlayerMobile mobile && mobile.MountBlockReason != BlockMountType.None) { @@ -560,51 +560,50 @@ namespace Server.Spells.Ninjitsu { AnimalForm.RemoveContext(m_Mobile, true); Stop(); + return; } - else + + if (m_Body == 0x115) // Cu Sidhe { - if (m_Body == 0x115) // Cu Sidhe + if (m_Counter++ >= 8) { - if (m_Counter++ >= 8) + if (m_Mobile.Hits < m_Mobile.HitsMax && m_Mobile.Backpack != null) { - if (m_Mobile.Hits < m_Mobile.HitsMax && m_Mobile.Backpack != null) + var b = m_Mobile.Backpack.FindItemByType(); + + if (b != null) { - var b = m_Mobile.Backpack.FindItemByType(); - - if (b != null) - { - m_Mobile.Hits += Utility.RandomMinMax(20, 50); - b.Consume(); - } + m_Mobile.Hits += Utility.RandomMinMax(20, 50); + b.Consume(); } - - m_Counter = 0; } + + m_Counter = 0; } - else if (m_Body == 0x114) // Reptalon + } + else if (m_Body == 0x114) // Reptalon + { + if (m_Mobile.Combatant != null && m_Mobile.Combatant != m_LastTarget) { - if (m_Mobile.Combatant != null && m_Mobile.Combatant != m_LastTarget) + m_Counter = 1; + m_LastTarget = m_Mobile.Combatant; + } + + if (m_Mobile.Warmode && m_LastTarget?.Alive == true && m_LastTarget?.Deleted != true && + m_Counter-- <= 0) + { + if (m_Mobile.CanBeHarmful(m_LastTarget) && m_LastTarget.Map == m_Mobile.Map && + m_LastTarget.InRange(m_Mobile.Location, BaseCreature.DefaultRangePerception) && + m_Mobile.InLOS(m_LastTarget)) { - m_Counter = 1; - m_LastTarget = m_Mobile.Combatant; + m_Mobile.Direction = m_Mobile.GetDirectionTo(m_LastTarget); + m_Mobile.Freeze(TimeSpan.FromSeconds(1)); + m_Mobile.PlaySound(0x16A); + + StartTimer(TimeSpan.FromSeconds(1.3), () => BreathEffect_Callback(m_LastTarget)); } - if (m_Mobile.Warmode && m_LastTarget?.Alive == true && m_LastTarget?.Deleted != true && - m_Counter-- <= 0) - { - if (m_Mobile.CanBeHarmful(m_LastTarget) && m_LastTarget.Map == m_Mobile.Map && - m_LastTarget.InRange(m_Mobile.Location, BaseCreature.DefaultRangePerception) && - m_Mobile.InLOS(m_LastTarget)) - { - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_LastTarget); - m_Mobile.Freeze(TimeSpan.FromSeconds(1)); - m_Mobile.PlaySound(0x16A); - - DelayCall(TimeSpan.FromSeconds(1.3), BreathEffect_Callback, m_LastTarget); - } - - m_Counter = Math.Min((int)m_Mobile.GetDistanceToSqrt(m_LastTarget), 10); - } + m_Counter = Math.Min((int)m_Mobile.GetDistanceToSqrt(m_LastTarget), 10); } } } @@ -617,7 +616,7 @@ namespace Server.Spells.Ninjitsu m_Mobile.PlaySound(0x227); Effects.SendMovingEffect(m_Mobile, target, 0x36D4, 5, 0); - DelayCall(TimeSpan.FromSeconds(1), BreathDamage_Callback, target); + StartTimer(TimeSpan.FromSeconds(1), () => BreathDamage_Callback(target)); } } diff --git a/Projects/UOContent/Spells/Ninjitsu/Backstab.cs b/Projects/UOContent/Spells/Ninjitsu/Backstab.cs index 1fbf4b3b3..a7857462f 100644 --- a/Projects/UOContent/Spells/Ninjitsu/Backstab.cs +++ b/Projects/UOContent/Spells/Ninjitsu/Backstab.cs @@ -38,7 +38,7 @@ namespace Server.Spells.Ninjitsu if (valid) { attacker.BeginAction(); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), attacker.EndAction); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), attacker.EndAction); } return valid; diff --git a/Projects/UOContent/Spells/Ninjitsu/SurpriseAttack.cs b/Projects/UOContent/Spells/Ninjitsu/SurpriseAttack.cs index 9874cbb8b..d918509bd 100644 --- a/Projects/UOContent/Spells/Ninjitsu/SurpriseAttack.cs +++ b/Projects/UOContent/Spells/Ninjitsu/SurpriseAttack.cs @@ -34,7 +34,7 @@ namespace Server.Spells.Ninjitsu if (valid) { attacker.BeginAction(); - Timer.DelayCall(TimeSpan.FromSeconds(5.0), attacker.EndAction); + Timer.StartTimer(TimeSpan.FromSeconds(5.0), attacker.EndAction); } return valid; @@ -53,17 +53,14 @@ namespace Server.Spells.Ninjitsu attacker.RevealingAction(); - if (m_Table.Remove(defender, out var info)) - { - info.m_Timer?.Stop(); - } + StopTimer(defender); var ninjitsu = attacker.Skills.Ninjitsu.Fixed; var malus = ninjitsu / 60 + (int)Tracking.GetStalkingBonus(attacker, defender); - info = new SurpriseAttackInfo(defender, malus); - info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(8.0), EndSurprise, info); + var info = new SurpriseAttackInfo(defender, malus); + Timer.StartTimer(TimeSpan.FromSeconds(8.0), () => EndSurprise(info), out info._timerToken); m_Table[defender] = info; @@ -90,19 +87,25 @@ namespace Server.Spells.Ninjitsu return true; } + private static void StopTimer(Mobile m) + { + if (m_Table.Remove(m, out var info)) + { + info._timerToken.Cancel(); + } + } + private static void EndSurprise(SurpriseAttackInfo info) { - info.m_Timer?.Stop(); + StopTimer(info.m_Target); info.m_Target.SendLocalizedMessage(1063131); // Your defenses have returned to normal. - - m_Table.Remove(info.m_Target); } private class SurpriseAttackInfo { public readonly int m_Malus; public readonly Mobile m_Target; - public Timer m_Timer; + public TimerExecutionToken _timerToken; public SurpriseAttackInfo(Mobile target, int effect) { diff --git a/Projects/UOContent/Spells/Seventh/Polymorph.cs b/Projects/UOContent/Spells/Seventh/Polymorph.cs index 70b16f03e..a473a7e04 100644 --- a/Projects/UOContent/Spells/Seventh/Polymorph.cs +++ b/Projects/UOContent/Spells/Seventh/Polymorph.cs @@ -19,7 +19,7 @@ namespace Server.Spells.Seventh Reagent.MandrakeRoot ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); private readonly int m_NewBody; @@ -158,14 +158,7 @@ namespace Server.Spells.Seventh caster.BodyMod = m_NewBody; - if (m_NewBody == 400 || m_NewBody == 401) - { - caster.HueMod = caster.Race.RandomSkinHue(); - } - else - { - caster.HueMod = 0; - } + caster.HueMod = m_NewBody is 400 or 401 ? caster.Race.RandomSkinHue() : 0; BaseArmor.ValidateMobile(caster); BaseClothing.ValidateMobile(caster); @@ -176,7 +169,8 @@ namespace Server.Spells.Seventh var duration = Math.Max((int)caster.Skills.Magery.Value, 120); - m_Table[caster] = Timer.DelayCall(TimeSpan.FromSeconds(duration), EndPolymorph, caster); + Timer.StartTimer(TimeSpan.FromSeconds(duration), () => EndPolymorph(caster), out var timerToken); + m_Table[caster] = timerToken; } } } @@ -193,11 +187,11 @@ namespace Server.Spells.Seventh { if (m_Table.Remove(m, out var timer)) { - timer.Stop(); + timer.Cancel(); } } - private static void EndPolymorph(Mobile m) + public static void EndPolymorph(Mobile m) { if (m.CanBeginAction()) { @@ -206,10 +200,13 @@ namespace Server.Spells.Seventh m.BodyMod = 0; m.HueMod = -1; + m.NameMod = null; m.EndAction(); BaseArmor.ValidateMobile(m); BaseClothing.ValidateMobile(m); + + StopTimer(m); } } } diff --git a/Projects/UOContent/Spells/Sixth/Invisibility.cs b/Projects/UOContent/Spells/Sixth/Invisibility.cs index b9dd8274e..ae69a58fe 100644 --- a/Projects/UOContent/Spells/Sixth/Invisibility.cs +++ b/Projects/UOContent/Spells/Sixth/Invisibility.cs @@ -18,7 +18,7 @@ namespace Server.Spells.Sixth Reagent.Nightshade ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public InvisibilitySpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) { @@ -58,25 +58,28 @@ namespace Server.Spells.Sixth m.Combatant = null; m.Warmode = false; - RemoveTimer(m); + StopTimer(m); var duration = TimeSpan.FromSeconds(1.2 * Caster.Skills.Magery.Fixed / 10); BuffInfo.RemoveBuff(m, BuffIcon.HidingAndOrStealth); BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Invisibility, 1075825, duration, m)); // Invisibility/Invisible - m_Table[m] = Timer.DelayCall(duration, EndInvisiblity, m); + Timer.StartTimer(duration, + () => + { + m.RevealingAction(); + StopTimer(m); + }, + out var timerToken + ); + + m_Table[m] = timerToken; } FinishSequence(); } - private static void EndInvisiblity(Mobile m) - { - m.RevealingAction(); - RemoveTimer(m); - } - public override bool CheckCast() { if (DuelContext.CheckSuddenDeath(Caster)) @@ -95,11 +98,11 @@ namespace Server.Spells.Sixth public static bool HasTimer(Mobile m) => m_Table.ContainsKey(m); - public static void RemoveTimer(Mobile m) + public static void StopTimer(Mobile m) { - if (m_Table.Remove(m, out var t)) + if (m_Table.Remove(m, out var timerToken)) { - t.Stop(); + timerToken.Cancel(); } } } diff --git a/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs b/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs index 79e9f5b4f..ce1db2da5 100644 --- a/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs +++ b/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs @@ -131,7 +131,7 @@ namespace Server.Spells.Spellweaving m_Table.Remove(m_Mobile); - DelayCall(TimeSpan.FromSeconds(120), m_Mobile.EndAction); + StartTimer(TimeSpan.FromSeconds(120), m_Mobile.EndAction); BuffInfo.RemoveBuff(m_Mobile, BuffIcon.AttuneWeapon); } } diff --git a/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs b/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs index 8d7926fd3..a68b50207 100644 --- a/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs +++ b/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs @@ -67,7 +67,7 @@ namespace Server.Spells.Spellweaving var duration = TimeSpan.FromSeconds(12 + (int)(skill / 24) + FocusLevel * 2); - Timer.DelayCall(duration, RemoveEffect, Caster); + Timer.StartTimer(duration, () => RemoveEffect(Caster)); Caster.BeginAction( typeof(EtherealVoyageSpell) @@ -82,7 +82,7 @@ namespace Server.Spells.Spellweaving TransformationSpellHelper.RemoveContext(m, true); - Timer.DelayCall(TimeSpan.FromMinutes(5), m.EndAction); + Timer.StartTimer(TimeSpan.FromMinutes(5), m.EndAction); BuffInfo.RemoveBuff(m, BuffIcon.EtherealVoyage); } diff --git a/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs b/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs index 3ec45eab2..06ccae164 100644 --- a/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs +++ b/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs @@ -95,7 +95,7 @@ namespace Server.Spells.Spellweaving { if (m_Table.ContainsKey(m)) { - Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(2, 4)), HandleDeath_OnCallback, m); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(2, 4)), () => HandleDeath_OnCallback(m)); } } diff --git a/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs b/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs index e29f524f8..692c909a4 100644 --- a/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs +++ b/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs @@ -91,7 +91,7 @@ namespace Server.Spells.Spellweaving if (m_Table.Remove(m, out var timer)) { timer.Stop(); - Timer.DelayCall(TimeSpan.FromSeconds(60), timer.m_Caster.EndAction); + Timer.StartTimer(TimeSpan.FromSeconds(60), timer.m_Caster.EndAction); return true; } diff --git a/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs b/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs index 77d560428..929f077f8 100644 --- a/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs +++ b/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs @@ -75,7 +75,7 @@ namespace Server.Spells.Spellweaving { timer.Stop(); - Timer.DelayCall(TimeSpan.FromSeconds(0.25), FinishEffect, target, timer); + Timer.StartTimer(TimeSpan.FromSeconds(0.25), () => FinishEffect(target, timer)); } } diff --git a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs index 8a4facc6d..49781eae5 100644 --- a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs +++ b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs @@ -4,7 +4,7 @@ namespace Server.Items { public class TransientItem : Item { - private Timer m_Timer; + private TimerExecutionToken _timerToken; [Constructible] public TransientItem(int itemID, TimeSpan lifeSpan) @@ -13,7 +13,7 @@ namespace Server.Items CreationTime = Core.Now; LifeSpan = lifeSpan; - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), CheckExpiry); + Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), CheckExpiry, out _timerToken); } public TransientItem(Serial serial) @@ -60,8 +60,7 @@ namespace Server.Items public override void OnDelete() { - m_Timer?.Stop(); - + _timerToken.Cancel(); base.OnDelete(); } @@ -103,7 +102,7 @@ namespace Server.Items LifeSpan = reader.ReadTimeSpan(); CreationTime = reader.ReadDateTime(); - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), CheckExpiry); + Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), CheckExpiry, out _timerToken); } } } diff --git a/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs b/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs index 10a783758..0c0d7f7ac 100644 --- a/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs +++ b/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs @@ -55,7 +55,7 @@ namespace Server.Mobiles public override void MoveToWorld(Point3D loc, Map map) { base.MoveToWorld(loc, map); - Timer.DelayCall(DoEffects); + Timer.StartTimer(DoEffects); } public void DoEffects() @@ -66,7 +66,7 @@ namespace Server.Mobiles if (Alive && !Deleted) { - Timer.DelayCall(TimeSpan.FromSeconds(7.0), DoEffects); + Timer.StartTimer(TimeSpan.FromSeconds(7.0), DoEffects); } } diff --git a/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs b/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs index 775a7c356..38fa38cff 100644 --- a/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs +++ b/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs @@ -11,7 +11,7 @@ namespace Server.Spells.Spellweaving -1 ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public ThunderstormSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) @@ -63,7 +63,10 @@ namespace Server.Spells.Spellweaving continue; } - m_Table[m] = Timer.DelayCall(duration, DoExpire, m); + StopTimer(m); + + Timer.StartTimer(duration, () => DoExpire(m), out var timerToken); + m_Table[m] = timerToken; BuffInfo.AddBuff( m, @@ -79,15 +82,17 @@ namespace Server.Spells.Spellweaving public static int GetCastRecoveryMalus(Mobile m) => m_Table.ContainsKey(m) ? 6 : 0; + private static void StopTimer(Mobile m) + { + if (m_Table.Remove(m, out var timerToken)) + { + timerToken.Cancel(); + } + } + public static void DoExpire(Mobile m) { - if (!m_Table.Remove(m, out var t)) - { - return; - } - - t.Stop(); - + StopTimer(m); BuffInfo.RemoveBuff(m, BuffIcon.Thunderstorm); } } diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index e2af2e833..0cfa74479 100755 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -35,7 +35,7 @@ false - + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f94c4f523..dbaaf944a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,7 @@ jobs: displayName: 'Install .NET 5' inputs: packageType: sdk - version: 5.0.301 + version: 5.0.302 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release win displayName: 'Build' @@ -62,7 +62,7 @@ jobs: displayName: 'Install .NET 5' inputs: packageType: sdk - version: 5.0.301 + version: 5.0.302 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release $(os) displayName: 'Build'