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>
This commit is contained in:
2026-05-28 00:28:37 +09:00
parent acd3dd995d
commit 201043e289

View File

@@ -1,8 +1,17 @@
import type { MusicListEntry, PackList } from '../shared/types.js' import type { MusicListEntry, PackList } from '../shared/types.js'
/** SNBT 문자열 리터럴 안에 들어갈 문자열을 escape. */ /**
* SNBT 문자열 리터럴 안에 들어갈 문자열을 escape.
* 백슬래시·따옴표 외에도 줄바꿈·탭을 이스케이프해서 `data modify` 한 줄 명령이
* description 같은 멀티라인 입력 때문에 깨지지 않게 한다.
*/
function escapeSnbtString(input: string): string { function escapeSnbtString(input: string): string {
return input.replace(/\\/g, '\\\\').replace(/"/g, '\\"') return input
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
} }
/** alias 배열을 SNBT 리스트 리터럴로 변환. 빈 배열도 `[]` 로 출력. */ /** alias 배열을 SNBT 리스트 리터럴로 변환. 빈 배열도 `[]` 로 출력. */
@@ -12,13 +21,14 @@ function aliasListSnbt(aliases: string[]): string {
return `[${parts.join(',')}]` return `[${parts.join(',')}]`
} }
/** 한 곡(MusicListEntry) → `{title:"...", author:"...", alias:[...]}` SNBT. */ /** 한 곡(MusicListEntry) → `{title:"...", author:"...", alias:[...], description:"..."}` SNBT. */
function entrySnbt(entry: MusicListEntry): string { function entrySnbt(entry: MusicListEntry): string {
const title = escapeSnbtString(entry.title ?? '') const title = escapeSnbtString(entry.title ?? '')
// launcher 의 artist → 데이터팩 SNBT 의 author. 빈 값은 빈 문자열로 그대로 둔다. // launcher 의 artist → 데이터팩 SNBT 의 author. 빈 값은 빈 문자열로 그대로 둔다.
const author = escapeSnbtString(entry.artist ?? '') const author = escapeSnbtString(entry.artist ?? '')
const alias = aliasListSnbt(entry.aliases ?? []) const alias = aliasListSnbt(entry.aliases ?? [])
return `{title:"${title}", author:"${author}", alias:${alias}}` const description = escapeSnbtString(entry.description ?? '')
return `{title:"${title}", author:"${author}", alias:${alias}, description:"${description}"}`
} }
/** /**
@@ -29,11 +39,11 @@ function entrySnbt(entry: MusicListEntry): string {
export function buildSongsMcfunction(list: PackList): string { export function buildSongsMcfunction(list: PackList): string {
const lines: string[] = [] const lines: string[] = []
lines.push('# 곡 한 개 = 한 줄.') lines.push('# 곡 한 개 = 한 줄.')
lines.push('# 필수 — title, author, alias') lines.push('# 필수 — title, author, alias, description')
lines.push('# 선택 — volume (이 곡만의 /playsound 음량. 미지정시 init/config.mcfunction') lines.push('# 선택 — volume (이 곡만의 /playsound 음량. 미지정시 init/config.mcfunction')
lines.push('# 의 audio.volume 사용)') lines.push('# 의 audio.volume 사용)')
lines.push('# 곡 순서가 리소스팩의 track_NN / cover_NN 인덱스와 1:1 매칭된다.') lines.push('# 곡 순서가 리소스팩의 track_NN / cover_NN 인덱스와 1:1 매칭된다.')
lines.push('# 예) {title:"Quiet Song", author:"...", alias:[...], volume:2.0}') lines.push('# 예) {title:"Quiet Song", author:"...", alias:[...], description:"...", volume:2.0}')
lines.push('data modify storage mq:main songs set value []') lines.push('data modify storage mq:main songs set value []')
for (const entry of list.music) { for (const entry of list.music) {
lines.push(`data modify storage mq:main songs append value ${entrySnbt(entry)}`) lines.push(`data modify storage mq:main songs append value ${entrySnbt(entry)}`)