feat: Add overlay item exporter
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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++))
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>()
|
||||
object ItemExporter {
|
||||
|
||||
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()
|
||||
@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())
|
||||
|
||||
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" }
|
||||
}"
|
||||
)
|
||||
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]!!
|
||||
}
|
||||
}
|
||||
|
||||
240
src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt
Normal file
240
src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt
Normal file
@@ -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]!!
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
39
src/main/kotlin/repo/ModernOverlaysData.kt
Normal file
39
src/main/kotlin/repo/ModernOverlaysData.kt
Normal file
@@ -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 }
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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> {
|
||||
|
||||
25
src/main/kotlin/util/mc/InitLevel.kt
Normal file
25
src/main/kotlin/util/mc/InitLevel.kt
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user