diff --git a/IPA.Loader/JsonConverters/SemverRangeConverter.cs b/IPA.Loader/JsonConverters/SemverRangeConverter.cs index f570b4af..edff82b8 100644 --- a/IPA.Loader/JsonConverters/SemverRangeConverter.cs +++ b/IPA.Loader/JsonConverters/SemverRangeConverter.cs @@ -1,15 +1,16 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Newtonsoft.Json; -using SemVer; - -namespace IPA.JsonConverters -{ - [SuppressMessage("ReSharper", "UnusedMember.Global")] - internal class SemverRangeConverter : JsonConverter - { - public override Range ReadJson(JsonReader reader, Type objectType, Range existingValue, bool hasExistingValue, JsonSerializer serializer) => new Range(reader.Value as string); - - public override void WriteJson(JsonWriter writer, Range value, JsonSerializer serializer) => writer.WriteValue(value.ToString()); - } -} +#nullable enable +using System; +using Hive.Versioning; +using Newtonsoft.Json; + +namespace IPA.JsonConverters +{ + internal class SemverRangeConverter : JsonConverter + { + public override VersionRange? ReadJson(JsonReader reader, Type objectType, VersionRange? existingValue, bool hasExistingValue, JsonSerializer serializer) + => reader.Value is string s && VersionRange.TryParse(s, out var range) ? range : existingValue; + + public override void WriteJson(JsonWriter writer, VersionRange? value, JsonSerializer serializer) + => writer.WriteValue(value?.ToString()); + } +} diff --git a/IPA.Loader/JsonConverters/SemverVersionConverter.cs b/IPA.Loader/JsonConverters/SemverVersionConverter.cs index c79d2959..c0c7c10a 100644 --- a/IPA.Loader/JsonConverters/SemverVersionConverter.cs +++ b/IPA.Loader/JsonConverters/SemverVersionConverter.cs @@ -1,17 +1,19 @@ -using System; -using Newtonsoft.Json; -using Version = SemVer.Version; - -namespace IPA.JsonConverters -{ - internal class SemverVersionConverter : JsonConverter - { - public override Version ReadJson(JsonReader reader, Type objectType, Version existingValue, bool hasExistingValue, JsonSerializer serializer) => reader.Value == null ? null : new Version(reader.Value as string, true); - - public override void WriteJson(JsonWriter writer, Version value, JsonSerializer serializer) - { - if (value == null) writer.WriteNull(); - else writer.WriteValue(value.ToString()); - } - } -} +#nullable enable +using System; +using Newtonsoft.Json; +using Version = Hive.Versioning.Version; + +namespace IPA.JsonConverters +{ + internal class SemverVersionConverter : JsonConverter + { + public override Version? ReadJson(JsonReader reader, Type objectType, Version? existingValue, bool hasExistingValue, JsonSerializer serializer) + => reader.Value is string s && Version.TryParse(s, out var version) ? version : existingValue; + + public override void WriteJson(JsonWriter writer, Version? value, JsonSerializer serializer) + { + if (value == null) writer.WriteNull(); + else writer.WriteValue(value.ToString()); + } + } +} diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index 7bcd8bc2..56a08cc7 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -18,6 +18,7 @@ using System.Diagnostics.CodeAnalysis; using HarmonyLib; using System.Diagnostics; using IPA.AntiMalware; +using Hive.Versioning; #if NET4 using Task = System.Threading.Tasks.Task; using TaskEx = System.Threading.Tasks.Task; @@ -770,7 +771,7 @@ namespace IPA.Loader Logger.loader.Debug(string.Join(", ", PluginsMetadata.StrJP())); #endif - PluginsMetadata.Sort((a, b) => b.Version.CompareTo(a.Version)); + PluginsMetadata.Sort((a, b) => b.HVersion.CompareTo(a.HVersion)); #if DEBUG // print base resolution order @@ -830,20 +831,20 @@ namespace IPA.Loader foreach (var (id, range) in meta.Manifest.Conflicts) { if (metadataCache.TryGetValue(id, out var plugin) - && range.IsSatisfied(plugin.Meta.Version)) + && range.Matches(plugin.Meta.HVersion)) { // make sure that there's a mutual dependency - var targetRange = new Range($"={meta.Version}", true); + var targetRange = VersionRange.ForVersion(meta.HVersion); var targetConflicts = plugin.Meta.Manifest.Conflicts; if (!targetConflicts.TryGetValue(meta.Id, out var realRange)) { // there's not already a listed conflict targetConflicts.Add(meta.Id, targetRange); } - else if (!realRange.IsSatisfied(meta.Version)) + else if (!realRange.Matches(meta.HVersion)) { // there is already a listed conflict that isn't mutual - targetRange = new Range($"{realRange} || {targetRange}", true); + targetRange = realRange | targetRange; targetConflicts[meta.Id] = targetRange; } } @@ -960,7 +961,7 @@ namespace IPA.Loader if (id == SelfMeta.Id) dependsOnSelf = true; if (!TryResolveId(id, out var depMeta, out var depDisabled, out var depIgnored) - || !range.IsSatisfied(depMeta.Version)) + || !range.Matches(depMeta.HVersion)) { Logger.loader.Warn($"Dependency '{id}@{range}' for '{plugin.Id}' does not exist; ignoring '{plugin.Id}'"); ignoredPlugins.Add(plugin, new(Reason.Dependency) @@ -1033,19 +1034,19 @@ namespace IPA.Loader } // after we handle dependencies and loadafters, then check conflicts - foreach (var conflict in plugin.Manifest.Conflicts) + foreach (var (id, range) in plugin.Manifest.Conflicts) { - Logger.loader.Trace($">- Checking conflict '{conflict.Key}' {conflict.Value}"); + Logger.loader.Trace($">- Checking conflict '{id}' {range}"); // this lookup must be partial to prevent loadBefore/conflictsWith from creating a recursion loop - if (TryResolveId(conflict.Key, out var meta, out var conflDisabled, out var conflIgnored, partial: true) - && conflict.Value.IsSatisfied(meta.Version) + if (TryResolveId(id, out var meta, out var conflDisabled, out var conflIgnored, partial: true) + && range.Matches(meta.HVersion) && !conflIgnored && !conflDisabled) // the conflict is only *actually* a problem if it is both not ignored and not disabled { - Logger.loader.Warn($"Plugin '{plugin.Id}' conflicts with {meta.Id}@{meta.Version}; ignoring '{plugin.Id}'"); + Logger.loader.Warn($"Plugin '{plugin.Id}' conflicts with {meta.Id}@{meta.HVersion}; ignoring '{plugin.Id}'"); ignoredPlugins.Add(plugin, new(Reason.Conflict) { - ReasonText = $"Conflicts with {meta.Id}@{meta.Version}", + ReasonText = $"Conflicts with {meta.Id}@{meta.HVersion}", RelatedTo = meta }); ignored = true; diff --git a/IPA.Loader/Loader/PluginManifest.cs b/IPA.Loader/Loader/PluginManifest.cs index 104b9234..6a394f2a 100644 --- a/IPA.Loader/Loader/PluginManifest.cs +++ b/IPA.Loader/Loader/PluginManifest.cs @@ -1,4 +1,5 @@ #nullable enable +using Hive.Versioning; using IPA.JsonConverters; using IPA.Utilities; using Newtonsoft.Json; @@ -7,7 +8,7 @@ using SemVer; using System; using System.Collections.Generic; using AlmostVersionConverter = IPA.JsonConverters.AlmostVersionConverter; -using Version = SemVer.Version; +using Version = Hive.Versioning.Version; #if NET3 using Net3_Proxy; using Array = Net3_Proxy.Array; @@ -36,10 +37,10 @@ namespace IPA.Loader public string Author = null!; [JsonProperty("dependsOn", Required = Required.DisallowNull, ItemConverterType = typeof(SemverRangeConverter))] - public Dictionary Dependencies = new(); + public Dictionary Dependencies = new(); [JsonProperty("conflictsWith", Required = Required.DisallowNull, ItemConverterType = typeof(SemverRangeConverter))] - public Dictionary Conflicts = new(); + public Dictionary Conflicts = new(); [JsonProperty("features", Required = Required.DisallowNull), JsonConverter(typeof(FeaturesFieldConverter))] public Dictionary Features = new(); diff --git a/IPA.Loader/Loader/PluginMetadata.cs b/IPA.Loader/Loader/PluginMetadata.cs index 6712091b..0bb89f9c 100644 --- a/IPA.Loader/Loader/PluginMetadata.cs +++ b/IPA.Loader/Loader/PluginMetadata.cs @@ -7,7 +7,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using Version = SemVer.Version; +using SVersion = SemVer.Version; +using Version = Hive.Versioning.Version; #if NET3 using Net3_Proxy; using Path = Net3_Proxy.Path; @@ -60,7 +61,14 @@ namespace IPA.Loader /// The version of the plugin. /// /// the version of the plugin - public Version Version => manifest.Version; + [Obsolete("Use HVersion instead.")] + public SVersion Version => SVersion.ForHiveVersion(manifest.Version); + + /// + /// The version of the plugin. + /// + /// the version of the plugin + public Version HVersion => manifest.Version; /// /// The file the plugin was loaded from. @@ -144,6 +152,6 @@ namespace IPA.Loader /// Gets all of the metadata as a readable string. /// /// the readable printable metadata string - public override string ToString() => $"{Name}({Id}@{Version})({PluginType?.FullName}) from '{Utils.GetRelativePath(File?.FullName ?? "", UnityGame.InstallPath)}'"; + public override string ToString() => $"{Name}({Id}@{HVersion})({PluginType?.FullName}) from '{Utils.GetRelativePath(File?.FullName ?? "", UnityGame.InstallPath)}'"; } } \ No newline at end of file diff --git a/IPA.Loader/Utilities/AlmostVersion.cs b/IPA.Loader/Utilities/AlmostVersion.cs index 182c1371..22ec7c19 100644 --- a/IPA.Loader/Utilities/AlmostVersion.cs +++ b/IPA.Loader/Utilities/AlmostVersion.cs @@ -1,9 +1,11 @@ -using IPA.Config.Data; +#nullable enable +using IPA.Config.Data; using IPA.Config.Stores; using IPA.Config.Stores.Converters; using System; using System.Collections.Generic; -using Version = SemVer.Version; +using SVersion = SemVer.Version; +using Version = Hive.Versioning.Version; namespace IPA.Utilities { @@ -11,7 +13,10 @@ namespace IPA.Utilities /// A type that wraps so that the string of the version is stored when the string is /// not a valid . /// - public class AlmostVersion : IComparable, IComparable + public class AlmostVersion : IComparable, IComparable, +#pragma warning disable CS0618 // Type or member is obsolete + IComparable +#pragma warning restore CS0618 // Type or member is obsolete { /// /// Represents a storage type of either parsed object or raw . @@ -19,7 +24,7 @@ namespace IPA.Utilities public enum StoredAs { /// - /// The version was stored as a . + /// The version was stored as a . /// SemVer, /// @@ -35,7 +40,7 @@ namespace IPA.Utilities public AlmostVersion(string vertext) { if (!TryParseFrom(vertext, StoredAs.SemVer)) - TryParseFrom(vertext, StoredAs.String); + _ = TryParseFrom(vertext, StoredAs.String); } /// @@ -46,7 +51,14 @@ namespace IPA.Utilities { SemverValue = ver; StorageMode = StoredAs.SemVer; - } + } + + /// + /// Creates an from the provided in . + /// + /// the to store + [Obsolete("Use Hive.Versioning.Version constructor instead.")] + public AlmostVersion(SVersion ver) : this(ver?.UnderlyingVersion ?? throw new ArgumentNullException(nameof(ver))) { } /// /// Creates an from the version string in stored using @@ -68,26 +80,22 @@ namespace IPA.Utilities /// an to copy the storage mode of public AlmostVersion(string vertext, AlmostVersion copyMode) { - if (copyMode == null) + if (copyMode is null) throw new ArgumentNullException(nameof(copyMode)); if (!TryParseFrom(vertext, copyMode.StorageMode)) - TryParseFrom(vertext, StoredAs.String); // silently parse differently + _ = TryParseFrom(vertext, StoredAs.String); // silently parse differently } private bool TryParseFrom(string str, StoredAs mode) { - if (mode == StoredAs.SemVer) - try - { - SemverValue = new Version(str, true); - StorageMode = StoredAs.SemVer; - return true; - } - catch - { - return false; - } + if (mode == StoredAs.SemVer) + { + StorageMode = StoredAs.SemVer; + var result = Version.TryParse(str, out var version); + SemverValue = version; + return result; + } else { StringValue = str; @@ -100,13 +108,13 @@ namespace IPA.Utilities /// The value of the if it was stored as a . /// /// the stored value as a , or if not stored as a string. - public string StringValue { get; private set; } = null; + public string? StringValue { get; private set; } /// /// The value of the if it was stored as a . /// /// the stored value as a , or if not stored as a version. - public Version SemverValue { get; private set; } = null; + public Version? SemverValue { get; private set; } /// /// The way the value is stored, whether it be as a or a . @@ -114,15 +122,14 @@ namespace IPA.Utilities /// the storage mode used to store this value public StoredAs StorageMode { get; private set; } - // can I just this? /// /// Gets a string representation of the current version. If the value is stored as a string, this returns it. If it is - /// stored as a , it is equivalent to calling . + /// stored as a , it is equivalent to calling . /// /// a string representation of the current version /// public override string ToString() => - StorageMode == StoredAs.SemVer ? SemverValue.ToString() : StringValue; + StorageMode == StoredAs.SemVer ? SemverValue!.ToString() : StringValue!; /// /// Compares to the in using @@ -136,14 +143,11 @@ namespace IPA.Utilities /// public int CompareTo(AlmostVersion other) { - if (other == null) return -1; - if (StorageMode != other.StorageMode) - throw new InvalidOperationException("Cannot compare AlmostVersions with different stores!"); + if (other is null) return 1; - if (StorageMode == StoredAs.SemVer) - return SemverValue.CompareTo(other.SemverValue); - else - return StringValue.CompareTo(other.StringValue); + return StorageMode == StoredAs.SemVer && other.StorageMode == StoredAs.SemVer + ? SemverValue!.CompareTo(other.SemverValue!) + : string.Compare(ToString(), other.ToString(), StringComparison.Ordinal); } /// @@ -161,8 +165,21 @@ namespace IPA.Utilities if (StorageMode != StoredAs.SemVer) throw new InvalidOperationException("Cannot compare a SemVer version with an AlmostVersion stored as a string!"); - return SemverValue.CompareTo(other); - } + return SemverValue!.CompareTo(other); + } + + /// + /// Compares to the in using . + /// + /// + /// The storage method of must be , else an will + /// be thrown. + /// + /// the to compare to + /// less than 0 if is considered bigger than , 0 if equal, and greater than zero if smaller + /// + [Obsolete("Use the Hive.Versioning.Version overload instead.")] + public int CompareTo(SVersion other) => CompareTo(other.UnderlyingVersion); /// /// Performs a strict equality check between and . @@ -189,12 +206,12 @@ namespace IPA.Utilities public override int GetHashCode() { var hashCode = -126402897; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SemverValue); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StringValue); - hashCode = hashCode * -1521134295 + StorageMode.GetHashCode(); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(SemverValue); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(StringValue); + hashCode = (hashCode * -1521134295) + StorageMode.GetHashCode(); return hashCode; - } - + } + /// /// Compares two versions, only taking into account the numeric part of the version if they are stored as s, /// or strict equality if they are stored as s. @@ -212,10 +229,9 @@ namespace IPA.Utilities if (l is null && r is null) return true; if (l is null || r is null) return false; if (l.StorageMode != r.StorageMode) return false; - if (l.StorageMode == StoredAs.SemVer) - return Utils.VersionCompareNoPrerelease(l.SemverValue, r.SemverValue) == 0; - else - return l.StringValue == r.StringValue; + return l.StorageMode == StoredAs.SemVer + ? Utils.VersionCompareNoPrerelease(l.SemverValue!, r.SemverValue!) == 0 + : l.StringValue == r.StringValue; } /// @@ -225,24 +241,57 @@ namespace IPA.Utilities /// the second value to compare /// if they are not mostly equal, otherwise /// - public static bool operator!=(AlmostVersion l, AlmostVersion r) => !(l == r); - - // implicitly convertible from Version + public static bool operator!=(AlmostVersion l, AlmostVersion r) => !(l == r); + + // implicitly convertible from Version +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable CA2225 // Operator overloads have named alternates /// - /// Implicitly converts a to using . + /// Implicitly converts a to using . /// - /// the to convert - /// - public static implicit operator AlmostVersion(Version ver) => new AlmostVersion(ver); - - // implicitly convertible to Version + /// the to convert + /// + [Obsolete("Use Hive.Versioning.Version instead of SemVer.Version")] + public static implicit operator AlmostVersion?(SVersion? ver) => ver is null ? null : new(ver); + + // implicitly convertible to Version + /// + /// Implicitly converts an to , if applicable, using . + /// If not applicable, returns + /// + /// the to convert to a + /// + [Obsolete("Use Hive.Versioning.Version instead of SemVer.Version")] + public static implicit operator SVersion?(AlmostVersion? av) => av?.SemverValue is not null ? SVersion.ForHiveVersion(av.SemverValue) : null; +#pragma warning restore CS0618 // Type or member is obsolete + /// + /// Implicitly converts a to using . + /// + /// the to convert + /// + public static implicit operator AlmostVersion?(Version? ver) => ver is null ? null : new(ver); + + // implicitly convertible to Version /// - /// Implicitly converts an to , if applicable, using . + /// Implicitly converts an to , if applicable, using . /// If not applicable, returns /// - /// the to convert to a + /// the to convert to a /// - public static implicit operator Version(AlmostVersion av) => av?.SemverValue; + public static implicit operator Version?(AlmostVersion av) => av?.SemverValue; +#pragma warning restore CA2225 // Operator overloads have named alternates + + public static bool operator <(AlmostVersion left, AlmostVersion right) + => left is null ? right is not null : left.CompareTo(right) < 0; + + public static bool operator <=(AlmostVersion left, AlmostVersion right) + => left is null || left.CompareTo(right) <= 0; + + public static bool operator >(AlmostVersion left, AlmostVersion right) + => left is not null && left.CompareTo(right) > 0; + + public static bool operator >=(AlmostVersion left, AlmostVersion right) + => left is null ? right is null : left.CompareTo(right) >= 0; } /// @@ -257,7 +306,7 @@ namespace IPA.Utilities /// the owner of the new object /// public override AlmostVersion FromValue(Value value, object parent) - => new AlmostVersion(Converter.Default.FromValue(value, parent)); + => new(Converter.Default.FromValue(value, parent)); /// /// Converts an to a node. /// diff --git a/IPA.Loader/Utilities/Utils.cs b/IPA.Loader/Utilities/Utils.cs index f7774005..75ae9ce2 100644 --- a/IPA.Loader/Utilities/Utils.cs +++ b/IPA.Loader/Utilities/Utils.cs @@ -8,6 +8,7 @@ using Mono.Cecil; using System.Runtime.CompilerServices; using System.Threading; using System.Diagnostics.CodeAnalysis; +using Version = Hive.Versioning.Version; #if NET3 using File = Net3_Proxy.File; #endif @@ -193,16 +194,26 @@ namespace IPA.Utilities /// the left value /// the right value /// < 0 if l is less than r, 0 if they are equal in the numeric portion, or > 0 if l is greater than r + [Obsolete("Use Hive.Versioning.Version overload instead.")] public static int VersionCompareNoPrerelease(SemVer.Version l, SemVer.Version r) + => VersionCompareNoPrerelease(l?.UnderlyingVersion!, r?.UnderlyingVersion!); + + /// + /// Compares a pair of s ignoring both the prerelease and build fields. + /// + /// the left value + /// the right value + /// < 0 if l is less than r, 0 if they are equal in the numeric portion, or > 0 if l is greater than r + public static int VersionCompareNoPrerelease(Version l, Version r) { if (l is null) throw new ArgumentNullException(nameof(l)); if (r is null) throw new ArgumentNullException(nameof(r)); - var cmpVal = l.Major - r.Major; + var cmpVal = l.Major.CompareTo(r.Major); if (cmpVal != 0) return cmpVal; - cmpVal = l.Minor - r.Minor; + cmpVal = l.Minor.CompareTo(r.Minor); if (cmpVal != 0) return cmpVal; - cmpVal = l.Patch - r.Patch; + cmpVal = l.Patch.CompareTo(r.Patch); return cmpVal; }