diff options
Diffstat (limited to 'src/main/kotlin/features/debug')
3 files changed, 281 insertions, 232 deletions
diff --git a/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt b/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt index a817dd6..f0250dc 100644 --- a/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt +++ b/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt @@ -3,17 +3,25 @@ package moe.nea.firmament.features.debug import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import java.util.Optional +import net.minecraft.SharedConstants +import moe.nea.firmament.Firmament data class ExportedTestConstantMeta( val dataVersion: Int, val modVersion: Optional<String>, ) { companion object { + val current = ExportedTestConstantMeta( + SharedConstants.getGameVersion().saveVersion.id, + Optional.of("Firmament ${Firmament.version.friendlyString}") + ) + val CODEC: Codec<ExportedTestConstantMeta> = RecordCodecBuilder.create { it.group( Codec.INT.fieldOf("dataVersion").forGetter(ExportedTestConstantMeta::dataVersion), Codec.STRING.optionalFieldOf("modVersion").forGetter(ExportedTestConstantMeta::modVersion), ).apply(it, ::ExportedTestConstantMeta) } + val SOURCE_CODEC = CODEC.fieldOf("source").codec() } } diff --git a/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt b/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt index db2f36f..5cf1c85 100644 --- a/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt +++ b/src/main/kotlin/features/debug/itemeditor/ItemExporter.kt @@ -1,252 +1,53 @@ package moe.nea.firmament.features.debug.itemeditor -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put -import kotlin.concurrent.thread import kotlin.io.path.createParentDirectories import kotlin.io.path.relativeTo import kotlin.io.path.writeText -import net.minecraft.component.DataComponentTypes import net.minecraft.item.ItemStack -import net.minecraft.nbt.NbtCompound -import net.minecraft.nbt.NbtInt -import net.minecraft.nbt.NbtString -import net.minecraft.text.Text -import net.minecraft.util.Unit +import net.minecraft.nbt.NbtOps import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ClientStartedEvent import moe.nea.firmament.events.HandledScreenKeyPressedEvent +import moe.nea.firmament.features.debug.ExportedTestConstantMeta import moe.nea.firmament.features.debug.PowerUserTools import moe.nea.firmament.repo.RepoDownloadManager -import moe.nea.firmament.util.HypixelPetInfo -import moe.nea.firmament.util.LegacyTagWriter.Companion.toLegacyString -import moe.nea.firmament.util.StringUtil.words -import moe.nea.firmament.util.directLiteralStringContent -import moe.nea.firmament.util.extraAttributes import moe.nea.firmament.util.focusedItemStack -import moe.nea.firmament.util.getLegacyFormatString -import moe.nea.firmament.util.json.toJsonArray -import moe.nea.firmament.util.mc.displayNameAccordingToNbt -import moe.nea.firmament.util.mc.loreAccordingToNbt -import moe.nea.firmament.util.mc.toNbtList -import moe.nea.firmament.util.skyBlockId -import moe.nea.firmament.util.skyblock.Rarity +import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString import moe.nea.firmament.util.tr -import moe.nea.firmament.util.transformEachRecursively -import moe.nea.firmament.util.unformattedString -class ItemExporter(var itemStack: ItemStack) { - var lore = itemStack.loreAccordingToNbt - var name = itemStack.displayNameAccordingToNbt - val extraAttribs = itemStack.extraAttributes.copy() - val legacyNbt = NbtCompound() - val warnings = mutableListOf<String>() - - fun preprocess() { - // TODO: split up preprocess steps into preprocess actions that can be toggled in a ui - extraAttribs.remove("timestamp") - extraAttribs.remove("uuid") - extraAttribs.remove("modifier") - extraAttribs.getString("petInfo").ifPresent { petInfoJson -> - var petInfo = Firmament.json.decodeFromString<HypixelPetInfo>(petInfoJson) - petInfo = petInfo.copy(candyUsed = 0, heldItem = null, exp = 0.0, active = null, uuid = null) - extraAttribs.putString("petInfo", Firmament.tightJson.encodeToString(petInfo)) - } - itemStack.skyBlockId?.let { - extraAttribs.putString("id", it.neuItem) - } - trimLore() - } - - fun trimLore() { - val rarityIdx = lore.indexOfLast { - val firstWordInLine = it.unformattedString.words().filter { it.length > 2 }.firstOrNull() - firstWordInLine?.let(Rarity::fromString) != null - } - if (rarityIdx >= 0) { - lore = lore.subList(0, rarityIdx + 1) - } - deleteLineUntilNextSpace { it.startsWith("Held Item: ") } - deleteLineUntilNextSpace { it.startsWith("Progress to Level ") } - deleteLineUntilNextSpace { it.startsWith("MAX LEVEL") } - collapseWhitespaces() - - name = name.transformEachRecursively { - var string = it.directLiteralStringContent ?: return@transformEachRecursively it - string = string.replace("Lvl \\d+".toRegex(), "Lvl {LVL}") - Text.literal(string).setStyle(it.style) - } - } - - fun collapseWhitespaces() { - lore = (listOf(null as Text?) + lore).zipWithNext() - .filter { !it.first?.unformattedString.isNullOrBlank() || !it.second?.unformattedString.isNullOrBlank() } - .map { it.second!! } - } - - fun deleteLineUntilNextSpace(search: (String) -> Boolean) { - val idx = lore.indexOfFirst { search(it.unformattedString) } - if (idx < 0) return - val l = lore.toMutableList() - val p = l.subList(idx, l.size) - val nextBlank = p.indexOfFirst { it.unformattedString.isEmpty() } - if (nextBlank < 0) - p.clear() - else - p.subList(0, nextBlank).clear() - lore = l - } - - fun processNbt() { - // TODO: calculate hideflags - legacyNbt.put("HideFlags", NbtInt.of(254)) - copyUnbreakable() - copyItemModel() - copyExtraAttributes() - copyLegacySkullNbt() - copyDisplay() - copyEnchantments() - copyEnchantGlint() - // TODO: copyDisplay - } - - private fun copyItemModel() { - val itemModel = itemStack.get(DataComponentTypes.ITEM_MODEL) ?: return - legacyNbt.put("ItemModel", NbtString.of(itemModel.toString())) - } - - private fun copyDisplay() { - legacyNbt.put("display", NbtCompound().apply { - put("Lore", lore.map { NbtString.of(it.getLegacyFormatString(trimmed = true)) }.toNbtList()) - putString("Name", name.getLegacyFormatString(trimmed = true)) - }) - } - - fun exportJson(): JsonElement { - preprocess() - processNbt() - return buildJsonObject { - val (itemId, damage) = legacyifyItemStack() - put("itemid", itemId) - put("displayname", name.getLegacyFormatString(trimmed = true)) - put("nbttag", legacyNbt.toLegacyString()) - put("damage", damage) - put("lore", lore.map { it.getLegacyFormatString(trimmed = true) }.toJsonArray()) - val sbId = itemStack.skyBlockId - if (sbId == null) - warnings.add("Could not find skyblock id") - put("internalname", sbId?.neuItem) - put("clickcommand", "") - put("crafttext", "") - put("modver", "Firmament ${Firmament.version.friendlyString}") - put("infoType", "") - put("info", JsonArray(listOf())) - } - - } - - companion object { - @Subscribe - fun load(event: ClientStartedEvent) { - thread(start = true, name = "ItemExporter Meta Load Thread") { - LegacyItemData.itemLut - } - } - - @Subscribe - fun onKeyBind(event: HandledScreenKeyPressedEvent) { - if (event.matches(PowerUserTools.TConfig.exportItemStackToRepo)) { - val itemStack = event.screen.focusedItemStack ?: return - val exporter = ItemExporter(itemStack) - val json = exporter.exportJson() - val jsonFormatted = Firmament.twoSpaceJson.encodeToString(json) - val itemFile = RepoDownloadManager.repoSavedLocation.resolve("items") - .resolve("${json.jsonObject["internalname"]!!.jsonPrimitive.content}.json") - itemFile.createParentDirectories() - itemFile.writeText(jsonFormatted) - PowerUserTools.lastCopiedStack = Pair( - itemStack, - tr( - "firmament.repoexport.success", - "Exported item to ${itemFile.relativeTo(RepoDownloadManager.repoSavedLocation)}${ - exporter.warnings.joinToString( - "" - ) { "\nWarning: $it" } - }" - ) +object ItemExporter { + + + @Subscribe + fun onKeyBind(event: HandledScreenKeyPressedEvent) { + if (event.matches(PowerUserTools.TConfig.exportItemStackToRepo)) { + val itemStack = event.screen.focusedItemStack ?: return + val exporter = LegacyItemExporter.createExporter(itemStack) + val json = exporter.exportJson() + val jsonFormatted = Firmament.twoSpaceJson.encodeToString(json) + val fileName = json.jsonObject["internalname"]!!.jsonPrimitive.content + val itemFile = RepoDownloadManager.repoSavedLocation.resolve("items").resolve("${fileName}.json") + itemFile.createParentDirectories() + itemFile.writeText(jsonFormatted) + val overlayFile = RepoDownloadManager.repoSavedLocation.resolve("itemsOverlay") + .resolve(ExportedTestConstantMeta.current.dataVersion.toString()) + .resolve("${fileName}.snbt") + overlayFile.createParentDirectories() + overlayFile.writeText(exporter.exportModernSnbt().toPrettyString()) + + PowerUserTools.lastCopiedStack = Pair( + itemStack, + tr( + "firmament.repoexport.success", + "Exported item to ${itemFile.relativeTo(RepoDownloadManager.repoSavedLocation)}${ + exporter.warnings.joinToString( + "" + ) { "\nWarning: $it" } + }" ) - } - } - } - - fun copyEnchantGlint() { - if (itemStack.get(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE) == true) { - val ench = legacyNbt.getListOrEmpty("ench") - legacyNbt.put("ench", ench) - } - } - - private fun copyUnbreakable() { - if (itemStack.get(DataComponentTypes.UNBREAKABLE) == Unit.INSTANCE) { - legacyNbt.putBoolean("Unbreakable", true) + ) } } - - fun copyEnchantments() { - val enchantments = itemStack.get(DataComponentTypes.ENCHANTMENTS)?.takeIf { !it.isEmpty } ?: return - val enchTag = legacyNbt.getListOrEmpty("ench") - legacyNbt.put("ench", enchTag) - enchantments.enchantmentEntries.forEach { entry -> - val id = entry.key.key.get().value - val legacyId = LegacyItemData.enchantmentLut[id] - if (legacyId == null) { - warnings.add("Could not find legacy enchantment id for ${id}") - return@forEach - } - enchTag.add(NbtCompound().apply { - putShort("lvl", entry.intValue.toShort()) - putShort( - "id", - legacyId.id.toShort() - ) - }) - } - } - - fun copyExtraAttributes() { - legacyNbt.put("ExtraAttributes", extraAttribs) - } - - fun copyLegacySkullNbt() { - val profile = itemStack.get(DataComponentTypes.PROFILE) ?: return - legacyNbt.put("SkullOwner", NbtCompound().apply { - profile.id.ifPresent { - putString("Id", it.toString()) - } - putBoolean("hypixelPopulated", true) - put("Properties", NbtCompound().apply { - profile.properties().forEach { prop, value -> - val list = getListOrEmpty(prop) - put(prop, list) - list.add(NbtCompound().apply { - value.signature?.let { - putString("Signature", it) - } - putString("Value", value.value) - putString("Name", value.name) - }) - } - }) - }) - } - - fun legacyifyItemStack(): LegacyItemData.LegacyItemType { - // TODO: add a default here - return LegacyItemData.itemLut[itemStack.item]!! - } } diff --git a/src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt b/src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt new file mode 100644 index 0000000..f9d57d4 --- /dev/null +++ b/src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt @@ -0,0 +1,240 @@ +package moe.nea.firmament.features.debug.itemeditor + +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put +import kotlin.concurrent.thread +import net.minecraft.component.DataComponentTypes +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NbtCompound +import net.minecraft.nbt.NbtElement +import net.minecraft.nbt.NbtInt +import net.minecraft.nbt.NbtOps +import net.minecraft.nbt.NbtString +import net.minecraft.text.Text +import net.minecraft.util.Unit +import moe.nea.firmament.Firmament +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.ClientStartedEvent +import moe.nea.firmament.features.debug.ExportedTestConstantMeta +import moe.nea.firmament.util.HypixelPetInfo +import moe.nea.firmament.util.LegacyTagWriter.Companion.toLegacyString +import moe.nea.firmament.util.StringUtil.words +import moe.nea.firmament.util.directLiteralStringContent +import moe.nea.firmament.util.extraAttributes +import moe.nea.firmament.util.getLegacyFormatString +import moe.nea.firmament.util.json.toJsonArray +import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.mc.loreAccordingToNbt +import moe.nea.firmament.util.mc.toNbtList +import moe.nea.firmament.util.skyBlockId +import moe.nea.firmament.util.skyblock.Rarity +import moe.nea.firmament.util.transformEachRecursively +import moe.nea.firmament.util.unformattedString + +class LegacyItemExporter private constructor(var itemStack: ItemStack) { + var lore = itemStack.loreAccordingToNbt + var name = itemStack.displayNameAccordingToNbt + val extraAttribs = itemStack.extraAttributes.copy() + val legacyNbt = NbtCompound() + val warnings = mutableListOf<String>() + + // TODO: check if lore contains non 1.8.9 able hex codes and emit lore in overlay files if so + + fun preprocess() { + // TODO: split up preprocess steps into preprocess actions that can be toggled in a ui + extraAttribs.remove("timestamp") + extraAttribs.remove("uuid") + extraAttribs.remove("modifier") + extraAttribs.getString("petInfo").ifPresent { petInfoJson -> + var petInfo = Firmament.json.decodeFromString<HypixelPetInfo>(petInfoJson) + petInfo = petInfo.copy(candyUsed = 0, heldItem = null, exp = 0.0, active = null, uuid = null) + extraAttribs.putString("petInfo", Firmament.tightJson.encodeToString(petInfo)) + } + itemStack.skyBlockId?.let { + extraAttribs.putString("id", it.neuItem) + } + trimLore() + itemStack.loreAccordingToNbt = itemStack.item.defaultStack.loreAccordingToNbt + itemStack.remove(DataComponentTypes.CUSTOM_NAME) + } + + fun trimLore() { + val rarityIdx = lore.indexOfLast { + val firstWordInLine = it.unformattedString.words().filter { it.length > 2 }.firstOrNull() + firstWordInLine?.let(Rarity::fromString) != null + } + if (rarityIdx >= 0) { + lore = lore.subList(0, rarityIdx + 1) + } + deleteLineUntilNextSpace { it.startsWith("Held Item: ") } + deleteLineUntilNextSpace { it.startsWith("Progress to Level ") } + deleteLineUntilNextSpace { it.startsWith("MAX LEVEL") } + collapseWhitespaces() + + name = name.transformEachRecursively { + var string = it.directLiteralStringContent ?: return@transformEachRecursively it + string = string.replace("Lvl \\d+".toRegex(), "Lvl {LVL}") + Text.literal(string).setStyle(it.style) + } + } + + fun collapseWhitespaces() { + lore = (listOf(null as Text?) + lore).zipWithNext() + .filter { !it.first?.unformattedString.isNullOrBlank() || !it.second?.unformattedString.isNullOrBlank() } + .map { it.second!! } + } + + fun deleteLineUntilNextSpace(search: (String) -> Boolean) { + val idx = lore.indexOfFirst { search(it.unformattedString) } + if (idx < 0) return + val l = lore.toMutableList() + val p = l.subList(idx, l.size) + val nextBlank = p.indexOfFirst { it.unformattedString.isEmpty() } + if (nextBlank < 0) + p.clear() + else + p.subList(0, nextBlank).clear() + lore = l + } + + fun processNbt() { + // TODO: calculate hideflags + legacyNbt.put("HideFlags", NbtInt.of(254)) + copyUnbreakable() + copyItemModel() + copyExtraAttributes() + copyLegacySkullNbt() + copyDisplay() + copyEnchantments() + copyEnchantGlint() + // TODO: copyDisplay + } + + private fun copyItemModel() { + val itemModel = itemStack.get(DataComponentTypes.ITEM_MODEL) ?: return + legacyNbt.put("ItemModel", NbtString.of(itemModel.toString())) + } + + private fun copyDisplay() { + legacyNbt.put("display", NbtCompound().apply { + put("Lore", lore.map { NbtString.of(it.getLegacyFormatString(trimmed = true)) }.toNbtList()) + putString("Name", name.getLegacyFormatString(trimmed = true)) + }) + } + + fun exportModernSnbt(): NbtElement { + val overlay = ItemStack.CODEC.encodeStart(NbtOps.INSTANCE, itemStack) + .orThrow + val overlayWithVersion = + ExportedTestConstantMeta.SOURCE_CODEC.encode(ExportedTestConstantMeta.current, NbtOps.INSTANCE, overlay) + .orThrow + return overlayWithVersion + } + + fun prepare() { + preprocess() + processNbt() + } + + fun exportJson(): JsonElement { + return buildJsonObject { + val (itemId, damage) = legacyifyItemStack() + put("itemid", itemId) + put("displayname", name.getLegacyFormatString(trimmed = true)) + put("nbttag", legacyNbt.toLegacyString()) + put("damage", damage) + put("lore", lore.map { it.getLegacyFormatString(trimmed = true) }.toJsonArray()) + val sbId = itemStack.skyBlockId + if (sbId == null) + warnings.add("Could not find skyblock id") + put("internalname", sbId?.neuItem) + put("clickcommand", "") + put("crafttext", "") + put("modver", "Firmament ${Firmament.version.friendlyString}") + put("infoType", "") + put("info", JsonArray(listOf())) + } + + } + + companion object { + fun createExporter(itemStack: ItemStack): LegacyItemExporter { + return LegacyItemExporter(itemStack.copy()).also { it.prepare() } + } + + @Subscribe + fun load(event: ClientStartedEvent) { + thread(start = true, name = "ItemExporter Meta Load Thread") { + LegacyItemData.itemLut + } + } + } + + fun copyEnchantGlint() { + if (itemStack.get(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE) == true) { + val ench = legacyNbt.getListOrEmpty("ench") + legacyNbt.put("ench", ench) + } + } + + private fun copyUnbreakable() { + if (itemStack.get(DataComponentTypes.UNBREAKABLE) == Unit.INSTANCE) { + legacyNbt.putBoolean("Unbreakable", true) + } + } + + fun copyEnchantments() { + val enchantments = itemStack.get(DataComponentTypes.ENCHANTMENTS)?.takeIf { !it.isEmpty } ?: return + val enchTag = legacyNbt.getListOrEmpty("ench") + legacyNbt.put("ench", enchTag) + enchantments.enchantmentEntries.forEach { entry -> + val id = entry.key.key.get().value + val legacyId = LegacyItemData.enchantmentLut[id] + if (legacyId == null) { + warnings.add("Could not find legacy enchantment id for ${id}") + return@forEach + } + enchTag.add(NbtCompound().apply { + putShort("lvl", entry.intValue.toShort()) + putShort( + "id", + legacyId.id.toShort() + ) + }) + } + } + + fun copyExtraAttributes() { + legacyNbt.put("ExtraAttributes", extraAttribs) + } + + fun copyLegacySkullNbt() { + val profile = itemStack.get(DataComponentTypes.PROFILE) ?: return + legacyNbt.put("SkullOwner", NbtCompound().apply { + profile.id.ifPresent { + putString("Id", it.toString()) + } + putBoolean("hypixelPopulated", true) + put("Properties", NbtCompound().apply { + profile.properties().forEach { prop, value -> + val list = getListOrEmpty(prop) + put(prop, list) + list.add(NbtCompound().apply { + value.signature?.let { + putString("Signature", it) + } + putString("Value", value.value) + putString("Name", value.name) + }) + } + }) + }) + } + + fun legacyifyItemStack(): LegacyItemData.LegacyItemType { + // TODO: add a default here + return LegacyItemData.itemLut[itemStack.item]!! + } +} |