|
|
|
|
@@ -285,11 +285,12 @@ ipcMain.handle('finalResourcepack:install', async (): Promise<{ ok: boolean; pat
|
|
|
|
|
if (!pack) return { ok: false, message: t('errors.packNotFound2') }
|
|
|
|
|
const finalName = pack.pack.finalResourcepackPath
|
|
|
|
|
if (!finalName || finalName === '.') return { ok: false, message: 'no final resourcepack' }
|
|
|
|
|
let dest = ''
|
|
|
|
|
try {
|
|
|
|
|
const cleaned = path.basename(finalName.replace(/^\/+/, ''))
|
|
|
|
|
const url = `${state.baseUrl}/file/resourcepacks/outputs/${encodeURIComponent(cleaned)}`
|
|
|
|
|
const destDir = path.join(getAppDataDir(), getMcCustomDirName(), 'resourcepacks')
|
|
|
|
|
const dest = path.join(destDir, cleaned)
|
|
|
|
|
dest = path.join(destDir, cleaned)
|
|
|
|
|
await fsp.mkdir(destDir, { recursive: true })
|
|
|
|
|
sendLog(t('log.finalResourcepackDownload', { url }))
|
|
|
|
|
// 진행률(퍼센트)을 렌더러로 흘려보내며 스트리밍 다운로드.
|
|
|
|
|
@@ -307,6 +308,8 @@ ipcMain.handle('finalResourcepack:install', async (): Promise<{ ok: boolean; pat
|
|
|
|
|
sendLog(t('log.finalResourcepackSaved', { path: dest }))
|
|
|
|
|
return { ok: true, path: dest }
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// 실패 시 부분/0바이트 zip 이 마인크래프트 리소스팩 목록에 깨진 채로 남지 않도록 삭제.
|
|
|
|
|
if (dest) await fsp.rm(dest, { force: true }).catch(() => {})
|
|
|
|
|
return { ok: false, message: (error as Error).message }
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
@@ -1399,11 +1402,19 @@ async function installFabricLoader(pack: PackDefinition, customRoot: string): Pr
|
|
|
|
|
// 다시 쓰려고 시도해서, 마인크래프트나 다른 프로세스가 그 파일을 잡고
|
|
|
|
|
// 있으면 FileSystemException 으로 실패한다. 결과 파일이 그대로 있으면
|
|
|
|
|
// 재실행할 필요가 없으므로 그냥 통과.
|
|
|
|
|
// fabric 은 .minecraft(런처 폴더)에 설치한다. gameDir(.mc_custom)에 설치하면 fabric 이
|
|
|
|
|
// versions/ 와 libraries/ 를 거기에 실제 폴더로 만드는데, 이 폴더에는 fabric 자체 것만
|
|
|
|
|
// 들어가고 바닐라 마인크래프트의 버전 JSON·라이브러리는 빠져 있다. 그러면 .minecraft 로의
|
|
|
|
|
// 링크가 "이미 폴더 있음" 으로 건너뛰어져, 런처가 바닐라 데이터를 찾지 못하고
|
|
|
|
|
// "Unable to prepare assets for download" 로 실패한다. 그래서 .minecraft 에 설치해
|
|
|
|
|
// 바닐라와 같은 위치에 두고, 링크는 뒤(linkMinecraftRuntimeDirs)에서 성공하게 한다.
|
|
|
|
|
const mcRoot = path.join(getAppDataDir(), '.minecraft')
|
|
|
|
|
await fsp.mkdir(mcRoot, { recursive: true })
|
|
|
|
|
const versionId = `fabric-loader-${loaderVersion}-${pack.mcVersion}`
|
|
|
|
|
const versionDir = path.join(customRoot, 'versions', versionId)
|
|
|
|
|
const versionJar = path.join(versionDir, `${versionId}.jar`)
|
|
|
|
|
const versionDir = path.join(mcRoot, 'versions', versionId)
|
|
|
|
|
const versionJson = path.join(versionDir, `${versionId}.json`)
|
|
|
|
|
if (fs.existsSync(versionJar) && fs.existsSync(versionJson)) {
|
|
|
|
|
// 최신 fabric 은 버전 폴더에 .json 만 만든다(.jar 없음). json 존재를 설치 완료 기준으로 본다.
|
|
|
|
|
if (fs.existsSync(versionJson)) {
|
|
|
|
|
sendLog(t('log.fabricAlreadyInstalled', { id: versionId, dir: versionDir }))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
@@ -1443,10 +1454,10 @@ async function installFabricLoader(pack: PackDefinition, customRoot: string): Pr
|
|
|
|
|
'client',
|
|
|
|
|
'-mcversion', pack.mcVersion,
|
|
|
|
|
'-loader', loaderVersion,
|
|
|
|
|
'-dir', customRoot,
|
|
|
|
|
'-dir', mcRoot,
|
|
|
|
|
'-noprofile'
|
|
|
|
|
]
|
|
|
|
|
sendLog(t('log.fabricInstallStart', { mc: pack.mcVersion, loader: loaderVersion, dir: customRoot }))
|
|
|
|
|
sendLog(t('log.fabricInstallStart', { mc: pack.mcVersion, loader: loaderVersion, dir: mcRoot }))
|
|
|
|
|
await runJavaProcess(javaCmd, args)
|
|
|
|
|
sendLog(t('log.fabricInstallDone'))
|
|
|
|
|
}
|
|
|
|
|
@@ -1752,13 +1763,39 @@ async function linkMinecraftRuntimeDirs(customRoot: string): Promise<void> {
|
|
|
|
|
const src = path.join(mcRoot, dir)
|
|
|
|
|
const dst = path.join(customRoot, dir)
|
|
|
|
|
if (!fs.existsSync(src)) {
|
|
|
|
|
// .minecraft 에 원본이 없으면(바닐라를 한 번도 안 받았거나 런처가 다른 위치를 쓰는
|
|
|
|
|
// 환경) 링크를 만들 수 없다. 이때 그냥 건너뛰면 .mc_custom/<dir> 이 아예 없어
|
|
|
|
|
// 런처가 "Unable to prepare assets for download" 로 실패한다. 빈 폴더라도 만들어
|
|
|
|
|
// 런처가 그 gameDir 에 직접 받아갈 수 있게 한다(없을 때만).
|
|
|
|
|
sendLog(t('log.runtimeDirMissing', { dir }))
|
|
|
|
|
try {
|
|
|
|
|
if (!fs.existsSync(dst)) {
|
|
|
|
|
await fsp.mkdir(dst, { recursive: true })
|
|
|
|
|
sendLog(t('log.runtimeDirCreated', { dir }))
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
sendLog(t('log.runtimeLinkFail', { dir, message: (err as Error).message }))
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
let existing: import('node:fs').Stats | null = null
|
|
|
|
|
try { existing = await fsp.lstat(dst) } catch { existing = null }
|
|
|
|
|
if (existing) {
|
|
|
|
|
if (existing.isSymbolicLink()) continue // 이미 링크됨
|
|
|
|
|
// versions/libraries 가 실제 폴더면, 예전 버전이 gameDir 에 fabric 을 설치하며 만든
|
|
|
|
|
// 불완전한(바닐라 누락) 폴더일 수 있다. .minecraft 의 완전한 데이터로 링크되도록
|
|
|
|
|
// 교체한다. 이 두 폴더는 런처가 관리하는 데이터라 삭제해도 세이브/리소스팩 같은
|
|
|
|
|
// 사용자 데이터 손실은 없다. (assets 는 받아둔 에셋 보존을 위해 그대로 둔다.)
|
|
|
|
|
if (dir === 'versions' || dir === 'libraries') {
|
|
|
|
|
try {
|
|
|
|
|
await fsp.rm(dst, { recursive: true, force: true })
|
|
|
|
|
await fsp.symlink(src, dst, 'junction')
|
|
|
|
|
sendLog(t('log.runtimeLinkReplaced', { dir }))
|
|
|
|
|
} catch (err) {
|
|
|
|
|
sendLog(t('log.runtimeLinkFail', { dir, message: (err as Error).message }))
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
sendLog(t('log.runtimeDirExists', { dir }))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|