Commit Graph

38 Commits

Author SHA1 Message Date
9ba5dc6b7b terms: per-term installer visibility toggles + universal delete (v0.3.4)
- _meta.json: customLabels -> terms.{label,showInInstaller,showInInstallerRp}
- Drop builtin protection; any term kind can be deleted/added/toggled
- New public route /manifest/terms/<pack>/index.json for installer term lists
- Installers fetch terms:list dynamically; skip agreement step if list empty
- Term editor: 2 visibility checkboxes (설치기 / 리소스팩 설치기), multi-select
- Migration from old schema preserves custom labels (default: visible in both)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 10:14:42 +09:00
25977d894b terms: per-pack storage + import from another pack (v0.3.2)
- store.ts: 약관을 manifest/terms/<packKey>/ 폴더별로 저장. 첫 접근 시
  legacy 전역 .md 파일을 시드로 자동 복사한다.
- importTerms() 추가: 다른 음악퀴즈의 .md + _meta.json 을 현재 pack 으로
  복사한다. 동일 kind 는 source 값으로 덮어쓴다.
- /op/agreement 라우트를 세 단계로 분리:
  · /op/agreement → 음악퀴즈 카드 선택 페이지
  · /op/agreement/:packName → 해당 pack 의 약관 목록 + 추가 + 불러오기
  · /op/agreement/:packName/:kind → 에디터
- 공개 라우트도 /manifest/terms/:packKey/:fileName 으로 변경.
- 설치기 main.ts: state.selectedKey 를 약관 URL 에 포함하도록 수정 (메인 +
  rp 양쪽). pack 미선택 상태에서는 에러 반환.
- termsEditor.js: PACK_KEY 를 받아 저장 URL 에 포함.
- 다른 음악퀴즈 후보 select + 확인 모달 + locale 추가.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 01:29:04 +09:00
ffb2048627 terms: agreement pages + site Notion-style editor + rp cancel fix
- 5종 약관(map/resourcepack/mod/installer/installer-rp) markdown 시드 + manifest/terms/ 노출
- 사이트 /op/agreement 목록 + Notion 스타일 markdown 에디터 (슬래시 명령어, 미리보기)
- 메인 installer: 음악퀴즈 선택 직후 약관 동의 페이지(맵·모드·설치기) 추가
- rp installer: 음악퀴즈 선택 직후 약관 동의 페이지(리소스팩·설치기) 추가
- rp installer 취소 버그 수정: buildResourcepackZip 단계간 + archive.abort() 폴링
- rp installer 취소 UX: 즉시 "취소 중…" 표시, 취소 시 installFailed 알림 생략
- 0.2.6 → 0.3.0 (큰 기능)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 00:55:36 +09:00
bc3841147f installer: write custom icon into Minecraft launcher profile
Minecraft launcher's "설치 설정" screen reads `profile.icon` from
launcher_profiles.json. We were leaving it unset, so the launcher fell
back to the default Furnace icon. Inline build/icon.png as a base64
data URL at build time (scripts/build-launcher-icon.cjs generates
src/installer/launcherIcon.ts) and set it on the profile we write.

The build/ directory isn't included in the electron-builder asar (it's
only used to point at the .ico for the exe), so a runtime read isn't
possible — the icon ships compiled into the bundle. To refresh after
changing icon.png, run `npm run build:launcher-icon` (it's wired into
`dist:win` so a fresh exe build always regenerates it).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 23:56:02 +09:00
bf225f51e1 installer: force fabric-installer JVM stdout to UTF-8
Korean Windows defaults the JVM's stdout to cp949 (MS949), so the
fabric-installer's Korean status lines came through as mojibake when
Node decoded them as UTF-8 (e.g. "���가져오는중 (org.ow2.asm:asm:9.9)").

