Throws SyncValidationError when downloading corrupted Game or GameSave record
This commit is contained in:
parent
ca8c2cb8c5
commit
fcdd3c7840
@ -201,6 +201,7 @@
|
||||
D5A9C01D29DE058C00A8D610 /* VariableFastForward.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C01C29DE058C00A8D610 /* VariableFastForward.swift */; };
|
||||
D5AAF27729884F8600F21ACF /* CheatDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AAF27629884F8600F21ACF /* CheatDevice.swift */; };
|
||||
D5ADD12229F33FBF00CE0560 /* Features.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADD12129F33FBF00CE0560 /* Features.swift */; };
|
||||
D5CDCCEE2A859DC200E22131 /* SyncValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CDCCEC2A859B2B00E22131 /* SyncValidationError.swift */; };
|
||||
D5CDCCEF2A859E5300E22131 /* OSLog+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CDCCC32A85765900E22131 /* OSLog+Delta.swift */; };
|
||||
D5CDCCF02A859E5500E22131 /* UserDefaults+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CDCCEA2A8593FC00E22131 /* UserDefaults+Delta.swift */; };
|
||||
D5CDCCF12A859E7500E22131 /* ReviewSaveStatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A1375A2A7D8F2600AB1372 /* ReviewSaveStatesViewController.swift */; };
|
||||
@ -478,6 +479,7 @@
|
||||
D5ADD12129F33FBF00CE0560 /* Features.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Features.swift; sourceTree = "<group>"; };
|
||||
D5CDCCC32A85765900E22131 /* OSLog+Delta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OSLog+Delta.swift"; sourceTree = "<group>"; };
|
||||
D5CDCCEA2A8593FC00E22131 /* UserDefaults+Delta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Delta.swift"; sourceTree = "<group>"; };
|
||||
D5CDCCEC2A859B2B00E22131 /* SyncValidationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncValidationError.swift; sourceTree = "<group>"; };
|
||||
D5D78AE429F9BC3700E064F0 /* DSAirPlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSAirPlay.swift; sourceTree = "<group>"; };
|
||||
D5D78AE629F9D40A00E064F0 /* EnvironmentValues+FeatureOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnvironmentValues+FeatureOption.swift"; sourceTree = "<group>"; };
|
||||
D5D797E5298D946200738869 /* Contributor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contributor.swift; sourceTree = "<group>"; };
|
||||
@ -888,6 +890,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFAB9F7C219A43380080EC7D /* SyncManager.swift */,
|
||||
D5CDCCEC2A859B2B00E22131 /* SyncValidationError.swift */,
|
||||
BF1F45A321AF274D00EF9895 /* SyncResultViewController.swift */,
|
||||
BF1F45AA21AF4B5800EF9895 /* SyncResultsViewController.storyboard */,
|
||||
);
|
||||
@ -1660,6 +1663,7 @@
|
||||
BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */,
|
||||
BFEF24F31F7DD4FD00454C62 /* SaveStateMigrationPolicy.swift in Sources */,
|
||||
BF5942931E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m in Sources */,
|
||||
D5CDCCEE2A859DC200E22131 /* SyncValidationError.swift in Sources */,
|
||||
BF4828881F90290F00028B97 /* Action.swift in Sources */,
|
||||
BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */,
|
||||
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */,
|
||||
|
||||
@ -199,4 +199,14 @@ extension Game: Syncable
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.name
|
||||
}
|
||||
|
||||
public func awakeFromSync(_ record: AnyRecord) throws
|
||||
{
|
||||
guard let gameCollection = self.gameCollection else { throw SyncValidationError.incorrectGameCollection(nil) }
|
||||
|
||||
if gameCollection.identifier != self.type.rawValue
|
||||
{
|
||||
throw SyncValidationError.incorrectGameCollection(gameCollection.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,9 @@ extension GameSave: Syncable
|
||||
|
||||
public var syncableMetadata: [HarmonyMetadataKey : String] {
|
||||
guard let game = self.game else { return [:] }
|
||||
return [.gameID: game.identifier, .gameName: game.name]
|
||||
|
||||
// Use self.identifier to always link with exact matching game.
|
||||
return [.gameID: self.identifier, .gameName: game.name]
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
@ -66,4 +68,30 @@ extension GameSave: Syncable
|
||||
|
||||
return self.game?.identifier != Game.melonDSBIOSIdentifier && self.game?.identifier != Game.melonDSDSiBIOSIdentifier
|
||||
}
|
||||
|
||||
public func awakeFromSync(_ record: AnyRecord) throws
|
||||
{
|
||||
guard let game = self.game else { throw SyncValidationError.incorrectGame(nil) }
|
||||
|
||||
if game.identifier != self.identifier
|
||||
{
|
||||
let fetchRequest = GameSave.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(GameSave.identifier), game.identifier)
|
||||
|
||||
if let misplacedGameSave = try self.managedObjectContext?.fetch(fetchRequest).first, misplacedGameSave.game == nil
|
||||
{
|
||||
// Relink game with its correct gameSave, in case we accidentally misplaced it.
|
||||
// Otherwise, corrupted records might displace already-downloaded GameSaves
|
||||
// due to automatic Core Data relationship propagation, despite us throwing error.
|
||||
game.gameSave = misplacedGameSave
|
||||
}
|
||||
else
|
||||
{
|
||||
// Either there is no misplacedGameSave, or there is but it's linked to another game somehow.
|
||||
game.gameSave = nil
|
||||
}
|
||||
|
||||
throw SyncValidationError.incorrectGame(game.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,6 +173,15 @@ private extension SyncResultViewController
|
||||
errorMessage = NSLocalizedString("The game for this item is missing. Please re-import the game to resume syncing its data.", comment: "")
|
||||
}
|
||||
|
||||
case .other(_, let error as SyncValidationError):
|
||||
var message = error.failureReason ?? error.localizedDescription
|
||||
if let recoverySuggestion = error.recoverySuggestion
|
||||
{
|
||||
message += "\n\n" + recoverySuggestion
|
||||
}
|
||||
|
||||
errorMessage = message
|
||||
|
||||
case .other(_, let error as NSError): errorMessage = error.localizedFailureReason ?? error.localizedDescription
|
||||
default: errorMessage = error.failureReason
|
||||
}
|
||||
@ -249,9 +258,7 @@ private extension SyncResultViewController
|
||||
case .gameControllerInputMapping: group = .gameControllerInputMapping
|
||||
|
||||
case .gameSave:
|
||||
guard let gameID = metadata?[.gameID] else { continue }
|
||||
|
||||
let recordID = RecordID(type: SyncManager.RecordType.game.rawValue, identifier: gameID)
|
||||
let recordID = RecordID(type: SyncManager.RecordType.game.rawValue, identifier: error.record.recordID.identifier)
|
||||
group = .game(recordID)
|
||||
|
||||
case .saveState:
|
||||
@ -387,7 +394,11 @@ extension SyncResultViewController
|
||||
|
||||
case .game:
|
||||
guard let error = section.errors.first as? RecordError else { return nil }
|
||||
return error.record.localizedName
|
||||
|
||||
let recordID = RecordID(type: SyncManager.RecordType.game.rawValue, identifier: error.record.recordID.identifier)
|
||||
|
||||
let gameName = self.gameNamesByRecordID[recordID] // In case the remote record is corrupted, rely on local names.
|
||||
return gameName ?? error.record.localizedName
|
||||
|
||||
case .saveState(let gameID):
|
||||
guard let error = section.errors.first as? RecordError else { return nil }
|
||||
|
||||
40
Delta/Syncing/SyncValidationError.swift
Normal file
40
Delta/Syncing/SyncValidationError.swift
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// SyncValidationError.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 8/10/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SyncValidationError: LocalizedError
|
||||
{
|
||||
case incorrectGame(String?)
|
||||
case incorrectGameCollection(String?)
|
||||
|
||||
var failureReason: String? {
|
||||
switch self
|
||||
{
|
||||
case .incorrectGame(let name?):
|
||||
return String(format: NSLocalizedString("The downloaded record is associated with the wrong game (%@).", comment: ""), name)
|
||||
|
||||
case .incorrectGame(nil):
|
||||
return NSLocalizedString("The downloaded record is not associated with a game.", comment: "")
|
||||
|
||||
case .incorrectGameCollection(let name?):
|
||||
return String(format: NSLocalizedString("The downloaded record is associated with the wrong game system (%@).", comment: ""), name)
|
||||
|
||||
case .incorrectGameCollection(nil):
|
||||
return NSLocalizedString("The downloaded record is not associated with a game system.", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
var recoverySuggestion: String? {
|
||||
switch self
|
||||
{
|
||||
case .incorrectGame: return NSLocalizedString("Try restoring an older version to resolve this issue.", comment: "")
|
||||
case .incorrectGameCollection: return NSLocalizedString("Try restoring an older version, or manually re-import the game to resolve this issue.", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user