## Summary - Replaces scan-based decay checking during world saves with an event-driven timer wheel scheduler - Removes virtual property checks from serialization hot path, achieving 6-8x faster world saves ## Changes ### DecayScheduler (new): - Timer wheel with 12 HashSet buckets (5-min intervals) + PriorityQueue for active processing - O(1) register/unregister vs O(n) scan of all items - Auto start/stop when items exist/empty - Configurable tick interval with jitter to prevent system synchronization ### Item.cs: - Added ScheduledDecayTime computed property - Added UpdateDecayRegistration() called from SetLastMoved(), property setters, AddItem()/RemoveItem() - Hooks in Visible, Movable, Spawner setters and Delete() ### World.cs: - Removed _decayQueue, EnqueueForDecay(), ProcessDecay() - ItemPersistence.Serialize() now tight loop without virtual calls ## Performance | Metric | Before | After | Improvement | |---------------|------------|-----------|-------------| | Items (230K) | 245K ticks | 34K ticks | 8x faster | | Mobiles (43K) | 56K ticks | 6K ticks | 9x faster | | Total | 302K ticks | 40K ticks | 7.5x faster | ## Configuration decay.maxItemsPerTick = 250 # Items processed per tick decay.tickInterval = 256ms # Base processing interval decay.bucketInterval = 5min # Timer wheel bucket size decay.jitterMaxMs = 25 # ±25ms tick jitter
59 lines
1.4 KiB
C#
59 lines
1.4 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using Server.Items;
|
|
using Server.Misc;
|
|
using Xunit;
|
|
|
|
namespace Server.Tests;
|
|
|
|
[CollectionDefinition("Sequential UOContent Tests", DisableParallelization = true)]
|
|
public class UOContentFixture : ICollectionFixture<UOContentFixture>, IDisposable
|
|
{
|
|
public UOContentFixture()
|
|
{
|
|
Core.ApplicationAssembly = Assembly.GetExecutingAssembly();
|
|
Core.LoopContext = new EventLoopContext();
|
|
Core.Expansion = Expansion.EJ;
|
|
|
|
// Load Configurations
|
|
ServerConfiguration.Load(true);
|
|
|
|
// Load UOContent.dll into the type resolver
|
|
ServerConfiguration.AssemblyDirectories.Add(Core.BaseDirectory);
|
|
AssemblyHandler.LoadAssemblies(["Server.dll", "UOContent.dll"]);
|
|
|
|
// Load Skills
|
|
SkillsInfo.Configure();
|
|
|
|
// Configure / Initialize
|
|
TestMapDefinitions.ConfigureTestMapDefinitions();
|
|
|
|
// Configure the world
|
|
World.Configure();
|
|
|
|
Timer.Init(0);
|
|
|
|
// Configure Races
|
|
RaceDefinitions.Configure();
|
|
|
|
// Load the world
|
|
World.Load();
|
|
|
|
World.ExitSerializationThreads();
|
|
|
|
DecayScheduler.Configure();
|
|
}
|
|
|
|
private static int _counter;
|
|
|
|
public void Dispose()
|
|
{
|
|
_counter++;
|
|
|
|
if (_counter > 1)
|
|
{
|
|
throw new Exception("NO!");
|
|
}
|
|
Timer.Init(0);
|
|
}
|
|
}
|