From 7bc602fb2cc5b3c10de06db1dd09ca5bf1a643d5 Mon Sep 17 00:00:00 2001 From: AnemoFlower Date: Sat, 4 Apr 2026 14:01:34 +0800 Subject: [PATCH 1/2] refactor: remove -javaagent parameter for log-bridge-agent --- Sources/Launcher/main.swift | 23 +++++++++----- Sources/Runner/main.swift | 61 +++++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/Sources/Launcher/main.swift b/Sources/Launcher/main.swift index 9425945..26dc937 100644 --- a/Sources/Launcher/main.swift +++ b/Sources/Launcher/main.swift @@ -142,12 +142,20 @@ while true { } } -var runnerArguments: [String] = Array(arguments.dropFirst()) -runnerArguments.insert(FileManager.default.currentDirectoryPath, at: 0) -runnerArguments.insert( - "-javaagent:\(appBundleURL.appending(path: "Contents/Resources/log-bridge-agent.jar").path)=\(socketPath)", - at: 2 -) +var runnerArguments: [String] = [ + "--holder", + "--working-directory", FileManager.default.currentDirectoryPath, + "--args" +] + arguments.dropFirst() + +print(runnerArguments) + +//let process: Process = .init() +//process.executableURL = appBundleURL.appending(path: "Contents/MacOS/runner") +//process.arguments = runnerArguments +//process.currentDirectoryURL = .init(filePath: FileManager.default.currentDirectoryPath) +//try process.run() + let configuration: NSWorkspace.OpenConfiguration = .init() configuration.createsNewApplicationInstance = true @@ -171,6 +179,7 @@ NSWorkspace.shared.openApplication(at: appBundleURL, configuration: configuratio signal(SIGINT, SIG_IGN) let handler: () -> Void = { + if application.isTerminated { return } kill(pid, SIGTERM) exit(0) } @@ -188,7 +197,7 @@ NSWorkspace.shared.openApplication(at: appBundleURL, configuration: configuratio } DispatchQueue.global(qos: .background).async { - startSocket() +// startSocket() } } } diff --git a/Sources/Runner/main.swift b/Sources/Runner/main.swift index 4f5c24e..5391765 100644 --- a/Sources/Runner/main.swift +++ b/Sources/Runner/main.swift @@ -8,20 +8,40 @@ import Foundation import AppKit -func launch() { - let workingDirectory: String = ProcessInfo.processInfo.arguments[1] - if chdir(workingDirectory) != 0 { - perror("chdir") - exit(EXIT_FAILURE) +if CommandLine.arguments.count == 1 { + let alert: NSAlert = .init() + alert.messageText = "You can't open this app directly." + alert.alertStyle = .warning + alert.runModal() + exit(1) +} + +let arguments: [String] = Array(CommandLine.arguments.dropFirst()) + +var holder: Bool = false +var workingDirectory: String! +var javaArguments: [String]! + +for (index, arg) in arguments.enumerated() { + if arg == "-h" || arg == "--holder" { + holder = true + } else if arg == "-w" || arg == "--working-directory" { + workingDirectory = arguments[index + 1] + } else if arg == "--args" { + javaArguments = Array(arguments.dropFirst(index + 1)) + break } - let arguments: [String] = Array(ProcessInfo.processInfo.arguments.dropFirst(2)) - let executablePath: String = arguments[0] - let argv: [UnsafeMutablePointer?] = arguments.map { strdup($0) } + [nil] +} + +@MainActor +func launch() { + let executablePath: String = javaArguments[0] + let argv: [UnsafeMutablePointer?] = javaArguments.map { strdup($0) } + [nil] argv.withUnsafeBufferPointer { buffer in execv(executablePath, buffer.baseAddress) perror("execv") } - exit(EXIT_FAILURE) + exit(1) } class ApplicationDelegate: NSObject, NSApplicationDelegate { @@ -30,6 +50,23 @@ class ApplicationDelegate: NSObject, NSApplicationDelegate { } } -let delegate: ApplicationDelegate = .init() -NSApplication.shared.delegate = delegate -NSApplication.shared.run() +guard let javaArguments, javaArguments.count > 2 else { + exit(1) +} + +if holder { + guard let workingDirectory else { + exit(1) + } + let process: Process = .init() + process.executableURL = Bundle.main.executableURL + process.arguments = ["--args"] + javaArguments + process.currentDirectoryURL = .init(filePath: workingDirectory) + try process.run() + process.waitUntilExit() + exit(process.terminationStatus) +} else { + let delegate: ApplicationDelegate = .init() + NSApplication.shared.delegate = delegate + NSApplication.shared.run() +} From 1e2d8135f8356ba9340339ea795bad9b880aa959 Mon Sep 17 00:00:00 2001 From: AnemoFlower Date: Sat, 4 Apr 2026 17:19:26 +0800 Subject: [PATCH 2/2] update --- Sources/Launcher/main.swift | 19 ++++---- Sources/Runner/main.swift | 95 +++++++++++++++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/Sources/Launcher/main.swift b/Sources/Launcher/main.swift index 26dc937..55b65a8 100644 --- a/Sources/Launcher/main.swift +++ b/Sources/Launcher/main.swift @@ -87,7 +87,7 @@ func startSocket() { defer { close(clientSocket) } var lastMessages: [String] = [] - var javaQuitReceived: Bool = false + var exitCode: Int32? while true { var buffer: [UInt8] = .init(repeating: 0, count: 16384) @@ -106,17 +106,15 @@ func startSocket() { log("Failed to decode UTF-8 message (bytesRead=\(bytesRead))", error: true) } } else if buffer[0] == 0xFF { - javaQuitReceived = true + exitCode = buffer.dropFirst().withUnsafeBytes { $0.load(as: Int32.self) } } } else if bytesRead == 0 { - if lastMessages.contains(where: { $0.contains("#@!@# Game crashed!") }) { - log("Game crashed (Minecraft crash log marker detected)") - exit(1) - } else if !javaQuitReceived { - log("JVM terminated unexpectedly (unexpected socket EOF)") + guard let exitCode else { + log("JVM holder terminated unexpectedly (unexpected socket EOF)", error: true) exit(1) } - exit(0) + log("Game exited with exit code \(exitCode)") + exit(exitCode) } else { perror("read") exit(1) @@ -145,11 +143,10 @@ while true { var runnerArguments: [String] = [ "--holder", "--working-directory", FileManager.default.currentDirectoryPath, + "--socket-path", socketPath, "--args" ] + arguments.dropFirst() -print(runnerArguments) - //let process: Process = .init() //process.executableURL = appBundleURL.appending(path: "Contents/MacOS/runner") //process.arguments = runnerArguments @@ -197,7 +194,7 @@ NSWorkspace.shared.openApplication(at: appBundleURL, configuration: configuratio } DispatchQueue.global(qos: .background).async { -// startSocket() + startSocket() } } } diff --git a/Sources/Runner/main.swift b/Sources/Runner/main.swift index 5391765..bbadd34 100644 --- a/Sources/Runner/main.swift +++ b/Sources/Runner/main.swift @@ -19,14 +19,17 @@ if CommandLine.arguments.count == 1 { let arguments: [String] = Array(CommandLine.arguments.dropFirst()) var holder: Bool = false -var workingDirectory: String! -var javaArguments: [String]! +var workingDirectory: String? +var socketPath: String? +var javaArguments: [String]? for (index, arg) in arguments.enumerated() { if arg == "-h" || arg == "--holder" { holder = true } else if arg == "-w" || arg == "--working-directory" { workingDirectory = arguments[index + 1] + } else if arg == "--socket-path" { + socketPath = arguments[index + 1] } else if arg == "--args" { javaArguments = Array(arguments.dropFirst(index + 1)) break @@ -35,6 +38,7 @@ for (index, arg) in arguments.enumerated() { @MainActor func launch() { + guard let javaArguments else { exit(1) } let executablePath: String = javaArguments[0] let argv: [UnsafeMutablePointer?] = javaArguments.map { strdup($0) } + [nil] argv.withUnsafeBufferPointer { buffer in @@ -44,6 +48,65 @@ func launch() { exit(1) } +func connectSocket(at path: String) -> Int32? { + let sockfd: Int32 = socket(AF_UNIX, SOCK_STREAM, 0) + if sockfd < 0 { + perror("socket") + return nil + } + + var addr = sockaddr_un() + addr.sun_family = sa_family_t(AF_UNIX) + let bytes = path.utf8CString + withUnsafeMutablePointer(to: &addr.sun_path) { sunptr in + let buffer = UnsafeMutableRawPointer(sunptr).assumingMemoryBound(to: CChar.self) + _ = bytes.withUnsafeBufferPointer { ptr in + memcpy(buffer, ptr.baseAddress, bytes.count) + } + } + let len = socklen_t(MemoryLayout.size + bytes.count) + let result = withUnsafePointer(to: &addr) { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + connect(sockfd, $0, len) + } + } + if result != 0 { + perror("connect") + close(sockfd) + return nil + } + return sockfd +} + +func send(data: Data, to sockfd: Int32, type: UInt8, queue: DispatchQueue, completion: (@Sendable () -> Void)? = nil) { + queue.async { + let payload: Data = [type] + data + queue.async { + payload.withUnsafeBytes { + let base = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) + var written: Int = 0 + while written < payload.count { + let rc = write(sockfd, base! + written, payload.count - written) + if rc <= 0 { break } + written += rc + } + completion?() + } + } + } +} + +func handlePipe(_ pipe: Pipe, to sockfd: Int32, error: Bool, queue: DispatchQueue) { + let handle: FileHandle = pipe.fileHandleForReading + DispatchQueue.global().async { + while true { + let data: Data = handle.availableData + if data.isEmpty { break } + send(data: data, to: sockfd, type: error ? 1 : 0, queue: queue) + } + } +} + class ApplicationDelegate: NSObject, NSApplicationDelegate { func applicationWillFinishLaunching(_ notification: Notification) { launch() @@ -55,16 +118,38 @@ guard let javaArguments, javaArguments.count > 2 else { } if holder { - guard let workingDirectory else { + guard let workingDirectory, let socketPath else { exit(1) } + + guard let sockfd: Int32 = connectSocket(at: socketPath) else { + exit(1) + } + let socketQueue: DispatchQueue = .init(label: "socket_write_queue") + let process: Process = .init() process.executableURL = Bundle.main.executableURL process.arguments = ["--args"] + javaArguments process.currentDirectoryURL = .init(filePath: workingDirectory) + + let outputPipe: Pipe = .init() + let errorPipe: Pipe = .init() + process.standardOutput = outputPipe + process.standardError = errorPipe + + process.terminationHandler = { process in + let data: Data = withUnsafeBytes(of: process.terminationStatus) { Data($0) } + send(data: data, to: sockfd, type: 0xFF, queue: socketQueue) { + exit(process.terminationStatus) + } + } + try process.run() - process.waitUntilExit() - exit(process.terminationStatus) + + handlePipe(outputPipe, to: sockfd, error: false, queue: socketQueue) + handlePipe(errorPipe, to: sockfd, error: true, queue: socketQueue) + + dispatchMain() } else { let delegate: ApplicationDelegate = .init() NSApplication.shared.delegate = delegate