Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 13e4e0eb13 | |||
| e80933d21e |
@@ -78,7 +78,7 @@
|
|||||||
2. `.minecraft` 최상위 설정 파일(`options.txt`, `optionsof.txt`, `servers.dat`, `usercache.json`, …) 을 `.mc_custom` 으로 복사. 이미 같은 이름이 있으면 보존.
|
2. `.minecraft` 최상위 설정 파일(`options.txt`, `optionsof.txt`, `servers.dat`, `usercache.json`, …) 을 `.mc_custom` 으로 복사. 이미 같은 이름이 있으면 보존.
|
||||||
3. 플랫폼 설치:
|
3. 플랫폼 설치:
|
||||||
- `vanilla` → 건너뜀.
|
- `vanilla` → 건너뜀.
|
||||||
- `fabric` → Adoptium 자동 설치 → 최신 fabric-installer.jar 다운로드 → `java -jar fabric-installer.jar client -mcversion X -loader Y -dir .mc_custom -noprofile` 자동 실행.
|
- `fabric` → Adoptium 자동 설치 → 최신 fabric-installer.jar 다운로드 → `java -jar fabric-installer.jar client -mcversion X -loader Y -dir .minecraft -noprofile` 자동 실행. (fabric 버전/라이브러리는 런처 폴더 `.minecraft` 에 바닐라와 함께 설치해야 런처가 찾을 수 있다. `.mc_custom` 에 설치하면 바닐라 버전·라이브러리가 빠져 "Unable to prepare assets for download" 로 실패한다.)
|
||||||
- `forge` / `neoforge` → `platform.downloadUrl` 의 설치 jar 다운로드(사용자가 직접 실행하거나 마인크래프트 런처가 인식).
|
- `forge` / `neoforge` → `platform.downloadUrl` 의 설치 jar 다운로드(사용자가 직접 실행하거나 마인크래프트 런처가 인식).
|
||||||
4. `modsFolder` 의 모든 `.jar` 와 `resourcepackPath` zip, `mapPath` zip 을 자동 다운로드.
|
4. `modsFolder` 의 모든 `.jar` 와 `resourcepackPath` zip, `mapPath` zip 을 자동 다운로드.
|
||||||
5. `.minecraft\{assets,libraries,versions}` 를 `.mc_custom\{assets,libraries,versions}` 로 junction 링크. (없으면 "Unable to prepare assets for download" 오류로 마인크래프트가 실패하기 때문)
|
5. `.minecraft\{assets,libraries,versions}` 를 `.mc_custom\{assets,libraries,versions}` 로 junction 링크. (없으면 "Unable to prepare assets for download" 오류로 마인크래프트가 실패하기 때문)
|
||||||
|
|||||||
@@ -290,9 +290,11 @@
|
|||||||
"settingCopyFail": "설정 복사 실패 ({{name}}): {{message}}",
|
"settingCopyFail": "설정 복사 실패 ({{name}}): {{message}}",
|
||||||
"settingCopySummary": "기존 마인크래프트 설정 복사: 새로 복사 {{copied}}개 / 동기화(options 류 덮어쓰기) {{synced}}개 / 보존(이미 존재) {{skipped}}개.",
|
"settingCopySummary": "기존 마인크래프트 설정 복사: 새로 복사 {{copied}}개 / 동기화(options 류 덮어쓰기) {{synced}}개 / 보존(이미 존재) {{skipped}}개.",
|
||||||
"settingCopyError": "기존 설정 복사 중 오류: {{message}}",
|
"settingCopyError": "기존 설정 복사 중 오류: {{message}}",
|
||||||
"runtimeDirMissing": ".minecraft/{{dir}} 가 없습니다. 마인크래프트 런처를 한 번 실행한 뒤 다시 시도해주세요.",
|
"runtimeDirMissing": ".minecraft/{{dir}} 가 없어 공용 링크를 만들 수 없습니다(런처가 이 폴더에 직접 받게 됩니다).",
|
||||||
|
"runtimeDirCreated": ".mc_custom/{{dir}} 폴더를 새로 만들었습니다(런처가 여기에 직접 다운로드).",
|
||||||
"runtimeDirExists": ".mc_custom/{{dir}} 가 실제 폴더로 이미 존재 — 건너뜀.",
|
"runtimeDirExists": ".mc_custom/{{dir}} 가 실제 폴더로 이미 존재 — 건너뜀.",
|
||||||
"runtimeLinkCreated": "링크 생성: .mc_custom/{{dir}} → .minecraft/{{dir}}",
|
"runtimeLinkCreated": "링크 생성: .mc_custom/{{dir}} → .minecraft/{{dir}}",
|
||||||
|
"runtimeLinkReplaced": "불완전한 .mc_custom/{{dir}} 폴더를 .minecraft/{{dir}} 링크로 교체했습니다(바닐라 데이터 포함).",
|
||||||
"runtimeLinkFail": "링크 생성 실패 ({{dir}}): {{message}}",
|
"runtimeLinkFail": "링크 생성 실패 ({{dir}}): {{message}}",
|
||||||
"shortcutCreated": "바로가기 생성: {{path}}",
|
"shortcutCreated": "바로가기 생성: {{path}}",
|
||||||
"shortcutFailed": "바로가기 생성 실패",
|
"shortcutFailed": "바로가기 생성 실패",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "minecraft-music-quiz-installer",
|
"name": "minecraft-music-quiz-installer",
|
||||||
"version": "0.4.20",
|
"version": "0.4.22",
|
||||||
"description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트",
|
"description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트",
|
||||||
"main": "dist/installer/main.js",
|
"main": "dist/installer/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1402,11 +1402,19 @@ async function installFabricLoader(pack: PackDefinition, customRoot: string): Pr
|
|||||||
// 다시 쓰려고 시도해서, 마인크래프트나 다른 프로세스가 그 파일을 잡고
|
// 다시 쓰려고 시도해서, 마인크래프트나 다른 프로세스가 그 파일을 잡고
|
||||||
// 있으면 FileSystemException 으로 실패한다. 결과 파일이 그대로 있으면
|
// 있으면 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 versionId = `fabric-loader-${loaderVersion}-${pack.mcVersion}`
|
||||||
const versionDir = path.join(customRoot, 'versions', versionId)
|
const versionDir = path.join(mcRoot, 'versions', versionId)
|
||||||
const versionJar = path.join(versionDir, `${versionId}.jar`)
|
|
||||||
const versionJson = path.join(versionDir, `${versionId}.json`)
|
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 }))
|
sendLog(t('log.fabricAlreadyInstalled', { id: versionId, dir: versionDir }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1446,10 +1454,10 @@ async function installFabricLoader(pack: PackDefinition, customRoot: string): Pr
|
|||||||
'client',
|
'client',
|
||||||
'-mcversion', pack.mcVersion,
|
'-mcversion', pack.mcVersion,
|
||||||
'-loader', loaderVersion,
|
'-loader', loaderVersion,
|
||||||
'-dir', customRoot,
|
'-dir', mcRoot,
|
||||||
'-noprofile'
|
'-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)
|
await runJavaProcess(javaCmd, args)
|
||||||
sendLog(t('log.fabricInstallDone'))
|
sendLog(t('log.fabricInstallDone'))
|
||||||
}
|
}
|
||||||
@@ -1755,13 +1763,39 @@ async function linkMinecraftRuntimeDirs(customRoot: string): Promise<void> {
|
|||||||
const src = path.join(mcRoot, dir)
|
const src = path.join(mcRoot, dir)
|
||||||
const dst = path.join(customRoot, dir)
|
const dst = path.join(customRoot, dir)
|
||||||
if (!fs.existsSync(src)) {
|
if (!fs.existsSync(src)) {
|
||||||
|
// .minecraft 에 원본이 없으면(바닐라를 한 번도 안 받았거나 런처가 다른 위치를 쓰는
|
||||||
|
// 환경) 링크를 만들 수 없다. 이때 그냥 건너뛰면 .mc_custom/<dir> 이 아예 없어
|
||||||
|
// 런처가 "Unable to prepare assets for download" 로 실패한다. 빈 폴더라도 만들어
|
||||||
|
// 런처가 그 gameDir 에 직접 받아갈 수 있게 한다(없을 때만).
|
||||||
sendLog(t('log.runtimeDirMissing', { dir }))
|
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
|
continue
|
||||||
}
|
}
|
||||||
let existing: import('node:fs').Stats | null = null
|
let existing: import('node:fs').Stats | null = null
|
||||||
try { existing = await fsp.lstat(dst) } catch { existing = null }
|
try { existing = await fsp.lstat(dst) } catch { existing = null }
|
||||||
if (existing) {
|
if (existing) {
|
||||||
if (existing.isSymbolicLink()) continue // 이미 링크됨
|
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 }))
|
sendLog(t('log.runtimeDirExists', { dir }))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user