feat(IconExporter): blocks/items 폴더 구분 및 ZIP 압축 기능 추가

- BlockItem 여부로 blocks/items 폴더 자동 분류
- 추출 완료 후 icons_<mod>_<timestamp>.zip 자동 생성
- README 업데이트
This commit is contained in:
Caadiq 2025-12-26 18:30:03 +09:00
parent bf8f980fed
commit 27888c557d
2 changed files with 98 additions and 19 deletions

View file

@ -48,19 +48,27 @@
## 📁 출력 구조
추출된 아이콘은 `.minecraft/icons/` 폴더에 저장됩니다.
아이콘은 `.minecraft/icons/` 폴더에 **blocks/items**로 구분되어 저장됩니다.
완료 후 자동으로 ZIP 파일이 생성됩니다.
```
.minecraft/
└── icons/
├── minecraft/
│ ├── diamond.png
│ ├── iron_ingot.png
├── icons/
│ ├── minecraft/
│ │ ├── blocks/
│ │ │ ├── stone.png
│ │ │ └── ...
│ │ └── items/
│ │ ├── diamond.png
│ │ └── ...
│ └── create/
│ ├── blocks/
│ │ ├── brass_casing.png
│ │ └── ...
│ └── items/
│ ├── brass_ingot.png
│ └── ...
└── create/
├── mechanical_press.png
├── brass_ingot.png
└── ...
└── icons_create_20251226_181500.zip ← 자동 생성
```
---

View file

@ -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/<namespace>/<type>/<path>.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/<namespace>/<path>.png
return gameDir.resolve("icons").resolve(namespace).resolve("$path.png")
// icons/<namespace>/<type>/<path>.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,17 +246,63 @@ object IconExportManager {
}
// 게임 내 메시지
val mc = Minecraft.getInstance()
mc.player?.let { player ->
player.displayClientMessage(Component.literal(summary), false)
// 저장 위치 안내
val outputDir = mc.gameDirectory.toPath().resolve("icons")
// ZIP 경로 안내
if (zipPath != null) {
player.displayClientMessage(
Component.literal("§7저장 위치: §f${outputDir.toAbsolutePath()}"),
Component.literal("§7ZIP 파일: §f${zipPath.toAbsolutePath()}"),
false
)
}
// 저장 위치 안내
player.displayClientMessage(
Component.literal("§7아이콘 폴더: §f${iconsDir.toAbsolutePath()}"),
false
)
}
}
/** icons 폴더를 ZIP으로 압축 파일명: icons_<modFilter>_<timestamp>.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 내 상대 경로: <namespace>/<type>/<item>.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
}
/** 작업 취소 */