diff --git a/Intersect.Server.Core/Core/LogicService.LogicThread.cs b/Intersect.Server.Core/Core/LogicService.LogicThread.cs index 1a40c4ff75..819d10ffa3 100644 --- a/Intersect.Server.Core/Core/LogicService.LogicThread.cs +++ b/Intersect.Server.Core/Core/LogicService.LogicThread.cs @@ -11,7 +11,6 @@ using Intersect.Server.Metrics; using Intersect.Server.Networking; using Intersect.Server.Database.PlayerData.Players; -using Intersect.Utilities; using Intersect.Server.Database.PlayerData.Api; using Intersect.Server.Core.MapInstancing; @@ -68,6 +67,12 @@ public LogicThread(LogicService logicService) : base("ServerLogic") /// public readonly Dictionary ActiveMapInstances = new Dictionary(); + // Reusable buffer for UpdateInstanceControllers — avoids ToArray() every 250ms. + private MapInstance[] _activeMapInstancesBuffer = Array.Empty(); + + // Track last console title to avoid redundant syscalls every second. + private string _lastConsoleTitle = string.Empty; + protected override void ThreadStart(ServerContext serverContext) { if (serverContext == null) @@ -87,6 +92,7 @@ protected override void ThreadStart(ServerContext serverContext) var processedMapInstances = new HashSet(); var sourceMapInstances = new HashSet(); + var toRemove = new List(); var players = 0; while (ServerContext.Instance.IsRunning) @@ -128,7 +134,12 @@ protected override void ThreadStart(ServerContext serverContext) if (Options.Instance.Metrics.Enable) { events += player.EventLookup.Count; - eventsProcessing += player.EventLookup.Values.Where(e => e.CallStack?.Count > 0).Count(); + + foreach (var evt in player.EventLookup.Values) + { + if (evt.CallStack?.Count > 0) eventsProcessing++; + } + autorunEvents += player.CommonAutorunEvents + player.MapAutorunEvents; } } @@ -143,7 +154,7 @@ protected override void ThreadStart(ServerContext serverContext) { if (!processedMapInstances.Contains(instance.Id)) { - if (!ActiveMapInstances.Keys.Contains(instance.Id)) + if (!ActiveMapInstances.ContainsKey(instance.Id)) // ContainsKey() directly instead of .Keys.Contains(). { AddToQueue(instance); } @@ -157,8 +168,9 @@ protected override void ThreadStart(ServerContext serverContext) } } - //Refresh list of active maps & their instances - foreach (var (instanceId, mapInstance) in ActiveMapInstances.ToArray()) + // Iterate dictionary directly; collect removals in reusable list. + toRemove.Clear(); + foreach (var (instanceId, mapInstance) in ActiveMapInstances) { if (processedMapInstances.Contains(instanceId) || mapInstance.ShouldBeActive()) { @@ -176,12 +188,23 @@ protected override void ThreadStart(ServerContext serverContext) mapInstance.ResetNpcSpawns(); } } + toRemove.Add(instanceId); + } - ActiveMapInstances.Remove(instanceId); + foreach (var id in toRemove) + { + ActiveMapInstances.Remove(id); } - // Allow our instance controllers to keep their instances up to date - InstanceProcessor.UpdateInstanceControllers(ActiveMapInstances.Values.ToArray()); + // Reuse buffer; only reallocate when count changes. + var activeCount = ActiveMapInstances.Count; + if (_activeMapInstancesBuffer.Length != activeCount) + { + _activeMapInstancesBuffer = new MapInstance[activeCount]; + } + + ActiveMapInstances.Values.CopyTo(_activeMapInstancesBuffer, 0); + InstanceProcessor.UpdateInstanceControllers(_activeMapInstancesBuffer); if (Options.Instance.Metrics.Enable) { @@ -237,7 +260,14 @@ protected override void ThreadStart(ServerContext serverContext) swCps = 0; var cyclesPerSecond = ApplicationContext.GetCurrentContext().LogicService.CyclesPerSecond; - Console.Title = $"Intersect Server - CPS: {cyclesPerSecond}, Players: {players}, Active Maps: {ActiveMapInstances.Count}, Logic Threads: {LogicPool.ActiveThreads} ({LogicPool.InUseThreads} In Use), Pool Queue: {LogicPool.CurrentWorkItemsCount}, Idle: {LogicPool.IsIdle}"; + + // Only call Console.Title when the value actually changed. + var newTitle = $"Intersect Server - CPS: {cyclesPerSecond}, Players: {players}, Active Maps: {ActiveMapInstances.Count}, Logic Threads: {LogicPool.ActiveThreads} ({LogicPool.InUseThreads} In Use), Pool Queue: {LogicPool.CurrentWorkItemsCount}, Idle: {LogicPool.IsIdle}"; + if (newTitle != _lastConsoleTitle) + { + Console.Title = newTitle; + _lastConsoleTitle = newTitle; + } if (Options.Instance.Metrics.Enable) { @@ -368,7 +398,7 @@ private void UpdateMap(MapInstance mapInstance, bool onlyProjectiles) if (onlyProjectiles) { mapInstance.UpdateProjectiles(Timing.Global.Milliseconds); - if (ActiveMapInstances.Keys.Contains(mapInstance.Id)) + if (ActiveMapInstances.ContainsKey(mapInstance.Id)) { MapInstanceProjectileQueue.Enqueue(mapInstance); } @@ -392,7 +422,7 @@ private void UpdateMap(MapInstance mapInstance, bool onlyProjectiles) mapInstance.Update(Timing.Global.Milliseconds); } - if (ActiveMapInstances.Keys.Contains(mapInstance.Id)) + if (ActiveMapInstances.ContainsKey(mapInstance.Id)) { MapInstanceUpdateQueue.Enqueue(mapInstance); }