Skip to content

Commit d8d82ba

Browse files
authored
[Re-push][Emergency Hotfix] Preview 1.83.12 (#815)
# Main Goal Due to a reported recent ban for using this feature, we are disabling it for now by force. While the ban seems to be only applicable for co-op, we are not taking the report lightly and are disabling this feature until we are sure that it is safe to use. If you have similar issue with this feature and/or other feature that causes something to your account, please contact us immediately. If you are from HoYo, thank you, for doing this to your players, I'm sure that game cloud subscription is worth it. The other changes pertain to Honkai: Impact 3rd's switch to the new Sophon downloader system, thus requiring a complete rewrite of the codebase for that game's installation management to support it. # What's changed? - **[Imp]** Update Discord RPC submodule, by @bagusnl - **[New]** Update backend install and repair systems for Honkai: Impact 3rd Sophon changes: - Add fetcher for Block files. This implementation should now accepts patching for block files which are older than current version (e.g. 8.4 -> 8.5) - Re-implement Generic file parsing and updating - Process Audio and Block fetching in parallel simulatneously - Make `KianaDispatch` and `SenadinaFileResult` fetch asynchronously - Improve CDN caching mechanism - Reimplement Audio & Video file parsing and updating - Add mechanism to fix basic asset - `ProgressPerFile` counters are now containing how many bytes of downloadable data instead of the byte size of each of progressing files - Add additional speed counter on `ProgressAll` status to the UI - Simplify `base.ProgressBase` `PopRepairAssetEntry()` - Refactor Unused files checking mechanism - `HonkaiRepairV2` now uses its own new `UpdateProgressCounter()` method to update the `ProgressAll` or `ProgressPerFile` counters - Add matching field exclude mechanism - Use `HashSet` for checking ignored assets - Implement repair for Block/BlockUpdate and Audio/AudioUpdate kind - **[New]** Keep ScreenShot folder post-uninstall for Genshin Impact, by @Cryotechnic - **[Fix]** Use MemoryStream instead of FileStream for image background, by @shatyuka - This prevents file conflict issues when possible - **[Imp]** Use `CDNCacheUtil`'s `GetCachedUrlStatus` to reduce duplicate code, by @neon-nyan - **[Regression]** Disable MobileMode on all games, by @bagusnl - [Loc] Update Localizations by Localizers ❤️ - **[Fix]** Fix metadata update causing file.Length == 0, by @neon-nyan - **[Fix]** Fix updater window crashing due to early application resource sets, by @neon-nyan ### Templates <details> <summary>Changelog Prefixes</summary> ``` **[New]** **[Imp]** **[Fix]** **[Loc]** **[Doc]** ``` </details>
2 parents 7681bb5 + 83dc26e commit d8d82ba

File tree

2 files changed

+52
-35
lines changed

2 files changed

+52
-35
lines changed

CollapseLauncher/Classes/Extension/UIElementExtensions.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,15 @@ internal static TextBlock AddTextBlockLine(this TextBlock textBlock, string mess
390390
return textBlock;
391391
}
392392

393-
private static ResourceDictionary _currentDictionary;
393+
[field: AllowNull, MaybeNull]
394+
private static ResourceDictionary CurrentDictionary
395+
{
396+
get => field ??= Application.Current.Resources ?? throw new NullReferenceException("Application.Current.Resources is null or not initialized!");
397+
}
394398

395399
internal static TReturnType GetApplicationResource<TReturnType>(string resourceKey)
396400
{
397-
_currentDictionary ??= Application.Current.Resources;
398-
399-
if (!(_currentDictionary?.TryGetValue(resourceKey, out object resourceObj) ?? false))
401+
if (!CurrentDictionary.TryGetValue(resourceKey, out object resourceObj))
400402
throw new KeyNotFoundException($"Application resource with key: {resourceKey} does not exist!");
401403

402404
if (resourceObj is not TReturnType resource)
@@ -408,9 +410,7 @@ internal static TReturnType GetApplicationResource<TReturnType>(string resourceK
408410
internal static ref TReturnType GetApplicationResourceRef<TReturnType>(string resourceKey)
409411
where TReturnType : struct
410412
{
411-
_currentDictionary ??= Application.Current.Resources;
412-
413-
if (!(_currentDictionary?.TryGetValue(resourceKey, out object resourceObj) ?? false))
413+
if (!CurrentDictionary.TryGetValue(resourceKey, out object resourceObj))
414414
{
415415
return ref Unsafe.NullRef<TReturnType>();
416416
}
@@ -420,10 +420,10 @@ internal static ref TReturnType GetApplicationResourceRef<TReturnType>(string re
420420

421421
internal static void SetApplicationResource(string resourceKey, object value)
422422
{
423-
if (!_currentDictionary.ContainsKey(resourceKey))
423+
if (!CurrentDictionary.ContainsKey(resourceKey))
424424
throw new KeyNotFoundException($"Application resource with key: {resourceKey} does not exist!");
425425

426-
_currentDictionary[resourceKey] = value;
426+
CurrentDictionary[resourceKey] = value;
427427
}
428428

429429
internal static CornerRadius GetElementCornerRadius(FrameworkElement element, CornerRadiusKind kind = CornerRadiusKind.Normal)

CollapseLauncher/Classes/Helper/Metadata/LauncherMetadataHelper.cs

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using CollapseLauncher.Helper.LauncherApiLoader.HoYoPlay;
33
using CollapseLauncher.Helper.LauncherApiLoader.Legacy;
44
using CollapseLauncher.Helper.Loading;
5+
using CollapseLauncher.Helper.StreamUtility;
56
using CollapseLauncher.Plugins;
67
using CollapseLauncher.Statics;
78
using Hi3Helper;
@@ -500,10 +501,9 @@ async ValueTask LoadRegionMetadataConfig((int, (Stamp?, ConcurrentDictionary<str
500501
CurrentGameRegionMaxCount = LauncherMetadataConfig.Max(x => x.Value?.Count ?? 0);
501502
}
502503

503-
internal static async Task<object?> LoadAndGetConfig(Stamp stamp,
504-
string currentChannel,
505-
bool throwAfterRetry = false,
506-
bool allowDeserializeKey = false)
504+
internal static async Task<FileStream> LoadOrGetConfigStream(
505+
Stamp stamp,
506+
string currentChannel)
507507
{
508508
if (string.IsNullOrEmpty(stamp.MetadataPath))
509509
{
@@ -514,24 +514,47 @@ async ValueTask LoadRegionMetadataConfig((int, (Stamp?, ConcurrentDictionary<str
514514
string configRemoteFilePath =
515515
$"/metadata/{MetadataVersion}/{currentChannel}/".CombineURLFromString(stamp.MetadataPath);
516516

517-
FileStream? configLocalStream = null;
518-
try
519-
{
520-
// Get the local stream
521-
configLocalStream = new FileStream(configLocalFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
517+
FileInfo fileInfo = new FileInfo(configLocalFilePath)
518+
.EnsureNoReadOnly();
522519

523-
// Check if the file doesn't exist, then download the file
524-
if (configLocalStream.Length == 0)
520+
if (!fileInfo.Exists ||
521+
fileInfo.Length == 0)
522+
{
523+
FileStream fileStream = fileInfo.Create();
524+
try
525525
{
526526
// Get the stream and download the file
527527
await using Stream stampRemoteStream =
528528
await FallbackCDNUtil.TryGetCDNFallbackStream(configRemoteFilePath);
529-
await stampRemoteStream.CopyToAsync(configLocalStream);
529+
await stampRemoteStream.CopyToAsync(fileStream);
530530

531531
// Reset the position to 0
532-
configLocalStream.Position = 0;
532+
fileStream.Position = 0;
533+
534+
return fileStream;
535+
}
536+
catch
537+
{
538+
fileStream.Dispose();
539+
throw;
533540
}
541+
}
542+
543+
return fileInfo.Open(FileMode.Open, FileAccess.Read);
544+
}
534545

546+
internal static async Task<object?> LoadAndGetConfig(Stamp stamp,
547+
string currentChannel,
548+
bool throwAfterRetry = false,
549+
bool allowDeserializeKey = false)
550+
{
551+
// Get the local stream
552+
FileStream configLocalStream = await LoadOrGetConfigStream(stamp, currentChannel);
553+
string configLocalFilePath = configLocalStream.Name;
554+
bool isSuccess = true;
555+
556+
try
557+
{
535558
switch (stamp.MetadataType)
536559
{
537560
case MetadataType.MasterKey when allowDeserializeKey:
@@ -554,12 +577,8 @@ async ValueTask LoadRegionMetadataConfig((int, (Stamp?, ConcurrentDictionary<str
554577
case MetadataType.PresetConfigV2:
555578
{
556579
PresetConfig? presetConfig =
557-
await configLocalStream.DeserializeAsync(PresetConfigJsonContext.Default.PresetConfig);
558-
559-
if (presetConfig == null)
560-
{
561-
throw new InvalidDataException("Config seems to be empty!");
562-
}
580+
await configLocalStream.DeserializeAsync(PresetConfigJsonContext.Default.PresetConfig)
581+
?? throw new InvalidDataException("Config seems to be empty!");
563582

564583
// Generate HashID and GameName
565584
string hashComposition = $"{stamp.LastUpdated} - {stamp.GameName} - {stamp.GameRegion}";
@@ -588,6 +607,8 @@ async ValueTask LoadRegionMetadataConfig((int, (Stamp?, ConcurrentDictionary<str
588607
}
589608
catch (Exception ex)
590609
{
610+
isSuccess = false;
611+
591612
await SentryHelper.ExceptionHandlerAsync(ex, SentryHelper.ExceptionType.UnhandledOther);
592613
// Throw if it's allowed
593614
if (throwAfterRetry)
@@ -602,8 +623,7 @@ async ValueTask LoadRegionMetadataConfig((int, (Stamp?, ConcurrentDictionary<str
602623
LogType.Warning, true);
603624

604625
// Try to dispose and delete the old file first, then retry to initialize the config once again.
605-
if (configLocalStream != null)
606-
await configLocalStream.DisposeAsync();
626+
await configLocalStream.DisposeAsync();
607627

608628
if (File.Exists(configLocalFilePath))
609629
File.Delete(configLocalFilePath);
@@ -612,12 +632,9 @@ async ValueTask LoadRegionMetadataConfig((int, (Stamp?, ConcurrentDictionary<str
612632
}
613633
finally
614634
{
615-
// Dispose the local stream
616-
if (configLocalStream != null)
635+
await configLocalStream.DisposeAsync();
636+
if (isSuccess)
617637
{
618-
await configLocalStream.DisposeAsync();
619-
620-
// Register last write timestamp into Stamp
621638
stamp.LastModifiedTimeUtc = GetFileLastModifiedStampUtc(configLocalFilePath);
622639
}
623640
}

0 commit comments

Comments
 (0)