Pass -Dfile.encoding/-Dstdout.encoding/-Dstderr.encoding=UTF-8 before
-jar so the JVM writes UTF-8 and our existing utf-8 decode matches.
stdout/stderr.encoding properties take effect on Java 18+;
file.encoding covers older JDKs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 21:22:44 +09:00
2371af4411 installer: clean platform-cache in finally so failures don't leak
Previous version only deleted platform-cache at the very end of the
success path. If anything between platform install and launcher
profile update failed, the cache jar stuck around. Move the rm into
a finally block so the directory is always cleaned up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 19:10:01 +09:00
1f59f6a98b installer: move yt-dlp/ffmpeg under .mc_custom/installer/, clean platform-cache
- yt-dlp.exe, ffmpeg.exe now live in %appdata%/.mc_custom/installer/ so
  the .mc_custom root stays a clean Minecraft game folder. Existing
  binaries at the old location are migrated on first run.
- After a successful install, the platform-cache (downloaded fabric /
  forge / neoforge installer jars) is deleted — it's regenerable and
  was just wasting disk space.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 19:02:01 +09:00
c527efc42f installer: address vanilla mods preservation, robust map rename, new app icon
Reviewer follow-ups:

1) Preserve mods/ for vanilla packs. `downloadModsFolder` now checks
   `!pack.modsFolder` BEFORE wiping — vanilla packs (no modsFolder) no
   longer clobber a user's hand-curated mods directory. Wipe still runs
   for modded packs to keep different MC versions from colliding.

2) Always rename the extracted map to `saves/<퀴즈이름>/`, regardless of
   the zip's top-level layout. The zip is now extracted into a temp
   directory under saves/, and:
   - if the temp has a single subdirectory, that subdirectory's content
     becomes the world;
   - otherwise the temp dir itself (e.g. level.dat + region/ at root) is
     the world.
   In either case, it is renamed atomically to `saves/<sanitized name>`
   (or `<name>_2` etc. if a user world collides). Marker tracks the
   final folder name for participant cleanup.

User request: replace both .exe icons.

- Added build/icon.ico (multi-size 16/32/48/64/128/256) and build/icon.png
  generated from the new music-note artwork.
- electron-builder.yml: set win.icon, nsis installer/uninstaller icons,
  buildResources=build, include build/icon.* in files for runtime use.
- New electron-builder-rp.yml + dist:win:rp script so the resourcepack
  installer also packages with the same icon.
- BrowserWindow({ icon }) wired in both installer and installer-rp main
  processes so the running window's titlebar/taskbar icon matches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 00:14:44 +09:00
4ee0a59f2b installer: wipe mods/ before install and rename extracted map to pack name
Two follow-ups requested by the user (and the first flagged by the
reviewer for omission):

1) Different Minecraft versions or different packs leave behind mod jars
   that crash Fabric on load. `downloadModsFolder` now removes the entire
   `.mc_custom/mods/` directory before every install — including when the
   pack is vanilla (no modsFolder) so leftovers from a previous modded
   pack get cleared too.

2) `downloadMapZip` renames the single extracted top-level folder to the
   pack name (sanitized for Windows: forbidden chars `<>:"/\|?*` and
   control chars → `_`, trailing space/dot trimmed, reserved names like
   CON/NUL prefixed, empty fallback to `map`). Collisions with user
   worlds get `_2`, `_3` … suffixes so we never overwrite the user's
   own worlds. The marker file tracks the post-rename folder so future
   participant cleanup still removes only what the installer created.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 00:06:11 +09:00
ca1c5f237f installer: clean up installer-extracted map when switching to participant
When the user installs as single (skipMap=false) and then navigates back
to choose participant (skipMap=true), the previously-extracted map files
in .mc_custom/saves/ would remain because skipMap=true only skipped the
download. The final participant install state was therefore inconsistent
with the chosen role.

Track the top-level entries that downloadMapZip extracts via a marker
file (.musicquiz-installer-map.json) inside saves/. On participant
install (skipMap=true) or before a re-download, only the entries listed
in the marker are removed, so user-created worlds are preserved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 22:16:43 +09:00
49f320fa71 installer: multi-role host/participant split + auto-platform + EULA + port UX
step2:
- 멀티 선택 시 호스트 / 참가자 sub-choice 추가. 호스트 는 기존 멀티 흐름 그대로,
  참가자 는 step3 (서버 설치) 를 건너뛰고 step4 client install 만 진행.

