From 0dfaf12cfc551be0d52da19c3f1945bb8878954e Mon Sep 17 00:00:00 2001 From: blackxfiied <41133734+blackxfiied@users.noreply.github.com> Date: Tue, 23 Apr 2024 21:08:19 +0800 Subject: [PATCH] Commit dump + fix legendary signin crash --- Mythic.xcodeproj/project.pbxproj | 4 +- Mythic/AppDelegate.swift | 11 +++- .../EvilSteam.imageset/Contents.json | 21 ++++++++ .../Assets.xcassets/EvilSteam.imageset/t.png | Bin 0 -> 16210 bytes .../Legendary/LegendaryInterface.swift | 51 ++++++++++-------- Mythic/Controllers/Wine/WineInterface.swift | 4 +- Mythic/Extensions/Double.swift | 2 +- Mythic/Extensions/Global.swift | 2 +- Mythic/Localizable.xcstrings | 6 +++ Mythic/Views/InstallationProgress.swift | 6 +-- Mythic/Views/Navigation/DownloadsEvo.swift | 16 +++++- 11 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 Mythic/Assets.xcassets/EvilSteam.imageset/Contents.json create mode 100644 Mythic/Assets.xcassets/EvilSteam.imageset/t.png diff --git a/Mythic.xcodeproj/project.pbxproj b/Mythic.xcodeproj/project.pbxproj index d060cf0b..4dda45b6 100644 --- a/Mythic.xcodeproj/project.pbxproj +++ b/Mythic.xcodeproj/project.pbxproj @@ -865,7 +865,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2582; + CURRENT_PROJECT_VERSION = 2599; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Mythic/Preview Content\""; DEVELOPMENT_TEAM = 67ZBY275P8; @@ -904,7 +904,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2582; + CURRENT_PROJECT_VERSION = 2599; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Mythic/Preview Content\""; DEVELOPMENT_TEAM = 67ZBY275P8; diff --git a/Mythic/AppDelegate.swift b/Mythic/AppDelegate.swift index 107c3e63..c929a234 100644 --- a/Mythic/AppDelegate.swift +++ b/Mythic/AppDelegate.swift @@ -65,6 +65,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { // https://arc.net/l/quote/ ) } catch { Logger.file.error("Unable to move Mythic to Applications: \(error)") + + let error = NSAlert() + error.messageText = "Unable to move Mythic to \"\(globalApps.prettyPath())\"." + error.addButton(withTitle: "Quit") + + if error.runModal() == .alertFirstButtonReturn { + exit(1) + } } } } @@ -109,8 +117,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { // https://arc.net/l/quote/ func applicationWillTerminate(_: Notification) { if defaults.bool(forKey: "quitOnAppClose") { Wine.killAll() } - // TODO: stop download alert if downloading before closure - Legendary.stopAllCommands() + Legendary.stopAllCommands(forced: true) } } diff --git a/Mythic/Assets.xcassets/EvilSteam.imageset/Contents.json b/Mythic/Assets.xcassets/EvilSteam.imageset/Contents.json new file mode 100644 index 00000000..edc9710c --- /dev/null +++ b/Mythic/Assets.xcassets/EvilSteam.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "t.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mythic/Assets.xcassets/EvilSteam.imageset/t.png b/Mythic/Assets.xcassets/EvilSteam.imageset/t.png new file mode 100644 index 0000000000000000000000000000000000000000..2189a4d125cdf226a97dfeeb1e46abf5cbb29f25 GIT binary patch literal 16210 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 KQ+Lnh<^KcZqloVS literal 0 HcmV?d00001 diff --git a/Mythic/Controllers/Legendary/LegendaryInterface.swift b/Mythic/Controllers/Legendary/LegendaryInterface.swift index 4ae97e3a..69368f65 100644 --- a/Mythic/Controllers/Legendary/LegendaryInterface.swift +++ b/Mythic/Controllers/Legendary/LegendaryInterface.swift @@ -63,12 +63,12 @@ class Legendary { Executes Legendary's command-line process with the specified arguments and handles its output and input interactions. - Parameters: - - args: The arguments to pass to the command-line process. - - waits: Indicates whether the function should wait for the command-line process to complete before returning. - - identifier: A unique identifier for the command-line process. - - input: A closure that processes the output of the command-line process and provides input back to it. - - environment: Additional environment variables to set for the command-line process. - - completion: A closure to call with the output of the command-line process. + - args: The arguments to pass to the command-line process. + - waits: Indicates whether the function should wait for the command-line process to complete before returning. + - identifier: A unique identifier for the command-line process. + - input: A closure that processes the output of the command-line process and provides input back to it. + - environment: Additional environment variables to set for the command-line process. + - completion: A closure to call with the output of the command-line process. - Throws: An error if the command-line process encounters an issue. @@ -105,7 +105,7 @@ class Legendary { stdin.fileHandleForWriting.write(data) } output.stderr = availableOutput - completion(output, task) + completion(output, task) // ⚠️ FIXME: critical performance issues } stdout.fileHandleForReading.readabilityHandler = { [stdin, output] handle in @@ -115,7 +115,7 @@ class Legendary { stdin.fileHandleForWriting.write(data) } output.stdout = availableOutput - completion(output, task) + completion(output, task) // ⚠️ FIXME: critical performance issues } task.terminationHandler = { [stdin] _ in @@ -147,9 +147,13 @@ class Legendary { - Parameter identifier: The unique identifier of the command to be stopped. */ - static func stopCommand(identifier: String) { // TODO: pause and replay downloads using task.suspend() and task.resume() + static func stopCommand(identifier: String, forced: Bool = false) { // TODO: pause and replay downloads using task.suspend() and task.resume() if let task = runningCommands[identifier] { - task.interrupt() // SIGTERM + if forced { + task.interrupt() // SIGTERM + } else { + task.terminate() // SIGKILL + } runningCommands.removeValue(forKey: identifier) } else { log.error("Unable to stop Legendary command: Bad identifier.") @@ -157,20 +161,20 @@ class Legendary { } /// Stops the execution of all commands. - static func stopAllCommands() { runningCommands.keys.forEach { stopCommand(identifier: $0) } } + static func stopAllCommands(forced: Bool) { runningCommands.keys.forEach { stopCommand(identifier: $0, forced: forced) } } // MARK: Install Method /** Installs, updates, or repairs games using legendary. - Parameters: - - game: The game's `app_name`. (referred to as id) - - platform: The game's platform. - - type: The nature of the game modification. - - optionalPacks: Optional packs to install along with the base game. - - baseURL: A custom ``URL`` for the game to install to. - - gameFolder: The folder where the game should be installed. - - priority: Whether the game should interrupt the currently queued game installation. + - game: The game's `app_name`. (referred to as id) + - platform: The game's platform. + - type: The nature of the game modification. + - optionalPacks: Optional packs to install along with the base game. + - baseURL: A custom ``URL`` for the game to install to. + - gameFolder: The folder where the game should be installed. + - priority: Whether the game should interrupt the currently queued game installation. - Throws: A `NotSignedInError` or an `InstallationError`. */ @@ -323,18 +327,21 @@ class Legendary { return try await withCheckedThrowingContinuation { continuation in Task.sync { do { + var isLoggedIn = true + try await command( arguments: ["auth", "--code", authKey], - identifier: "signin" + identifier: "signin", + waits: true ) { output, _ in - continuation.resume(returning: output.stderr.contains("Successfully logged in as")) + isLoggedIn = (isLoggedIn == true ? true : output.stderr.contains("Successfully logged in as")) } + + continuation.resume(returning: isLoggedIn) } catch { continuation.resume(throwing: error) } } - - continuation.resume(returning: false) } } diff --git a/Mythic/Controllers/Wine/WineInterface.swift b/Mythic/Controllers/Wine/WineInterface.swift index 5bed8e14..97e4444c 100644 --- a/Mythic/Controllers/Wine/WineInterface.swift +++ b/Mythic/Controllers/Wine/WineInterface.swift @@ -181,7 +181,7 @@ class Wine { // TODO: https://forum.winehq.org/viewtopic.php?t=15416 stdin.fileHandleForWriting.write(data) } output.stderr = availableOutput - completion(output, task) + completion(output, task) // ⚠️ FIXME: critical performance issues } stdout.fileHandleForReading.readabilityHandler = { [stdin, output] handle in @@ -191,7 +191,7 @@ class Wine { // TODO: https://forum.winehq.org/viewtopic.php?t=15416 stdin.fileHandleForWriting.write(data) } output.stdout = availableOutput - completion(output, task) + completion(output, task) // ⚠️ FIXME: critical performance issues } task.terminationHandler = { [stdin] _ in diff --git a/Mythic/Extensions/Double.swift b/Mythic/Extensions/Double.swift index ce108f32..634901e5 100644 --- a/Mythic/Extensions/Double.swift +++ b/Mythic/Extensions/Double.swift @@ -8,7 +8,7 @@ import Foundation extension Double { - func rounded(_ to: Int) -> Double { + func rounded(toPlaces: Int) -> Double { let multiplier = pow(10, Double(to)) return Darwin.round(self * multiplier) / multiplier } diff --git a/Mythic/Extensions/Global.swift b/Mythic/Extensions/Global.swift index 52053b18..7bd2660a 100644 --- a/Mythic/Extensions/Global.swift +++ b/Mythic/Extensions/Global.swift @@ -247,7 +247,7 @@ class GameOperation: ObservableObject { static func advance() { log.debug("[operation.advance] attempting operation advancement") guard shared.current == nil, let first = shared.queue.first else { return } - // shared.status = .init() FIXME: reset status before next game installation - this implementation doesn't work. + shared.status = InstallStatus() log.debug("[operation.advance] queuing configuration can advance, no active downloads, game present in queue") DispatchQueue.main.async { shared.current = first; shared.queue.removeFirst() diff --git a/Mythic/Localizable.xcstrings b/Mythic/Localizable.xcstrings index 4f37a6a9..c6a5d210 100644 --- a/Mythic/Localizable.xcstrings +++ b/Mythic/Localizable.xcstrings @@ -26094,6 +26094,9 @@ } } } + }, + "No other downloads are pending." : { + }, "Not implemented yet" : { "localizations" : { @@ -28567,6 +28570,9 @@ } } } + }, + "Remove from download queue" : { + }, "Remove Mythic Engine" : { "localizations" : { diff --git a/Mythic/Views/InstallationProgress.swift b/Mythic/Views/InstallationProgress.swift index 96f30b6a..bb9b3f23 100644 --- a/Mythic/Views/InstallationProgress.swift +++ b/Mythic/Views/InstallationProgress.swift @@ -13,7 +13,7 @@ struct InstallationProgressView: View { @ObservedObject private var operation: GameOperation = .shared @State private var isStopGameModificationAlertPresented: Bool = false @State private var isInstallStatusViewPresented: Bool = false - @State private var hoveringOverDestructiveButton: Bool = false + @State private var isHoveringOverDestructiveButton: Bool = false @State private var paused: Bool = false // https://github.com/derrod/legendary/issues/40 @@ -44,12 +44,12 @@ struct InstallationProgressView: View { } label: { Image(systemName: "xmark") .padding(5) - .foregroundStyle(hoveringOverDestructiveButton ? .red : .primary) + .foregroundStyle(isHoveringOverDestructiveButton ? .red : .primary) } .clipShape(.circle) .help("Stop installing \"\(game.title)\"") .onHover { hovering in - withAnimation(.easeInOut(duration: 0.1)) { hoveringOverDestructiveButton = hovering } + withAnimation(.easeInOut(duration: 0.1)) { isHoveringOverDestructiveButton = hovering } } .alert(isPresented: $isStopGameModificationAlertPresented) { stopGameOperationAlert( diff --git a/Mythic/Views/Navigation/DownloadsEvo.swift b/Mythic/Views/Navigation/DownloadsEvo.swift index 6b2f13f7..cbb9f605 100644 --- a/Mythic/Views/Navigation/DownloadsEvo.swift +++ b/Mythic/Views/Navigation/DownloadsEvo.swift @@ -22,7 +22,8 @@ struct DownloadsEvo: View { if operation.queue.isEmpty { Text("No other downloads are pending.") - .font(.bold(.title)()) + .bold() + .padding() } else { ForEach(operation.queue, id: \.self) { args in DownloadCard(game: args.game, style: .normal) @@ -40,6 +41,7 @@ struct DownloadsEvo: View { struct DownloadCard: View { @ObservedObject private var operation: GameOperation = .shared + @State private var isHoveringOverDestructiveButton: Bool = false var game: Game var style: DownloadCardStyle @@ -156,7 +158,7 @@ struct DownloadCard: View { case .normal: return .caption case .prominent: - return .callout + return .callout } }()) @@ -168,6 +170,16 @@ struct DownloadCard: View { if operation.current?.game == game { InstallationProgressView(withPercentage: false) + } else if operation.queue.contains(where: { $0.game == game }) { + Button { + operation.queue.removeAll(where: { $0.game == game }) + } label: { + Image(systemName: "minus") + .padding(5) + .foregroundStyle(isHoveringOverDestructiveButton ? .red : .primary) + } + .clipShape(.circle) + .help("Remove from download queue") } } .padding(.horizontal)