From 9b554f69b0c2406a218156a02a847ee876c3acf7 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 7 Aug 2021 14:33:35 -0700 Subject: [PATCH] feat(timers): Adds timer pooling, fixes timer related bugs, and changes timer api (#667) ### Changes/Fixes: * Adds timer pooling. * Allows pool to be configurable in ModernUO.json * Pool replenishes itself asynchronously if depleted. * Fixes an issue with barkeeps and town criers * Fixes an issue with incognito buff icons not being removed * Fixes an issue with polymorph name mod not being removed * Fixes several places where timers go on forever even after an object is deleted, keeping a reference (memory leak) * Eliminates the timer for MiningCart altogether. * Deletes `AcidSlime` since it is a duplicate of `PoolOfAcid` * Fixes HonorableExecution and standardizes the code for other Bushido moves. ## Changes to the Timer API: ```cs public class Timer { // Creates a timer that will be returned to the pool once execution stops. public static void StartTimer(Action callback); public static void StartTimer(TimeSpan delay, Action callback); public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback); public static void StartTimer(TimeSpan interval, int count, Action callback); public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback); // Creates a timer and returns a token for more control. Requires manual cancellation in order for the timer to be returned to the pool. // If the token is dereferenced, the timer will be dereferenced too. While not returning a timer to the pool is not considered hazardous, it does defeat the purpose of pooled timers. public static void StartTimer(Action callback, out TimerExecutionToken token); public static void StartTimer(TimeSpan delay, Action callback, out TimerExecutionToken token); public static void StartTimer(TimeSpan delay, TimeSpan interval, Action callback, out TimerExecutionToken token); public static void StartTimer(TimeSpan interval, int count, Action callback, out TimerExecutionToken token); public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, out TimerExecutionToken token); // If you aren't sure how to use the API above, or you don't care about performance, then you can use the old RunUO Timer.DelayCall public static DelayCallTimer DelayCall(Action callback); public static DelayCallTimer DelayCall(TimeSpan delay, Action callback); public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, Action callback); public static DelayCallTimer DelayCall(TimeSpan interval, int count, Action callback); public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback); } public struct TimerExecutionToken { public bool Running { get; } public int Index { get; } public int RemainingCount { get; } public DateTime Next { get; } } ``` ## When to use `TimerExecutionToken`? Use tokens when you want to gain the performance benefit of using a pooled timer, but you need one of the following: * Access to the next time the timer will tick:`token.Next` * Access to which interval, how many intervals there are, or how many are remaining: `token.Index`, `token.Count`, and `token.RemainingCount` * Stop a timer manually. * Determine if the timer is running: `timer.Running` * See notes below about requirements for using tokens! ## Notes about using the TimerExecutionToken: When you opt-in to receive a token, you must call `Cancel()` to return the timer. This can be done inside of the callback, or outside of the callback at any time. If this is not called and your timer is an infinite interval, then you will create a potential memory leak, or null pointer exception in your callback. If the timer ends and is stopped, but cancel is not called, then the timer will never return to the pool and stay referenced until the token is deleted or cancel is called. (Memory leak) ## Is this thread safe? No. The ModernUO timer system is not thread safe at all. If you require a thread safe timer system, contact me and I'll help adapt this system. Keep in mind that there is a massive performance hit to make this thread safe when there are literally no use cases for it. If you need to synchronize execution, meaning you want to execute code from another thread on the core thread. Let's say you have a discord bot that is pushing commands to the game server. Then use `EventLoopContext.Post(SendOrPostCallback callback, object state);`. --- .github/workflows/build-test.yml | 2 +- Projects/Benchmarks/Benchmarks.csproj | 2 +- .../Server.Tests/Fixtures/ServerFixture.cs | 2 + .../Server.Tests/Tests/Timer/TimerTests.cs | 6 +- Projects/Server/EventLoopTasks.cs | 2 + Projects/Server/IEntity.cs | 2 +- Projects/Server/Items/Item.cs | 2 +- Projects/Server/Main.cs | 10 +- Projects/Server/Mobiles/Mobile.cs | 291 +++++---------- Projects/Server/Network/NetState/NetState.cs | 2 +- Projects/Server/SecureTrade.cs | 4 +- Projects/Server/Timer/Timer.DelayCall.cs | 334 ++++++++---------- Projects/Server/Timer/Timer.Pause.cs | 53 --- Projects/Server/Timer/Timer.Pool.cs | 128 +++++++ Projects/Server/Timer/Timer.TimerWheel.cs | 29 +- Projects/Server/Timer/Timer.cs | 24 +- Projects/Server/Timer/TimerExecutionToken.cs | 60 ++++ Projects/Server/World/World.cs | 2 +- Projects/UOContent/Accounting/Account.cs | 2 +- .../UOContent/Engines/Bulk Orders/BaseBOD.cs | 2 +- .../Engines/CannedEvil/ChampionSpawn.cs | 44 +-- .../Engines/ConPVP/AcceptDuelGump.cs | 2 +- Projects/UOContent/Engines/ConPVP/Arena.cs | 4 +- .../UOContent/Engines/ConPVP/DuelContext.cs | 80 ++--- .../Engines/ConPVP/Games/BombingRun.cs | 34 +- .../UOContent/Engines/ConPVP/Games/CTF.cs | 28 +- .../Engines/ConPVP/Games/DoubleDom.cs | 69 +--- .../Engines/ConPVP/Games/KingOfTheHill.cs | 30 +- .../Engines/ConPVP/Gumps/AcceptTeamGump.cs | 2 +- .../UOContent/Engines/ConPVP/Tournament.cs | 25 +- .../Engines/ConPVP/TournamentRegistrar.cs | 6 +- .../UOContent/Engines/Craft/Core/Repair.cs | 2 +- .../UOContent/Engines/Doom/GauntletSpawner.cs | 7 +- .../Doom/LeverPuzzle/LeverPuzzleController.cs | 15 +- .../Doom/LeverPuzzle/LeverPuzzleItems.cs | 2 +- .../UOContent/Engines/Ethics/Core/Ethic.cs | 2 +- .../Engines/Factions/Core/Election.cs | 8 +- .../Engines/Factions/Core/Faction.cs | 10 +- .../Engines/Factions/Core/FactionState.cs | 2 +- .../Engines/Factions/Core/Keywords.cs | 2 +- .../UOContent/Engines/Factions/Core/Town.cs | 12 +- .../Power Faction Items/ClarityPotion.cs | 2 +- .../Power Faction Items/PowerFactionItem.cs | 4 +- .../Items/Power Faction Items/StormsEye.cs | 6 +- .../Factions/Items/Traps/BaseFactionTrap.cs | 26 +- .../Mobiles/Guards/BaseFactionGuard.cs | 2 +- Projects/UOContent/Engines/Harvest/Fishing.cs | 2 +- .../UOContent/Engines/Khaldun/RaisableItem.cs | 3 +- .../UOContent/Engines/Khaldun/RaiseSwitch.cs | 2 +- .../Engines/ML Quests/Gumps/RaceChangeGump.cs | 6 +- .../Engines/ML Quests/MLQuestEntry.cs | 12 +- .../Engines/ML Quests/Mobiles/SirHelper.cs | 2 +- .../ML Quests/Objectives/EscortObjective.cs | 10 +- .../Quests/Core/Items/HornOfRetreat.cs | 10 +- .../Engines/Quests/Core/QuestSystem.cs | 57 +-- .../Quests/Dark Tides/Items/MaabusCoffin.cs | 4 +- .../Dark Tides/Mobiles/SummonedPaladin.cs | 4 +- .../Items/HaochisTreasureChest.cs | 4 +- .../Study of the Solen Hive/Objectives.cs | 35 +- .../The Summoning/Items/BellOfTheDead.cs | 2 +- .../Quests/The Summoning/Mobiles/Chyloth.cs | 6 +- .../Witch Apprentice/Mobiles/Blackheart.cs | 2 +- .../Quests/Witch Apprentice/Objectives.cs | 4 +- .../Character Statue Maker/CharacterStatue.cs | 2 +- .../CharacterStatuePlinth.cs | 2 +- Projects/UOContent/Engines/Virtues/Honor.cs | 4 +- Projects/UOContent/Engines/Virtues/Justice.cs | 2 +- .../UOContent/Engines/Virtues/Sacrifice.cs | 2 +- Projects/UOContent/Gumps/ReportMurderer.cs | 2 +- .../Halloween/2006/Engines/TrickOrTreat.cs | 14 +- .../Halloween/2009/Engines/PumpkinPatch.cs | 11 +- .../Halloween/2011/Mobiles/PumpkinHead.cs | 16 +- .../Halloween/2012/Engines/PlayerZombies.cs | 103 +++--- .../Items/Addons/FlourMillEastAddon.cs | 12 +- .../Items/Addons/FlourMillSouthAddon.cs | 12 +- .../UOContent/Items/Addons/JackOLantern.cs | 2 +- .../Items/Addons/RejuvinationAnkhs.cs | 2 +- Projects/UOContent/Items/Aquarium/Aquarium.cs | 23 +- Projects/UOContent/Items/Aquarium/BaseFish.cs | 14 +- .../Items/Containers/FillableContainers.cs | 33 +- .../UOContent/Items/Containers/Strongbox.cs | 2 +- .../StealableArtifactsSpawner.cs | 13 +- .../UOContent/Items/Farming/FarmableCrop.cs | 2 +- Projects/UOContent/Items/Guilds/Guildstone.cs | 2 +- Projects/UOContent/Items/Maps/TreasureMap.cs | 2 +- Projects/UOContent/Items/Misc/AcidSlime.cs | 106 ------ Projects/UOContent/Items/Misc/Bola.cs | 4 +- .../UOContent/Items/Misc/DeceitBrazier.cs | 25 +- .../UOContent/Items/Misc/EffectController.cs | 8 +- Projects/UOContent/Items/Misc/Firebomb.cs | 52 +-- Projects/UOContent/Items/Misc/Guillotine.cs | 6 +- Projects/UOContent/Items/Misc/MorphItem.cs | 2 +- Projects/UOContent/Items/Misc/PoolOfAcid.cs | 9 +- Projects/UOContent/Items/Misc/Teleporter.cs | 47 +-- Projects/UOContent/Items/Misc/WarningItem.cs | 2 +- .../Items/Skill Items/Camping/Bedroll.cs | 6 +- .../Items/Skill Items/Camping/Campfire.cs | 6 +- .../Carpenter Items/TaxidermyKit.cs | 2 +- .../Fishing/Misc/SpecialFishingNet.cs | 2 +- .../Skill Items/Magical/Misc/PotionKeg.cs | 2 +- .../BaseConflagrationPotion.cs | 18 +- .../BaseConfusionBlastPotion.cs | 18 +- .../Explosion Potions/BaseExplosionPotion.cs | 33 +- .../Potions/Heal Potions/BaseHealPotion.cs | 2 +- .../Magical/Potions/InvisibilityPotion.cs | 9 +- .../Items/Skill Items/Misc/FireHorn.cs | 14 +- .../Musical Instruments/BaseInstrument.cs | 21 +- .../Skill Items/Ninjitsu/NinjaWeapons.cs | 9 +- .../Dawn's Music Box/DawnsMusicBox.cs | 12 +- .../8th Anniversary Items/FountainOfLife.cs | 12 +- .../CreepyPortrait.cs | 4 +- .../DisturbingPortrait.cs | 18 +- .../SacrificialAltar.cs | 22 +- .../UnsettlingPortrait.cs | 20 +- .../Special/Heritage Items/FruitTrees.cs | 4 +- .../Special/Heritage Items/Guillotine.cs | 30 +- .../Special/Heritage Items/IronMaiden.cs | 13 +- .../Special/Holiday/Christmas/HolidayTree.cs | 2 +- .../Items/Special/Holiday/IcyPatch.cs | 34 +- .../UOContent/Items/Special/Holiday/Wreath.cs | 2 +- .../Special/House Raffle/HouseRaffleStone.cs | 2 +- .../Items/Special/ML/GrizzledMareStatuette.cs | 2 +- .../Special/Mutation Core/PlagueBeastBlood.cs | 113 +++--- .../Mutation Core/PlagueBeastMutationCore.cs | 14 +- .../Mutation Core/PlagueBeastOrgans.cs | 52 +-- .../Special/Mutation Core/PlagueBeastVein.cs | 29 +- .../Special Scrolls/ScrollofAlacrity.cs | 2 +- .../Special/Veteran Rewards/MiningCart.cs | 282 ++++++++------- .../Special/Veteran Rewards/TreeStump.cs | 21 +- .../UOContent/Items/Talismans/BaseTalisman.cs | 19 +- .../Items/Talismans/TalismanSummons.cs | 4 +- .../UOContent/Items/Traps/FlameSpurtTrap.cs | 11 +- .../UOContent/Items/Traps/MushroomTrap.cs | 2 +- Projects/UOContent/Items/Traps/SpikeTrap.cs | 4 +- .../UOContent/Items/Traps/StoneFaceTrap.cs | 4 +- Projects/UOContent/Items/Wands/BaseWand.cs | 9 +- .../Items/Weapons/Abilities/DefenseMastery.cs | 6 +- .../Weapons/Abilities/FrenziedWhirlwind.cs | 2 +- .../Items/Weapons/Abilities/MortalStrike.cs | 26 +- .../Items/Weapons/Abilities/ParalyzingBlow.cs | 16 +- .../Items/Weapons/Maces/FireworksWand.cs | 2 +- .../Items/Weapons/Ranged/BaseRanged.cs | 15 +- Projects/UOContent/Misc/AOS.cs | 24 +- Projects/UOContent/Misc/AutoRestart.cs | 4 +- Projects/UOContent/Misc/AutoSave.cs | 2 +- Projects/UOContent/Misc/BuffIcons.cs | 8 +- Projects/UOContent/Misc/CharacterCreation.cs | 6 +- Projects/UOContent/Misc/Cleanup.cs | 2 +- Projects/UOContent/Misc/ClientVerification.cs | 4 +- .../Misc/Gifts/Winter2004/Mistletoe.cs | 2 +- Projects/UOContent/Misc/Guild.cs | 2 +- Projects/UOContent/Misc/ShardPoller.cs | 11 +- Projects/UOContent/Misc/Weather.cs | 2 +- Projects/UOContent/Mobiles/AI/BaseAI.cs | 2 +- .../UOContent/Mobiles/Animals/Mounts/Hiryu.cs | 2 +- .../Mobiles/Animals/Mounts/LesserHiryu.cs | 2 +- Projects/UOContent/Mobiles/BaseCreature.cs | 33 +- .../UOContent/Mobiles/Familiars/ShadowWisp.cs | 2 +- .../Mobiles/Monsters/AOS/DemonKnight.cs | 2 +- .../Mobiles/Monsters/AOS/ShadowKnight.cs | 13 +- .../Monsters/Humanoid/Magic/Betrayer.cs | 2 +- .../Monsters/Humanoid/Magic/SavageShaman.cs | 2 +- .../Monsters/Humanoid/Melee/Juggernaut.cs | 2 +- .../Mobiles/Monsters/LBR/Jukas/JukaMage.cs | 11 +- .../Mobiles/Monsters/LBR/Meers/MeerEternal.cs | 6 +- .../Mobiles/Monsters/LBR/Meers/MeerMage.cs | 77 ++-- .../Mobiles/Monsters/ML/Animal/Ferret.cs | 4 +- .../Mobiles/Monsters/ML/Special/Ilhenir.cs | 30 +- .../Mobiles/Monsters/ML/Special/Meraktus.cs | 2 +- .../Monsters/Mammal/Melee/VorpalBunny.cs | 6 +- .../Mobiles/Monsters/Misc/Melee/Golem.cs | 2 +- .../Monsters/Misc/Melee/PlagueBeastLord.cs | 16 +- .../Mobiles/Monsters/SE/BakeKitsune.cs | 13 +- .../UOContent/Mobiles/Monsters/SE/Kappa.cs | 5 +- Projects/UOContent/Mobiles/PlayerMobile.cs | 41 ++- .../Mobiles/Townfolk/BaseEscortable.cs | 2 +- .../UOContent/Mobiles/Townfolk/TownCrier.cs | 52 ++- .../UOContent/Mobiles/Vendors/BaseVendor.cs | 2 +- .../UOContent/Mobiles/Vendors/GenericBuy.cs | 17 +- .../Mobiles/Vendors/PlayerBarkeeper.cs | 21 +- .../UOContent/Mobiles/Vendors/PlayerVendor.cs | 8 +- .../Mobiles/Vendors/VendorInventory.cs | 2 +- Projects/UOContent/Multis/Boats/BaseBoat.cs | 80 ++--- Projects/UOContent/Multis/Camps/BaseCamp.cs | 9 +- Projects/UOContent/Multis/Houses/BaseHouse.cs | 16 +- .../UOContent/Multis/Houses/MovingCrate.cs | 4 +- .../UOContent/Multis/Houses/PreviewHouse.cs | 2 +- Projects/UOContent/Skills/AnimalTaming.cs | 2 +- Projects/UOContent/Skills/Discordance.cs | 10 +- .../Special Systems/Engines/GiftGiving.cs | 2 +- .../UOContent/Spells/Bushido/Confidence.cs | 104 +++--- .../UOContent/Spells/Bushido/CounterAttack.cs | 39 +- Projects/UOContent/Spells/Bushido/Evasion.cs | 53 ++- .../Spells/Bushido/HonorableExecution.cs | 9 +- .../UOContent/Spells/Bushido/SamuraiSpell.cs | 33 +- .../UOContent/Spells/Chivalry/DivineFury.cs | 28 +- .../UOContent/Spells/Chivalry/EnemyOfOne.cs | 19 +- Projects/UOContent/Spells/Fifth/Incognito.cs | 17 +- Projects/UOContent/Spells/Fifth/MindBlast.cs | 11 +- Projects/UOContent/Spells/Fourth/Curse.cs | 2 +- Projects/UOContent/Spells/Fourth/ManaDrain.cs | 2 +- .../Spells/Mysticism/EagleStrikeSpell.cs | 2 +- .../Spells/Necromancy/AnimateDeadSpell.cs | 6 +- .../UOContent/Spells/Necromancy/EvilOmen.cs | 2 +- .../UOContent/Spells/Ninjitsu/AnimalForm.cs | 71 ++-- .../UOContent/Spells/Ninjitsu/Backstab.cs | 2 +- .../Spells/Ninjitsu/SurpriseAttack.cs | 25 +- .../UOContent/Spells/Seventh/Polymorph.cs | 21 +- .../UOContent/Spells/Sixth/Invisibility.cs | 27 +- .../Spells/Spellweaving/AttuneWeapon.cs | 2 +- .../Spells/Spellweaving/EtherealVoyage.cs | 4 +- .../Spells/Spellweaving/GiftOfLife.cs | 2 +- .../Spells/Spellweaving/GiftOfRenewal.cs | 2 +- .../Spells/Spellweaving/ImmolatingWeapon.cs | 2 +- .../Spellweaving/Items/TransientItem.cs | 9 +- .../Spells/Spellweaving/Mobiles/NatureFury.cs | 4 +- .../Spells/Spellweaving/Thunderstorm.cs | 23 +- Projects/UOContent/UOContent.csproj | 2 +- azure-pipelines.yml | 4 +- 219 files changed, 1897 insertions(+), 2241 deletions(-) delete mode 100644 Projects/Server/Timer/Timer.Pause.cs create mode 100644 Projects/Server/Timer/Timer.Pool.cs create mode 100644 Projects/Server/Timer/TimerExecutionToken.cs delete mode 100644 Projects/UOContent/Items/Misc/AcidSlime.cs 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'