step4 client install:
- 플랫폼 설치/생략 선택 화면(sub41) 제거. 음악퀴즈 platform.type 이 vanilla 가
  아니면 무조건 자동 설치, vanilla 면 자동 건너뜀. 사용자 결정 없음.
- 참가자 모드에서는 ClientInstallPayload.skipMap=true 로 보내 client 측
  saves/ 에 맵을 풀지 않는다 (서버에 이미 있음).
- types.ts 에 skipMap 필드 추가. main.ts client:install 핸들러에서 분기.

step3 EULA modal:
- eula.txt 의 내용과 무관하게 항상 minecraft.net 의 공식 서버 EULA 페이지를
  받아 iframe 에 표시. readEula() 분기 제거.

step3 포트포워딩 결과:
- 성공(preForwarded/upnpOk) 시 "친구는 <address> 주소로 서버에 접속할 수
  있습니다" 처럼 외부 주소를 강조해 표시.
- 포트가 25565 면 :포트 를 생략하고 ip 만 보여줌 (마인크래프트 자바판
  기본 포트라 클라이언트에서도 생략 가능).

step5:
- 서버 마무리 액션 (바로가기/서버 실행 토글) 은 호스트 만 노출. 참가자는
  서버를 띄우지 않으므로 런처 토글만 보인다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 22:06:48 +09:00
9db70d0bea installer: options.txt 류는 .mc_custom 으로 매번 덮어쓰기 동기화
기존 copyMinecraftUserSettings 는 .mc_custom 에 같은 이름의 파일이
있으면 무조건 보존했기 때문에, 사용자가 .minecraft 에서 새로 바꾼
키설정·옵션이 .mc_custom 으로 이어지지 못했다. options.txt /
optionsof.txt / optionsshaders.txt 는 사용자가 원래 쓰던 설정을
그대로 가져오기 위한 파일이므로 매번 .minecraft 쪽으로 덮어써서
동기화하고, servers.dat 같은 그 외 파일은 종전대로 보존한다.
2026-05-14 00:43:55 +09:00
ea72051e43 installer: Fabric 이미 설치돼 있으면 fabric-installer 재실행 건너뛰기
증상: 두 번째 설치 시도에서 fabric-installer 가
  FileSystemException: ...fabric-loader-X-Y.jar: 다른 프로세스가 파일을
  사용 중이기 때문에 프로세스가 액세스 할 수 없습니다
로 실패. 마인크래프트(또는 OS 인덱서)가 jar 핸들을 잡고 있을 때 발생.

원인: fabric-installer 는 매 실행마다 versions/<id>/<id>.jar 를
deleteIfExists 한 뒤 다시 쓰려고 한다. 이미 설치돼 있으면 굳이 다시
쓸 필요가 없다.

수정: installFabricLoader 에서 customRoot/versions/<versionId>/<versionId>.jar
와 .json 이 둘 다 존재하면 곧바로 return 하고 안내 로그만 남긴다.
2026-05-13 23:51:13 +09:00
135bc98840 i18n: 음악퀴즈 설치기 UI 문구를 locales/installer/ko-kr.json 으로 분리
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 03:53:55 +09:00
69ed4ad744 config: 사이트 도메인·서버 설정을 .env 로 중앙화 + 설치기 자동 종료 복구
- dotenv 도입, src/shared/env.ts 추가
  - loadEnv() 가 프로젝트 루트 .env 를 로드 (override=false: 쉘 env 우선)
  - getSiteBaseUrl() / getManifestUrl() 헬퍼
- 서버/설치기/리소스팩설치기 진입점에서 loadEnv() 호출
- 설치기 두 종의 기본 MANIFEST_URL 을 SITE_BASE_URL 기반으로 변경
  (운영 도메인을 한 곳에서만 바꾸면 됨)
