diff --git a/src/c#/GeneralUpdate.Core/Configuration/VersionEntry.cs b/src/c#/GeneralUpdate.Core/Configuration/VersionEntry.cs index 6195ff8e..5b791b27 100644 --- a/src/c#/GeneralUpdate.Core/Configuration/VersionEntry.cs +++ b/src/c#/GeneralUpdate.Core/Configuration/VersionEntry.cs @@ -205,4 +205,12 @@ public class VersionEntry : VersionIdentity /// [JsonPropertyName("fallbackFullHash")] public string? FallbackFullHash { get; set; } + + /// + /// The version string of the fallback full replacement package. + /// Used by to skip chain packages + /// already covered by a previous fallback. + /// + [JsonPropertyName("fallbackFullVersion")] + public string? FallbackFullVersion { get; set; } } diff --git a/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs b/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs index 21fe3f68..eca0432e 100644 --- a/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs +++ b/src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs @@ -205,7 +205,8 @@ public static DownloadPlan Build( { FallbackFullName = match.Name, FallbackFullUrl = match.Url, - FallbackFullHash = match.SHA256 + FallbackFullHash = match.SHA256, + FallbackFullVersion = match.Version }; } return chain; diff --git a/src/c#/GeneralUpdate.Core/Download/Models/DownloadAsset.cs b/src/c#/GeneralUpdate.Core/Download/Models/DownloadAsset.cs index b84c784c..8c406cfb 100644 --- a/src/c#/GeneralUpdate.Core/Download/Models/DownloadAsset.cs +++ b/src/c#/GeneralUpdate.Core/Download/Models/DownloadAsset.cs @@ -15,6 +15,7 @@ public record DownloadAsset( string? FallbackFullName = null, string? FallbackFullUrl = null, string? FallbackFullHash = null, + string? FallbackFullVersion = null, bool IsForcibly = false, bool IsFreeze = false, int RecordId = 0, diff --git a/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs index 49f6e56a..e2029d36 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs @@ -7,6 +7,7 @@ using GeneralUpdate.Core.Event; using GeneralUpdate.Core.Pipeline; using GeneralUpdate.Core.Configuration; +using GeneralUpdate.Core.Utilities; using GeneralUpdate.Core.Hooks; using IUpdateReporter = GeneralUpdate.Core.Download.Reporting.IUpdateReporter; @@ -146,8 +147,25 @@ public virtual async Task ExecuteAsync() AllPackagesSucceeded = true; var status = ReportType.None; patchRoot = StorageManager.GetTempDirectory(Patchs); + + // Track the highest version already applied via full-package fallback, + // so subsequent chain packages covered by that version are skipped. + SemVersion? fallbackEffectiveVersion = null; + foreach (var version in _configinfo.UpdateVersions) { + // When a previous chain package fell back to a full package, + // the application is already at or beyond that full version. + // Skip any remaining chain packages whose version is ≤ the + // fallback version — applying them would be redundant. + if (fallbackEffectiveVersion != null + && version.PackageType == (int)PackageType.Chain + && Semver.TryParse(version.Version, out var versionSv) + && versionSv <= fallbackEffectiveVersion) + { + GeneralTracer.Info($"AbstractStrategy.ExecuteAsync: skipping {version.Version} ({version.Name}) — already covered by fallback full package v{fallbackEffectiveVersion}."); + continue; + } try { // Use a version-specific subdirectory under patchRoot so that @@ -193,6 +211,14 @@ public virtual async Task ExecuteAsync() var fallbackBuilder = BuildPipeline(fallbackContext); await fallbackBuilder.Build(); status = ReportType.Success; + // Record the fallback full package version so subsequent + // chain packages ≤ this version are skipped. + if (!string.IsNullOrEmpty(version.FallbackFullVersion) + && Semver.TryParse(version.FallbackFullVersion, out var ffv) + && (fallbackEffectiveVersion == null || ffv > fallbackEffectiveVersion)) + { + fallbackEffectiveVersion = ffv; + } } catch (Exception e) { diff --git a/src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs b/src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs index 660b2514..44fb76c0 100644 --- a/src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs @@ -631,7 +631,8 @@ async Task DownloadAndApplyAsync(Download.Models.DownloadPlan plan, UpdateScenar PackageType = a.PackageType, FallbackFullName = a.FallbackFullName, FallbackFullUrl = a.FallbackFullUrl, - FallbackFullHash = a.FallbackFullHash + FallbackFullHash = a.FallbackFullHash, + FallbackFullVersion = a.FallbackFullVersion }).ToList(); var uVersions = dVersions.Where(v => v.AppType == (int)AppType.Upgrade).ToList(); diff --git a/tests/ClientTest/Program.cs b/tests/ClientTest/Program.cs index 7a3dba76..26ba9884 100644 --- a/tests/ClientTest/Program.cs +++ b/tests/ClientTest/Program.cs @@ -7,6 +7,8 @@ using GeneralUpdate.Core.Event; using GeneralUpdate.Core.Hooks; using GeneralUpdate.Core.Security; +using GeneralUpdate.Core.Pipeline; +using GeneralUpdate.Core.Configuration; try { @@ -87,6 +89,7 @@ static async Task RunUpdateTestAsync() } }) .SetOption(Option.AppType, AppType.Client) + .UseDiffPipeline(builder => builder.WithStopOnFirstError(true)) .Hooks() .AddListenerMultiDownloadStatistics(OnDownloadStatistics) .AddListenerMultiDownloadCompleted(OnDownloadCompleted) @@ -140,7 +143,7 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e) { foreach (var vi in e.Info.Body) { - var mode = vi.IsCrossVersion == true ? "CVP" : "Chain"; + var mode = vi.PackageType == (int)PackageType.Full ? "Full" : "Chain"; var appType = vi.AppType switch { 1 => "Client", @@ -149,8 +152,7 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e) }; Console.WriteLine($" - [{mode}] {vi.Version} ({vi.Name}) [{vi.Size} bytes] " + $"AppType={appType} " + - $"{(vi.IsForcibly == true ? "(forced)" : "")}" + - $"{(!string.IsNullOrEmpty(vi.FromVersion) ? $" from={vi.FromVersion}" : "")}"); + $"{(vi.IsForcibly == true ? "(forced)" : "")}"); } } else diff --git a/tests/UpgradeTest/Program.cs b/tests/UpgradeTest/Program.cs index 091150dc..96992e0b 100644 --- a/tests/UpgradeTest/Program.cs +++ b/tests/UpgradeTest/Program.cs @@ -3,6 +3,7 @@ using GeneralUpdate.Core.Download; using GeneralUpdate.Core.Event; using GeneralUpdate.Core.Hooks; +using GeneralUpdate.Core.Pipeline; try { @@ -41,6 +42,7 @@ static async Task RunStandardUpgradeAsync() await new GeneralUpdateBootstrap() .SetOption(Option.AppType, AppType.Upgrade) + .UseDiffPipeline(builder => builder.WithStopOnFirstError(true)) .Hooks() .AddListenerMultiDownloadStatistics(OnDownloadStatistics) .AddListenerMultiDownloadCompleted(OnDownloadCompleted)