From 27888c557dc3a55725cc1c4712981111da89e8c5 Mon Sep 17 00:00:00 2001 From: Caadiq Date: Fri, 26 Dec 2025 18:30:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(IconExporter):=20blocks/items=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EA=B5=AC=EB=B6=84=20=EB=B0=8F=20ZIP=20=EC=95=95?= =?UTF-8?q?=EC=B6=95=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BlockItem 여부로 blocks/items 폴더 자동 분류 - 추출 완료 후 icons__.zip 자동 생성 - README 업데이트 --- IconExporter/README.md | 28 +++--- .../iconexporter/export/IconExportManager.kt | 89 +++++++++++++++++-- 2 files changed, 98 insertions(+), 19 deletions(-) diff --git a/IconExporter/README.md b/IconExporter/README.md index 14869b3..69465db 100644 --- a/IconExporter/README.md +++ b/IconExporter/README.md @@ -48,19 +48,27 @@ ## 📁 출력 구조 -추출된 아이콘은 `.minecraft/icons/` 폴더에 저장됩니다. +아이콘은 `.minecraft/icons/` 폴더에 **blocks/items**로 구분되어 저장됩니다. +완료 후 자동으로 ZIP 파일이 생성됩니다. ``` .minecraft/ -└── icons/ - ├── minecraft/ - │ ├── diamond.png - │ ├── iron_ingot.png - │ └── ... - └── create/ - ├── mechanical_press.png - ├── brass_ingot.png - └── ... +├── icons/ +│ ├── minecraft/ +│ │ ├── blocks/ +│ │ │ ├── stone.png +│ │ │ └── ... +│ │ └── items/ +│ │ ├── diamond.png +│ │ └── ... +│ └── create/ +│ ├── blocks/ +│ │ ├── brass_casing.png +│ │ └── ... +│ └── items/ +│ ├── brass_ingot.png +│ └── ... +└── icons_create_20251226_181500.zip ← 자동 생성 ``` --- diff --git a/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt b/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt index 1bc8057..ac1c9c1 100644 --- a/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt +++ b/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt @@ -2,20 +2,27 @@ package com.beemer.iconexporter.export import com.beemer.iconexporter.IconExporter import com.beemer.iconexporter.render.IconRenderer +import java.io.FileOutputStream import java.io.IOException import java.nio.file.Files +import java.nio.file.Path import java.util.* +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream import kotlin.io.path.exists +import kotlin.io.path.name +import kotlin.io.path.pathString import net.minecraft.client.Minecraft import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.network.chat.Component +import net.minecraft.world.item.BlockItem import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack /** * 아이콘 추출 관리자 * - * 대량의 아이템을 처리할 때 게임 프리즈를 방지하기 위해 틱 기반 큐 시스템으로 아이템을 순차적으로 처리합니다. + * 대량의 아이템을 처리할 때 게임 프리즈를 방지하기 위해 틱 기반 큐 시스템으로 아이템을 순차적으로 처리합니다. 완료 후 icons 폴더를 ZIP으로 압축합니다. */ object IconExportManager { // 틱당 처리할 아이템 수 (너무 높으면 랙, 너무 낮으면 느림) @@ -127,8 +134,12 @@ object IconExportManager { } try { + // 아이템이 블록인지 확인 (BlockItem이면 블록, 아니면 아이템) + val isBlock = item is BlockItem + val type = if (isBlock) "blocks" else "items" + // 저장 경로 생성 - val outputPath = getOutputPath(itemId.namespace, itemId.path) + val outputPath = getOutputPath(itemId.namespace, type, itemId.path) // 이미 파일이 존재하고 덮어쓰기가 비활성화된 경우 스킵 if (outputPath.exists() && !overwrite) { @@ -162,13 +173,16 @@ object IconExportManager { } } - /** 아이템 ID로 출력 경로 생성 */ - private fun getOutputPath(namespace: String, path: String): java.nio.file.Path { + /** + * 아이템 ID로 출력 경로 생성 형식: icons///.png 예: + * icons/create/blocks/brass_casing.png + */ + private fun getOutputPath(namespace: String, type: String, path: String): Path { val mc = Minecraft.getInstance() val gameDir = mc.gameDirectory.toPath() - // icons//.png - return gameDir.resolve("icons").resolve(namespace).resolve("$path.png") + // icons///.png + return gameDir.resolve("icons").resolve(namespace).resolve(type).resolve("$path.png") } /** 진행률 로그 출력 */ @@ -199,6 +213,17 @@ object IconExportManager { isRunning = false val elapsed = System.currentTimeMillis() - startTime + val mc = Minecraft.getInstance() + val iconsDir = mc.gameDirectory.toPath().resolve("icons") + + // ZIP 압축 생성 + var zipPath: Path? = null + try { + zipPath = createZipArchive(iconsDir) + } catch (e: Exception) { + IconExporter.LOGGER.error("ZIP 압축 실패: {}", e.message) + } + val summary = """ §a=== 추출 완료 === @@ -221,19 +246,65 @@ object IconExportManager { } // 게임 내 메시지 - val mc = Minecraft.getInstance() mc.player?.let { player -> player.displayClientMessage(Component.literal(summary), false) + // ZIP 경로 안내 + if (zipPath != null) { + player.displayClientMessage( + Component.literal("§7ZIP 파일: §f${zipPath.toAbsolutePath()}"), + false + ) + } + // 저장 위치 안내 - val outputDir = mc.gameDirectory.toPath().resolve("icons") player.displayClientMessage( - Component.literal("§7저장 위치: §f${outputDir.toAbsolutePath()}"), + Component.literal("§7아이콘 폴더: §f${iconsDir.toAbsolutePath()}"), false ) } } + /** icons 폴더를 ZIP으로 압축 파일명: icons__.zip */ + private fun createZipArchive(iconsDir: Path): Path? { + if (!iconsDir.exists()) { + return null + } + + val mc = Minecraft.getInstance() + val gameDir = mc.gameDirectory.toPath() + + // ZIP 파일명 생성 + val timestamp = + java.time.LocalDateTime.now() + .format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + val modName = currentModFilter ?: "all" + val zipFileName = "icons_${modName}_$timestamp.zip" + val zipPath = gameDir.resolve(zipFileName) + + IconExporter.LOGGER.info("ZIP 압축 생성 중: {}", zipPath) + + ZipOutputStream(FileOutputStream(zipPath.toFile())).use { zos -> + Files.walk(iconsDir).use { paths -> + paths.filter { Files.isRegularFile(it) && it.name.endsWith(".png") }.forEach { file + -> + try { + // ZIP 내 상대 경로: //.png + val relativePath = iconsDir.relativize(file).pathString.replace("\\", "/") + zos.putNextEntry(ZipEntry(relativePath)) + Files.copy(file, zos) + zos.closeEntry() + } catch (e: Exception) { + IconExporter.LOGGER.error("ZIP에 파일 추가 실패: {} - {}", file, e.message) + } + } + } + } + + IconExporter.LOGGER.info("ZIP 압축 완료: {}", zipPath) + return zipPath + } + /** 작업 취소 */ fun cancel() { if (!isRunning) return