diff options
-rw-r--r-- | src/main/java/moe/nea/firmament/mixins/MinecraftInitLevelListener.java | 26 | ||||
-rw-r--r-- | src/main/kotlin/Firmament.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/features/debug/ExportedTestConstantMeta.kt | 8 | ||||
-rw-r--r-- | src/main/kotlin/features/debug/itemeditor/ItemExporter.kt | 265 | ||||
-rw-r--r-- | src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt | 240 | ||||
-rw-r--r-- | src/main/kotlin/gui/config/ManagedOption.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/keybindings/SavedKeyBinding.kt | 8 | ||||
-rw-r--r-- | src/main/kotlin/repo/ItemCache.kt | 50 | ||||
-rw-r--r-- | src/main/kotlin/repo/ModernOverlaysData.kt | 39 | ||||
-rw-r--r-- | src/main/kotlin/repo/RepoManager.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/util/ErrorUtil.kt | 11 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/InitLevel.kt | 25 |
12 files changed, 430 insertions, 248 deletions
diff --git a/src/main/java/moe/nea/firmament/mixins/MinecraftInitLevelListener.java b/src/main/java/moe/nea/firmament/mixins/MinecraftInitLevelListener.java new file mode 100644 index 0000000..550180a --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MinecraftInitLevelListener.java @@ -0,0 +1,26 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.util.mc.InitLevel; +import net.minecraft.client.MinecraftClient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MinecraftClient.class) +public class MinecraftInitLevelListener { + @Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;initBackendSystem()Lnet/minecraft/util/TimeSupplier$Nanoseconds;")) + private void onInitRenderBackend(CallbackInfo ci) { + InitLevel.bump(InitLevel.RENDER_INIT); + } + + @Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;initRenderer(JIZLjava/util/function/BiFunction;Z)V")) + private void onInitRender(CallbackInfo ci) { + InitLevel.bump(InitLevel.RENDER); + } + + @Inject(method = "onFinishedLoading", at = @At(value = "HEAD")) + private void onFinishedLoading(CallbackInfo ci) { + InitLevel.bump(InitLevel.MAIN_MENU); + } +} diff --git a/src/main/kotlin/Firmament.kt b/src/main/kotlin/Firmament.kt index 717808f..b00546a 100644 --- a/src/main/kotlin/Firmament.kt +++ b/src/main/kotlin/Firmament.kt @@ -51,6 +51,7 @@ import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.MC import moe.nea.firmament.util.SBData import moe.nea.firmament.util.data.IDataHolder +import moe.nea.firmament.util.mc.InitLevel import moe.nea.firmament.util.tr object Firmament { @@ -134,6 +135,7 @@ object Firmament { @JvmStatic fun onClientInitialize() { + InitLevel.bump(InitLevel.MC_INIT) FeatureManager.subscribeEvents() ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { instance -> TickEvent.publish(TickEvent(MC.currentTick++)) 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]!! + } +} diff --git a/src/main/kotlin/gui/config/ManagedOption.kt b/src/main/kotlin/gui/config/ManagedOption.kt index 383f392..830086c 100644 --- a/src/main/kotlin/gui/config/ManagedOption.kt +++ b/src/main/kotlin/gui/config/ManagedOption.kt @@ -49,7 +49,7 @@ class ManagedOption<T : Any>( value = handler.fromJson(root[propertyName]!!) return } catch (e: Exception) { - ErrorUtil.softError( + ErrorUtil.logError( "Exception during loading of config file ${element.name}. This will reset this config.", e ) diff --git a/src/main/kotlin/keybindings/SavedKeyBinding.kt b/src/main/kotlin/keybindings/SavedKeyBinding.kt index fc0270d..01baa8f 100644 --- a/src/main/kotlin/keybindings/SavedKeyBinding.kt +++ b/src/main/kotlin/keybindings/SavedKeyBinding.kt @@ -6,6 +6,7 @@ import net.minecraft.client.MinecraftClient import net.minecraft.client.util.InputUtil import net.minecraft.text.Text import moe.nea.firmament.util.MC +import moe.nea.firmament.util.mc.InitLevel // TODO: add support for mouse keybindings @Serializable @@ -113,8 +114,11 @@ data class SavedKeyBinding( if (shift) { stroke.append("SHIFT + ") // TODO: translations? } - - stroke.append(InputUtil.Type.KEYSYM.createFromCode(keyCode).localizedText) + if (InitLevel.isAtLeast(InitLevel.RENDER_INIT)) { + stroke.append(InputUtil.Type.KEYSYM.createFromCode(keyCode).localizedText) + } else { + stroke.append(keyCode.toString()) + } return stroke } diff --git a/src/main/kotlin/repo/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt index 15b3a27..3518257 100644 --- a/src/main/kotlin/repo/ItemCache.kt +++ b/src/main/kotlin/repo/ItemCache.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlin.io.path.readText import kotlin.jvm.optionals.getOrNull import net.minecraft.SharedConstants import net.minecraft.component.DataComponentTypes @@ -28,11 +29,13 @@ import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtElement import net.minecraft.nbt.NbtOps import net.minecraft.nbt.NbtString +import net.minecraft.nbt.StringNbtReader import net.minecraft.text.MutableText import net.minecraft.text.Style import net.minecraft.text.Text import net.minecraft.util.Identifier import moe.nea.firmament.Firmament +import moe.nea.firmament.features.debug.ExportedTestConstantMeta import moe.nea.firmament.repo.RepoManager.initialize import moe.nea.firmament.util.LegacyFormattingCode import moe.nea.firmament.util.LegacyTagParser @@ -67,6 +70,7 @@ object ItemCache : IReloadable { @ExpensiveItemCacheApi private fun NbtCompound.transformFrom10809ToModern() = convert189ToModern(this@transformFrom10809ToModern) + val currentSaveVersion = SharedConstants.getGameVersion().saveVersion.id @ExpensiveItemCacheApi fun convert189ToModern(nbtComponent: NbtCompound): NbtCompound? = @@ -75,7 +79,7 @@ object ItemCache : IReloadable { TypeReferences.ITEM_STACK, Dynamic(NbtOps.INSTANCE, nbtComponent), -1, - SharedConstants.getGameVersion().saveVersion.id + currentSaveVersion ).value as NbtCompound } catch (e: Exception) { isFlawless = false @@ -138,24 +142,48 @@ object ItemCache : IReloadable { return base } + fun tryFindFromModernFormat(skyblockId: SkyblockId): NbtCompound? { + val overlayFile = + RepoManager.overlayData.getMostModernReadableOverlay(skyblockId, currentSaveVersion) ?: return null + val overlay = StringNbtReader.readCompound(overlayFile.path.readText()) + val result = ExportedTestConstantMeta.SOURCE_CODEC.decode( + NbtOps.INSTANCE, overlay + ).result().getOrNull() ?: return null + val meta = result.first + return df.update( + TypeReferences.ITEM_STACK, + Dynamic(NbtOps.INSTANCE, result.second), + meta.dataVersion, + currentSaveVersion + ).value as NbtCompound + } + @ExpensiveItemCacheApi private fun NEUItem.asItemStackNow(): ItemStack { + try { + var modernItemTag = tryFindFromModernFormat(this.skyblockId) val oldItemTag = get10809CompoundTag() - val modernItemTag = oldItemTag.transformFrom10809ToModern() - ?: return brokenItemStack(this) + var usedOldNbt = false + if (modernItemTag == null) { + usedOldNbt = true + modernItemTag = oldItemTag.transformFrom10809ToModern() + ?: return brokenItemStack(this) + } val itemInstance = ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this) + if (usedOldNbt) { + val tag = oldItemTag.getCompound("tag") + val extraAttributes = tag.flatMap { it.getCompound("ExtraAttributes") } + .getOrNull() + if (extraAttributes != null) + itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes)) + val itemModel = tag.flatMap { it.getString("ItemModel") }.getOrNull() + if (itemModel != null) + itemInstance.set(DataComponentTypes.ITEM_MODEL, Identifier.of(itemModel)) + } itemInstance.loreAccordingToNbt = lore.map { un189Lore(it) } itemInstance.displayNameAccordingToNbt = un189Lore(displayName) - val tag = oldItemTag.getCompound("tag") - val extraAttributes = tag.flatMap { it.getCompound("ExtraAttributes") } - .getOrNull() - if (extraAttributes != null) - itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes)) - val itemModel = tag.flatMap { it.getString("ItemModel") }.getOrNull() - if (itemModel != null) - itemInstance.set(DataComponentTypes.ITEM_MODEL, Identifier.of(itemModel)) return itemInstance } catch (e: Exception) { e.printStackTrace() diff --git a/src/main/kotlin/repo/ModernOverlaysData.kt b/src/main/kotlin/repo/ModernOverlaysData.kt new file mode 100644 index 0000000..7e7acf1 --- /dev/null +++ b/src/main/kotlin/repo/ModernOverlaysData.kt @@ -0,0 +1,39 @@ +package moe.nea.firmament.repo + +import io.github.moulberry.repo.IReloadable +import io.github.moulberry.repo.NEURepository +import java.nio.file.Path +import kotlin.io.path.extension +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.nameWithoutExtension +import moe.nea.firmament.util.SkyblockId + +// TODO: move this over to the repo parser +class ModernOverlaysData : IReloadable { + data class OverlayFile( + val version: Int, + val path: Path, + ) + + var overlays: Map<SkyblockId, List<OverlayFile>> = mapOf() + override fun reload(repo: NEURepository) { + val items = mutableMapOf<SkyblockId, MutableList<OverlayFile>>() + repo.baseFolder.resolve("itemsOverlay") + .listDirectoryEntries() + .forEach { versionFolder -> + val version = versionFolder.fileName.toString().toIntOrNull() ?: return@forEach + versionFolder.listDirectoryEntries() + .forEach { item -> + if (item.extension != "snbt") return@forEach + val itemId = item.nameWithoutExtension + items.getOrPut(SkyblockId(itemId)) { mutableListOf() }.add(OverlayFile(version, item)) + } + } + this.overlays = items + } + + fun getOverlayFiles(skyblockId: SkyblockId) = overlays[skyblockId] ?: listOf() + fun getMostModernReadableOverlay(skyblockId: SkyblockId, version: Int) = getOverlayFiles(skyblockId) + .filter { it.version <= version } + .maxByOrNull { it.version } +} diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt index f4146e0..df89092 100644 --- a/src/main/kotlin/repo/RepoManager.kt +++ b/src/main/kotlin/repo/RepoManager.kt @@ -66,9 +66,11 @@ object RepoManager { val essenceRecipeProvider = EssenceRecipeProvider() val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider, ReforgeStore) val miningData = MiningRepoData() + val overlayData = ModernOverlaysData() fun makeNEURepository(path: Path): NEURepository { return NEURepository.of(path).apply { + registerReloadListener(overlayData) registerReloadListener(ItemCache) registerReloadListener(RepoItemTypeCache) registerReloadListener(ExpLadders) diff --git a/src/main/kotlin/util/ErrorUtil.kt b/src/main/kotlin/util/ErrorUtil.kt index f12bb12..3db4ecd 100644 --- a/src/main/kotlin/util/ErrorUtil.kt +++ b/src/main/kotlin/util/ErrorUtil.kt @@ -29,12 +29,19 @@ object ErrorUtil { inline fun softError(message: String, exception: Throwable) { if (aggressiveErrors) throw IllegalStateException(message, exception) - else Firmament.logger.error(message, exception) + else logError(message, exception) + } + + fun logError(message: String, exception: Throwable) { + Firmament.logger.error(message, exception) + } + fun logError(message: String) { + Firmament.logger.error(message) } inline fun softError(message: String) { if (aggressiveErrors) error(message) - else Firmament.logger.error(message) + else logError(message) } fun <T> Result<T>.intoCatch(message: String): Catch<T> { diff --git a/src/main/kotlin/util/mc/InitLevel.kt b/src/main/kotlin/util/mc/InitLevel.kt new file mode 100644 index 0000000..2c3eedb --- /dev/null +++ b/src/main/kotlin/util/mc/InitLevel.kt @@ -0,0 +1,25 @@ +package moe.nea.firmament.util.mc + +enum class InitLevel { + STARTING, + MC_INIT, + RENDER_INIT, + RENDER, + MAIN_MENU, + ; + + companion object { + var initLevel = InitLevel.STARTING + private set + + @JvmStatic + fun isAtLeast(wantedLevel: InitLevel): Boolean = initLevel >= wantedLevel + + @JvmStatic + fun bump(nextLevel: InitLevel) { + if (nextLevel.ordinal != initLevel.ordinal + 1) + error("Cannot bump initLevel $nextLevel from $initLevel") + initLevel = nextLevel + } + } +} |