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>
기존 방식은 자기 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>
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>
리소스팩 간편설치기:
- 베이스 리소스팩 다운로드 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>
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>
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
- 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>
- 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>