diff --git a/ModernUO.Serialization.Annotations/AfterDeserializationAttribute.cs b/ModernUO.Serialization.Annotations/AfterDeserializationAttribute.cs index dc39484..13ccf54 100644 --- a/ModernUO.Serialization.Annotations/AfterDeserializationAttribute.cs +++ b/ModernUO.Serialization.Annotations/AfterDeserializationAttribute.cs @@ -33,5 +33,5 @@ public class AfterDeserializationAttribute : Attribute /// public bool Synchronous { get; set; } - public AfterDeserializationAttribute(bool synchronous = true) => Synchronous = true; + public AfterDeserializationAttribute(bool synchronous = true) => Synchronous = synchronous; } diff --git a/ModernUO.Serialization.Annotations/AnchoredDateTimeAttribute.cs b/ModernUO.Serialization.Annotations/AnchoredDateTimeAttribute.cs new file mode 100644 index 0000000..659905c --- /dev/null +++ b/ModernUO.Serialization.Annotations/AnchoredDateTimeAttribute.cs @@ -0,0 +1,29 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2026 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AnchoredDateTimeAttribute.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 ModernUO.Serialization; + +/// +/// Hints to the source generator that a serializable DateTime field or property is anchored to +/// the save time: it is written as an absolute value and re-anchored once at load, so downtime +/// does not age it and an unchanged entity serializes to identical bytes. Takes precedence +/// over when both are present. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +public class AnchoredDateTimeAttribute : Attribute +{ +} diff --git a/ModernUO.Serialization.Generator.Tests/Helpers/SourceGeneratorTestHelper.cs b/ModernUO.Serialization.Generator.Tests/Helpers/SourceGeneratorTestHelper.cs index 3c34ac1..3314029 100644 --- a/ModernUO.Serialization.Generator.Tests/Helpers/SourceGeneratorTestHelper.cs +++ b/ModernUO.Serialization.Generator.Tests/Helpers/SourceGeneratorTestHelper.cs @@ -31,6 +31,7 @@ public interface IGenericReader decimal ReadDecimal(); System.DateTime ReadDateTime(); System.DateTime ReadDeltaTime(); + System.DateTime ReadAnchoredTime(); System.TimeSpan ReadTimeSpan(); System.Guid ReadGuid(); int ReadEncodedInt(); @@ -59,6 +60,7 @@ public interface IGenericWriter void Write(decimal value); void Write(System.DateTime value); void WriteDeltaTime(System.DateTime value); + void WriteAnchoredTime(System.DateTime value); void Write(System.TimeSpan value); void Write(System.Guid value); void WriteEncodedInt(int value); @@ -168,6 +170,7 @@ public class Timer { public System.TimeSpan Delay { get; set; } public System.DateTime Next { get; set; } + public static void DelayCall(System.Action callback) { } } public static class Core diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Expected/Server.TestContent.BasicFieldsItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Expected/Server.TestContent.BasicFieldsItem.Serialization.g.cs index 7ce5196..43e8181 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Expected/Server.TestContent.BasicFieldsItem.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Expected/Server.TestContent.BasicFieldsItem.Serialization.g.cs @@ -275,6 +275,19 @@ private set } } + public System.DateTime LastRested + { + get => _lastRested; + set + { + if (value != _lastRested) + { + _lastRested = value; + Server.ISerializableExtensions.MarkDirty(this); + } + } + } + public BasicFieldsItem(Server.Serial serial) { Serial = serial; @@ -323,6 +336,8 @@ public virtual void Serialize(Server.IGenericWriter writer) writer.Write(_floatValue); writer.Write(_privateSet); + + writer.WriteAnchoredTime(_lastRested); } public virtual void Deserialize(Server.IGenericReader reader) @@ -368,6 +383,8 @@ public virtual void Deserialize(Server.IGenericReader reader) _floatValue = reader.ReadFloat(); _privateSet = reader.ReadInt(); + + _lastRested = reader.ReadAnchoredTime(); } } } diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Input.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Input.cs index 039045b..5133ad8 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Input.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/BasicFields/Input.cs @@ -70,6 +70,10 @@ public partial class BasicFieldsItem : ISerializable [SerializableField(19, setter: "private")] private int _privateSet; + [SerializableField(20)] + [AnchoredDateTime] + private DateTime _lastRested; + public DateTime Created { get; set; } public Serial Serial { get; } public bool Deleted => false; diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Expected/TestContent.ExternalItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Expected/TestContent.ExternalItem.Serialization.g.cs index edefd8c..0ea30de 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Expected/TestContent.ExternalItem.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Expected/TestContent.ExternalItem.Serialization.g.cs @@ -72,6 +72,8 @@ public virtual void Deserialize(Server.IGenericReader reader) { MigrateFrom(new V0Content(reader, this)); Server.ISerializableExtensions.MarkDirty(this); + ValidateState(); + Server.Timer.DelayCall(RebuildCaches); return; } @@ -80,6 +82,8 @@ public virtual void Deserialize(Server.IGenericReader reader) var RefreshTimerNext = reader.ReadDateTime(); var RefreshTimerDelay = RefreshTimerNext == System.DateTime.MinValue ? System.TimeSpan.MinValue : RefreshTimerNext - Server.Core.Now; DeserializeRefreshTimer(RefreshTimerDelay); + ValidateState(); + Server.Timer.DelayCall(RebuildCaches); } } } diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Input.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Input.cs index f300329..7e2e1ec 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Input.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NonServerNamespace/Input.cs @@ -28,5 +28,15 @@ private void MigrateFrom(V0Content content) { _name = content.Name; } + + [AfterDeserialization] + private void ValidateState() + { + } + + [AfterDeserialization(false)] + private void RebuildCaches() + { + } } } diff --git a/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs b/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs index 939fb6b..519d129 100644 --- a/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs +++ b/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DeserializeMethod.cs @@ -205,7 +205,7 @@ private void GenerateAfterDeserialization( } else { - source.AppendLine($"{indent}Timer.DelayCall({method.MethodName});"); + source.AppendLine($"{indent}Server.Timer.DelayCall({method.MethodName});"); } } } diff --git a/ModernUO.Serialization.Generator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs b/ModernUO.Serialization.Generator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs index aec840c..b16ba64 100644 --- a/ModernUO.Serialization.Generator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs +++ b/ModernUO.Serialization.Generator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs @@ -68,6 +68,8 @@ SpecialType.System_Decimal and not { SpecialType.System_Int32 when attributes.Any(a => a.IsEncodedInt(compilation)) => ["EncodedInt"], + SpecialType.System_DateTime when attributes.Any(a => a.IsAnchoredDateTime(compilation)) => + ["AnchoredTime"], SpecialType.System_DateTime when attributes.Any(a => a.IsDeltaDateTime(compilation)) => ["DeltaTime"], SpecialType.System_String when attributes.Any(a => a.IsInternString(compilation)) => @@ -114,8 +116,9 @@ public override void GenerateDeserializationMethod( "double" => "ReadDouble", "string" => "ReadString", "decimal" => "ReadDecimal", - date when argument == "DeltaTime" => "ReadDeltaTime", - date => "ReadDateTime", + date when argument == "DeltaTime" => "ReadDeltaTime", + date when argument == "AnchoredTime" => "ReadAnchoredTime", + date => "ReadDateTime", SymbolMetadata.IPADDRESS_CLASS => "ReadIPAddress", SymbolMetadata.TIMESPAN_STRUCT => "ReadTimeSpan", SymbolMetadata.GUID_STRUCT => "ReadGuid", @@ -144,9 +147,10 @@ public override void GenerateSerializationMethod(StringBuilder source, string in var writeMethod = property.Type switch { - date when argument == "DeltaTime" => "WriteDeltaTime", - "int" when argument == "EncodedInt" => "WriteEncodedInt", - _ => "Write" + date when argument == "DeltaTime" => "WriteDeltaTime", + date when argument == "AnchoredTime" => "WriteAnchoredTime", + "int" when argument == "EncodedInt" => "WriteEncodedInt", + _ => "Write" }; source.AppendLine($"{indent}writer.{writeMethod}({propertyName});"); diff --git a/ModernUO.Serialization.Generator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs b/ModernUO.Serialization.Generator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs index 5db536e..cff5894 100644 --- a/ModernUO.Serialization.Generator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs +++ b/ModernUO.Serialization.Generator/SourceGeneration/SymbolMetadata/SymbolMetadata.UO.cs @@ -28,6 +28,7 @@ public static partial class SymbolMetadata public const string SERIALIZABLE_FIELD_ATTRIBUTE = "ModernUO.Serialization.SerializableFieldAttribute"; public const string SERIALIZABLE_PROPERTY_ATTRIBUTE = "ModernUO.Serialization.SerializablePropertyAttribute"; public const string DELTA_DATE_TIME_ATTRIBUTE = "ModernUO.Serialization.DeltaDateTimeAttribute"; + public const string ANCHORED_DATE_TIME_ATTRIBUTE = "ModernUO.Serialization.AnchoredDateTimeAttribute"; public const string INTERN_STRING_ATTRIBUTE = "ModernUO.Serialization.InternStringAttribute"; public const string ENCODED_INT_ATTRIBUTE = "ModernUO.Serialization.EncodedIntAttribute"; public const string CAN_BE_NULL_ATTRIBUTE = "ModernUO.Serialization.CanBeNullAttribute"; @@ -104,6 +105,9 @@ public bool IsEncodedInt(Compilation compilation) => public bool IsDeltaDateTime(Compilation compilation) => attr?.IsAttribute(compilation.GetTypeByMetadataName(DELTA_DATE_TIME_ATTRIBUTE)) == true; + public bool IsAnchoredDateTime(Compilation compilation) => + attr?.IsAttribute(compilation.GetTypeByMetadataName(ANCHORED_DATE_TIME_ATTRIBUTE)) == true; + public bool IsInternString(Compilation compilation) => attr?.IsAttribute(compilation.GetTypeByMetadataName(INTERN_STRING_ATTRIBUTE)) == true;