Commit Graph

91 Commits

Author SHA1 Message Date
201043e289 datapack: include description in SNBT entry output
entrySnbt() now emits {title, author, alias, description} so the
mcfunction export carries the operator-entered song description
into the data modify storage command. Multiline descriptions are
flattened via newline/tab escapes inside the SNBT string literal
(escapeSnbtString extended to handle \r, \n, \t alongside the
existing backslash + quote escapes) so each `data modify` stays a
single line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 00:28:37 +09:00
b4160aefc1 list-editor: add per-track description button + modal
Each music list row now shows a 설명 button immediately to the left of
the 별칭 button. Click opens a modal with a multi-line textarea; on
close the value is persisted into MusicListEntry.description and saved
to the same pack list JSON. The button gets a hasDesc visual indicator
when filled. Description is stored but intentionally not consumed by
datapack export or alias matching — purely informational metadata.

- types.ts: add description: string to MusicListEntry
- store.ts: normalize entry.description via sanitizeStr (defaults to '')
- listEditor.ejs: new #descModal alongside aliasModal
- listEditor.js: render descBtn left of aliasBtn, attach handlers,
  also set description: '' on playlist-fetched entries
- styles.css: extend trackRow grid to 6 cols, reuse aliasBtn styling
  for descBtn, add descTextarea sizing
- locale (ko-kr): descBtn / descModalTitle / descBack / descPlaceholder
  / descHint

Backwards-compatible: existing list JSON files without description
field normalize to ''.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:44:24 +09:00
1ac13a03ff server-youtube: fast-path reuse for cached zipapp
ensureYtDlp() and prepareYtDlp() now check the on-disk yt-dlp_zipapp
before re-running the native-fail -> network-download path. On Linux
servers where the native binary always fails verification (glibc/musl/
arch mismatch), every previous request was re-downloading both the
native (~33MB) and the zipapp (~3MB). With the fast path, after the
first successful zipapp install all subsequent requests short-circuit
to the cached zipapp.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:39:10 +09:00
3248d096e4 server-youtube: add POSIX zipapp fallback when native bundled binary won't run
If the native yt-dlp_linux/yt-dlp_macos binary fails to execute (glibc
mismatch, musl libc, wrong arch) AND no system yt-dlp is on PATH, fall
back to downloading the universal Python zipapp ('yt-dlp', ~3MB) and
running it via shebang. Requires python3 on PATH, which is standard on
modern Linux servers. Also: rewrite stale Windows-flavored install-path
comments to reflect actual cross-platform behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:35:11 +09:00
8c9dc88e8b server-youtube: strip Zone.Identifier ADS on Windows after download
NTFS marks files downloaded over HTTP with a Zone.Identifier alternate
data stream, which SmartScreen/Attachment Manager can use to block
execution of yt-dlp.exe. Remove the ADS best-effort after each
download to reduce one likely cause of "execution verification failed"
in the user-reported failure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:29:27 +09:00
b769f453a3 server-youtube: diagnostic detail + PATH fallback when bundled yt-dlp won't run
probeVersion() now captures stderr/exit-code/signal/spawn-error instead of
returning a bare boolean, and ensureYtDlp() tries the bundled binary first,
falls back to `yt-dlp(.exe)` on PATH if the bundled one won't execute (AV
block, missing libc symbol, broken download), and only then re-downloads.
The final user-facing error includes the per-attempt diagnostics so we can
actually see WHY verification failed instead of the opaque
"yt-dlp 다운로드는 됐지만 실행 검증에 실패했습니다." message.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:24:54 +09:00
5c13648f63 rp-pack: fail-fast on base track/painting collision (was: silent skip)
Reviewer correctly flagged that the previous skip-on-collision
behavior silently drops new quiz tracks when the base resourcepack
already has the same track_NN key. That makes the install LOOK
successful but breaks the quiz at runtime (datapack references the
missing track).

The new behavior throws a clear error explaining which key collided
and what the user must do (remove the conflicting base entry, or
use a different base). The base assets are still preserved (we
never overwrite); we just refuse to build a broken pack.

Removed the now-unused skip-summary log keys.
2026-05-23 17:26:41 +09:00
9efd4a696a rp-pack: never overwrite base resourcepack sounds/paintings (v0.3.5)
If the base resourcepack already has audio files under
assets/musicquiz/sounds/ or entries in assets/musicquiz/sounds.json,
the build now PRESERVES them and skips any new track that would
collide. Same policy for painting textures: existing cover_*.png
in the base are not overwritten by new ones.

