fix: Eliminates allocations in canned evil timer (#2209)

This commit is contained in:
Kamron Batman 2025-06-02 22:48:14 -10:00 committed by GitHub
parent 0d2ed60fed
commit ea4080ce0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 145 additions and 146 deletions

View file

@ -15,6 +15,7 @@
using System;
using System.Collections.Generic;
using Server.Collections;
using Server.Misc;
namespace Server.Engines.CannedEvil
@ -29,8 +30,8 @@ namespace Server.Engines.CannedEvil
Instance.OnTick();
}
private static readonly HashSet<DungeonChampionSpawn> _dungeonSpawns = new();
private static readonly HashSet<LLChampionSpawn> _lostLandsSpawns = new();
private static readonly HashSet<ChampionSpawn> _dungeonSpawns = new();
private static readonly HashSet<ChampionSpawn> _lostLandsSpawns = new();
private static DateTime _sliceTime;
public static CannedEvilTimer Instance { get; private set; }
@ -38,25 +39,25 @@ namespace Server.Engines.CannedEvil
public static void AddSpawn(DungeonChampionSpawn spawn)
{
_dungeonSpawns.Add(spawn);
Instance?.OnSlice(_dungeonSpawns, false);
OnSlice(_dungeonSpawns, false);
}
public static void AddSpawn(LLChampionSpawn spawn)
{
_lostLandsSpawns.Add(spawn);
Instance?.OnSlice(_lostLandsSpawns, false);
OnSlice(_lostLandsSpawns, false);
}
public static void RemoveSpawn(DungeonChampionSpawn spawn)
{
_dungeonSpawns.Remove(spawn);
Instance?.OnSlice(_dungeonSpawns, false);
OnSlice(_dungeonSpawns, false);
}
public static void RemoveSpawn(LLChampionSpawn spawn)
{
_lostLandsSpawns.Remove(spawn);
Instance?.OnSlice(_lostLandsSpawns, false);
OnSlice(_lostLandsSpawns, false);
}
public CannedEvilTimer() : base(TimeSpan.Zero, TimeSpan.FromMinutes(1.0))
@ -64,32 +65,34 @@ namespace Server.Engines.CannedEvil
_sliceTime = Core.Now;
}
public void OnSlice<T>(ICollection<T> list, bool rotate = true) where T : ChampionSpawn
public static void OnSlice(HashSet<ChampionSpawn> spawns, bool rotate = true)
{
if (list.Count > 0)
if (spawns.Count <= 0)
{
List<T> valid = new List<T>();
return;
}
foreach (T spawn in list)
using var queue = rotate ? PooledRefQueue<Item>.Create() : default;
foreach (var spawn in spawns)
{
if (spawn.AlwaysActive && !spawn.Active)
{
if (spawn.AlwaysActive && !spawn.Active)
{
spawn.ReadyToActivate = true;
}
else if (rotate && (!spawn.Active || spawn.Kills == 0 && spawn.Level == 0))
{
spawn.Active = false;
spawn.ReadyToActivate = false;
valid.Add(spawn);
}
spawn.ReadyToActivate = true;
}
if (valid.Count > 0)
else if (rotate && (!spawn.Active || spawn.Kills == 0 && spawn.Level == 0))
{
valid[Utility.Random(valid.Count)].ReadyToActivate = true;
spawn.Active = false;
spawn.ReadyToActivate = false;
queue.Enqueue(spawn);
}
}
if (rotate && queue.Count > 0)
{
((ChampionSpawn)queue.PeekRandom()).ReadyToActivate = true;
}
}
protected override void OnTick()

View file

@ -26,11 +26,6 @@ public partial class DungeonChampionSpawn : ChampionSpawn
CannedEvilTimer.AddSpawn(this);
}
public DungeonChampionSpawn(Serial serial) : base(serial)
{
CannedEvilTimer.AddSpawn(this);
}
public override bool ProximitySpawn => true;
public override bool AlwaysActive => false;
@ -39,4 +34,7 @@ public partial class DungeonChampionSpawn : ChampionSpawn
base.OnAfterDelete();
CannedEvilTimer.RemoveSpawn(this);
}
[AfterDeserialization]
private void AfterDeserialization() => CannedEvilTimer.AddSpawn(this);
}

View file