- .env.example 템플릿 + .gitignore 에 .env 추가
- README / docs/admin-site.md 에 환경변수 표·사용법 추가
- installer/renderer.js: 4단계 완료 후 자동 종료 다시 활성화
2026-05-13 02:55:58 +09:00
475bf924a0 installer: JVM 튜닝 플래그를 마인크래프트 런처 프로필 javaArgs 에 적용
이전 커밋이 server run.bat 에 잘못 적용됐던 것을 되돌리고, 본래 의도대로
launcher_profiles.json 의 javaArgs 에 Aikar 권장 G1 GC 플래그 6종을 병합한다.
  - mergeRamArgs(-Xmx) 후 mergeJvmTuningFlags 로 누락 플래그만 추가
  - 사용자가 같은 키를 이미 지정했으면 그 값을 존중(덮어쓰지 않음)
  - run.bat 의 injectJvmFlags 호출 및 함수 제거
2026-05-13 02:07:47 +09:00
d194e28cf2 installer: run.bat 후처리에 JVM 튜닝 플래그 주입 추가
메모리(-Xms/-Xmx)는 기존 설정 그대로 두고 Aikar 권장 G1 GC 플래그 6종
(-XX:+UnlockExperimentalVMOptions, -XX:+UseG1GC, -XX:G1NewSizePercent=20,
-XX:G1ReservePercent=20, -XX:MaxGCPauseMillis=50, -XX:G1HeapRegionSize=32M)
을 -jar 앞에 끼워 넣는다. -XX:+UseG1GC 가 이미 있으면 멱등 처리.
2026-05-13 02:05:25 +09:00
a9b766d14d installer: 마인크래프트 런처 실행 전략 재정렬 — Win32/MSIX 직접 실행 우선
minecraft:// URL 스킴이 핸들러가 깨졌거나 비어 있을 때 MS Store 로 폴백되어
실제 런처가 안 떠는 케이스 대응. 실행 순서를 아래로 변경:
  1) Win32 설치판 직접 spawn (Program Files / Xbox / portable)
  2) App Execution Alias(Minecraft.exe / MinecraftLauncher.exe, reparse point
     이므로 cmd /c start 경유)
  3) explorer.exe shell:AppsFolder\\Microsoft.4297127D64EC6_8wekyb3d8bbwe!Minecraft
     로 MSIX(Microsoft Store) 런처 직접 호출
  4) 마지막 수단: minecraft:// URL 스킴
2026-05-13 01:59:30 +09:00
99ed5076c1 installer: 3-2 JDK 자동 설치(Temurin 21) 버튼 추가, 취소 가능
JDK 가 없을 때 사용자가 "자동 설치" 를 눌러 Adoptium Temurin 21 LTS
(Windows x64 zip) 를 받아 %APPDATA% 의 jdk/temurin-21 으로 풀어 사용하도록
한다. 다운로드는 streaming + AbortController 로 묶어, 설치 진행 중 같은
버튼이 "설치 취소" 로 바뀌며 누르면 다운로드를 즉시 중단하고 부분 파일을
정리한다. jdk:detect 후보에 자동 설치 경로도 추가해 다음 실행 시 자동 탐색됨.
2026-05-13 01:57:24 +09:00
c621185abc installer: run.bat 에 서버 기동/종료시 UPnP 자동 등록·해제 주입
서버가 실행 중일 때만 25565(또는 server-port) 가 열려 있도록 run.bat 을
후처리해 java 호출 전 UPnP Add, 종료 후 UPnP Remove 를 PowerShell 한 줄
(HNetCfg.NATUPnP.1)로 끼워 넣는다. Add 전에 같은 포트 매핑을 Remove 하므로
재실행에도 idempotent. 포트체크 단계에서 만든 테스트용 UPnP 매핑은 테스트
직후 제거해 실제 개방은 run.bat 이 단독으로 책임지게 한다.

