diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs index be4ebcb7c..5e376a765 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Class.cs @@ -101,11 +101,11 @@ namespace SerializationGenerator source.GenerateClassStart( className, - isOverride ? - ImmutableArray.Empty : - ImmutableArray.Create(serializableInterface) + ImmutableArray.Empty ); + const string indent = " "; + source.GenerateClassField( AccessModifier.Private, InstanceModifier.Const, @@ -199,15 +199,16 @@ namespace SerializationGenerator // If we are not inheriting ISerializable, then we need to define some stuff if (!isOverride) { - // long ISerializable.SavePosition { get; set; } + // long ISerializable.SavePosition { get; set; } = -1; source.GenerateAutoProperty( AccessModifier.None, "long", "ISerializable.SavePosition", AccessModifier.None, - AccessModifier.None + AccessModifier.None, + indent, + defaultValue: "-1" ); - source.AppendLine(); // BufferWriter ISerializable.SaveBuffer { get; set; } source.GenerateAutoProperty( @@ -215,7 +216,35 @@ namespace SerializationGenerator "BufferWriter", "ISerializable.SaveBuffer", AccessModifier.None, - AccessModifier.None + AccessModifier.None, + indent + ); + + // bool ISerializable.UseDirtyChecking { get; } = true; + source.GenerateAutoProperty( + AccessModifier.None, + "bool", + "ISerializable.UseDirtyChecking", + AccessModifier.None, + null, + indent, + defaultValue: "true" + ); + source.AppendLine(); + } + else + { + // If this type does not *directly* inherit `ISerializable`, then we assume it has an overridable `UseDirtyChecking` + // public override bool ISerializable.UseDirtyChecking { get; } = true; + source.GenerateAutoProperty( + AccessModifier.Public, + "bool", + "UseDirtyChecking", + AccessModifier.None, + null, + indent, + defaultValue: "true", + isOverride: true ); source.AppendLine(); } diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs index b2a648f35..24d4f4cef 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.Property.cs @@ -47,7 +47,7 @@ namespace SerializationGenerator // Setter source.GeneratePropertySetterStart(false); const string indent = " "; - source.AppendLine($"{indent}if(value != {fieldName})"); + source.AppendLine($"{indent}if (value != {fieldName})"); source.AppendLine($"{indent}{{"); source.AppendLine($"{indent} {fieldName} = value;"); source.AppendLine($"{indent} ((ISerializable)this).MarkDirty();"); diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs index 7823d3e9a..75b91d75c 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerialCtor.cs @@ -40,7 +40,7 @@ namespace SerializationGenerator if (!isOverride) { - source.Append(@$" Serial = serial; + source.AppendLine(@$" Serial = serial; SetTypeRef(typeof({className}));"); } diff --git a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs index b865834da..bdad39969 100644 --- a/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs +++ b/Projects/SerializationGenerator/SerializableEntityGeneration/SerializableEntityGeneration.SerializeMethod.cs @@ -41,13 +41,6 @@ namespace SerializationGenerator const string indent = " "; - source.AppendLine($"{indent}var savePosition = ((Server.ISerializable)this).SavePosition;"); - source.AppendLine(@$"{indent}if (savePosition > -1) -{indent}{{ -{indent} writer.Seek(savePosition, System.IO.SeekOrigin.Begin); -{indent} return; -{indent}}}"); - if (isOverride) { source.AppendLine(); diff --git a/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs b/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs index 1c16055ae..3fa6b0ad6 100644 --- a/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs +++ b/Projects/SerializationGenerator/SerializableMigration/Rules/PrimitiveTypeMigrationRule.cs @@ -87,34 +87,33 @@ namespace SerializationGenerator } var propertyName = property.Name; + var argument = property.RuleArguments.Length >= 1 ? property.RuleArguments[0] : null; const string ipAddress = SerializableEntityGeneration.IPADDRESS_CLASS; + const string date = "System.DateTime"; var readMethod = property.Type switch { - "bool" => "ReadBool", - "sbyte" => "ReadSByte", - "short" => "ReadShort", - "int" => "ReadInt", - "long" => "ReadLong", - "byte" => "ReadByte", - "ushort" => "ReadUShort", - "uint" => "ReadUInt", - "ulong" => "ReadULong", - "float" => "ReadFloat", - "double" => "ReadDouble", - "string" => "ReadString", - "decimal" => "ReadDecimal", - ipAddress => "ReadIPAddress", - "System.DateTime" => property.RuleArguments.Length >= 1 && - property.RuleArguments[0] == "DeltaTime" ? - "ReadDeltaTime" : - "ReadDateTime" + "bool" => "ReadBool", + "sbyte" => "ReadSByte", + "short" => "ReadShort", + "int" when argument == "EncodedInt" => "ReadEncodedInt", + "int" => "ReadInt", + "long" => "ReadLong", + "byte" => "ReadByte", + "ushort" => "ReadUShort", + "uint" => "ReadUInt", + "ulong" => "ReadULong", + "float" => "ReadFloat", + "double" => "ReadDouble", + "string" => "ReadString", + "decimal" => "ReadDecimal", + date when argument == "DeltaTime" => "ReadDeltaTime", + date => "ReadDateTime", + ipAddress => "ReadIPAddress" }; - var readArgument = readMethod == "ReadString" && - property.RuleArguments.Length >= 1 && - property.RuleArguments[0] == "InternString" ? "true" : ""; + var readArgument = readMethod == "ReadString" && argument == "InternString" ? "true" : ""; source.AppendLine($"{indent}{propertyName} = reader.{readMethod}({readArgument});"); } @@ -129,15 +128,16 @@ namespace SerializationGenerator } var propertyName = property.Name; + var argument = property.RuleArguments.Length >= 1 ? property.RuleArguments[0] : null; - if (property.Type == "System.DateTime" && property.RuleArguments.Length >= 1 && property.RuleArguments[0] == "DeltaTime") + var writeMethod = property.Type switch { - source.AppendLine($"{indent}writer.WriteDeltaTime({propertyName});"); - } - else - { - source.AppendLine($"{indent}writer.Write({propertyName});"); - } + "System.DateTime" when argument == "DeltaTime" => "WriteDeltaTime", + "int" when argument == "EncodedInt" => "WriteEncodedInt", + _ => "Write" + }; + + source.AppendLine($"{indent}writer.{writeMethod}({propertyName});"); } } } diff --git a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Property.cs b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Property.cs index e803944a5..bb360e70b 100644 --- a/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Property.cs +++ b/Projects/SerializationGenerator/SourceGeneration/SourceGeneration.Property.cs @@ -59,7 +59,10 @@ namespace SerializationGenerator string propertyName, AccessModifier? getAccessor, AccessModifier? setAccessor, - bool useInit = false + string indent, + bool useInit = false, + string defaultValue = null, + bool isOverride = false ) { if (getAccessor == null && setAccessor == null) @@ -74,12 +77,18 @@ namespace SerializationGenerator var getterSpace = getAccessor != null ? " " : ""; var setOrInit = useInit ? "init;" : "set;"; - var setterAccessor = setAccessor != AccessModifier.None ? $"{setAccessor?.ToFriendlyString() ?? ""} " : ""; - var setter = setterAccessor == "" ? "" : $"{getterSpace}{setterAccessor}{setOrInit}"; + var setterAccessor = setAccessor is null or AccessModifier.None + ? "" + : $"{setAccessor.Value.ToFriendlyString() ?? ""} "; + + var setter = setAccessor == null ? "" : $"{getterSpace}{setterAccessor}{setOrInit}"; var propertyAccessor = accessors == AccessModifier.None ? "" : $"{accessors.ToFriendlyString()} "; + var printOverride = isOverride ? "override " : ""; + var printDefaultValue = defaultValue != null ? $"{(setAccessor != null ? " =" : "")} {defaultValue};" : ""; + var printGetterSetter = setAccessor == null ? "=>" : $"{{ {getter}{setter} }}"; - source.AppendLine($"{propertyAccessor}{type} {propertyName} {{ {getter}{setter} }}"); + source.AppendLine($"{indent}{propertyAccessor}{printOverride}{type} {propertyName} {printGetterSetter}{printDefaultValue}"); } public static void GeneratePropertyEnd(this StringBuilder source) => source.AppendLine(" }"); diff --git a/Projects/Server/Guild.cs b/Projects/Server/Guild.cs index 2ec19c7f5..58cfd4865 100644 --- a/Projects/Server/Guild.cs +++ b/Projects/Server/Guild.cs @@ -33,7 +33,6 @@ namespace Server.Guilds World.AddGuild(this); SetTypeRef(GetType()); - ((ISerializable)this).MarkDirty(); } protected BaseGuild(Serial serial) @@ -64,7 +63,9 @@ namespace Server.Guilds [CommandProperty(AccessLevel.Counselor)] public Serial Serial { get; } - long ISerializable.SavePosition { get; set; } + bool ISerializable.UseDirtyChecking => false; + + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } diff --git a/Projects/Server/IEntity.cs b/Projects/Server/IEntity.cs index f30269b17..756aa18f5 100644 --- a/Projects/Server/IEntity.cs +++ b/Projects/Server/IEntity.cs @@ -48,7 +48,9 @@ namespace Server { } - long ISerializable.SavePosition { get; set; } + bool ISerializable.UseDirtyChecking => false; + + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 7d6682436..b6b01bdfc 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -231,7 +231,6 @@ namespace Server World.AddEntity(this); SetTypeRef(GetType()); - ((ISerializable)this).MarkDirty(); } public Item(Serial serial) @@ -784,7 +783,9 @@ namespace Server AddNameProperties(list); } - long ISerializable.SavePosition { get; set; } + public virtual bool UseDirtyChecking => false; + + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 579de0561..8a9da5fef 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -560,7 +560,6 @@ namespace Server World.AddEntity(this); SetTypeRef(GetType()); - ((ISerializable)this).MarkDirty(); } public Mobile(Serial serial) @@ -2522,7 +2521,9 @@ namespace Server AddNameProperties(list); } - long ISerializable.SavePosition { get; set; } + public virtual bool UseDirtyChecking => false; + + long ISerializable.SavePosition { get; set; } = -1; BufferWriter ISerializable.SaveBuffer { get; set; } diff --git a/Projects/Server/Serialization/ISerializable.cs b/Projects/Server/Serialization/ISerializable.cs index d4e313b09..bfc19fb86 100644 --- a/Projects/Server/Serialization/ISerializable.cs +++ b/Projects/Server/Serialization/ISerializable.cs @@ -20,6 +20,10 @@ namespace Server { public interface ISerializable { + // Make sure all properties that will be serialized are calling `MarkDirty()` when they get modified. + // This should be done manually or via code gen through SerializedField attribute. + // This attribute should be virtual for any base serializalbe type (Item, Mobile, etc) that can be opt-in per type. + bool UseDirtyChecking { get; } long SavePosition { get; protected set; } BufferWriter SaveBuffer { get; protected internal set; } int TypeRef { get; } @@ -39,7 +43,7 @@ namespace Server public void InitializeSaveBuffer(byte[] buffer) { SaveBuffer = new BufferWriter(buffer, true); - if (World.DirtyTrackingEnabled) + if (UseDirtyChecking) { SavePosition = SaveBuffer.Position; } @@ -63,7 +67,7 @@ namespace Server SaveBuffer.Seek(0, SeekOrigin.Begin); Serialize(SaveBuffer); - if (World.DirtyTrackingEnabled) + if (UseDirtyChecking) { SavePosition = SaveBuffer.Position; } diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index f4bb661fa..2fcfd2c79 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -48,7 +48,6 @@ namespace Server private static string _tempSavePath; // Path to the temporary folder for the save private static string _savePath; // Path to "Saves" folder - public const bool DirtyTrackingEnabled = false; public const uint ItemOffset = 0x40000000; public const uint MaxItemSerial = 0x7FFFFFFF; public const uint MaxMobileSerial = ItemOffset - 1; diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index 7a9b6c1d4..90f74715a 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -265,6 +265,8 @@ namespace Server.Accounting } } + bool ISerializable.UseDirtyChecking => false; + long ISerializable.SavePosition { get; set; } BufferWriter ISerializable.SaveBuffer { get; set; }