installer: 런처 실행을 URL 스킴으로 보강, 완료 후 자동 종료
Minecraft Launcher 실행 핸들러가 옛 Program Files 경로 두 곳만 보고 있어서 Microsoft Store/UWP/Xbox 앱 설치 등 최근 설치 형태에서 거의 못 찾았다.
- 1순위로 shell.openExternal('minecraft://') 사용. OS에 등록된 프로토콜 핸들러가 설치 형태(UWP/Win32/Xbox)에 무관하게 처리.
- 폴백 경로 후보 확장: Program Files / Program Files (x86) 양쪽의 Minecraft, Minecraft Launcher, XboxGames 경로, LOCALAPPDATA\Programs\minecraft-launcher까지 검사.
- 못 찾았을 때 메시지에 설치처(Microsoft Store/minecraft.net) 안내 추가.
5단계 완료 버튼: 모든 단계가 끝난 뒤이므로 마무리 액션(바로가기/서버 실행/런처 실행)을 처리한 다음 app.quit으로 설치기를 자동 종료한다. 'app:quit' IPC 핸들러와 preload 노출(quitApp) 추가.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -616,13 +616,21 @@ function renderStep5() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
section.querySelector('#finish').addEventListener('click', async function () {
|
section.querySelector('#finish').addEventListener('click', async function () {
|
||||||
if (multi) {
|
var finishBtn = section.querySelector('#finish')
|
||||||
if (section.querySelector('#shortcut').checked) await installerApi.createDesktopShortcut()
|
finishBtn.disabled = true
|
||||||
if (section.querySelector('#startServer').checked) await installerApi.startServer()
|
finishBtn.textContent = '마무리 중…'
|
||||||
|
try {
|
||||||
|
if (multi) {
|
||||||
|
if (section.querySelector('#shortcut').checked) await installerApi.createDesktopShortcut()
|
||||||
|
if (section.querySelector('#startServer').checked) await installerApi.startServer()
|
||||||
|
}
|
||||||
|
if (section.querySelector('#startLauncher').checked) await installerApi.startMinecraftLauncher()
|
||||||
|
} catch (err) {
|
||||||
|
// 마무리 액션 실패는 무시하고 종료 진행
|
||||||
}
|
}
|
||||||
if (section.querySelector('#startLauncher').checked) await installerApi.startMinecraftLauncher()
|
finishBtn.textContent = '완료됨'
|
||||||
section.querySelector('#finish').disabled = true
|
// 모든 단계가 끝났으므로 설치기 종료
|
||||||
section.querySelector('#finish').textContent = '완료됨'
|
if (installerApi.quitApp) installerApi.quitApp()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -933,17 +933,44 @@ ipcMain.handle('finish:startServer', async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('finish:startLauncher', async () => {
|
ipcMain.handle('finish:startLauncher', async () => {
|
||||||
|
// 1순위: minecraft:// URL 스킴. UWP(Microsoft Store) / Win32 / Xbox 앱 어떤 형태로 설치돼
|
||||||
|
// 있어도 OS의 등록된 프로토콜 핸들러가 처리하므로 가장 견고하다.
|
||||||
|
try {
|
||||||
|
sendLog('마인크래프트 런처 실행 요청(URL 스킴 minecraft://)...')
|
||||||
|
await shell.openExternal('minecraft://')
|
||||||
|
sendLog('마인크래프트 런처 실행 요청 완료.')
|
||||||
|
return
|
||||||
|
} catch (err) {
|
||||||
|
sendLog(`URL 스킴 실행 실패: ${(err as Error).message}. 직접 경로 탐색으로 폴백합니다.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2순위: 알려진 설치 경로 탐색. 구버전 .exe 설치판 / Xbox 앱 설치 위치까지 커버.
|
||||||
|
const programFilesX86 = process.env['ProgramFiles(x86)'] ?? 'C:\\Program Files (x86)'
|
||||||
|
const programFiles = process.env['ProgramFiles'] ?? 'C:\\Program Files'
|
||||||
|
const localAppData = process.env['LOCALAPPDATA'] ?? path.join(os.homedir(), 'AppData', 'Local')
|
||||||
const candidates = [
|
const candidates = [
|
||||||
path.join(process.env['ProgramFiles(x86)'] ?? 'C:\\Program Files (x86)', 'Minecraft Launcher', 'MinecraftLauncher.exe'),
|
path.join(programFilesX86, 'Minecraft Launcher', 'MinecraftLauncher.exe'),
|
||||||
path.join(process.env['ProgramFiles'] ?? 'C:\\Program Files', 'Minecraft Launcher', 'MinecraftLauncher.exe')
|
path.join(programFiles, 'Minecraft Launcher', 'MinecraftLauncher.exe'),
|
||||||
|
path.join(programFilesX86, 'Minecraft', 'MinecraftLauncher.exe'),
|
||||||
|
path.join(programFiles, 'Minecraft', 'MinecraftLauncher.exe'),
|
||||||
|
'C:\\XboxGames\\Minecraft Launcher\\Content\\Minecraft.exe',
|
||||||
|
path.join(localAppData, 'Programs', 'minecraft-launcher', 'MinecraftLauncher.exe')
|
||||||
]
|
]
|
||||||
const target = candidates.find((candidate) => fs.existsSync(candidate))
|
const target = candidates.find((candidate) => {
|
||||||
if (!target) {
|
try { return fs.existsSync(candidate) } catch { return false }
|
||||||
sendLog('Minecraft Launcher를 찾을 수 없습니다. 직접 실행해 주세요.')
|
})
|
||||||
|
if (target) {
|
||||||
|
spawn(target, [], { detached: true, stdio: 'ignore' }).unref()
|
||||||
|
sendLog(`마인크래프트 런처 실행: ${target}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
spawn(target, [], { detached: true, stdio: 'ignore' }).unref()
|
|
||||||
sendLog('마인크래프트 런처 실행 요청 완료.')
|
sendLog('Minecraft Launcher를 찾을 수 없습니다. Microsoft Store에서 "Minecraft Launcher"를 설치하거나 minecraft.net에서 받은 뒤 직접 실행해 주세요.')
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('app:quit', () => {
|
||||||
|
// 모든 창을 닫고 앱 종료. macOS에서도 종료(설치기는 한 번 쓰고 끝이니 잔류시키지 않음).
|
||||||
|
app.quit()
|
||||||
})
|
})
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ const api = {
|
|||||||
createDesktopShortcut: (): Promise<void> => ipcRenderer.invoke('finish:desktopShortcut'),
|
createDesktopShortcut: (): Promise<void> => ipcRenderer.invoke('finish:desktopShortcut'),
|
||||||
startServer: (): Promise<void> => ipcRenderer.invoke('finish:startServer'),
|
startServer: (): Promise<void> => ipcRenderer.invoke('finish:startServer'),
|
||||||
startMinecraftLauncher: (): Promise<void> => ipcRenderer.invoke('finish:startLauncher'),
|
startMinecraftLauncher: (): Promise<void> => ipcRenderer.invoke('finish:startLauncher'),
|
||||||
|
quitApp: (): Promise<void> => ipcRenderer.invoke('app:quit'),
|
||||||
|
|
||||||
// log stream
|
// log stream
|
||||||
onLog: (handler: (line: string) => void): (() => void) => {
|
onLog: (handler: (line: string) => void): (() => void) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user