제한: 콘솔창 X 강제 종료 시 teardown 미실행. 라우터 TTL 만료 또는 다음
실행 시 재등록 직전 Remove 로 자연 정리.
2026-05-13 01:52:51 +09:00
d0e7aa4f41 installer: .mc_custom 에 .minecraft 기존 설정 파일 복사
options.txt, optionsof.txt, servers.dat, usercache.json 등 .minecraft 최상위
파일을 .mc_custom 으로 복사해 사용자가 기존에 만들어둔 키바인딩/볼륨/렌더거리/
서버목록을 음악퀴즈 인스턴스에서도 그대로 사용할 수 있도록 한다.
이미 .mc_custom 에 같은 이름의 파일이 있으면 보존(덮어쓰지 않음). 디렉터리는
복사 대상에서 제외(mods/saves/versions/assets 등은 별도 처리).
2026-05-13 01:46:59 +09:00
b407a2ca6a installer: fabric 실행 실패 / 자동 종료 / UPnP 우선순위 정리
세 가지 문제를 정리한다.

1) fabric 으로 마인크래프트 실행 시 "Unable to prepare assets for download" 로 실패하던 문제.
   - launcher_profiles.lastVersionId 를 "<mc>-fabric" 으로 만들고 있었는데 fabric-installer 가 실제로 만드는 폴더 이름은 `fabric-loader-<loaderVer>-<mcVer>` 라서 런처가 존재하지 않는 버전을 받으려다 실패.
   - resolveLastVersionId 헬퍼 추가: fabric 일 때 platform.loaderVersion 으로 정확한 이름을 만든다. loaderVersion 미지정이면 .minecraft/versions 에서 `fabric-loader-*-<mcVer>` 패턴 자동 탐색. forge/neoforge 도 동일 패턴으로 후보 탐색.
   - 결정된 lastVersionId 의 versions 폴더 존재 여부도 점검해 경고 로그를 남긴다.

2) 5단계 완료 후 자동 종료를 임시 비활성화. 마인크래프트 런처 실행 실패 메시지를 사용자가 확인할 수 있도록 quitApp 호출만 주석 처리. X 버튼으로 직접 닫는다.

3) 포트포워딩 점검에서 사용자 라우터 규칙의 활성/비활성을 우리 UPnP 매핑과 구분.
   - 점검 시작 즉시 removeUpnpMapping 으로 이전 실행에서 남았을 수 있는 우리 UPnP 매핑을 제거.
   - 그 뒤 1차 점검을 돌리면 "사용자 규칙이 활성화돼 외부 접근 가능" 인 경우만 reachable=true 가 되어 preForwarded.
   - 사용자 규칙이 비활성/없으면 reachable=false → UPnP 등록 단계로 자연스럽게 진행.
   - 기존 preForwarded 분기의 사후 unmap 은 제거(시작 단계에서 이미 했으므로 중복).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 01:37:47 +09:00
7d0f1719f3 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>
2026-05-13 01:28:45 +09:00
536e94474f 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>
2026-05-13 01:13:30 +09:00
d440514fdc installer: 외부 포트체크 서비스 기반 점검으로 교체
기존 방식은 자기 PC에서 자기 외부 IP로 TCP 연결을 시도해 도달성을 판정했는데, 가정용 라우터의 헤어핀(hairpin) NAT 미지원으로 실제 외부 접근은 가능해도 내부 검증은 실패하던 문제가 있었다.

- probePortFromOutside: 임시 TCP 리스너를 대상 포트에 띄우고 ifconfig.co/port/PORT를 호출해 외부에서 해당 포트로 TCP 연결을 시도하게 한 뒤, 리스너에 연결이 도달했는지 + ifconfig.co의 JSON reachable 값으로 종합 판정.
- 포트가 이미 사용 중(서버 동작 중)이면 임시 리스너를 띄우지 않고 외부 서비스 응답만으로 판정.
- ifconfig.co 응답에서 IP도 같이 얻어 외부 IP 폴백 경로 추가.

또한 1차 점검에서 이미 외부 접근이 가능한 상태(사용자가 라우터에 수동 포워딩 규칙을 등록한 경우)에는 UPnP로 추가 매핑을 만들지 않고, 우리가 이전 실행에서 만들어 둔 UPnP 매핑이 남아 있으면 portUnmapping으로 제거하여 중복/충돌 가능성을 줄인다.