@ -15,35 +15,34 @@
using System;
namespace Server.Engines.CannedEvil
namespace Server.Engines.CannedEvil;
public class ChampionEntry
{
public record ChampionEntry
public readonly bool _randomizeType;
public readonly ChampionSpawnType _type;
public readonly Point3D _signLocation;
public readonly Type _champType;
public readonly Map _map;
public readonly Point3D _ejectLocation;
public readonly Map _ejectMap;
public ChampionEntry(Type champtype, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap) :
this(champtype, ChampionSpawnType.Abyss, signloc, map, ejectloc, ejectmap, true)
{
public readonly bool m_RandomizeType;
public readonly ChampionSpawnType m_Type;
public readonly Point3D m_SignLocation;
public readonly Type m_ChampType;
public readonly Map m_Map;
public readonly Point3D m_EjectLocation;
public readonly Map m_EjectMap;
}
public ChampionEntry(Type champtype, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap) :
this(champtype, ChampionSpawnType.Abyss, signloc, map, ejectloc, ejectmap, true)
{
}
public ChampionEntry(
Type champtype, ChampionSpawnType type, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap,
bool randomizetype = false
)
{
m_ChampType = champtype;
m_RandomizeType = randomizetype;
m_Type = type;
m_SignLocation = signloc;
m_Map = map;
m_EjectLocation = ejectloc;
m_EjectMap = ejectmap;
}
public ChampionEntry(
Type champtype, ChampionSpawnType type, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap,
bool randomizetype = false
)
{
_champType = champtype;
_randomizeType = randomizetype;
_type = type;
_signLocation = signloc;
_map = map;
_ejectLocation = ejectloc;
_ejectMap = ejectmap;
}
}

View file

