From 360143478af8641ef1b9d7c7ae1183def1b962e2 Mon Sep 17 00:00:00 2001
From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com>
Date: Sat, 14 Aug 2021 03:02:53 -0700
Subject: [PATCH] feat: Adds tidy option for serialization. Codegens ballotbox.
Fixes pooled timer leaking (#681)
* Fixes pooled timer leaking
* Fixes `[dumptimers` command so it outputs properly, adds spacing, and stacktraces
* Adds `[Tidy]` for serializing Lists. This will remove deleted entities during world save before serializing the list.
* Adds helpers for managing Lists/Sets/Dictionaries
### New API
```cs
// Creates the list if it is null, then adds
Utility.Add(ref list, value);
Utility.Add(ref set, value);
Utility.Add(ref dict, key, value);
// Nulls the variable if the count is zero
Utility.Remove(ref list, value);
Utility.Remove(ref set, value);
Utility.Remove(ref dict, key);
// Marks entity as dirty in addition to doing the action
entity.Add(list, value);
// Marks entity as dirty, and will create list if it doesn't exist
entity.Add(ref list, value);
// Marks entity as dirty in addition to doing the action
entity.Remove(list, value);
// Marks entity as dirty, and will null the list count is zero
entity.Remove(ref list, value);
```
### Updates to [dumptimers
---
.../SerializableEntityGeneration.Class.cs | 4 +-
...zableEntityGeneration.DeserializeMethod.cs | 3 +-
.../SerializableEntityGeneration.Property.cs | 2 +-
...SerializableEntityGeneration.SerialCtor.cs | 3 +-
...lizableEntityGeneration.SerializeMethod.cs | 3 +-
.../Rules/HashSetMigrationRule.cs | 48 +++--
.../Rules/ListMigrationRule.cs | 49 ++++--
.../SourceGeneration/Helpers.cs | 2 +-
.../SourceGeneration.Arguments.cs | 2 +-
.../SourceGeneration.Attribute.cs | 5 +
.../SourceGeneration.Method.cs | 18 +-
.../SymbolMetadata/SymbolMetadata.UO.cs | 4 +
Projects/Server/Collections/PooledRefQueue.cs | 2 +-
Projects/Server/Items/Container.cs | 67 ++++---
Projects/Server/Network/NetState/NetState.cs | 2 +-
Projects/Server/Sector.cs | 68 ++------
.../DeltaDateTimeAttribute.cs | 0
.../{ => Attributes}/EncodedIntAttribute.cs | 0
.../{ => Attributes}/InternStringAttribute.cs | 0
.../InvalidatePropertiesAttribute.cs | 0
.../{ => Attributes}/SerializableAttribute.cs | 0
.../SerializableFieldAttribute.cs | 2 +-
.../SerializableFieldAttributeAttribute.cs | 0
.../Serialization/Attributes/TidyAttribute.cs | 27 +++
.../Server/Serialization/ISerializable.cs | 7 +-
.../Serialization/ISerializableExtensions.cs | 135 ++++++++++++++
Projects/Server/Timer/Timer.DelayCall.cs | 13 +-
Projects/Server/Timer/Timer.Pool.cs | 2 +-
Projects/Server/Timer/Timer.TimerWheel.cs | 23 ++-
Projects/Server/Utilities/RefPool.cs | 165 ------------------
Projects/Server/Utilities/Utility.cs | 154 ++++++++++++++++
Projects/UOContent/Accounting/Account.cs | 20 +--
Projects/UOContent/Commands/Profiling.cs | 1 +
.../Engines/Factions/Core/Election.cs | 7 +-
.../Engines/Factions/Core/Faction.cs | 5 +-
.../Valentine/2012/Items/CupidsArrow.cs | 5 +-
Projects/UOContent/Items/Addons/BallotBox.cs | 110 +++---------
Projects/UOContent/Items/Addons/BaseAddon.cs | 2 +-
.../Items/Containers/FillableContainers.cs | 23 +--
.../Special/House Raffle/HouseRaffleStone.cs | 2 +-
.../Server.Accounting.Account.v2.json | 2 +
.../Migrations/Server.Items.BallotBox.v0.json | 35 ++++
.../Server.Items.BallotBoxAddon.v0.json | 5 +
.../Server.Items.BallotBoxDeed.v0.json | 5 +
.../Migrations/Server.Items.BaseAddon.v2.json | 1 +
Projects/UOContent/Misc/Weather.cs | 2 +-
.../Mobiles/Monsters/SE/BakeKitsune.cs | 6 +-
Projects/UOContent/Mobiles/PlayerMobile.cs | 2 +-
.../UOContent/Mobiles/Vendors/BaseVendor.cs | 14 +-
Projects/UOContent/Multis/Camps/BaseCamp.cs | 12 +-
Projects/UOContent/Multis/Houses/BaseHouse.cs | 2 +-
.../UOContent/Spells/Fifth/PoisonField.cs | 10 +-
Projects/UOContent/Spells/Fourth/FireField.cs | 10 +-
53 files changed, 627 insertions(+), 464 deletions(-)
rename Projects/Server/Serialization/{ => Attributes}/DeltaDateTimeAttribute.cs (100%)
rename Projects/Server/Serialization/{ => Attributes}/EncodedIntAttribute.cs (100%)
rename Projects/Server/Serialization/{ => Attributes}/InternStringAttribute.cs (100%)
rename Projects/Server/Serialization/{ => Attributes}/InvalidatePropertiesAttribute.cs (100%)
rename Projects/Server/Serialization/{ => Attributes}/SerializableAttribute.cs (100%)
rename Projects/Server/Serialization/{ => Attributes}/SerializableFieldAttribute.cs (93%)
rename Projects/Server/Serialization/{ => Attributes}/SerializableFieldAttributeAttribute.cs (100%)
create mode 100644 Projects/Server/Serialization/Attributes/TidyAttribute.cs
create mode 100644 Projects/Server/Serialization/ISerializableExtensions.cs
delete mode 100644 Projects/Server/Utilities/RefPool.cs
create mode 100644 Projects/UOContent/Migrations/Server.Items.BallotBox.v0.json
create mode 100644 Projects/UOContent/Migrations/Server.Items.BallotBoxAddon.v0.json
create mode 100644 Projects/UOContent/Migrations/Server.Items.BallotBoxDeed.v0.json
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;