diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs index 4a13970bc..f8502795c 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs @@ -182,8 +182,8 @@ namespace SerializationGenerator var attrCtorArgs = serializableFieldAttr.ConstructorArguments; var order = (int)attrCtorArgs[0].Value!; - var getterAccessor = Helpers.GetAccessibility(attrCtorArgs[1].Value!.ToString()); - var setterAccessor = Helpers.GetAccessibility(attrCtorArgs[2].Value!.ToString()); + var getterAccessor = Helpers.GetAccessibility(attrCtorArgs[1].Value?.ToString()); + var setterAccessor = Helpers.GetAccessibility(attrCtorArgs[2].Value?.ToString()); var virtualProperty = (bool)attrCtorArgs[3].Value!; if (fieldOrPropertySymbol is IFieldSymbol fieldSymbol) diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs index 1dac574fb..cce9ac72f 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs @@ -37,6 +37,7 @@ namespace SerializationGenerator var genericReaderInterface = compilation.GetTypeByMetadataName(SymbolMetadata.GENERIC_READER_INTERFACE); source.GenerateMethodStart( + " ", "Deserialize", Accessibility.Public, isOverride, @@ -120,7 +121,7 @@ namespace SerializationGenerator source.AppendLine($"{indent}Timer.DelayCall({afterDeserialization.Name});"); } - source.GenerateMethodEnd(); + source.GenerateMethodEnd(" "); } } } diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs index edd938baf..1bc98c869 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs @@ -43,6 +43,7 @@ namespace SerializationGenerator ); const string indent = " "; + const string innerIndent = " "; const string propertyIndent = " "; var propertyAccessor = setter > getter ? setter : getter; @@ -59,7 +60,6 @@ namespace SerializationGenerator // Setter source.GeneratePropertySetterStart(propertyIndent, false, setterAccessor.Value); - const string innerIndent = " "; source.AppendLine($"{innerIndent}if (value != {fieldName})"); source.AppendLine($"{innerIndent}{{"); source.AppendLine($"{innerIndent} {fieldName} = value;"); diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs index 8f961f724..da4685076 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs @@ -32,6 +32,7 @@ namespace SerializationGenerator var serialType = (ITypeSymbol)compilation.GetTypeByMetadataName("Server.Serial"); source.GenerateConstructorStart( + " ", className, Accessibility.Public, new []{ (serialType, "serial") }.ToImmutableArray(), @@ -44,7 +45,7 @@ namespace SerializationGenerator SetTypeRef(typeof({className}));"); } - source.GenerateMethodEnd(); + source.GenerateMethodEnd(" "); } } } diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs index 8bed1941e..47dd61dc4 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs @@ -33,6 +33,7 @@ namespace SerializationGenerator var genericWriterInterface = compilation.GetTypeByMetadataName(SymbolMetadata.GENERIC_WRITER_INTERFACE); source.GenerateMethodStart( + " ", "Serialize", Accessibility.Public, isOverride, @@ -61,7 +62,7 @@ namespace SerializationGenerator ); } - source.GenerateMethodEnd(); + source.GenerateMethodEnd(" "); } } } diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/HashSetMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/HashSetMigrationRule.cs index 81d13468e..5c46e040f 100644 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/HashSetMigrationRule.cs +++ b/Projects/SerializationGenerator/SerializableMigration/Rules/HashSetMigrationRule.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Immutable; +using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using SerializationGenerator; @@ -52,11 +53,18 @@ namespace SerializableMigration parentSymbol ); + var extraOptions = ""; + if (attributes.Any(a => a.IsTidy(compilation))) + { + extraOptions += "@Tidy"; + } + var length = serializableSetType.RuleArguments.Length; - ruleArguments = new string[length + 2]; - ruleArguments[0] = setTypeSymbol.ToDisplayString(); - ruleArguments[1] = serializableSetType.Rule; - Array.Copy(serializableSetType.RuleArguments, 0, ruleArguments, 2, length); + ruleArguments = new string[length + 3]; + ruleArguments[0] = extraOptions; + ruleArguments[1] = setTypeSymbol.ToDisplayString(); + ruleArguments[2] = serializableSetType.Rule; + Array.Copy(serializableSetType.RuleArguments, 0, ruleArguments, 3, length); return true; } @@ -71,9 +79,12 @@ namespace SerializableMigration } var ruleArguments = property.RuleArguments; - var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1]]; - var setElementRuleArguments = new string[ruleArguments.Length - 2]; - Array.Copy(ruleArguments, 2, setElementRuleArguments, 0, ruleArguments.Length - 2); + var hasExtraOptions = ruleArguments[0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); + var argumentsOffset = hasExtraOptions ? 1 : 0; + + var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]]; + var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; + Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); var propertyName = property.Name; var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; @@ -81,16 +92,16 @@ namespace SerializableMigration var propertyEntry = $"{propertyVarPrefix}Entry"; var propertyCount = $"{propertyVarPrefix}Count"; - source.AppendLine($"{indent}{ruleArguments[0]} {propertyEntry};"); + source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};"); source.AppendLine($"{indent}var {propertyCount} = reader.ReadInt();"); - source.AppendLine($"{indent}{property.Name} = new System.Collections.Generic.HashSet<{ruleArguments[0]}>({propertyCount});"); + source.AppendLine($"{indent}{property.Name} = new System.Collections.Generic.HashSet<{ruleArguments[argumentsOffset]}>({propertyCount});"); source.AppendLine($"{indent}for (var {propertyIndex} = 0; i < {propertyCount}; {propertyIndex}++)"); source.AppendLine($"{indent}{{"); var serializableSetElement = new SerializableProperty { Name = propertyEntry, - Type = ruleArguments[0], + Type = ruleArguments[argumentsOffset], Rule = setElementRule.RuleName, RuleArguments = setElementRuleArguments }; @@ -111,14 +122,23 @@ namespace SerializableMigration } var ruleArguments = property.RuleArguments; - var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1]]; - var setElementRuleArguments = new string[ruleArguments.Length - 2]; - Array.Copy(ruleArguments, 2, setElementRuleArguments, 0, ruleArguments.Length - 2); + var hasExtraOptions = ruleArguments[0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); + var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy"); + var argumentsOffset = hasExtraOptions ? 1 : 0; + + var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]]; + var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; + Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); var propertyName = property.Name; var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; var propertyEntry = $"{propertyVarPrefix}Entry"; var propertyCount = $"{propertyVarPrefix}Count"; + + if (shouldTidy) + { + source.AppendLine($"{indent}{property.Name}?.Tidy();"); + } source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;"); source.AppendLine($"{indent}writer.Write({propertyCount});"); source.AppendLine($"{indent}if ({propertyCount} > 0)"); @@ -129,7 +149,7 @@ namespace SerializableMigration var serializableSetElement = new SerializableProperty { Name = propertyEntry, - Type = ruleArguments[0], + Type = ruleArguments[argumentsOffset], Rule = setElementRule.RuleName, RuleArguments = setElementRuleArguments }; diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/ListMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/ListMigrationRule.cs index 5dde802f5..bc0685fb1 100644 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/ListMigrationRule.cs +++ b/Projects/SerializationGenerator/SerializableMigration/Rules/ListMigrationRule.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Immutable; +using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using SerializationGenerator; @@ -52,11 +53,18 @@ namespace SerializableMigration parentSymbol ); + var extraOptions = ""; + if (attributes.Any(a => a.IsTidy(compilation))) + { + extraOptions += "@Tidy"; + } + var length = serializableListType.RuleArguments.Length; - ruleArguments = new string[length + 2]; - ruleArguments[0] = listTypeSymbol.ToDisplayString(); - ruleArguments[1] = serializableListType.Rule; - Array.Copy(serializableListType.RuleArguments, 0, ruleArguments, 2, length); + ruleArguments = new string[length + 3]; + ruleArguments[0] = extraOptions; + ruleArguments[1] = listTypeSymbol.ToDisplayString(); + ruleArguments[2] = serializableListType.Rule; + Array.Copy(serializableListType.RuleArguments, 0, ruleArguments, 3, length); return true; } @@ -71,9 +79,13 @@ namespace SerializableMigration } var ruleArguments = property.RuleArguments; - var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1]]; - var listElementRuleArguments = new string[ruleArguments.Length - 2]; - Array.Copy(ruleArguments, 2, listElementRuleArguments, 0, ruleArguments.Length - 2); + var hasExtraOptions = ruleArguments[0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); + var argumentsOffset = hasExtraOptions ? 1 : 0; + + var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[argumentsOffset + 1]]; + + var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; + Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); var propertyName = property.Name; var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; @@ -81,16 +93,16 @@ namespace SerializableMigration var propertyEntry = $"{propertyVarPrefix}Entry"; var propertyCount = $"{propertyVarPrefix}Count"; - source.AppendLine($"{indent}{ruleArguments[0]} {propertyEntry};"); + source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};"); source.AppendLine($"{indent}var {propertyCount} = reader.ReadInt();"); - source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.List<{ruleArguments[0]}>({propertyCount});"); + source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.List<{ruleArguments[argumentsOffset]}>({propertyCount});"); source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)"); source.AppendLine($"{indent}{{"); var serializableListElement = new SerializableProperty { Name = propertyEntry, - Type = ruleArguments[0], + Type = ruleArguments[argumentsOffset], Rule = listElementRule.RuleName, RuleArguments = listElementRuleArguments }; @@ -111,14 +123,23 @@ namespace SerializableMigration } var ruleArguments = property.RuleArguments; - var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1]]; - var listElementRuleArguments = new string[ruleArguments.Length - 2]; - Array.Copy(ruleArguments, 2, listElementRuleArguments, 0, ruleArguments.Length - 2); + var hasExtraOptions = ruleArguments[0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal); + var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy"); + var argumentsOffset = hasExtraOptions ? 1 : 0; + + var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]]; + var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset]; + Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset); var propertyName = property.Name; var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}"; var propertyEntry = $"{propertyVarPrefix}Entry"; var propertyCount = $"{propertyVarPrefix}Count"; + + if (shouldTidy) + { + source.AppendLine($"{indent}{property.Name}?.Tidy();"); + } source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;"); source.AppendLine($"{indent}writer.Write({propertyCount});"); source.AppendLine($"{indent}if ({propertyCount} > 0)"); @@ -129,7 +150,7 @@ namespace SerializableMigration var serializableListElement = new SerializableProperty { Name = propertyEntry, - Type = ruleArguments[0], + Type = ruleArguments[argumentsOffset], Rule = listElementRule.RuleName, RuleArguments = listElementRuleArguments }; diff --git a/Projects/SerializationGenerator/SourceGeneration/Helpers.cs b/Projects/SerializationGenerator/SourceGeneration/Helpers.cs index a180ca750..46a194a29 100644 --- a/Projects/SerializationGenerator/SourceGeneration/Helpers.cs +++ b/Projects/SerializationGenerator/SourceGeneration/Helpers.cs @@ -44,7 +44,7 @@ namespace SerializationGenerator public static string ToFriendlyString(this Accessibility accessibility) => SyntaxFacts.GetText(accessibility); - public static Accessibility GetAccessibility(string value) => + public static Accessibility GetAccessibility(string? value) => value switch { "private" => Accessibility.Private, diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Arguments.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Arguments.cs index 0d194d65f..53dc5823c 100644 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Arguments.cs +++ b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Arguments.cs @@ -42,7 +42,7 @@ namespace SerializationGenerator for (var i = 0; i < parameters.Length; i++) { var (t, v) = parameters[i]; - source.AppendFormat("{0} {1}", t.Name, v); + source.AppendFormat("{0} {1}", t.ToDisplayString(), v); if (i < parameters.Length - 1) { source.Append(", "); diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Attribute.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Attribute.cs index 98e312beb..1e0441f36 100644 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Attribute.cs +++ b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Attribute.cs @@ -88,5 +88,10 @@ namespace SerializationGenerator source.AppendLine("]"); } + + public static void AggressiveInline(this StringBuilder source, string indent) => + source.AppendLine( + $"{indent}[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]" + ); } } diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Method.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Method.cs index 46f17cf0d..407fd3e32 100644 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Method.cs +++ b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Method.cs @@ -21,22 +21,24 @@ namespace SerializationGenerator { public static partial class SourceGeneration { - public static void GenerateMethodStart(this StringBuilder source, string methodName, Accessibility accessors, bool isOverride, string returnType, ImmutableArray<(ITypeSymbol, string)> parameters) + public static void GenerateMethodStart( + this StringBuilder source, string indent, string methodName, Accessibility accessors, bool isOverride, + string returnType, ImmutableArray<(ITypeSymbol, string)> parameters + ) { - source.Append($" {accessors.ToFriendlyString()}{(isOverride ? " override" : "")} {returnType} {methodName}("); + source.Append($"{indent}{accessors.ToFriendlyString()}{(isOverride ? " override" : "")} {returnType} {methodName}("); source.GenerateSignatureArguments(parameters); - source.AppendLine(@") - {"); + source.AppendLine($")\n{indent}{{"); } - public static void GenerateMethodEnd(this StringBuilder source) => source.AppendLine(@" }"); + public static void GenerateMethodEnd(this StringBuilder source, string indent) => source.AppendLine($"{indent}}}"); public static void GenerateConstructorStart( - this StringBuilder source, string className, Accessibility accessors, ImmutableArray<(ITypeSymbol, string)> parameters, + this StringBuilder source, string indent, string className, Accessibility accessors, ImmutableArray<(ITypeSymbol, string)> parameters, ImmutableArray baseParameters, bool isOverload = false ) { - source.Append($" {accessors.ToFriendlyString()} {className}("); + source.Append($"{indent}{accessors.ToFriendlyString()} {className}("); source.GenerateSignatureArguments(parameters); source.Append(')'); bool hasBaseParams = baseParameters.Length > 0; @@ -54,7 +56,7 @@ namespace SerializationGenerator source.Append(')'); } - source.AppendLine("\n {"); + source.AppendLine($"\n{indent}{{"); } } } diff --git a/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs b/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs index 9c439941b..4c389ebc0 100644 --- a/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs +++ b/Projects/SerializationGenerator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs @@ -32,6 +32,7 @@ namespace SerializationGenerator public const string DELTA_DATE_TIME_ATTRIBUTE = "Server.DeltaDateTimeAttribute"; public const string INTERN_STRING_ATTRIBUTE = "Server.InternStringAttribute"; public const string ENCODED_INT_ATTRIBUTE = "Server.EncodedIntAttribute"; + public const string TIDY_ATTRIBUTE = "Server.TidyAttribute"; public const string POINT2D_STRUCT = "Server.Point2D"; public const string POINT3D_STRUCT = "Server.Point3D"; public const string RECTANGLE2D_STRUCT = "Server.Rectangle2D"; @@ -48,6 +49,9 @@ namespace SerializationGenerator public static bool IsInternString(this AttributeData attr, Compilation compilation) => attr?.IsAttribute(compilation.GetTypeByMetadataName(INTERN_STRING_ATTRIBUTE)) == true; + public static bool IsTidy(this AttributeData attr, Compilation compilation) => + attr?.IsAttribute(compilation.GetTypeByMetadataName(TIDY_ATTRIBUTE)) == true; + public static bool IsAttribute(this AttributeData attr, ISymbol symbol) => attr?.AttributeClass?.Equals(symbol, SymbolEqualityComparer.Default) == true; diff --git a/Projects/Server/Collections/PooledRefQueue.cs b/Projects/Server/Collections/PooledRefQueue.cs index 359c42635..b3a1b3937 100644 --- a/Projects/Server/Collections/PooledRefQueue.cs +++ b/Projects/Server/Collections/PooledRefQueue.cs @@ -22,7 +22,7 @@ namespace Server.Collections private int _version; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PooledRefQueue Create() => new(32); + public static PooledRefQueue Create(int capacity = 32) => new(capacity); // Creates a queue with room for capacity objects. The default grow factor // is used. diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index dc484c94a..f0dd8e630 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +using Server.Collections; using Server.Logging; using Server.Network; -using Server.Utilities; -using QueuePool = Server.Utilities.RefPool>; namespace Server.Items { @@ -16,7 +15,6 @@ namespace Server.Items public class Container : Item { - private static readonly QueuePool m_QueuePool = new(QueueRef.Generate, 2, 5); private static readonly List m_FindItemsList = new(); private ContainerData m_ContainerData; @@ -1299,8 +1297,7 @@ namespace Server.Items { var consumed = 0; - var toDelete = new Queue(); - + using var toDelete = PooledRefQueue.Create(); RecurseConsumeUpTo(this, type, amount, recurse, ref consumed, toDelete); while (toDelete.Count > 0) @@ -1313,7 +1310,7 @@ namespace Server.Items private static void RecurseConsumeUpTo( Item current, Type type, int amount, bool recurse, ref int consumed, - Queue toDelete + PooledRefQueue toDelete ) { if (current == null || current.Items.Count == 0) @@ -1721,28 +1718,26 @@ namespace Server.Items /// public List FindItemsByType(bool recurse = true, Predicate predicate = null) where T : Item { - using (var queue = m_QueuePool.Get()) + using var queue = PooledRefQueue.Create(128); + queue.Enqueue(this); + var items = new List(); + while (queue.Count > 0) { - queue.Enqueue(this); - var items = new List(); - while (queue.Count > 0) + var container = queue.Dequeue(); + foreach (var item in container.Items) { - var container = queue.Dequeue(); - foreach (var item in container.Items) + if (item is T typedItem && predicate?.Invoke(typedItem) != false) { - if (item is T typedItem && predicate?.Invoke(typedItem) != false) - { - items.Add(typedItem); - } - else if (recurse && item is Container itemContainer) - { - queue.Enqueue(itemContainer); - } + items.Add(typedItem); + } + else if (recurse && item is Container itemContainer) + { + queue.Enqueue(itemContainer); } } - - return items; } + + return items; } /// @@ -1765,28 +1760,26 @@ namespace Server.Items /// public T FindItemByType(bool recurse = true, Predicate predicate = null) where T : Item { - using (var queue = m_QueuePool.Get()) + using var queue = PooledRefQueue.Create(128); + queue.Enqueue(this); + while (queue.Count > 0) { - queue.Enqueue(this); - while (queue.Count > 0) + var container = queue.Dequeue(); + foreach (var item in container.Items) { - var container = queue.Dequeue(); - foreach (var item in container.Items) + if (item is T typedItem && predicate?.Invoke(typedItem) != false) { - if (item is T typedItem && predicate?.Invoke(typedItem) != false) - { - return typedItem; - } + return typedItem; + } - if (recurse && item is Container itemContainer) - { - queue.Enqueue(itemContainer); - } + if (recurse && item is Container itemContainer) + { + queue.Enqueue(itemContainer); } } - - return null; } + + return null; } private class GroupComparer : IComparer diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index 1d3e1b364..84ce948be 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.StartTimer(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); + Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive); } public NetState(ISocket connection) diff --git a/Projects/Server/Sector.cs b/Projects/Server/Sector.cs index 844a83866..8a8fa9cf1 100644 --- a/Projects/Server/Sector.cs +++ b/Projects/Server/Sector.cs @@ -66,76 +66,28 @@ namespace Server public int Y { get; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Add(ref List list, T value) - { - list ??= new List(); - - list.Add(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Remove(ref List list, T value) - { - if (list != null) - { - list.Remove(value); - - if (list.Count == 0) - { - list = null; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Replace(ref List list, T oldValue, T newValue) - { - if (oldValue != null && newValue != null) - { - var index = list?.IndexOf(oldValue) ?? -1; - - if (index >= 0) - { - list![index] = newValue; - } - else - { - Add(ref list, newValue); - } - } - else if (oldValue != null) - { - Remove(ref list, oldValue); - } - else if (newValue != null) - { - Add(ref list, newValue); - } - } - public void OnClientChange(NetState oldState, NetState newState) { - Replace(ref m_Clients, oldState, newState); + Utility.Replace(ref m_Clients, oldState, newState); } public void OnEnter(Item item) { - Add(ref m_Items, item); + Utility.Add(ref m_Items, item); } public void OnLeave(Item item) { - Remove(ref m_Items, item); + Utility.Remove(ref m_Items, item); } public void OnEnter(Mobile mob) { - Add(ref m_Mobiles, mob); + Utility.Add(ref m_Mobiles, mob); if (mob.NetState != null) { - Add(ref m_Clients, mob.NetState); + Utility.Add(ref m_Clients, mob.NetState); Owner.ActivateSectors(X, Y); } @@ -143,11 +95,11 @@ namespace Server public void OnLeave(Mobile mob) { - Remove(ref m_Mobiles, mob); + Utility.Remove(ref m_Mobiles, mob); if (mob.NetState != null) { - Remove(ref m_Clients, mob.NetState); + Utility.Remove(ref m_Clients, mob.NetState); Owner.DeactivateSectors(X, Y); } @@ -155,7 +107,7 @@ namespace Server public void OnEnter(Region region, Rectangle3D rect) { - Add(ref m_RegionRects, new RegionRect(region, rect)); + Utility.Add(ref m_RegionRects, new RegionRect(region, rect)); m_RegionRects.Sort(); @@ -200,12 +152,12 @@ namespace Server public void OnMultiEnter(BaseMulti multi) { - Add(ref m_Multis, multi); + Utility.Add(ref m_Multis, multi); } public void OnMultiLeave(BaseMulti multi) { - Remove(ref m_Multis, multi); + Utility.Remove(ref m_Multis, multi); } public void Activate() diff --git a/Projects/Server/Serialization/DeltaDateTimeAttribute.cs b/Projects/Server/Serialization/Attributes/DeltaDateTimeAttribute.cs similarity index 100% rename from Projects/Server/Serialization/DeltaDateTimeAttribute.cs rename to Projects/Server/Serialization/Attributes/DeltaDateTimeAttribute.cs diff --git a/Projects/Server/Serialization/EncodedIntAttribute.cs b/Projects/Server/Serialization/Attributes/EncodedIntAttribute.cs similarity index 100% rename from Projects/Server/Serialization/EncodedIntAttribute.cs rename to Projects/Server/Serialization/Attributes/EncodedIntAttribute.cs diff --git a/Projects/Server/Serialization/InternStringAttribute.cs b/Projects/Server/Serialization/Attributes/InternStringAttribute.cs similarity index 100% rename from Projects/Server/Serialization/InternStringAttribute.cs rename to Projects/Server/Serialization/Attributes/InternStringAttribute.cs diff --git a/Projects/Server/Serialization/InvalidatePropertiesAttribute.cs b/Projects/Server/Serialization/Attributes/InvalidatePropertiesAttribute.cs similarity index 100% rename from Projects/Server/Serialization/InvalidatePropertiesAttribute.cs rename to Projects/Server/Serialization/Attributes/InvalidatePropertiesAttribute.cs diff --git a/Projects/Server/Serialization/SerializableAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableAttribute.cs similarity index 100% rename from Projects/Server/Serialization/SerializableAttribute.cs rename to Projects/Server/Serialization/Attributes/SerializableAttribute.cs diff --git a/Projects/Server/Serialization/SerializableFieldAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableFieldAttribute.cs similarity index 93% rename from Projects/Server/Serialization/SerializableFieldAttribute.cs rename to Projects/Server/Serialization/Attributes/SerializableFieldAttribute.cs index 56dd79d87..52a3dc097 100755 --- a/Projects/Server/Serialization/SerializableFieldAttribute.cs +++ b/Projects/Server/Serialization/Attributes/SerializableFieldAttribute.cs @@ -20,7 +20,7 @@ namespace Server /// /// Hints to the source generator that this field or property should be serialized. /// When used on a field, the source generator will generate the property entirely. - /// When used on a property, the user must call ((ISerializable)this).MarkDirty(). + /// When used on a property, the user must call this.MarkDirty() after reassigning the value or modifying the value internally. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class SerializableFieldAttribute : Attribute diff --git a/Projects/Server/Serialization/SerializableFieldAttributeAttribute.cs b/Projects/Server/Serialization/Attributes/SerializableFieldAttributeAttribute.cs similarity index 100% rename from Projects/Server/Serialization/SerializableFieldAttributeAttribute.cs rename to Projects/Server/Serialization/Attributes/SerializableFieldAttributeAttribute.cs diff --git a/Projects/Server/Serialization/Attributes/TidyAttribute.cs b/Projects/Server/Serialization/Attributes/TidyAttribute.cs new file mode 100644 index 000000000..0e2ba92d9 --- /dev/null +++ b/Projects/Server/Serialization/Attributes/TidyAttribute.cs @@ -0,0 +1,27 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: TidyAttribute.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; + +namespace Server +{ + /// + /// Hints to the source generator that a serializable list should be tidied up + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class TidyAttribute : Attribute + { + } +} diff --git a/Projects/Server/Serialization/ISerializable.cs b/Projects/Server/Serialization/ISerializable.cs index d4e313b09..d91366c9b 100644 --- a/Projects/Server/Serialization/ISerializable.cs +++ b/Projects/Server/Serialization/ISerializable.cs @@ -20,7 +20,7 @@ namespace Server { public interface ISerializable { - long SavePosition { get; protected set; } + long SavePosition { get; protected internal set; } BufferWriter SaveBuffer { get; protected internal set; } int TypeRef { get; } Serial Serial { get; } @@ -29,11 +29,6 @@ namespace Server void Delete(); bool Deleted { get; } - void MarkDirty() - { - SavePosition = -1; - } - void SetTypeRef(Type type); public void InitializeSaveBuffer(byte[] buffer) diff --git a/Projects/Server/Serialization/ISerializableExtensions.cs b/Projects/Server/Serialization/ISerializableExtensions.cs new file mode 100644 index 000000000..b9640bd05 --- /dev/null +++ b/Projects/Server/Serialization/ISerializableExtensions.cs @@ -0,0 +1,135 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ISerializableExtensions.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.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Server +{ + public static class ISerializableExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MarkDirty(this ISerializable entity) + { + entity.SavePosition = -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(this ISerializable entity, ICollection list, T value) + { + list.Add(value); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(this ISerializable entity, IDictionary dict, K key, V value) + { + dict[key] = value; + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Insert(this ISerializable entity, IList list, T value, int index) + { + list.Insert(index, value); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(this ISerializable entity, ICollection list, T value) + { + if (list.Remove(value)) + { + entity.MarkDirty(); + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(this ISerializable entity, IDictionary dict, K key, out V value) + { + if (dict.Remove(key, out value)) + { + entity.MarkDirty(); + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RemoveAt(this ISerializable entity, IList list, int index) + { + list.RemoveAt(index); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(this ISerializable entity, ICollection list) + { + list.Clear(); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(this ISerializable entity, ref List list, T value) + { + Utility.Add(ref list, value); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(this ISerializable entity, ref Dictionary dict, K key, V value) + { + Utility.Add(ref dict, key, value); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(this ISerializable entity, ref List list, T value) + { + if (Utility.Remove(ref list, value)) + { + entity.MarkDirty(); + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(this ISerializable entity, ref List list) + { + Utility.Clear(ref list); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(this ISerializable entity, ref HashSet set) + { + Utility.Clear(ref set); + entity.MarkDirty(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(this ISerializable entity, ref Dictionary dict) + { + Utility.Clear(ref dict); + entity.MarkDirty(); + } + } +} diff --git a/Projects/Server/Timer/Timer.DelayCall.cs b/Projects/Server/Timer/Timer.DelayCall.cs index 2c156a744..ab06fb076 100644 --- a/Projects/Server/Timer/Timer.DelayCall.cs +++ b/Projects/Server/Timer/Timer.DelayCall.cs @@ -133,7 +133,7 @@ namespace Server internal DelayCallTimer(TimeSpan delay) : base(delay) { #if DEBUG_TIMERS - t._allowFinalization = true; + _allowFinalization = true; #endif Start(); } @@ -164,12 +164,15 @@ namespace Server Version++; // Increment the version so if this is called from OnTick() and another timer is started, we don't have a problem +#if DEBUG_TIMERS + _stackTraces.Remove(GetHashCode()); +#endif + if (_poolCount >= _poolCapacity) { #if DEBUG_TIMERS - logger.Warning($"DelayCallTimer pool reached maximum of {_poolSize} timers"); + logger.Warning($"DelayCallTimer pool reached maximum of {_poolCapacity} timers"); _allowFinalization = true; - _stackTraces.Remove(GetHashCode()); #endif return; } @@ -184,7 +187,7 @@ namespace Server { _poolCount--; #if DEBUG_TIMERS - logger.Information($"Pool count changed: {_poolCount} ({_poolCapacity})"); + logger.Information($"Pool count: {_poolCount} / {_poolCapacity}"); #endif var timer = GetFromPool(); @@ -202,7 +205,7 @@ namespace Server _timerPoolDepletionAmount++; #if DEBUG_TIMERS - logger.Warning($"Timer pool depleted and timer was allocated.\n{new StackTrace()}); + logger.Warning($"Timer pool depleted and timer was allocated.\n{new StackTrace()}"); #endif return new DelayCallTimer(delay, interval, count, callback); } diff --git a/Projects/Server/Timer/Timer.Pool.cs b/Projects/Server/Timer/Timer.Pool.cs index 2776610e6..8c9864ae4 100644 --- a/Projects/Server/Timer/Timer.Pool.cs +++ b/Projects/Server/Timer/Timer.Pool.cs @@ -62,7 +62,7 @@ namespace Server _poolHead = head; _poolCount += amount; #if DEBUG_TIMERS - logger.Information($"Pool count changed: {_poolCount} ({_poolCapacity})"); + logger.Information($"Pool count: {_poolCount} / {_poolCapacity}"); #endif } diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index db3076f19..239e8136a 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -203,9 +203,7 @@ namespace Server public static void DumpInfo(TextWriter tw) { - var now = DateTime.UtcNow; - - tw.WriteLine("Date: {0}\n", now); + tw.WriteLine("Date: {0}\n", Core.Now.ToLocalTime()); tw.WriteLine("Pool - Count: {0}; Size {1}\n", _poolCount - _timerPoolDepletionAmount, _poolCapacity); var total = 0.0; @@ -216,6 +214,10 @@ namespace Server for (var j = 0; j < _ringSize; j++) { var t = _rings[i][j]; + if (t == null) + { + continue; + } var name = t.ToString(); @@ -230,9 +232,22 @@ namespace Server foreach (var (name, count) in hash.OrderByDescending(o => o.Value)) { - tw.WriteLine($"- Type: {name}; Count: {count}; Percent: {count / total}%"); + var percent = count / total; + var line = $"{count:#,0} ({percent:P1})"; + // 6 - 15 / 8 = 1 + var tabs = new string('\t', line.Length < 12 ? 2 : 1); + tw.WriteLine($"{line}{tabs}{name}"); } +#if DEBUG_TIMERS + tw.WriteLine("\nStack Traces:"); + foreach (var kvp in DelayCallTimer._stackTraces) + { + tw.WriteLine(kvp.Value); + tw.WriteLine(); + } +#endif + tw.WriteLine(); tw.WriteLine(); } diff --git a/Projects/Server/Utilities/RefPool.cs b/Projects/Server/Utilities/RefPool.cs deleted file mode 100644 index a2d256279..000000000 --- a/Projects/Server/Utilities/RefPool.cs +++ /dev/null @@ -1,165 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: RefPool.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.Collections.Generic; - -namespace Server.Utilities -{ - /// A resource reference object that can be disposed. - /// - /// Disposing the reference is expected to return itself back into the - /// original pool that created it. - /// - public interface IRef : IDisposable - { - } - - /// - /// Base implementation of the interface. - /// - /// - /// New implementations of should either derive from, or mirror - /// the functionality of this base implementation. - /// - /// - public abstract class BaseRef : IRef where TDerived : IRef - { - private readonly RefPool m_Pool; - public BaseRef(RefPool pool) => m_Pool = pool; - - public void Dispose() - { - OnDispose(); - m_Pool.Return((TDerived)(object)this); - } - - protected abstract void OnDispose(); - } - - /// - /// A resource reference pool that manages a collection of reusable resources. - /// - /// The resource type the pool will contain. - public class RefPool where TRef : IRef - { - public delegate TRef Generator(RefPool targetPool); - - public const int DEFAULT_RESOURCE_RETENTION = 10; - private readonly Generator m_Generator; - - private readonly Stack m_Resources = new(); - private int m_MaxRefrenceRetention; - - /// The generator function for creating new resources. - /// - /// An amount of resources that should be pre-generated during initialization of the resource - /// pool. - /// - public RefPool(Generator generator, int preGenerateCount = 0, int maxRefrenceRetention = DEFAULT_RESOURCE_RETENTION) - { - if (generator == null) - { - throw new ArgumentNullException(nameof(generator)); - } - - if (preGenerateCount > maxRefrenceRetention) - { - throw new IndexOutOfRangeException( - $"{nameof(preGenerateCount)} greater than {nameof(maxRefrenceRetention)}" - ); - } - - m_Generator = generator; - m_MaxRefrenceRetention = maxRefrenceRetention; - while (--preGenerateCount >= 0) - { - m_Resources.Push(generator(this)); - } - } - - /// - /// The maximum number of unused resources to hold in the pool. - /// - public int MaxRefrenceRetention - { - get => m_MaxRefrenceRetention; - set - { - m_MaxRefrenceRetention = value; - while (m_Resources.Count > value) - { - m_Resources.Pop(); - } - } - } - - /// - /// Retrieves a resource reference that is managed by this . If the pool is has unused - /// resources, - /// it will remove one from the pool and return it; otherwise, a new resource will be generated. - /// - /// Unused resource, or a new resource if no unused resources available. - public TRef Get() => m_Resources.TryPop(out var item) ? item : m_Generator(this); - - /// - /// Returns a resource reference to the pool of unused resources. - /// - /// Resource to be returned. - public void Return(TRef queueRef) - { - if (m_Resources.Count < MaxRefrenceRetention) - { - m_Resources.Push(queueRef); - } - } - } - - public class QueueRef : Queue, IRef - { - /// - /// Generator function for creating instances of the resource. - /// - public static RefPool>.Generator Generate = targetPool => new QueueRef(targetPool); - - private readonly RefPool> m_Pool; - private QueueRef(RefPool> pool) => m_Pool = pool; - - /// Clears the queue and returns this resource to its parent resource pool. - public void Dispose() - { - Clear(); - m_Pool.Return(this); - } - } - - public class StackRef : Stack, IRef - { - /// - /// Generator function for creating instances of the resource. - /// - public static RefPool>.Generator Generate = targetPool => new StackRef(targetPool); - - private readonly RefPool> m_Pool; - private StackRef(RefPool> pool) => m_Pool = pool; - - /// Clears the stack and returns this resource to its parent resource pool. - public void Dispose() - { - Clear(); - m_Pool.Return(this); - } - } -} diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 4baae1869..0749c2882 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -1492,5 +1492,159 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string GetTimeStamp() => Core.Now.ToString("yyyy-MM-dd-HH-mm-ss"); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref List list, T value) + { + list ??= new List(); + list.Add(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref HashSet set, T value) + { + set ??= new HashSet(); + set.Add(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Dictionary dict, K key, V value) + { + dict ??= new Dictionary(); + dict.Add(key, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref List list, T value) + { + if (list != null) + { + var removed = list.Remove(value); + + if (list.Count == 0) + { + list = null; + } + + return removed; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref HashSet set, T value) + { + if (set != null) + { + var removed = set.Remove(value); + + if (set.Count == 0) + { + set = null; + } + + return removed; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref Dictionary dict, K key) + { + if (dict != null) + { + var removed = dict.Remove(key); + + if (dict.Count == 0) + { + dict = null; + } + + return removed; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref Dictionary dict, K key, out V value) + { + if (dict != null) + { + var removed = dict.Remove(key, out value); + + if (dict.Count == 0) + { + dict = null; + } + + return removed; + } + + value = default; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replace(ref List list, T oldValue, T newValue) + { + if (oldValue != null && newValue != null) + { + var index = list?.IndexOf(oldValue) ?? -1; + + if (index >= 0) + { + list![index] = newValue; + } + else + { + Add(ref list, newValue); + } + } + else if (oldValue != null) + { + Remove(ref list, oldValue); + } + else if (newValue != null) + { + Add(ref list, newValue); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replace(ref Dictionary dict, K key, V oldValue, V newValue) + { + if (newValue != null) + { + Add(ref dict, key, newValue); + } + else if (oldValue != null) + { + Remove(ref dict, key); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref List list) + { + list.Clear(); + list = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref HashSet set) + { + set.Clear(); + set = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref Dictionary dict) + { + dict.Clear(); + dict = null; + } } } diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index e6d11f918..24d897102 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -70,7 +70,7 @@ namespace Server.Accounting private set { _comments = value; - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } } @@ -83,7 +83,7 @@ namespace Server.Accounting private set { _tags = value; - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } } @@ -121,7 +121,7 @@ namespace Server.Accounting private set { _totalGameTime = value; - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } } @@ -148,7 +148,7 @@ namespace Server.Accounting _loginIPs = Array.Empty(); Accounts.Add(this); - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } public Account(XmlElement node) @@ -224,7 +224,7 @@ namespace Server.Accounting } Accounts.Add(this); - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } public void SetTypeRef(Type type) @@ -527,7 +527,7 @@ namespace Server.Accounting // outside of an entire account deletion. m.Account = null; _mobiles[index] = null; - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } return null; @@ -542,7 +542,7 @@ namespace Server.Accounting } _mobiles[index] = value; - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); if (_mobiles[index] != null) { @@ -677,7 +677,7 @@ namespace Server.Accounting public void AddTag(string name, string value) { Tags.Add(new AccountTag(name, value)); - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } /// @@ -698,7 +698,7 @@ namespace Server.Accounting if (tag.Name == name) { _tags?.RemoveAt(i); - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } } } @@ -717,7 +717,7 @@ namespace Server.Accounting if (tag.Name == name) { tag.Value = value; - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); return; } } diff --git a/Projects/UOContent/Commands/Profiling.cs b/Projects/UOContent/Commands/Profiling.cs index 44dde487c..79f035202 100644 --- a/Projects/UOContent/Commands/Profiling.cs +++ b/Projects/UOContent/Commands/Profiling.cs @@ -78,6 +78,7 @@ namespace Server.Commands { using var sw = new StreamWriter("timerdump.log", true); Timer.DumpInfo(sw); + e.Mobile.SendMessage("Timers dumped to timerdump.log"); } catch { diff --git a/Projects/UOContent/Engines/Factions/Core/Election.cs b/Projects/UOContent/Engines/Factions/Core/Election.cs index 977e3d5df..cedc23d12 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 TimerExecutionToken _timerToken; + private Timer _timer; public Election(Faction faction) { @@ -109,7 +109,7 @@ namespace Server.Factions public void StartTimer() { - Timer.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Slice, out _timerToken); + _timer = Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Slice); } public void Serialize(IGenericWriter writer) @@ -274,7 +274,8 @@ namespace Server.Factions { if (Faction.Election != this) { - _timerToken.Cancel(); + _timer?.Stop(); + _timer = null; return; } diff --git a/Projects/UOContent/Engines/Factions/Core/Faction.cs b/Projects/UOContent/Engines/Factions/Core/Faction.cs index dce3f15db..967951e57 100644 --- a/Projects/UOContent/Engines/Factions/Core/Faction.cs +++ b/Projects/UOContent/Engines/Factions/Core/Faction.cs @@ -621,9 +621,8 @@ namespace Server.Factions EventSink.Login += EventSink_Login; EventSink.Logout += EventSink_Logout; - Timer.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(10.0), HandleAtrophy); - - Timer.StartTimer(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), ProcessTick); + Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(10.0), HandleAtrophy); + Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), ProcessTick); CommandSystem.Register("FactionElection", AccessLevel.GameMaster, FactionElection_OnCommand); CommandSystem.Register("FactionCommander", AccessLevel.Administrator, FactionCommander_OnCommand); diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs index 511595454..184c9cec5 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs @@ -91,10 +91,9 @@ namespace Server.Items return; } - _from = from.Name; - _to = m.Name; + From = from.Name; + To = m.Name; - ((ISerializable)this).MarkDirty(); InvalidateProperties(); from.SendMessage("You inscribe the arrow."); diff --git a/Projects/UOContent/Items/Addons/BallotBox.cs b/Projects/UOContent/Items/Addons/BallotBox.cs index ff7e5232c..2f5b0eb19 100644 --- a/Projects/UOContent/Items/Addons/BallotBox.cs +++ b/Projects/UOContent/Items/Addons/BallotBox.cs @@ -7,7 +7,8 @@ using Server.Prompts; namespace Server.Items { - public class BallotBox : AddonComponent + [Serializable(0)] + public partial class BallotBox : AddonComponent { public static readonly int MaxTopicLines = 6; @@ -19,17 +20,18 @@ namespace Server.Items No = new List(); } - public BallotBox(Serial serial) : base(serial) - { - } - public override int LabelNumber => 1041006; // a ballot box - public string[] Topic { get; private set; } + [SerializableField(0, setter: "private")] + private string[] _topic; - public List Yes { get; private set; } + [Tidy] + [SerializableField(1, setter: "private")] + private List _yes; - public List No { get; private set; } + [Tidy] + [SerializableField(2, setter: "private")] + private List _no; public void ClearTopic() { @@ -56,8 +58,11 @@ namespace Server.Items public void ClearVotes() { - Yes.Clear(); - No.Clear(); + if (Yes.Count > 0 || No.Count > 0) + { + this.Clear(_yes); + this.Clear(_no); + } } public bool IsOwner(Mobile from) @@ -92,43 +97,6 @@ namespace Server.Items } } - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - - writer.WriteEncodedInt(Topic.Length); - - for (var i = 0; i < Topic.Length; i++) - { - writer.Write(Topic[i]); - } - - Yes.Tidy(); - writer.Write(Yes); - - No.Tidy(); - writer.Write(No); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - - Topic = new string[reader.ReadEncodedInt()]; - - for (var i = 0; i < Topic.Length; i++) - { - Topic[i] = reader.ReadString(); - } - - Yes = reader.ReadEntityList(); - No = reader.ReadEntityList(); - } - private class InternalGump : Gump { private readonly BallotBox m_Box; @@ -259,7 +227,7 @@ namespace Server.Items } else { - m_Box.Yes.Add(from); + m_Box.Add(m_Box._yes, from); from.SendLocalizedMessage(500373); // Your vote has been registered. } } @@ -276,7 +244,7 @@ namespace Server.Items } else { - m_Box.No.Add(from); + m_Box.Add(m_Box._no, from); from.SendLocalizedMessage(500373); // Your vote has been registered. } } @@ -344,61 +312,25 @@ namespace Server.Items } } - public class BallotBoxAddon : BaseAddon + [Serializable(0)] + public partial class BallotBoxAddon : BaseAddon { public BallotBoxAddon() { AddComponent(new BallotBox(), 0, 0, 0); } - - public BallotBoxAddon(Serial serial) : base(serial) - { - } - - public override BaseAddonDeed Deed => new BallotBoxDeed(); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } - public class BallotBoxDeed : BaseAddonDeed + [Serializable(0)] + public partial class BallotBoxDeed : BaseAddonDeed { [Constructible] public BallotBoxDeed() { } - public BallotBoxDeed(Serial serial) : base(serial) - { - } - public override BaseAddon Addon => new BallotBoxAddon(); public override int LabelNumber => 1044327; // ballot box - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.WriteEncodedInt(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadEncodedInt(); - } } } diff --git a/Projects/UOContent/Items/Addons/BaseAddon.cs b/Projects/UOContent/Items/Addons/BaseAddon.cs index 2176d86ac..d5eb50dc0 100644 --- a/Projects/UOContent/Items/Addons/BaseAddon.cs +++ b/Projects/UOContent/Items/Addons/BaseAddon.cs @@ -76,7 +76,7 @@ namespace Server.Items Hue = CraftResources.GetHue(m_Resource); InvalidateProperties(); - ((ISerializable)this).MarkDirty(); + this.MarkDirty(); } } } diff --git a/Projects/UOContent/Items/Containers/FillableContainers.cs b/Projects/UOContent/Items/Containers/FillableContainers.cs index 9f8a5ca58..72d371803 100644 --- a/Projects/UOContent/Items/Containers/FillableContainers.cs +++ b/Projects/UOContent/Items/Containers/FillableContainers.cs @@ -9,7 +9,7 @@ namespace Server.Items protected FillableContent m_Content; protected DateTime m_NextRespawnTime; - protected TimerExecutionToken _respawnTimerToken; + protected Timer _respawnTimer; public FillableContainer(int itemID) : base(itemID) => Movable = false; @@ -96,7 +96,8 @@ namespace Server.Items { base.OnAfterDelete(); - _respawnTimerToken.Cancel(); + _respawnTimer?.Stop(); + _respawnTimer = null; } public int GetItemsCount() @@ -118,24 +119,26 @@ namespace Server.Items if (canSpawn) { - if (!_respawnTimerToken.Running) + if (_respawnTimer?.Running != true) { var mins = Utility.RandomMinMax(MinRespawnMinutes, MaxRespawnMinutes); var delay = TimeSpan.FromMinutes(mins); m_NextRespawnTime = Core.Now + delay; - Timer.StartTimer(delay, Respawn, out _respawnTimerToken); + _respawnTimer = Timer.DelayCall(delay, Respawn); } } - else if (_respawnTimerToken.Running) + else { - _respawnTimerToken.Cancel(); + _respawnTimer?.Stop(); + _respawnTimer = null; } } public void Respawn() { - _respawnTimerToken.Cancel(); + _respawnTimer?.Stop(); + _respawnTimer = null; if (m_Content == null || Deleted) { @@ -217,7 +220,7 @@ namespace Server.Items { var subItem = list[j]; - if (!(subItem is Container) && subItem.StackWith(null, item, false)) + if (subItem is not Container && subItem.StackWith(null, item, false)) { break; } @@ -238,7 +241,7 @@ namespace Server.Items writer.Write((int)ContentType); - if (_respawnTimerToken.Running) + if (_respawnTimer?.Running == true) { writer.Write(true); writer.WriteDeltaTime(m_NextRespawnTime); @@ -269,7 +272,7 @@ namespace Server.Items m_NextRespawnTime = reader.ReadDeltaTime(); var delay = m_NextRespawnTime - Core.Now; - Timer.StartTimer(delay, Respawn, out _respawnTimerToken); + Timer.DelayCall(delay, Respawn); } else { diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs index c3a4d51ef..8e6f99915 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.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckEnd_OnTick); + Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckEnd_OnTick); } public bool ValidLocation() => diff --git a/Projects/UOContent/Migrations/Server.Accounting.Account.v2.json b/Projects/UOContent/Migrations/Server.Accounting.Account.v2.json index 607cb92ca..4fee22b2e 100644 --- a/Projects/UOContent/Migrations/Server.Accounting.Account.v2.json +++ b/Projects/UOContent/Migrations/Server.Accounting.Account.v2.json @@ -70,6 +70,7 @@ "type": "System.Collections.Generic.List\u003CServer.Accounting.AccountComment\u003E", "rule": "ListMigrationRule", "ruleArguments": [ + "", "Server.Accounting.AccountComment", "SerializationMethodSignatureMigrationRule" ] @@ -79,6 +80,7 @@ "type": "System.Collections.Generic.List\u003CServer.Accounting.AccountTag\u003E", "rule": "ListMigrationRule", "ruleArguments": [ + "", "Server.Accounting.AccountTag", "SerializationMethodSignatureMigrationRule" ] diff --git a/Projects/UOContent/Migrations/Server.Items.BallotBox.v0.json b/Projects/UOContent/Migrations/Server.Items.BallotBox.v0.json new file mode 100644 index 000000000..0c3c750e1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BallotBox.v0.json @@ -0,0 +1,35 @@ +{ + "version": 0, + "type": "Server.Items.BallotBox", + "properties": [ + { + "name": "Topic", + "type": "string[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "string", + "PrimitiveTypeMigrationRule" + ] + }, + { + "name": "Yes", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "No", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BallotBoxAddon.v0.json b/Projects/UOContent/Migrations/Server.Items.BallotBoxAddon.v0.json new file mode 100644 index 000000000..74a077e4a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BallotBoxAddon.v0.json @@ -0,0 +1,5 @@ +{ + "version": 0, + "type": "Server.Items.BallotBoxAddon", + "properties": [] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BallotBoxDeed.v0.json b/Projects/UOContent/Migrations/Server.Items.BallotBoxDeed.v0.json new file mode 100644 index 000000000..7fdd9747f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BallotBoxDeed.v0.json @@ -0,0 +1,5 @@ +{ + "version": 0, + "type": "Server.Items.BallotBoxDeed", + "properties": [] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseAddon.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseAddon.v2.json index ddc5bd0b7..dbf4d351f 100644 --- a/Projects/UOContent/Migrations/Server.Items.BaseAddon.v2.json +++ b/Projects/UOContent/Migrations/Server.Items.BaseAddon.v2.json @@ -7,6 +7,7 @@ "type": "System.Collections.Generic.List\u003CServer.Items.AddonComponent\u003E", "rule": "ListMigrationRule", "ruleArguments": [ + "", "Server.Items.AddonComponent", "SerializableInterfaceMigrationRule" ] diff --git a/Projects/UOContent/Misc/Weather.cs b/Projects/UOContent/Misc/Weather.cs index d868a1d56..f02977ff2 100644 --- a/Projects/UOContent/Misc/Weather.cs +++ b/Projects/UOContent/Misc/Weather.cs @@ -29,7 +29,7 @@ namespace Server.Misc list?.Add(this); - Timer.StartTimer( + Timer.DelayCall( TimeSpan.FromSeconds((0.2 + Utility.RandomDouble() * 0.8) * interval.TotalSeconds), interval, OnTick diff --git a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs index 2da33812e..a4d405e88 100644 --- a/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs +++ b/Projects/UOContent/Mobiles/Monsters/SE/BakeKitsune.cs @@ -151,7 +151,7 @@ namespace Server.Mobiles SetResistance(ResistanceType.Energy, 40, 60); } - Timer.StartTimer(RemoveDisguise); + Timer.StartTimer(RemoveDisguise, out _disguiseTimerToken); } public void Disguise() @@ -208,6 +208,8 @@ namespace Server.Mobiles public void RemoveDisguise() { + _disguiseTimerToken.Cancel(); + if (!IsBodyMod) { return; @@ -224,8 +226,6 @@ namespace Server.Mobiles DeleteItemOnLayer(Layer.OuterTorso); DeleteItemOnLayer(Layer.Shoes); - - _disguiseTimerToken.Cancel(); } public void DeleteItemOnLayer(Layer layer) diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 00be9495d..dfd6ceef4 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -1067,7 +1067,7 @@ namespace Server.Mobiles public static void UnequipMacro(Mobile m, List layers) { - if (m is PlayerMobile pm && pm.Backpack != null && pm.Alive) + if (m is PlayerMobile { Alive: true } pm && pm.Backpack != null) { var pack = pm.Backpack; var eq = m.Items; diff --git a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs index 313d24fe2..d60d3a6b6 100644 --- a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs @@ -30,6 +30,18 @@ namespace Server.Mobiles private readonly List _buyInfo = new(); private readonly List _sellInfo = new(); + public static void Initialize() + { + // This is technically more work than making timers, but we don't to deplete the timer pool immediately. + foreach (var m in World.Mobiles.Values) + { + if (m is BaseVendor bv) + { + bv.CheckMorph(); + } + } + } + public BaseVendor(string title = null) : base(AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2) { @@ -1360,8 +1372,6 @@ namespace Server.Mobiles { IsParagon = false; } - - Timer.StartTimer(CheckMorph); } public override void AddCustomContextEntries(Mobile from, List list) diff --git a/Projects/UOContent/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs index e5f0cbdc9..ab07f9373 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 TimerExecutionToken _decayTimerToken; + private Timer _decayTimer; private List m_Items; private List m_Mobiles; @@ -55,6 +55,12 @@ namespace Server.Multis { } + public override void OnDelete() + { + _decayTimer?.Stop(); + _decayTimer = null; + } + public virtual void RefreshDecay(bool setDecayTime) { if (Deleted) @@ -67,8 +73,8 @@ namespace Server.Multis m_DecayTime = Core.Now + DecayDelay; } - _decayTimerToken.Cancel(); - Timer.StartTimer(DecayDelay, Delete, out _decayTimerToken); + _decayTimer?.Stop(); + _decayTimer = Timer.DelayCall(DecayDelay, Delete); } 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 39dcc26d3..bd6a66b22 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -1197,7 +1197,7 @@ namespace Server.Multis LockedDownFlag = 1; SecureFlag = 2; - Timer.StartTimer(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Decay_OnTick); + Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Decay_OnTick); } public virtual int GetAosCurLockdowns() diff --git a/Projects/UOContent/Spells/Fifth/PoisonField.cs b/Projects/UOContent/Spells/Fifth/PoisonField.cs index c8f36551f..cb3a1fec2 100644 --- a/Projects/UOContent/Spells/Fifth/PoisonField.cs +++ b/Projects/UOContent/Spells/Fifth/PoisonField.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Server.Collections; using Server.Items; using Server.Misc; using Server.Mobiles; @@ -192,7 +193,6 @@ namespace Server.Spells.Fifth private class InternalTimer : Timer { - private static Queue m_Queue; private readonly bool m_CanFit; private readonly bool m_InLOS; private readonly InternalItem m_Item; @@ -259,21 +259,21 @@ namespace Server.Spells.Fifth ) ); + using var queue = PooledRefQueue.Create(); foreach (var m in eable) { if (m.Z + 16 > m_Item.Z && m_Item.Z + 12 > m.Z && (!Core.AOS || m != caster) && SpellHelper.ValidIndirectTarget(caster, m) && caster.CanBeHarmful(m, false)) { - m_Queue ??= new Queue(); - m_Queue.Enqueue(m); + queue.Enqueue(m); } } eable.Free(); - while (m_Queue?.Count > 0) + while (queue.Count > 0) { - var m = m_Queue.Dequeue(); + var m = queue.Dequeue(); caster.DoHarmful(m); diff --git a/Projects/UOContent/Spells/Fourth/FireField.cs b/Projects/UOContent/Spells/Fourth/FireField.cs index af62cb38b..54ee7087b 100644 --- a/Projects/UOContent/Spells/Fourth/FireField.cs +++ b/Projects/UOContent/Spells/Fourth/FireField.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Server.Collections; using Server.Items; using Server.Misc; using Server.Mobiles; @@ -199,7 +200,6 @@ namespace Server.Spells.Fourth private class InternalTimer : Timer { - private static Queue m_Queue; private readonly bool m_CanFit; private readonly bool m_InLOS; private readonly FireFieldItem m_Item; @@ -256,19 +256,19 @@ namespace Server.Spells.Fourth return; } + using var queue = PooledRefQueue.Create(); foreach (var m in m_Item.GetMobilesInRange(0)) { if (m.Z + 16 > m_Item.Z && m_Item.Z + 12 > m.Z && (!Core.AOS || m != caster) && SpellHelper.ValidIndirectTarget(caster, m) && caster.CanBeHarmful(m, false)) { - m_Queue ??= new Queue(); - m_Queue.Enqueue(m); + queue.Enqueue(m); } } - while (m_Queue?.Count > 0) + while (queue.Count > 0) { - var m = m_Queue.Dequeue(); + var m = queue.Dequeue(); if (m == null) { continue;