Per-track collision is logged so the user can see exactly what was
preserved and what was skipped. Summary counts (added / skipped)
are also logged.

Requested by 사금향: "기존에 있는걸 삭제하거나 이상하게 엎어쓰지
말것" — preserve base assets unconditionally.
2026-05-23 17:18:46 +09:00
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
05dc9d7166 terms: seed-on-fetch + rename/delete sync (v0.3.3)
- public route `/manifest/terms/:packKey/:fileName` 가 sendFile 전에
  `ensurePackTermsDir(packKey)` 를 호출하도록 수정. 관리자가 사이트 약관
  페이지를 한 번도 열지 않은 fresh 배포에서도 설치기가 정상적으로 약관을
  받을 수 있다. `loadPackDefinition` 으로 실제 pack 만 허용해 임의 키로
  빈 폴더가 생성되는 것을 차단.
- `renamePack`: pack JSON 이름이 바뀌면 `manifest/terms/<oldKey>/` 도
  `<newKey>/` 로 함께 rename.
- `deletePackKeys`: pack 삭제 시 약관 폴더도 `fs.rm` 으로 정리 — 동일 key
  재생성 시 옛 약관 부활 방지.
- `ensurePackTermsDir` export.

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

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 01:39:28 +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
c14b0507c7 terms: dark editor BG, vertical row layout, add/delete custom kinds
- 약관 편집기 배경/슬래시 메뉴를 사이트 다크 팔레트로 통일 (흰 배경 + 흰 글씨 가시성 문제 해결)
- 약관 목록을 가로 풀폭 1줄씩 세로로 쌓이는 레이아웃으로 변경
- 사용자 정의 약관 추가/삭제 지원
  - manifest/terms/_meta.json 에 라벨 저장
  - builtin 5종(map/resourcepack/mod/installer/installer-rp)은 삭제 불가, "기본" 배지 표시
  - kind 식별자 규칙: 소문자/숫자/하이픈 32자 이내
  - 공개 라우트 /manifest/terms/<file>.md 는 isPublicTermsFile() 로 _meta.json 차단
- 0.3.0 → 0.3.1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 01:12:10 +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
40986bee11 installer-rp: delete base resourcepack zip after composing final pack
The RP installer downloads a fresh copy of the base zip into its temp
dir and composes the final pack on top of it. The base zip the main
installer placed in .mc_custom/resourcepacks/ has nothing to do after
that — but it stays in the Minecraft resource-pack list as a second
entry. Delete it after the final zip is written.

Guard against the case where the user set outputPackName equal to the
base filename, which would make base path == final path; in that case
we leave it alone so we don't wipe the file we just wrote.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 21:28:36 +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
794ad9b778 installer-rp: rename fallback pack name musicquiz → resourcepack
Per user request: when outputPackName is empty, fall back to
`<packKey>_resourcepack` instead of `<packKey>_musicquiz`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:54:31 +09:00
f810719d92 installer-rp: site-configured outputPackName for built zip
Adds a new "생성되는 리소스팩 이름" admin field saved to the pack
manifest and consumed by the rp installer when naming the final zip.
Empty value falls back to <packKey>_musicquiz; Windows-invalid chars
are sanitized to '_'. Bumps version 0.1.1 → 0.2.0 (new feature).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 18:34:46 +09:00
3017e77479 env: merge .env + .env.build instead of stopping at first match
Reviewer noted that returning on the first found file meant a project-root
`.env.build` could shadow the dev `.env`, leaving the server without
`PORT`/`HOST`/`SESSION_SECRET`. Switch `loadEnv()` to iterate every
candidate with `override:false` so multiple files merge — first-loaded
value wins per key.

Order also reshuffled so dev's `.env` takes precedence over `.env.build`
at the project root (server settings stay alive), while in packaged
mode `resources/.env.build` still wins. Verified manually with a
temp-dir reproduction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 01:11:15 +09:00
c8da4207fc build: separate .env.build for packaging, keep .env dev-only
The previous setup packaged the development `.env` into the installer
resources, mixing local server settings (PORT/HOST/SESSION_SECRET) with
the build-time site domain. Introduce a dedicated `.env.build`:

- electron-builder configs now copy `.env.build` (gitignored) into
  `resources/`, no longer touching the dev `.env`.
- `loadEnv()` prefers `resources/.env.build` first, falling back to
  `resources/.env` (for operators who hand-edit the packaged file),
  then `<root>/.env.build`, then `<root>/.env`.
