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/ .minecraft/
└── icons/ ├── icons/
├── minecraft/ │ ├── minecraft/
│ ├── diamond.png │ │ ├── blocks/
│ ├── iron_ingot.png │ │ │ ├── stone.png
│ │ │ └── ...
│ │ └── items/
│ │ ├── diamond.png
│ │ └── ...
│ └── create/
│ ├── blocks/
│ │ ├── brass_casing.png
│ │ └── ...
│ └── items/
│ ├── brass_ingot.png
│ └── ... │ └── ...
└── create/ └── icons_create_20251226_181500.zip ← 자동 생성
├── mechanical_press.png
├── brass_ingot.png
└── ...
``` ```
--- ---

View file

@ -2,20 +2,27 @@ package com.beemer.iconexporter.export
import com.beemer.iconexporter.IconExporter import com.beemer.iconexporter.IconExporter
import com.beemer.iconexporter.render.IconRenderer import com.beemer.iconexporter.render.IconRenderer
import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path
import java.util.* import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.io.path.exists import kotlin.io.path.exists
import kotlin.io.path.name
import kotlin.io.path.pathString
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
/** /**
* 아이콘 추출 관리자 * 아이콘 추출 관리자
* *
* 대량의 아이템을 처리할 게임 프리즈를 방지하기 위해 기반 시스템으로 아이템을 순차적으로 처리합니다. * 대량의 아이템을 처리할 게임 프리즈를 방지하기 위해 기반 시스템으로 아이템을 순차적으로 처리합니다. 완료 icons 폴더를 ZIP으로 압축합니다.
*/ */
object IconExportManager { object IconExportManager {
// 틱당 처리할 아이템 수 (너무 높으면 랙, 너무 낮으면 느림) // 틱당 처리할 아이템 수 (너무 높으면 랙, 너무 낮으면 느림)
@ -127,8 +134,12 @@ object IconExportManager {
} }
try { 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) { 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 mc = Minecraft.getInstance()
val gameDir = mc.gameDirectory.toPath() val gameDir = mc.gameDirectory.toPath()
// icons/<namespace>/<path>.png // icons/<namespace>/<type>/<path>.png
return gameDir.resolve("icons").resolve(namespace).resolve("$path.png") return gameDir.resolve("icons").resolve(namespace).resolve(type).resolve("$path.png")
} }
/** 진행률 로그 출력 */ /** 진행률 로그 출력 */
@ -199,6 +213,17 @@ object IconExportManager {
isRunning = false isRunning = false
val elapsed = System.currentTimeMillis() - startTime 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 = val summary =
""" """
§a=== 추출 완료 === §a=== 추출 완료 ===
@ -221,17 +246,63 @@ object IconExportManager {
} }
// 게임 내 메시지 // 게임 내 메시지
val mc = Minecraft.getInstance()
mc.player?.let { player -> mc.player?.let { player ->
player.displayClientMessage(Component.literal(summary), false) player.displayClientMessage(Component.literal(summary), false)
// 저장 위치 안내 // ZIP 경로 안내
val outputDir = mc.gameDirectory.toPath().resolve("icons") if (zipPath != null) {
player.displayClientMessage( player.displayClientMessage(
Component.literal("§7저장 위치: §f${outputDir.toAbsolutePath()}"), Component.literal("§7ZIP 파일: §f${zipPath.toAbsolutePath()}"),
false 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
} }
/** 작업 취소 */ /** 작업 취소 */