UPnP 매핑 후 재점검도 외부 서비스 기반 probePortFromOutside로 1.5초 간격 3회 재시도해 NAT 상태 전파 지연을 흡수.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 01:08:50 +09:00
e31c6ed55b installer: 3-3 자동 다운로드 및 UPnP 안정화
3-3 서버 다운로드도 진입 즉시 자동 시작하도록 변경. "다운로드 시작" 버튼을 제거하고 4-2와 같은 자동 실행 패턴을 적용했다(EULA 모달은 그대로 유지). 실패 시 이전→다음으로 재시도.

UPnP 점검 안정화:
- openPortViaUpnp에 15초 타임아웃 추가. SSDP 응답 없을 때 영구 hang을 방지한다.
- detectExternalIp: ipify 단일 실패 시 ifconfig.me/icanhazip 폴백 후, 최종 폴백으로 UPnP 게이트웨이의 externalIp를 사용. 기존에는 IP를 못 얻으면 UPnP 시도조차 안 했음.
- portMapping 성공 후 NAT 상태 전파 지연을 고려해 testPortReachable을 1.5초 간격 3회 재시도.
- 각 단계마다 로그를 남겨 라우터 UPnP 비활성/SSDP 차단/이중 NAT 등의 원인을 구분할 수 있게 함.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 01:01:00 +09:00
5a018bcb8d fix(installer-rp): URL-encode base pack path, output to .mc_custom + installer: no auto -Xms
리소스팩 간편설치기:
- 베이스 리소스팩 다운로드 URL 에 encodeURIComponent 적용. "Puzzle Resource
  Pack (basic).zip" 같이 공백·괄호가 들어간 파일명 정상 처리.
- 출력 경로를 %appdata%/.minecraft/resourcepacks/ → %appdata%/.mc_custom/
  resourcepacks/ 로 변경 (renderer 안내문, openFolder, 빌드 출력 일괄).
- 로드 직후 각 음악퀴즈의 베이스 등록 여부를 로그에 노출 (디버그용).
- 베이스 다운로드 시 실제 URL 도 로그에 출력.

음악퀴즈 간편설치기:
- mergeRamArgs: -Xms 가 기존에 없으면 추가하지 않도록 수정. clientMinRam
  은 "유저 PC 사양 최소 요구치" 의미이지 JVM 초기 힙이 아님. -Xmx 는
  계속 추천 RAM 으로 강제 갱신.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 00:48:46 +09:00
82307d9d16 fix(installer): preserve JVM args + link runtime dirs from .minecraft
1) launcher_profiles.json 의 javaArgs 를 통째로 덮어쓰던 코드를 수정.
   mergeRamArgs() 로 -Xmx/-Xms 토큰만 새 값으로 교체하고 그 외
   사용자 추가 JVM 인수(-Xss, -XX:..., -Dfoo=bar 등)는 보존.

2) .mc_custom 을 gameDir 로 쓰면 마인크래프트 런처가 assets/libraries/
   versions 를 못 찾아 "Unable to prepare assets for download" 로 실패.
   linkMinecraftRuntimeDirs() 가 .minecraft 의 해당 세 폴더를
   .mc_custom 으로 junction(Windows) / symlink(POSIX) 연결. 이미 같은
   자리에 무언가 있으면 손대지 않음.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 00:41:45 +09:00
635c22c7ad Migrate tsconfig to Node16 module/moduleResolution
VS Code surfaced the TS deprecation:
  'moduleResolution=node10' is deprecated and won't work in TS 7.0.

Fix: switch the root tsconfig.json from
  module: CommonJS, moduleResolution: node
to
  module: Node16, moduleResolution: Node16

TypeScript 7 only supports node16/nodenext/bundler. node16 matches the
runtime semantics we already use (Node ≥ 16, CommonJS output via the
absence of "type": "module" in package.json), so the emit is unchanged.

