Adds GameSave.sha1 to sync hash between devices

Fixes redundant record uploads post-sync due to comparing hashes against locally-cached hash via extended attributes.
This commit is contained in:
Riley Testut 2023-06-28 13:33:16 -05:00
parent 707116a39b
commit 15e228f287
6 changed files with 10 additions and 63 deletions

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
@ -160,7 +160,6 @@
BFFA4C091E8A24D600D87934 /* GameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA4C081E8A24D600D87934 /* GameTableViewCell.swift */; };
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */; };
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFFA71E01AAC406100EE9DD1 /* Main.storyboard */; };
BFFBD3D9224A0756002EFC79 /* URL+ExtendedAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFBD3D8224A0756002EFC79 /* URL+ExtendedAttributes.swift */; };
BFFC461E1D59823500AF2CC6 /* GamesPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFC461B1D59823500AF2CC6 /* GamesPresentationController.swift */; };
BFFC461F1D59823500AF2CC6 /* GamesStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFC461C1D59823500AF2CC6 /* GamesStoryboardSegue.swift */; };
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFC461D1D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift */; };
@ -422,7 +421,6 @@
BFFA71DB1AAC406100EE9DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
BFFA71E11AAC406100EE9DD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
BFFBD3D8224A0756002EFC79 /* URL+ExtendedAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+ExtendedAttributes.swift"; sourceTree = "<group>"; };
BFFC461B1D59823500AF2CC6 /* GamesPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesPresentationController.swift; sourceTree = "<group>"; };
BFFC461C1D59823500AF2CC6 /* GamesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesStoryboardSegue.swift; sourceTree = "<group>"; };
BFFC461D1D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialGamesStoryboardSegue.swift; sourceTree = "<group>"; };
@ -536,7 +534,6 @@
BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */,
BFC3627F21ADE2BA00EF2BE6 /* UIAlertController+Error.swift */,
BF1F45AC21AF57BA00EF9895 /* HarmonyMetadataKey+Keys.swift */,
BFFBD3D8224A0756002EFC79 /* URL+ExtendedAttributes.swift */,
BF647A6922FB8FCE0061D76D /* Bundle+SwizzleBundleID.swift */,
BFD1EF3F2336BD8800D197CF /* UIDevice+Processor.swift */,
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */,
@ -1611,7 +1608,6 @@
BF4828861F9028F500028B97 /* System.swift in Sources */,
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */,
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
BFFBD3D9224A0756002EFC79 /* URL+ExtendedAttributes.swift in Sources */,
BF713C0822499ED4004A1A2B /* PreviousHarmony.xcdatamodeld in Sources */,
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
BFAB9F7D219A43380080EC7D /* SyncManager.swift in Sources */,

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="16119" systemVersion="19E287" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22158.8" systemVersion="22F66" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
<entity name="Cheat" representedClassName="Cheat" syncable="YES">
<attribute name="code" attributeType="String" syncable="YES"/>
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
@ -117,6 +117,7 @@
<entity name="GameSave" representedClassName="GameSave" syncable="YES">
<attribute name="identifier" attributeType="String" syncable="YES"/>
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="sha1" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="gameSave" inverseEntity="Game" syncable="YES"/>
<uniquenessConstraints>
<uniquenessConstraint>
@ -148,13 +149,4 @@
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<elements>
<element name="Cheat" positionX="-198" positionY="-63" width="128" height="165"/>
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="163"/>
<element name="Game" positionX="-378" positionY="-54" width="128" height="238"/>
<element name="GameCollection" positionX="-585" positionY="-27" width="128" height="90"/>
<element name="GameControllerInputMapping" positionX="-387" positionY="90" width="128" height="120"/>
<element name="GameSave" positionX="-387" positionY="90" width="128" height="90"/>
<element name="SaveState" positionX="-198" positionY="113" width="128" height="178"/>
</elements>
</model>

View File

@ -30,7 +30,7 @@ extension GameSave: Syncable
}
public var syncableKeys: Set<AnyKeyPath> {
return [\GameSave.modifiedDate]
return [\GameSave.modifiedDate, \GameSave.sha1]
}
public var syncableRelationships: Set<AnyKeyPath> {

View File

@ -18,6 +18,8 @@ public class _GameSave: NSManagedObject
@NSManaged public var modifiedDate: Date
@NSManaged public var sha1: String?
// MARK: - Relationships
@NSManaged public var game: Game?

View File

@ -755,23 +755,25 @@ private extension GameViewController
let game = context.object(with: game.objectID) as! Game
let hash = try RSTHasher.sha1HashOfFile(at: game.gameSaveURL)
let previousHash = game.gameSaveURL.extendedAttribute(name: "com.rileytestut.delta.sha1Hash")
let previousHash = game.gameSave?.sha1
guard hash != previousHash else { return }
if let gameSave = game.gameSave
{
gameSave.modifiedDate = Date()
gameSave.sha1 = hash
}
else
{
let gameSave = GameSave(context: context)
gameSave.identifier = game.identifier
gameSave.sha1 = hash
game.gameSave = gameSave
}
try context.save()
try game.gameSaveURL.setExtendedAttribute(name: "com.rileytestut.delta.sha1Hash", value: hash)
if ExperimentalFeatures.shared.toastNotifications.gameSaveEnabled
{

View File

@ -1,45 +0,0 @@
//
// URL+ExtendedAttributes.swift
// Delta
//
// Created by Riley Testut on 3/26/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
extension URL
{
func setExtendedAttribute(name: String, value: String) throws
{
try self.withUnsafeFileSystemRepresentation { (path) in
let data = value.data(using: .utf8)
let result = data?.withUnsafeBytes { (buffer) in
setxattr(path, name, buffer.baseAddress, buffer.count, 0, 0)
}
if let result = result, result < 0
{
throw POSIXError(POSIXErrorCode(rawValue: errno) ?? .ENOENT)
}
}
}
func extendedAttribute(name: String) -> String?
{
let value = self.withUnsafeFileSystemRepresentation { (path) -> String? in
let size = getxattr(path, name, nil, 0, 0, 0)
guard size >= 0 else { return nil }
var data = Data(count: size)
let result = data.withUnsafeMutableBytes { getxattr(path, name, $0.baseAddress, $0.count, 0, 0) }
guard result >= 0 else { return nil }
let value = String(data: data, encoding: .utf8)!
return value
}
return value
}
}