Fixes issue where creating auto save state would fail when backgrounded with SaveStatesViewController visible

This commit is contained in:
Riley Testut 2016-08-11 16:40:47 -05:00
parent 30eec7bc2f
commit b017be6368

View File

@ -14,22 +14,31 @@ import Roxas
private var kvoContext = 0 private var kvoContext = 0
private extension GameViewController
{
struct PausedSaveState: SaveStateProtocol
{
var fileURL: URL
var gameType: GameType
var isSaved = false
init(fileURL: URL, gameType: GameType)
{
self.fileURL = fileURL
self.gameType = gameType
}
}
}
class GameViewController: DeltaCore.GameViewController class GameViewController: DeltaCore.GameViewController
{ {
/// Assumed to be Delta.Game instance /// Assumed to be Delta.Game instance
override var game: GameProtocol? { override var game: GameProtocol? {
willSet willSet {
{
self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext) self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext)
} }
didSet didSet {
{
if self.game?.fileURL != oldValue?.fileURL
{
// Game changed, so we make sure auto save states are enabled again
self.ignoreAutoSaveStateUpdates = false
}
guard let emulatorCore = self.emulatorCore else { return } guard let emulatorCore = self.emulatorCore else { return }
self.preferredContentSize = emulatorCore.preferredRenderingSize self.preferredContentSize = emulatorCore.preferredRenderingSize
@ -47,8 +56,23 @@ class GameViewController: DeltaCore.GameViewController
private var pauseViewController: PauseViewController? private var pauseViewController: PauseViewController?
private var pausingGameController: GameController? private var pausingGameController: GameController?
// Prevents the "same" save state from being saved multiple times // Prevents the same save state from being saved multiple times
private var ignoreAutoSaveStateUpdates = false private var pausedSaveState: PausedSaveState? {
didSet
{
if let saveState = oldValue, self.pausedSaveState == nil
{
do
{
try FileManager.default.removeItem(at: saveState.fileURL)
}
catch
{
print(error)
}
}
}
}
private var context = CIContext(options: [kCIContextWorkingColorSpace: NSNull()]) private var context = CIContext(options: [kCIContextWorkingColorSpace: NSNull()])
@ -211,6 +235,15 @@ extension GameViewController
self.updateAutoSaveState() self.updateAutoSaveState()
case "pause": case "pause":
if let game = self.game
{
let fileURL = FileManager.uniqueTemporaryURL()
self.pausedSaveState = PausedSaveState(fileURL: fileURL, gameType: game.type)
self.emulatorCore?.saveSaveState(to: fileURL)
}
guard let gameController = sender as? GameController else { guard let gameController = sender as? GameController else {
fatalError("sender for pauseSegue must be the game controller that pressed the Menu button") fatalError("sender for pauseSegue must be the game controller that pressed the Menu button")
} }
@ -257,6 +290,9 @@ extension GameViewController
switch identifier switch identifier
{ {
case "unwindFromPauseMenu": case "unwindFromPauseMenu":
self.pausedSaveState = nil
DispatchQueue.main.async { DispatchQueue.main.async {
if if
let transitionCoordinator = self.transitionCoordinator, let transitionCoordinator = self.transitionCoordinator,
@ -299,6 +335,7 @@ extension GameViewController
@IBAction private func unwindFromGamesViewController(with segue: UIStoryboardSegue) @IBAction private func unwindFromGamesViewController(with segue: UIStoryboardSegue)
{ {
self.pausedSaveState = nil
self.emulatorCore?.resume() self.emulatorCore?.resume()
} }
@ -314,11 +351,6 @@ extension GameViewController
{ {
self.updateCheats() self.updateCheats()
} }
if self.emulatorCore?.state == .running
{
self.ignoreAutoSaveStateUpdates = false
}
} }
} }
@ -365,14 +397,14 @@ extension GameViewController: SaveStatesViewControllerDelegate
{ {
private func updateAutoSaveState() private func updateAutoSaveState()
{ {
guard !self.ignoreAutoSaveStateUpdates else { return }
// If not in view hierarchy, don't update auto save state
guard self.updatesAutoSaveState else { return } guard self.updatesAutoSaveState else { return }
// Ignore future update auto save state requests until we resume emulation again // If pausedSaveState exists and has already been saved, don't update auto save state
// This prevents us from filling our auto save state slots with the "same" save state // This prevents us from filling our auto save state slots with the same save state
self.ignoreAutoSaveStateUpdates = true let savedPausedSaveState = self.pausedSaveState?.isSaved ?? false
guard !savedPausedSaveState else { return }
self.pausedSaveState?.isSaved = true
// Must be done synchronously // Must be done synchronously
let backgroundContext = DatabaseManager.shared.newBackgroundContext() let backgroundContext = DatabaseManager.shared.newBackgroundContext()
@ -400,7 +432,7 @@ extension GameViewController: SaveStatesViewControllerDelegate
if let saveStates = saveStates, let saveState = saveStates.first, saveStates.count >= 2 if let saveStates = saveStates, let saveState = saveStates.first, saveStates.count >= 2
{ {
// If there are two or more auto save states, update the oldest one // If there are two or more auto save states, update the oldest one
self.update(saveState) self.update(saveState, with: self.pausedSaveState)
// Tiny hack; SaveStatesViewController sorts save states by creation date, so we update the creation date too // Tiny hack; SaveStatesViewController sorts save states by creation date, so we update the creation date too
// Simpler than deleting old save states ¯\_()_/¯ // Simpler than deleting old save states ¯\_()_/¯
@ -413,14 +445,14 @@ extension GameViewController: SaveStatesViewControllerDelegate
saveState.type = .auto saveState.type = .auto
saveState.game = game saveState.game = game
self.update(saveState) self.update(saveState, with: self.pausedSaveState)
} }
backgroundContext.saveWithErrorLogging() backgroundContext.saveWithErrorLogging()
} }
} }
private func update(_ saveState: SaveState) private func update(_ saveState: SaveState, with replacementSaveState: SaveStateProtocol? = nil)
{ {
let isRunning = (self.emulatorCore?.state == .running) let isRunning = (self.emulatorCore?.state == .running)
@ -429,7 +461,27 @@ extension GameViewController: SaveStatesViewControllerDelegate
self.pauseEmulation() self.pauseEmulation()
} }
self.emulatorCore?.saveSaveState(to: saveState.fileURL) if let replacementSaveState = replacementSaveState
{
do
{
if FileManager.default.fileExists(atPath: saveState.fileURL.path)
{
// Don't use replaceItem(), since that removes the original file as well
try FileManager.default.removeItem(at: saveState.fileURL)
}
try FileManager.default.copyItem(at: replacementSaveState.fileURL, to: saveState.fileURL)
}
catch
{
print(error)
}
}
else
{
self.emulatorCore?.saveSaveState(to: saveState.fileURL)
}
if if
let outputImage = self.gameView.outputImage, let outputImage = self.gameView.outputImage,