Side effect of Node16 resolution: relative imports must carry the .js
extension. Added .js to every relative import across src/* (17 sites,
8 files). Bare module specifiers (express, electron, node:fs, ...) are
unaffected.

Verified:
- tsc -p tsconfig.server.json — 0 errors
- tsc -p tsconfig.installer.json — 0 errors
- node dist/server/app.js boots; /op login → 302, /op/list → 200
2026-05-12 13:41:58 +09:00
01a34e08aa Sub-step 이전/다음 navigation + EULA popup flow
- Each 3-x and 4-x sub-step now has its own [이전][다음] action row at the bottom; 이전 returns to the previous sub-step (or previous main step on the first one) so users can move freely both directions
- 3-3 EULA: replace the inline checkbox with a modal popup. After server zip downloads, the renderer reads eula.txt via server:readEula; if absent, falls back to the live minecraft.net/en-us/eula HTML via server:fetchMinecraftEula and shows it in a sandboxed iframe
- Popup buttons: 동의 → server:acceptEula and proceed to RAM check; 비동의 / X / overlay click → "EULA 동의 실패. 다운로드를 취소합니다." and re-enable 다운로드 시작 for retry
- main.ts: stop auto-deleting eula.txt after server zip extraction so the popup can read whatever the zip provided
- 4-2 install completion now keeps a state.client.clientInstalled flag so backing into 4-2 doesn't force a re-install

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:25:10 +09:00
44847b8a55 Switch mods to per-folder auto-discovery and resourcepack to single zip
- PackDefinition: replace mods[]/resourcepacks[] with modsFolder (string) + resourcepackPath (string); drop PackAsset
- Editor: replace dynamic add/remove lists with two single inputs; remove the now-dead JS for adding/removing rows
- Server: expose GET /file/mods/<folder>/index.json that returns the list of .jar names; folder name restricted to [a-zA-Z0-9_-]+
- Installer: fetch the listing JSON and download each jar from /file/mods/<folder>/<file>.jar; download the single resourcepack from /file/resourcepacks/<file>.zip directly into resourcepacks/

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 20:51:44 +09:00
9c4f0e8dbc Switch login to password-only and split pack zip paths
- Login form/route accepts password only; matched account row provides session userId
- PackDefinition: replace packPath with mapPath (.mc_custom/saves) and serverPath (server install dir); editor exposes two .zip fields
- Installer resolves relative platform/map/server URLs against manifest origin under /file/{platforms,maps,servers}/<name>; downloads and extracts the zips

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 20:06:32 +09:00
8fd7cfaaef Build music-quiz installer and management site per spec
Implements the full spec described in README.md:

Management site (Node + TypeScript + Express + EJS):
- Public main page lists packs registered in manifest.json.
- /op login (account.json, internal-only), /op/dashboard manages packs
  with horizontal-scroll cards, add/select-and-delete flow, and the
  /op/dashboard/:packName editor (Mojang release dropdown, dynamic
  mods/resourcepacks lists, platform/RAM fields, file rename).
- Routes for /manifest.json (public) and /file/* (server pack files).
- Middleware blocks /account.json and /manifest/* directory access.

Installer (Electron):
- Five page renderer driven by IPC (preload contextBridge API):
  pack pick → single/multi → server install (path no-Korean check, JDK
  detect, file download, EULA, RAM gating, local web config editor,
  UPnP/port-forward check) → client install (.mc_custom mods +
  resourcepacks + launcher_profiles.json gameDir/javaArgs) → finish
  toggles (server folder, shortcut, server start, launcher start).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:34:27 +09:00
cd79378f3c Reset repository to README title only
Approach is changing entirely; clearing prior implementation
to start over from a clean slate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 20:41:19 +09:00
d03ad9d826 Patch run.bat to use installed JDK path
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 20:18:13 +09:00
4453dbd8f3 Add client apply flow and asset uploads 2026-05-08 20:03:07 +09:00
427b708277 Improve installer automation and config editor 2026-05-08 19:29:07 +09:00
af6e559682 Build installer and management site from spec 2026-05-07 23:22:34 +09:00