- `.env.build.example` documents the build-only keys (SITE_BASE_URL,
  MANIFEST_URL, MUSIC_CONCURRENCY); server-side keys stay in `.env`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 01:03:22 +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
848fac500e op/datapack: add painting_variant JSON zip export button
데이터팩 수정 페이지에 "이미지.zip 출력" 버튼과 크기 입력(기본 4, 1~16)
을 추가. 누르면 GET /op/datapack/:key/images-zip?size=N 으로 음악 개수만큼
cover_NN.json (asset_id, width=size, height=size, title, author) 을 zip 으로
스트리밍해서 내려준다. 사용자가 맵 데이터팩의 data/musicquiz/painting_variant/
에 그대로 풀어 넣을 수 있다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 23:22:23 +09:00
212e70cd56 installer-rp: adjust music download stagger 1500ms → 2000ms
사용자 요청에 따라 1.5s 는 너무 짧다고 판단해 2s 로 재조정.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 22:40:20 +09:00
3ca93abae9 installer-rp: reduce music download stagger 2500ms → 1500ms
체감 속도 향상을 위해 음악 다운로드 시작 간격을 1.5초로 단축한다.
동시성 시각 효과는 유지하면서 전체 대기시간을 줄인다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 22:40:05 +09:00
a8b9b689c2 resourcepack: gate shader strip on declared maxFmt, not build target
베이스팩의 vanilla 셰이더는 manifest 의 mcVersion(resolved.format) 이 64 이하
라도, 우리가 supported_formats/max_format 으로 1.21.9+ 까지 호환을 선언하면
새 GLSL API 환경에서 로드돼 "리소스 새로고침 실패" 가 다시 난다. 셰이더 제거
판정 기준을 resolved.format > 64 에서 maxFmt > 64 로 옮기고, 그 계산을
mcmeta 작성보다 먼저 수행한다. 로그의 format 값도 maxFmt 를 표시해 어떤
호환 상한 때문에 제거됐는지 추적 가능하게 했다.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 21:49:11 +09:00
1665f05c55 resourcepack: declare compatibility range from 1.21.6 to latest known
Pack.mcmeta now spans pack_format MIN_SUPPORTED_FORMAT (=63, 1.21.6) up to
max(LATEST_KNOWN_FORMAT, resolved.format) so a single build loads on every
MC from 1.21.6 through 26.1.2+ (currently extending to 86 = 26.2). Both
schemas are written: supported_formats for clients on pack_format <= 64,
and min_format/max_format for 1.21.9+ clients. pack_format itself stays
at the build target so newer clients see the pack as current rather than
legacy.
2026-05-14 21:43:31 +09:00
40b2ff81f5 resourcepack: strip vanilla shader overrides when pack_format > 64
Reviewer was right that warn-only let broken zips through. On 1.21.9+
(pack_format > 64) the vanilla shader GLSL API changed (ProjMat, FogColor
etc.) so any base pack carrying old assets/minecraft/shaders/* fails to
compile and causes the same "리소스 새로고침 실패" the pack.mcmeta fix
addressed. Strip that directory at build time when the target pack_format
exceeds 64; keep textures/models/sounds intact and log what was removed.
On <= 64 the old shaders still work, so leave the base pack untouched.
2026-05-14 21:34:30 +09:00
9cb7c05b43 resourcepack: warn when base zip overrides vanilla shaders
The actual "리소스 새로고침 실패" was caused by pack.mcmeta JsonParseException
(fixed in 6718315). The shader compile errors seen on the same log come from
the user-supplied base resourcepack overriding vanilla shaders for an older
MC. Auto-stripping would silently destroy the user's intended look, so emit
a clear warning during build instead and let the user decide whether to
update the base pack.
2026-05-14 21:28:41 +09:00
671831535b fix(resourcepack): emit min_format/max_format when pack_format > 64
MC 1.21.9+ (pack_format >= 65) deprecated supported_formats and now
requires min_format/max_format at pack root. On 26.1.x (format 84) the
old supported_formats made pack.mcmeta unparseable, so MC rejected the
music-quiz resourcepack with "리소스 새로고침 실패" on reload.
2026-05-14 21:23:45 +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
c8911a9a62 fix(editor): fabric에서도 platformDownloadUrl 저장되도록 수정
normalizePackDefinition 이 fabric 일 때 downloadUrl 을 의도적으로
스트립하고 있어 저장 후 입력값이 사라지는 문제. vanilla 외에는
모두 보관하도록 조건을 변경하고, 에디터 UI 도 fabric 에서 URL
입력 칸을 다시 보여주도록 되돌렸다.
2026-05-14 00:19:19 +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
de08f9a810 op: 데이터팩 출력을 zip 대신 songs.mcfunction 코드 텍스트로 변경
운영자가 mc_datapack 의 init/songs.mcfunction 파일에 직접 복사해 붙여넣
는 워크플로로 단순화. 전체 데이터팩을 패키징할 필요가 없다.

- /op/datapack/:packName/generate 가 buildSongsMcfunction(list) 결과를
  text/plain 으로 반환 (zip 스트리밍 제거).
- file/datapacks/music_quiz_template/ 정적 사본 제거.
- datapack.ejs 에 코드블록·복사 버튼 복원, 안내 문구 추가
  ("data/mq/function/init/songs.mcfunction 에 그대로 덮어쓰세요").
- datapack 로케일 라벨을 "코드 출력 / 복사 / 출력 완료" 로 정리.
2026-05-13 16:41:25 +09:00
af884706d4 op: 데이터팩 출력을 실제 music_quiz zip 으로 교체
가이드 (mc_datapack/launcher_datapack_연동_가이드.txt) 에 따라:
- file/datapacks/music_quiz_template/ 에 mc_datapack 의 music_quiz/ 정적
  파일을 미리 동봉 (data/mq/function/init/songs.mcfunction 제외).
- src/server/datapack.ts: list.music → SNBT (`{title, author, alias}`)
  songs.mcfunction 빌더와 archiver 기반 zip 스트리머 추가.
- /op/datapack/:packName/generate 가 텍스트 placeholder 대신
  music_quiz_<key>.zip 을 Content-Disposition attachment 로 내려준다.
- datapack.ejs 의 코드블록·복사 UI 제거, 곡 수는 서버 렌더 시점에 표시.
- 더 이상 쓰이지 않는 locales 의 datapackOutput.* 키 제거, datapack
  버튼 라벨/상태 문구를 zip 다운로드용으로 정리.
2026-05-13 16:34:34 +09:00
2344c4b8d2 site: 음악목록 항목별 별칭 편집 기능 추가
- MusicListEntry 에 aliases: string[] 필드 추가, 저장 시 trim·중복 제거.
- 목록 행에 "별칭" 버튼 표시(개수 있으면 강조), 클릭 시 모달 오픈.
- 모달에서 "별칭 추가" → 입력행 생성, "−" 버튼 → 해당 행 삭제,
  좌상단 "← 돌아가기" 또는 오버레이 클릭으로 저장 후 닫기.
2026-05-13 15:57:35 +09:00
6cd402121b i18n: 리소스팩 설치기 UI 문구를 locales/installer-rp/ko-kr.json 으로 분리
- main/preload/ytdlp/ffmpeg/music/images/pack/renderer 전반에서 로그·에러·진행
  메시지 문자열을 locales/installer-rp/ko-kr.json 사전 키로 교체
- preload 에 loadLocale 추가, main 에 rp:i18n:dict IPC 핸들러 추가
- 패키징된 .exe 에서도 한국어 사전이 적용되도록 electron-builder.yml 의
  extraResources 에 locales/ 폴더 추가

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 04:00:31 +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
c2fcc2fbbf i18n: 서버 측 모든 UI 문구를 locales/server/ko-kr.json 으로 분리
- src/shared/i18n.ts: 공용 i18n 로더 (dotted-key + {{placeholder}} 보간)
- locales/server/ko-kr.json: 사이트 + 라우터 + 데이터팩 출력 사전
- EJS 뷰는 res.locals.t 미들웨어로 일괄 적용
- listEditor.js 등 클라이언트 JS 는 사전을 inline <script> 로 주입받아 tt() 헬퍼 사용
2026-05-13 03:43:04 +09:00
401d72622e config: 빌드된 .exe 에도 .env 가 적용되도록 보강
- electron-builder.yml
  - dist/shared/** 추가 (env.js 등이 패키지에 들어가도록)
  - extraResources 로 빌드 시점 .env 를 resources/.env 에 배포
- loadEnv: 패키징된 Electron 앱이면 process.resourcesPath/.env 를 먼저 시도하고
  없으면 프로젝트 루트 .env 로 폴백
- docs/admin-site.md: .exe 빌드에도 .env 가 따라가는 동작 설명 추가
2026-05-13 03:29:30 +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