/************************************************************************* * ModernUO * * Copyright 2019-2021 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TimerMigrationRule.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.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using SerializationGenerator; namespace SerializableMigration { public class TimerMigrationRule : ISerializableMigrationRule, IPostDeserializeMethod { public string RuleName => nameof(TimerMigrationRule); public bool GenerateRuleState( Compilation compilation, ISymbol symbol, ImmutableArray attributes, ImmutableArray serializableTypes, ImmutableArray embeddedSerializableTypes, ISymbol? parentSymbol, out string[] ruleArguments ) { if (!(symbol is ITypeSymbol typeSymbol && typeSymbol.IsTimer(compilation))) { ruleArguments = null; return false; } ruleArguments = attributes.Any(a => a.IsTimerDrift(compilation)) ? new[] { "@TimerDrift" } : new[] { "" }; return true; } public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference) { var expectedRule = RuleName; var ruleName = property.Rule; if (expectedRule != ruleName) { throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); } var propertyName = property.Name; var ruleArguments = property.RuleArguments; var driftTimer = ruleArguments![0].Contains("@TimerDrift"); var readTimer = driftTimer ? "reader.ReadDeltaTime()" : "reader.ReadDateTime()"; source.AppendLine($"{indent}var {propertyName}Next = {readTimer};"); source.AppendLine($"{indent}var {propertyName}Delay = {propertyName}Next == System.DateTime.MinValue ? System.TimeSpan.MinValue : {propertyName}Next - Core.Now;"); } public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property) { var expectedRule = RuleName; var ruleName = property.Rule; if (expectedRule != ruleName) { throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}."); } var propertyName = property.Name; var ruleArguments = property.RuleArguments; var driftTimer = ruleArguments![0].Contains("@TimerDrift"); var writerMethod = driftTimer ? "WriteDeltaTime" : "Write"; source.AppendLine($"{indent}writer.{writerMethod}({propertyName}?.Next ?? System.DateTime.MinValue);"); } public void PostDeserializeMethod( StringBuilder source, string indent, SerializableProperty property, Compilation compilation, INamedTypeSymbol classSymbol ) { var deserializeTimerMethod = classSymbol .GetMembers() .OfType() .FirstOrDefault( m => { if (!m.ReturnsVoid || m.Parameters.Length != 1 || !m.Parameters[0].Type.IsTimeSpan(compilation)) { return false; } return m.GetAttributes() .FirstOrDefault( attr => { if (!SymbolEqualityComparer.Default.Equals( attr.AttributeClass, compilation.GetTypeByMetadataName( SymbolMetadata.DESERIALIZE_TIMER_FIELD_ATTRIBUTE ) )) { return false; } var order = (int)attr.ConstructorArguments[0].Value!; return order == property.Order; } ) != null; } ) ?? throw new Exception("Serializing a timer requires a method with the DeserializeTimerField attribute to handle creating the timer itself."); source.AppendLine($"{indent}{deserializeTimerMethod.Name}({property.Name}Delay);"); } } }