fabric: 로더 버전 선택 + fabric-installer CLI 자동 설치

관리 사이트에서 모드 플랫폼으로 fabric 을 선택하면 jar 파일 업로드 대신, 선택한 마인크래프트 버전을 기준으로 Fabric Meta v2 API 에서 호환 로더 목록을 가져와 드롭다운으로 선택하도록 했다. 설치기는 platform.loaderVersion 만 보고 최신 fabric-installer.jar 를 받아 CLI 로 자동 설치(GUI 미표시)한다.

스키마:
- PackPlatform 에 loaderVersion?: string 추가. fabric 일 때만 사용.
- normalizePackDefinition: fabric 이면 downloadUrl 무시하고 loaderVersion 만 저장, 그 외에는 기존 downloadUrl 유지.

웹 UI(views/op/editor.ejs):
- platformType 이 fabric 일 때 platformLoaderVersion select 노출. mcVersion 셀렉트 값을 가지고 https://meta.fabricmc.net/v2/versions/loader/<mcVersion> 호출.
- mcVersion 또는 platformType 변경 시 자동 재조회. 동시 요청 경쟁은 sequence 비교로 무시.
- 이전 저장값을 우선 선택하되 목록에 없으면 최신 stable 자동 선택.
- 폼 제출 시 fabric 인데 로더 미선택이면 경고.
- 라우트(op.ts): platformLoaderVersion 폼 필드 수신.

설치기(installer/main.ts):
- client:install 분기 추가. fabric 이면 installFabricLoader 호출.
- installFabricLoader: Fabric Meta installer 메타 조회 → 최신 stable installer jar 캐시 다운로드 → java -jar fabric-installer.jar client -mcversion <ver> -loader <ver> -dir <.mc_custom> -noprofile 실행. launcher_profiles 갱신은 우리 코드(updateLauncherProfile)가 담당하므로 -noprofile.
- findJavaExecutable: JAVA_HOME → .minecraft\runtime 의 번들 자바(델타/감마/베타 등 우선순위) → PATH 폴백.
- runJavaProcess: stdout/stderr 를 로그 뷰어에 prefix 와 함께 스트리밍. 실패 시 stderr 끝부분을 메시지에 포함.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-13 01:28:45 +09:00
parent 536e94474f
commit 7d0f1719f3
5 changed files with 229 additions and 5 deletions

View File

@@ -265,14 +265,16 @@ opRouter.post('/op/dashboard/:packName', requireAuth, async (req, res, next) =>
const platformType = pickFirstValue(req.body.platformType)
const platformDownloadUrl = pickFirstValue(req.body.platformDownloadUrl).trim()
const platformLoaderVersion = pickFirstValue(req.body.platformLoaderVersion).trim()
const partial: Partial<PackDefinition> & Record<string, unknown> = {
name: pickFirstValue(req.body.displayName),
mcVersion: pickFirstValue(req.body.mcVersion),
platform: {
type: (platformType as PackDefinition['platform']['type']) || 'vanilla',
downloadUrl: platformDownloadUrl.length > 0 ? platformDownloadUrl : undefined
},
downloadUrl: platformDownloadUrl.length > 0 ? platformDownloadUrl : undefined,
loaderVersion: platformLoaderVersion.length > 0 ? platformLoaderVersion : undefined
} as PackDefinition['platform'] & { loaderVersion?: string },
modsFolder: pickFirstValue(req.body.modsFolder),
resourcepackPath: pickFirstValue(req.body.resourcepackPath),
serverMinRam: Number(pickFirstValue(req.body.serverMinRam)),