@ -17,105 +17,106 @@ using System;
using System.Collections.Generic;
using Server.Logging;
namespace Server.Engines.CannedEvil
namespace Server.Engines.CannedEvil;
public static class ChampionGenerator
{
public static class ChampionGenerator
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ChampionGenerator));
public static void Configure()
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ChampionGenerator));
CommandSystem.Register("GenChamps", AccessLevel.Developer, ChampGen_OnCommand);
}
public static void Configure()
private static readonly ChampionEntry[] LLLocations =
[
new(typeof(LLChampionSpawn), new Point3D(5511, 2360, 42), Map.Felucca, new Point3D(5439, 2323, 26), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(6038, 2401, 47), Map.Felucca, new Point3D(5988, 2340, 24), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5549, 2640, 16), Map.Felucca, new Point3D(5645, 2696, -8), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5636, 2916, 37), Map.Felucca, new Point3D(5721, 2949, 28), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(6035, 2943, 50), Map.Felucca, new Point3D(6098, 2997, 17), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5265, 3171, 105), Map.Felucca, new Point3D(5314, 3232, 2), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5282, 3368, 50), Map.Felucca, new Point3D(5215, 3318, 3), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5207, 3637, 20), Map.Felucca, new Point3D(5263, 3687, 0), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5954, 3475, 25), Map.Felucca, new Point3D(6013, 3529, 0), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5982, 3882, 20), Map.Felucca, new Point3D(5929, 3820, -1), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5724, 3991, 41), Map.Felucca, new Point3D(5774, 4041, 26), Map.Felucca),
new(typeof(LLChampionSpawn), ChampionSpawnType.ForestLord, new Point3D(5559, 3757, 21), Map.Felucca, new Point3D(5513, 3878, 3), Map.Felucca)
];
private static readonly ChampionEntry[] DungeonLocations =
[
new(typeof(DungeonChampionSpawn), ChampionSpawnType.UnholyTerror, new Point3D(5179, 709, 20), Map.Felucca, new Point3D(4111, 432, 5), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.VerminHorde, new Point3D(5557, 827, 65), Map.Felucca, new Point3D(5580, 632, 30), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.ColdBlood, new Point3D(5259, 837, 64), Map.Felucca, new Point3D(1176, 2637, 0), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.Abyss, new Point3D(5815, 1352, 5), Map.Felucca, new Point3D(2923, 3406, 8), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.Arachnid, new Point3D(5190, 1607, 20), Map.Felucca, new Point3D(5482, 3161, -54), Map.Felucca)
];
[Usage("GenChamps")]
[Description("Generates champions for Felucca Dungeons & Lost Lands.")]
private static void ChampGen_OnCommand(CommandEventArgs e)
{
/*
//We take the assumption that we are spawning managed champions
for (int i = CannedEvilTimer.DungeonSpawns.Count - 1; i >= 0; i--)
CannedEvilTimer.DungeonSpawns[i].Delete();
for (int i = CannedEvilTimer.LLSpawns.Count - 1; i >= 0; i--)
CannedEvilTimer.LLSpawns[i].Delete();
*/
//We assume that all champion spawns are generated here.
List<ChampionSpawn> spawns = [];
foreach (Item item in World.Items.Values)
{
CommandSystem.Register("GenChamps", AccessLevel.Developer, ChampGen_OnCommand);
if (item is ChampionSpawn spawn)
{
spawns.Add(spawn);
}
}
private static readonly ChampionEntry[] LLLocations = {
new(typeof(LLChampionSpawn), new Point3D(5511, 2360, 42), Map.Felucca, new Point3D(5439, 2323, 26 ), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(6038, 2401, 47), Map.Felucca, new Point3D(5988, 2340, 24), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5549, 2640, 16), Map.Felucca, new Point3D(5645, 2696, -8), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5636, 2916, 37), Map.Felucca, new Point3D(5721, 2949, 28), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(6035, 2943, 50), Map.Felucca, new Point3D(6098, 2997, 17), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5265, 3171, 105), Map.Felucca, new Point3D(5314, 3232, 2), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5282, 3368, 50), Map.Felucca, new Point3D(5215, 3318, 3), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5207, 3637, 20), Map.Felucca, new Point3D(5263, 3687, 0), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5954, 3475, 25), Map.Felucca, new Point3D(6013, 3529, 0), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5982, 3882, 20), Map.Felucca, new Point3D(5929, 3820, -1), Map.Felucca),
new(typeof(LLChampionSpawn), new Point3D(5724, 3991, 41), Map.Felucca, new Point3D(5774, 4041, 26), Map.Felucca),
new(typeof(LLChampionSpawn), ChampionSpawnType.ForestLord, new Point3D(5559, 3757, 21), Map.Felucca, new Point3D(5513, 3878, 3), Map.Felucca),
};
private static readonly ChampionEntry[] DungeonLocations = {
new(typeof(DungeonChampionSpawn), ChampionSpawnType.UnholyTerror, new Point3D(5179, 709, 20), Map.Felucca, new Point3D(4111, 432, 5), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.VerminHorde, new Point3D(5557, 827, 65), Map.Felucca, new Point3D(5580, 632, 30), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.ColdBlood, new Point3D(5259, 837, 64), Map.Felucca, new Point3D(1176, 2637, 0), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.Abyss, new Point3D(5815, 1352, 5), Map.Felucca, new Point3D(2923, 3406, 8), Map.Felucca),
new(typeof(DungeonChampionSpawn), ChampionSpawnType.Arachnid, new Point3D(5190, 1607, 20), Map.Felucca, new Point3D(5482, 3161, -54), Map.Felucca),
};
[Usage("GenChamps")]
[Description("Generates champions for Felucca Dungeons & Lost Lands.")]
private static void ChampGen_OnCommand(CommandEventArgs e)
for (int i = spawns.Count - 1; i >= 0; i--)
{
/*
//We take the assumption that we are spawning managed champions
for (int i = CannedEvilTimer.DungeonSpawns.Count - 1; i >= 0; i--)
CannedEvilTimer.DungeonSpawns[i].Delete();
for (int i = CannedEvilTimer.LLSpawns.Count - 1; i >= 0; i--)
CannedEvilTimer.LLSpawns[i].Delete();
*/
//We assume that all champion spawns are generated here.
List<ChampionSpawn> spawns = new List<ChampionSpawn>();
foreach (Item item in World.Items.Values)
{
if (item is ChampionSpawn spawn)
{
spawns.Add(spawn);
}
}
for (int i = spawns.Count - 1; i >= 0; i--)
{
spawns[i].Delete();
}
Process(DungeonLocations);
Process(LLLocations);
//ProcessIlshenar();
//ProcessTokuno();
spawns[i].Delete();
}
private static void Process(ChampionEntry[] entries)
{
for (int i = 0; i < entries.Length; i++)
{
ChampionEntry entry = entries[i];
Process(DungeonLocations);
Process(LLLocations);
//ProcessIlshenar();
//ProcessTokuno();
}
try
private static void Process(ChampionEntry[] entries)
{
for (int i = 0; i < entries.Length; i++)
{
ChampionEntry entry = entries[i];
try
{
if (Activator.CreateInstance(entry._champType) is ChampionSpawn spawn)
{
if (Activator.CreateInstance(entry.m_ChampType) is ChampionSpawn spawn)
spawn.RandomizeType = entry._randomizeType;
spawn.Type = entry._type;
spawn.MoveToWorld(entry._signLocation, entry._map);
spawn.EjectLocation = entry._ejectLocation;
spawn.EjectMap = entry._ejectMap;
if (spawn.AlwaysActive)
{
spawn.RandomizeType = entry.m_RandomizeType;
spawn.Type = entry.m_Type;
spawn.MoveToWorld(entry.m_SignLocation, entry.m_Map);
spawn.EjectLocation = entry.m_EjectLocation;
spawn.EjectMap = entry.m_EjectMap;
if (spawn.AlwaysActive)
{
spawn.ReadyToActivate = true;
}
spawn.ReadyToActivate = true;
}
}
catch (Exception e)
{
logger.Error(
e,
"Failed to generate champion \"{Type}\" at {Location} ({Map}).",
entry.m_ChampType.FullName,
entry.m_SignLocation,
entry.m_Map
);
}
}
catch (Exception e)
{
logger.Error(
e,
"Failed to generate champion \"{Type}\" at {Location} ({Map}).",
entry._champType.FullName,
entry._signLocation,
entry._map
);
}
}
}

View file

@ -28,11 +28,6 @@ public partial class LLChampionSpawn : ChampionSpawn
CannedEvilTimer.AddSpawn(this);
}
public LLChampionSpawn(Serial serial) : base(serial)
{
CannedEvilTimer.AddSpawn(this);
}
public override bool AlwaysActive => false;
public override void OnAfterDelete()
@ -40,4 +35,7 @@ public partial class LLChampionSpawn : ChampionSpawn
base.OnAfterDelete();
CannedEvilTimer.RemoveSpawn(this);
}
[AfterDeserialization]
private void AfterDeserialization() => CannedEvilTimer.AddSpawn(this);
}