diff --git a/v2rayN/v2rayN/Base/StringEx.cs b/v2rayN/v2rayN/Base/StringEx.cs index 1e150a48..98fed4d8 100644 --- a/v2rayN/v2rayN/Base/StringEx.cs +++ b/v2rayN/v2rayN/Base/StringEx.cs @@ -46,5 +46,29 @@ namespace v2rayN.Base { return value == null ? string.Empty : value.Trim(); } + + public static string RemovePrefix(this string value, char prefix) + { + if (value.StartsWith(prefix)) + { + return value.Substring(1); + } + else + { + return value; + } + } + + public static string RemovePrefix(this string value, string prefix) + { + if (value.StartsWith(prefix)) + { + return value.Substring(prefix.Length); + } + else + { + return value; + } + } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/UpdateHandle.cs b/v2rayN/v2rayN/Handler/UpdateHandle.cs index 7e90e6ca..7e011e75 100644 --- a/v2rayN/v2rayN/Handler/UpdateHandle.cs +++ b/v2rayN/v2rayN/Handler/UpdateHandle.cs @@ -9,6 +9,7 @@ using System.Windows; using v2rayN.Base; using v2rayN.Mode; using v2rayN.Resx; +using v2rayN.Tool; namespace v2rayN.Handler { @@ -331,7 +332,7 @@ namespace v2rayN.Handler /// /// 获取V2RayCore版本 /// - private string getCoreVersion(ECoreType type) + private SemanticVersion getCoreVersion(ECoreType type) { try { @@ -352,7 +353,7 @@ namespace v2rayN.Handler { string msg = string.Format(ResUI.NotFoundCore, @"", "", ""); //ShowMsg(true, msg); - return ""; + return new SemanticVersion(""); } using Process p = new(); @@ -385,13 +386,13 @@ namespace v2rayN.Handler version = Regex.Match(echo, $"([0-9.]+)").Groups[1].Value; break; } - return version; + return new SemanticVersion(version); } catch (Exception ex) { Utils.SaveLog(ex.Message, ex); _updateFunc(false, ex.Message); - return ""; + return new SemanticVersion(""); } } @@ -400,18 +401,18 @@ namespace v2rayN.Handler try { var gitHubReleases = Utils.FromJson>(gitHubReleaseApi); - string version; + SemanticVersion version; if (preRelease) { - version = gitHubReleases!.First().TagName; + version = new SemanticVersion(gitHubReleases!.First().TagName); } else { - version = gitHubReleases!.First(r => r.Prerelease == false).TagName; + version = new SemanticVersion(gitHubReleases!.First(r => r.Prerelease == false).TagName); } var coreInfo = LazyConfig.Instance.GetCoreInfo(type); - string curVersion; + SemanticVersion curVersion; string message; string url; switch (type) @@ -421,8 +422,8 @@ namespace v2rayN.Handler case ECoreType.Xray: case ECoreType.v2fly_v5: { - curVersion = "v" + getCoreVersion(type); - message = string.Format(ResUI.IsLatestCore, curVersion); + curVersion = getCoreVersion(type); + message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v")); string osBit = "64"; switch (RuntimeInformation.ProcessArchitecture) { @@ -439,7 +440,7 @@ namespace v2rayN.Handler break; } - url = string.Format(coreInfo.coreDownloadUrl64, version, osBit); + url = string.Format(coreInfo.coreDownloadUrl64, version.ToVersionString("v"), osBit); break; } case ECoreType.clash: @@ -466,8 +467,8 @@ namespace v2rayN.Handler } case ECoreType.sing_box: { - curVersion = "v" + getCoreVersion(type); - message = string.Format(ResUI.IsLatestCore, curVersion); + curVersion = getCoreVersion(type); + message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v")); switch (RuntimeInformation.ProcessArchitecture) { case Architecture.Arm64: @@ -482,12 +483,12 @@ namespace v2rayN.Handler url = coreInfo.coreDownloadUrl64; break; } - url = string.Format(url, version, version.Replace("v", "")); + url = string.Format(url, version.ToVersionString("v"), version); break; } case ECoreType.v2rayN: { - curVersion = FileVersionInfo.GetVersionInfo(Utils.GetExePath()).FileVersion.ToString(); + curVersion = new SemanticVersion(FileVersionInfo.GetVersionInfo(Utils.GetExePath()).FileVersion.ToString()); message = string.Format(ResUI.IsLatestN, curVersion); switch (RuntimeInformation.ProcessArchitecture) { @@ -509,18 +510,7 @@ namespace v2rayN.Handler throw new ArgumentException("Type"); } - if (type == ECoreType.v2rayN) - { - decimal.TryParse(curVersion, out decimal decCur); - decimal.TryParse(version, out decimal dec); - if (decCur >= dec) - { - AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message)); - return; - } - } - - if (curVersion == version) + if (curVersion >= version) { AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message)); return; diff --git a/v2rayN/v2rayN/Tool/SemanticVersion.cs b/v2rayN/v2rayN/Tool/SemanticVersion.cs new file mode 100644 index 00000000..a79a3fbc --- /dev/null +++ b/v2rayN/v2rayN/Tool/SemanticVersion.cs @@ -0,0 +1,173 @@ +using v2rayN.Base; + +namespace v2rayN.Tool +{ + public class SemanticVersion + { + private int major; + private int minor; + private int patch; + private string version; + + public SemanticVersion(int major, int minor, int patch) + { + this.major = major; + this.minor = minor; + this.patch = patch; + this.version = $"{major}.{minor}.{patch}"; + } + + public SemanticVersion(string version) + { + this.version = version.RemovePrefix('v'); + try + { + string[] parts = this.version.Split('.'); + if (parts.Length == 2) + { + this.major = int.Parse(parts[0]); + this.minor = int.Parse(parts[1]); + this.patch = 0; + } + else if (parts.Length == 3) + { + this.major = int.Parse(parts[0]); + this.minor = int.Parse(parts[1]); + this.patch = int.Parse(parts[2]); + } + else + { + throw new ArgumentException("Invalid version string"); + } + } + catch + { + this.major = 0; + this.minor = 0; + this.patch = 0; + this.version = "0.0.0"; + } + } + + public override bool Equals(object? obj) + { + if (obj is SemanticVersion other) + { + return this.major == other.major && this.minor == other.minor && this.patch == other.patch; + } + else + { + return false; + } + } + + public override int GetHashCode() + { + return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode(); + } + + /// + /// Use ToVersionString(string? prefix) instead if possible. + /// + /// major.minor.patch + public override string ToString() + { + return this.version; + } + + public string ToVersionString(string? prefix = null) + { + if (prefix == null) + { + return this.version; + } + else + { + return $"{prefix}{this.version}"; + } + } + + public static bool operator ==(SemanticVersion v1, SemanticVersion v2) { return v1.Equals(v2); } + public static bool operator !=(SemanticVersion v1, SemanticVersion v2) { return !v1.Equals(v2); } + public static bool operator >=(SemanticVersion v1, SemanticVersion v2) { return v1.GreaterEquals(v2); } + public static bool operator <=(SemanticVersion v1, SemanticVersion v2) { return v1.LessEquals(v2); } + + #region Private + private bool GreaterEquals(SemanticVersion other) + { + if (this.major < other.major) + { + return false; + } + else if (this.major > other.major) + { + return true; + } + else + { + if (this.minor < other.minor) + { + return false; + } + else if (this.minor > other.minor) + { + return true; + } + else + { + if (this.patch < other.patch) + { + return false; + } + else if (this.patch > other.patch) + { + return true; + } + else + { + return true; + } + } + } + } + + private bool LessEquals(SemanticVersion other) + { + if (this.major < other.major) + { + return true; + } + else if (this.major > other.major) + { + return false; + } + else + { + if (this.minor < other.minor) + { + return true; + } + else if (this.minor > other.minor) + { + return false; + } + else + { + if (this.patch < other.patch) + { + return true; + } + else if (this.patch > other.patch) + { + return false; + } + else + { + return true; + } + } + } + } + #endregion Private + } +}