aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/debug
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/features/debug')
-rw-r--r--src/main/kotlin/features/debug/AnimatedClothingScanner.kt193
-rw-r--r--src/main/kotlin/features/debug/DebugLogger.kt11
-rw-r--r--src/main/kotlin/features/debug/DebugView.kt29
-rw-r--r--src/main/kotlin/features/debug/DeveloperFeatures.kt78
-rw-r--r--src/main/kotlin/features/debug/ExportedTestConstantMeta.kt27
-rw-r--r--src/main/kotlin/features/debug/MinorTrolling.kt10
-rw-r--r--src/main/kotlin/features/debug/PowerUserTools.kt192
-rw-r--r--src/main/kotlin/features/debug/SkinPreviews.kt91
-rw-r--r--src/main/kotlin/features/debug/SoundVisualizer.kt65
-rw-r--r--src/main/kotlin/features/debug/itemeditor/ExportRecipe.kt256
-rw-r--r--src/main/kotlin/features/debug/itemeditor/ItemExporter.kt250
-rw-r--r--src/main/kotlin/features/debug/itemeditor/LegacyItemData.kt87
-rw-r--r--src/main/kotlin/features/debug/itemeditor/LegacyItemExporter.kt318
13 files changed, 1483 insertions, 124 deletions
diff --git a/src/main/kotlin/features/debug/AnimatedClothingScanner.kt b/src/main/kotlin/features/debug/AnimatedClothingScanner.kt
new file mode 100644
index 0000000..dc77115
--- /dev/null
+++ b/src/main/kotlin/features/debug/AnimatedClothingScanner.kt
@@ -0,0 +1,193 @@
+package moe.nea.firmament.features.debug
+
+import net.minecraft.commands.arguments.ResourceKeyArgument
+import net.minecraft.core.component.DataComponentType
+import net.minecraft.world.entity.Entity
+import net.minecraft.world.entity.decoration.ArmorStand
+import net.minecraft.world.item.ItemStack
+import net.minecraft.nbt.Tag
+import net.minecraft.nbt.NbtOps
+import net.minecraft.core.registries.Registries
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.commands.get
+import moe.nea.firmament.commands.thenArgument
+import moe.nea.firmament.commands.thenExecute
+import moe.nea.firmament.commands.thenLiteral
+import moe.nea.firmament.events.CommandEvent
+import moe.nea.firmament.events.EntityUpdateEvent
+import moe.nea.firmament.events.WorldReadyEvent
+import moe.nea.firmament.util.ClipboardUtils
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.math.GChainReconciliation
+import moe.nea.firmament.util.math.GChainReconciliation.shortenCycle
+import moe.nea.firmament.util.mc.NbtPrism
+import moe.nea.firmament.util.tr
+
+object AnimatedClothingScanner {
+
+ data class LensOfFashionTheft<T>(
+ val prism: NbtPrism,
+ val component: DataComponentType<T>,
+ ) {
+ fun observe(itemStack: ItemStack): Collection<Tag> {
+ val x = itemStack.get(component) ?: return listOf()
+ val nbt = component.codecOrThrow().encodeStart(NbtOps.INSTANCE, x).orThrow
+ return prism.access(nbt)
+ }
+ }
+
+ var lens: LensOfFashionTheft<*>? = null
+ var subject: Entity? = null
+ var history: MutableList<String> = mutableListOf()
+ val metaHistory: MutableList<List<String>> = mutableListOf()
+
+ @OptIn(ExperimentalStdlibApi::class)
+ @Subscribe
+ fun onUpdate(event: EntityUpdateEvent) {
+ val s = subject ?: return
+ if (event.entity != s) return
+ val l = lens ?: return
+ if (event is EntityUpdateEvent.EquipmentUpdate) {
+ event.newEquipment.forEach {
+ val formatted = (l.observe(it.second)).joinToString()
+ history.add(formatted)
+ // TODO: add a slot filter
+ }
+ }
+ }
+
+ fun reduceHistory(reducer: (List<String>, List<String>) -> List<String>): List<String> {
+ return metaHistory.fold(history, reducer).shortenCycle()
+ }
+
+ @Subscribe
+ fun onSubCommand(event: CommandEvent.SubCommand) {
+ event.subcommand(DeveloperFeatures.DEVELOPER_SUBCOMMAND) {
+ thenLiteral("stealthisfit") {
+ thenLiteral("clear") {
+ thenExecute {
+ subject = null
+ metaHistory.clear()
+ history.clear()
+ MC.sendChat(tr("firmament.fitstealer.clear", "Cleared fit stealing history"))
+ }
+ }
+ thenLiteral("copy") {
+ thenExecute {
+ val history = reduceHistory { a, b -> a + b }
+ copyHistory(history)
+ MC.sendChat(tr("firmament.fitstealer.copied", "Copied the history"))
+ }
+ thenLiteral("deduplicated") {
+ thenExecute {
+ val history = reduceHistory { a, b ->
+ (a.toMutableSet() + b).toList()
+ }
+ copyHistory(history)
+ MC.sendChat(
+ tr(
+ "firmament.fitstealer.copied.deduplicated",
+ "Copied the deduplicated history"
+ )
+ )
+ }
+ }
+ thenLiteral("merged") {
+ thenExecute {
+ val history = reduceHistory(GChainReconciliation::reconcileCycles)
+ copyHistory(history)
+ MC.sendChat(tr("firmament.fitstealer.copied.merged", "Copied the merged history"))
+ }
+ }
+ }
+ thenLiteral("target") {
+ thenLiteral("self") {
+ thenExecute {
+ toggleObserve(MC.player!!)
+ }
+ }
+ thenLiteral("pet") {
+ thenExecute {
+ source.sendFeedback(
+ tr(
+ "firmament.fitstealer.stealingpet",
+ "Observing nearest marker armourstand"
+ )
+ )
+ val p = MC.player!!
+ val nearestPet = p.level.getEntitiesOfClass(
+ ArmorStand::class.java,
+ p.boundingBox.inflate(10.0),
+ { it.isMarker })
+ .minBy { it.distanceToSqr(p) }
+ toggleObserve(nearestPet)
+ }
+ }
+ thenExecute {
+ val ent = MC.instance.crosshairPickEntity
+ if (ent == null) {
+ source.sendFeedback(
+ tr(
+ "firmament.fitstealer.notargetundercursor",
+ "No entity under cursor"
+ )
+ )
+ } else {
+ toggleObserve(ent)
+ }
+ }
+ }
+ thenLiteral("path") {
+ thenArgument(
+ "component",
+ ResourceKeyArgument.key(Registries.DATA_COMPONENT_TYPE)
+ ) { component ->
+ thenArgument("path", NbtPrism.Argument) { path ->
+ thenExecute {
+ lens = LensOfFashionTheft(
+ get(path),
+ MC.unsafeGetRegistryEntry(get(component))!!,
+ )
+ source.sendFeedback(
+ tr(
+ "firmament.fitstealer.lensset",
+ "Analyzing path ${get(path)} for component ${get(component).location()}"
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun copyHistory(toCopy: List<String>) {
+ ClipboardUtils.setTextContent(toCopy.joinToString("\n"))
+ }
+
+ @Subscribe
+ fun onWorldSwap(event: WorldReadyEvent) {
+ subject = null
+ if (history.isNotEmpty()) {
+ metaHistory.add(history)
+ history = mutableListOf()
+ }
+ }
+
+ private fun toggleObserve(entity: Entity?) {
+ subject = if (subject == null) entity else null
+ if (subject == null) {
+ metaHistory.add(history)
+ history = mutableListOf()
+ }
+ MC.sendChat(
+ subject?.let {
+ tr(
+ "firmament.fitstealer.targeted",
+ "Observing the equipment of ${it.name}."
+ )
+ } ?: tr("firmament.fitstealer.targetlost", "No longer logging equipment."),
+ )
+ }
+}
diff --git a/src/main/kotlin/features/debug/DebugLogger.kt b/src/main/kotlin/features/debug/DebugLogger.kt
index 2c6b962..2c8ced0 100644
--- a/src/main/kotlin/features/debug/DebugLogger.kt
+++ b/src/main/kotlin/features/debug/DebugLogger.kt
@@ -1,24 +1,29 @@
package moe.nea.firmament.features.debug
import kotlinx.serialization.serializer
-import net.minecraft.text.Text
+import net.minecraft.network.chat.Component
import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.TestUtil
import moe.nea.firmament.util.collections.InstanceList
+import moe.nea.firmament.util.data.Config
import moe.nea.firmament.util.data.DataHolder
class DebugLogger(val tag: String) {
companion object {
val allInstances = InstanceList<DebugLogger>("DebugLogger")
}
+
+ @Config
object EnabledLogs : DataHolder<MutableSet<String>>(serializer(), "DebugLogs", ::mutableSetOf)
init {
allInstances.add(this)
}
- fun isEnabled() = DeveloperFeatures.isEnabled && EnabledLogs.data.contains(tag)
+ fun isEnabled() = TestUtil.isInTest || EnabledLogs.data.contains(tag)
+ fun log(text: String) = log { text }
fun log(text: () -> String) {
if (!isEnabled()) return
- MC.sendChat(Text.literal(text()))
+ MC.sendChat(Component.literal(text()))
}
}
diff --git a/src/main/kotlin/features/debug/DebugView.kt b/src/main/kotlin/features/debug/DebugView.kt
deleted file mode 100644
index ee54260..0000000
--- a/src/main/kotlin/features/debug/DebugView.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-package moe.nea.firmament.features.debug
-
-import moe.nea.firmament.Firmament
-import moe.nea.firmament.annotations.Subscribe
-import moe.nea.firmament.events.TickEvent
-import moe.nea.firmament.features.FirmamentFeature
-import moe.nea.firmament.util.TimeMark
-
-object DebugView : FirmamentFeature {
- private data class StoredVariable<T>(
- val obj: T,
- val timer: TimeMark,
- )
-
- private val storedVariables: MutableMap<String, StoredVariable<*>> = sortedMapOf()
- override val identifier: String
- get() = "debug-view"
- override val defaultEnabled: Boolean
- get() = Firmament.DEBUG
-
- fun <T : Any?> showVariable(label: String, obj: T) {
- synchronized(this) {
- storedVariables[label] = StoredVariable(obj, TimeMark.now())
- }
- }
-
-}
diff --git a/src/main/kotlin/features/debug/DeveloperFeatures.kt b/src/main/kotlin/features/debug/DeveloperFeatures.kt
index 8f0c25c..8638bb6 100644
--- a/src/main/kotlin/features/debug/DeveloperFeatures.kt
+++ b/src/main/kotlin/features/debug/DeveloperFeatures.kt
@@ -3,33 +3,38 @@ package moe.nea.firmament.features.debug
import java.io.File
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.Type
+import org.objectweb.asm.tree.ClassNode
+import org.spongepowered.asm.mixin.Mixin
import kotlinx.serialization.json.encodeToStream
import kotlin.io.path.absolute
import kotlin.io.path.exists
-import net.minecraft.client.MinecraftClient
-import net.minecraft.text.Text
+import net.minecraft.client.Minecraft
+import net.minecraft.network.chat.Component
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.DebugInstantiateEvent
import moe.nea.firmament.events.TickEvent
-import moe.nea.firmament.features.FirmamentFeature
-import moe.nea.firmament.gui.config.ManagedConfig
+import moe.nea.firmament.init.MixinPlugin
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.TimeMark
+import moe.nea.firmament.util.asm.AsmAnnotationUtil
+import moe.nea.firmament.util.data.Config
+import moe.nea.firmament.util.data.ManagedConfig
import moe.nea.firmament.util.iterate
-object DeveloperFeatures : FirmamentFeature {
- override val identifier: String
+object DeveloperFeatures {
+ val DEVELOPER_SUBCOMMAND: String = "dev"
+ val identifier: String
get() = "developer"
- override val config: TConfig
- get() = TConfig
- override val defaultEnabled: Boolean
- get() = Firmament.DEBUG
val gradleDir =
Path.of(".").absolute()
.iterate { it.parent }
.find { it.resolve("settings.gradle.kts").exists() }
+ @Config
object TConfig : ManagedConfig("developer", Category.DEV) {
val autoRebuildResources by toggle("auto-rebuild") { false }
}
@@ -42,6 +47,42 @@ object DeveloperFeatures : FirmamentFeature {
}
@Subscribe
+ fun loadAllMixinClasses(event: DebugInstantiateEvent) {
+ val allMixinClasses = mutableSetOf<String>()
+ MixinPlugin.instances.forEach { plugin ->
+ val prefix = plugin.mixinPackage + "."
+ val classes = plugin.mixins.map { prefix + it }
+ allMixinClasses.addAll(classes)
+ for (cls in classes) {
+ val targets = javaClass.classLoader.getResourceAsStream("${cls.replace(".", "/")}.class").use {
+ val node = ClassNode()
+ ClassReader(it).accept(node, 0)
+ val mixins = mutableListOf<Mixin>()
+ (node.visibleAnnotations.orEmpty() + node.invisibleAnnotations.orEmpty()).forEach {
+ val annotationType = Type.getType(it.desc)
+ val mixinType = Type.getType(Mixin::class.java)
+ if (mixinType == annotationType) {
+ mixins.add(AsmAnnotationUtil.createProxy(Mixin::class.java, it))
+ }
+ }
+ mixins.flatMap { it.targets.toList() } + mixins.flatMap { it.value.map { it.java.name } }
+ }
+ for (target in targets)
+ try {
+ Firmament.logger.debug("Loading ${target} to force instantiate ${cls}")
+ Class.forName(target, true, javaClass.classLoader)
+ } catch (ex: Throwable) {
+ Firmament.logger.error("Could not load class ${target} that has been mixind by $cls", ex)
+ }
+ }
+ }
+ Firmament.logger.info("Forceloaded all Firmament mixins:")
+ val applied = MixinPlugin.instances.flatMap { it.appliedMixins }.toSet()
+ applied.forEach { Firmament.logger.info(" - ${it}") }
+ require(allMixinClasses == applied)
+ }
+
+ @Subscribe
fun dumpMissingTranslations(tickEvent: TickEvent) {
val toDump = missingTranslations ?: return
missingTranslations = null
@@ -51,24 +92,27 @@ object DeveloperFeatures : FirmamentFeature {
}
@JvmStatic
- fun hookOnBeforeResourceReload(client: MinecraftClient): CompletableFuture<Void> {
- val reloadFuture = if (TConfig.autoRebuildResources && isEnabled && gradleDir != null) {
+ fun hookOnBeforeResourceReload(client: Minecraft): CompletableFuture<Void> {
+ val reloadFuture = if (TConfig.autoRebuildResources && Firmament.DEBUG && gradleDir != null) {
val builder = ProcessBuilder("./gradlew", ":processResources")
builder.directory(gradleDir.toFile())
builder.inheritIO()
val process = builder.start()
- MC.sendChat(Text.translatable("firmament.dev.resourcerebuild.start"))
+ MC.sendChat(Component.translatable("firmament.dev.resourcerebuild.start"))
val startTime = TimeMark.now()
process.toHandle().onExit().thenApply {
- MC.sendChat(Text.stringifiedTranslatable(
- "firmament.dev.resourcerebuild.done",
- startTime.passedTime()))
+ MC.sendChat(
+ Component.translatableEscape(
+ "firmament.dev.resourcerebuild.done",
+ startTime.passedTime()
+ )
+ )
Unit
}
} else {
CompletableFuture.completedFuture(Unit)
}
- return reloadFuture.thenCompose { client.reloadResources() }
+ return reloadFuture.thenCompose { client.reloadResourcePacks() }
}
}
diff --git a/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt b/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt
new file mode 100644
index 0000000..a2b42fd
--- /dev/null
+++ b/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt
@@ -0,0 +1,27 @@
+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.getCurrentVersion().dataVersion().version,
+ 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/MinorTrolling.kt b/src/main/kotlin/features/debug/MinorTrolling.kt
index 32035a6..7936521 100644
--- a/src/main/kotlin/features/debug/MinorTrolling.kt
+++ b/src/main/kotlin/features/debug/MinorTrolling.kt
@@ -2,15 +2,13 @@
package moe.nea.firmament.features.debug
-import net.minecraft.text.Text
+import net.minecraft.network.chat.Component
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ModifyChatEvent
-import moe.nea.firmament.features.FirmamentFeature
-
// In memorian Dulkir
-object MinorTrolling : FirmamentFeature {
- override val identifier: String
+object MinorTrolling {
+ val identifier: String
get() = "minor-trolling"
val trollers = listOf("nea89o", "lrg89")
@@ -22,6 +20,6 @@ object MinorTrolling : FirmamentFeature {
val (_, name, text) = m.groupValues
if (name !in trollers) return
if (!text.startsWith("c:")) return
- it.replaceWith = Text.literal(text.substring(2).replace("&", "§"))
+ it.replaceWith = Component.literal(text.substring(2).replace("&", "§"))
}
}
diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt
index 13320dc..145ea35 100644
--- a/src/main/kotlin/features/debug/PowerUserTools.kt
+++ b/src/main/kotlin/features/debug/PowerUserTools.kt
@@ -2,42 +2,55 @@ package moe.nea.firmament.features.debug
import com.mojang.serialization.JsonOps
import kotlin.jvm.optionals.getOrNull
-import net.minecraft.block.SkullBlock
-import net.minecraft.block.entity.SkullBlockEntity
-import net.minecraft.component.DataComponentTypes
-import net.minecraft.entity.Entity
-import net.minecraft.entity.LivingEntity
-import net.minecraft.item.ItemStack
-import net.minecraft.item.Items
+import net.minecraft.world.level.block.SkullBlock
+import net.minecraft.world.level.block.entity.SkullBlockEntity
+import net.minecraft.core.component.DataComponents
+import net.minecraft.world.item.component.ResolvableProfile
+import net.minecraft.world.entity.Entity
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.item.ItemStack
+import net.minecraft.world.item.Items
+import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtOps
-import net.minecraft.text.Text
-import net.minecraft.text.TextCodecs
-import net.minecraft.util.hit.BlockHitResult
-import net.minecraft.util.hit.EntityHitResult
-import net.minecraft.util.hit.HitResult
+import net.minecraft.advancements.critereon.NbtPredicate
+import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
+import net.minecraft.network.chat.Component
+import net.minecraft.network.chat.ComponentSerialization
+import net.minecraft.resources.ResourceLocation
+import net.minecraft.world.Nameable
+import net.minecraft.world.phys.BlockHitResult
+import net.minecraft.world.phys.EntityHitResult
+import net.minecraft.world.phys.HitResult
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.CustomItemModelEvent
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.events.ScreenChangeEvent
+import moe.nea.firmament.events.SlotRenderEvents
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.WorldKeyboardEvent
-import moe.nea.firmament.features.FirmamentFeature
-import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
-import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.ClipboardUtils
import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.data.Config
+import moe.nea.firmament.util.data.ManagedConfig
import moe.nea.firmament.util.focusedItemStack
+import moe.nea.firmament.util.grey
+import moe.nea.firmament.util.mc.IntrospectableItemModelManager
+import moe.nea.firmament.util.mc.SNbtFormatter
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.mc.iterableArmorItems
import moe.nea.firmament.util.mc.loreAccordingToNbt
+import moe.nea.firmament.util.mc.unsafeNbt
import moe.nea.firmament.util.skyBlockId
+import moe.nea.firmament.util.tr
-object PowerUserTools : FirmamentFeature {
- override val identifier: String
+object PowerUserTools {
+ val identifier: String
get() = "power-user"
+ @Config
object TConfig : ManagedConfig(identifier, Category.DEV) {
val showItemIds by toggle("show-item-id") { false }
val copyItemId by keyBindingWithDefaultUnbound("copy-item-id")
@@ -47,22 +60,26 @@ object PowerUserTools : FirmamentFeature {
val copySkullTexture by keyBindingWithDefaultUnbound("copy-skull-texture")
val copyEntityData by keyBindingWithDefaultUnbound("entity-data")
val copyItemStack by keyBindingWithDefaultUnbound("copy-item-stack")
+ val copyTitle by keyBindingWithDefaultUnbound("copy-title")
+ val exportItemStackToRepo by keyBindingWithDefaultUnbound("export-item-stack")
+ val exportUIRecipes by keyBindingWithDefaultUnbound("export-recipe")
+ val exportNpcLocation by keyBindingWithDefaultUnbound("export-npc-location")
+ val highlightNonOverlayItems by toggle("highlight-non-overlay") { false }
+ val dontHighlightSemicolonItems by toggle("dont-highlight-semicolon-items") { false }
+ val showSlotNumbers by keyBindingWithDefaultUnbound("slot-numbers")
+ val autoCopyAnimatedSkins by toggle("copy-animated-skins") { false }
}
- override val config
- get() = TConfig
-
- var lastCopiedStack: Pair<ItemStack, Text>? = null
+ var lastCopiedStack: Pair<ItemStack, Component>? = null
set(value) {
field = value
- if (value != null) lastCopiedStackViewTime = true
+ if (value != null) lastCopiedStackViewTime = 2
}
- var lastCopiedStackViewTime = false
+ var lastCopiedStackViewTime = 0
@Subscribe
fun resetLastCopiedStack(event: TickEvent) {
- if (!lastCopiedStackViewTime) lastCopiedStack = null
- lastCopiedStackViewTime = false
+ if (lastCopiedStackViewTime-- < 0) lastCopiedStack = null
}
@Subscribe
@@ -70,37 +87,58 @@ object PowerUserTools : FirmamentFeature {
lastCopiedStack = null
}
- fun debugFormat(itemStack: ItemStack): Text {
- return Text.literal(itemStack.skyBlockId?.toString() ?: itemStack.toString())
+ fun debugFormat(itemStack: ItemStack): Component {
+ return Component.literal(itemStack.skyBlockId?.toString() ?: itemStack.toString())
+ }
+
+ @Subscribe
+ fun onRender(event: SlotRenderEvents.After) {
+ if (TConfig.showSlotNumbers.isPressed()) {
+ event.context.drawString(
+ MC.font,
+ event.slot.index.toString(), event.slot.x, event.slot.y, 0xFF00FF00.toInt(), true
+ )
+ event.context.drawString(
+ MC.font,
+ event.slot.containerSlot.toString(), event.slot.x, event.slot.y + MC.font.lineHeight, 0xFFFF0000.toInt(), true
+ )
+ }
}
@Subscribe
fun onEntityInfo(event: WorldKeyboardEvent) {
if (!event.matches(TConfig.copyEntityData)) return
- val target = (MC.instance.crosshairTarget as? EntityHitResult)?.entity
+ val target = (MC.instance.hitResult as? EntityHitResult)?.entity
if (target == null) {
- MC.sendChat(Text.translatable("firmament.poweruser.entity.fail"))
+ MC.sendChat(Component.translatable("firmament.poweruser.entity.fail"))
return
}
showEntity(target)
}
fun showEntity(target: Entity) {
- MC.sendChat(Text.translatable("firmament.poweruser.entity.type", target.type))
- MC.sendChat(Text.translatable("firmament.poweruser.entity.name", target.name))
- MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.position", target.pos))
+ val nbt = NbtPredicate.getEntityTagToCompare(target)
+ nbt.remove("Inventory")
+ nbt.put("StyledName", ComponentSerialization.CODEC.encodeStart(NbtOps.INSTANCE, target.feedbackDisplayName).orThrow)
+ println(SNbtFormatter.prettify(nbt))
+ ClipboardUtils.setTextContent(SNbtFormatter.prettify(nbt))
+ MC.sendChat(Component.translatable("firmament.poweruser.entity.type", target.type))
+ MC.sendChat(Component.translatable("firmament.poweruser.entity.name", target.name))
+ MC.sendChat(Component.translatableEscape("firmament.poweruser.entity.position", target.position))
if (target is LivingEntity) {
- MC.sendChat(Text.translatable("firmament.poweruser.entity.armor"))
- for (armorItem in target.armorItems) {
- MC.sendChat(Text.translatable("firmament.poweruser.entity.armor.item", debugFormat(armorItem)))
+ MC.sendChat(Component.translatable("firmament.poweruser.entity.armor"))
+ for ((slot, armorItem) in target.iterableArmorItems) {
+ MC.sendChat(Component.translatable("firmament.poweruser.entity.armor.item", debugFormat(armorItem)))
}
}
- MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.passengers", target.passengerList.size))
- target.passengerList.forEach {
+ MC.sendChat(Component.translatableEscape("firmament.poweruser.entity.passengers", target.passengers.size))
+ target.passengers.forEach {
showEntity(it)
}
}
+ // TODO: leak this through some other way, maybe.
+ lateinit var getSkullId: (profile: ResolvableProfile) -> ResourceLocation?
@Subscribe
fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
@@ -109,58 +147,74 @@ object PowerUserTools : FirmamentFeature {
if (it.matches(TConfig.copyItemId)) {
val sbId = item.skyBlockId
if (sbId == null) {
- lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skyblockid.fail"))
+ lastCopiedStack = Pair(item, Component.translatable("firmament.tooltip.copied.skyblockid.fail"))
return
}
ClipboardUtils.setTextContent(sbId.neuItem)
lastCopiedStack =
- Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem))
+ Pair(item, Component.translatableEscape("firmament.tooltip.copied.skyblockid", sbId.neuItem))
} else if (it.matches(TConfig.copyTexturePackId)) {
- val model = CustomItemModelEvent.getModelIdentifier(item)
+ val model = CustomItemModelEvent.getModelIdentifier0(item, object : IntrospectableItemModelManager {
+ override fun hasModel_firmament(identifier: ResourceLocation): Boolean {
+ return true
+ }
+ }).getOrNull() // TODO: remove global texture overrides, maybe
if (model == null) {
- lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail"))
+ lastCopiedStack = Pair(item, Component.translatable("firmament.tooltip.copied.modelid.fail"))
return
}
ClipboardUtils.setTextContent(model.toString())
lastCopiedStack =
- Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.modelid", model.toString()))
+ Pair(item, Component.translatableEscape("firmament.tooltip.copied.modelid", model.toString()))
} else if (it.matches(TConfig.copyNbtData)) {
// TODO: copy full nbt
- val nbt = item.get(DataComponentTypes.CUSTOM_DATA)?.nbt?.toPrettyString() ?: "<empty>"
+ val nbt = item.get(DataComponents.CUSTOM_DATA)?.unsafeNbt?.toPrettyString() ?: "<empty>"
ClipboardUtils.setTextContent(nbt)
- lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.nbt"))
+ lastCopiedStack = Pair(item, Component.translatable("firmament.tooltip.copied.nbt"))
} else if (it.matches(TConfig.copyLoreData)) {
val list = mutableListOf(item.displayNameAccordingToNbt)
list.addAll(item.loreAccordingToNbt)
ClipboardUtils.setTextContent(list.joinToString("\n") {
- TextCodecs.CODEC.encodeStart(JsonOps.INSTANCE, it).result().getOrNull().toString()
+ ComponentSerialization.CODEC.encodeStart(JsonOps.INSTANCE, it).result().getOrNull().toString()
})
- lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.lore"))
+ lastCopiedStack = Pair(item, Component.translatable("firmament.tooltip.copied.lore"))
} else if (it.matches(TConfig.copySkullTexture)) {
if (item.item != Items.PLAYER_HEAD) {
- lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-skull"))
+ lastCopiedStack = Pair(item, Component.translatable("firmament.tooltip.copied.skull-id.fail.no-skull"))
return
}
- val profile = item.get(DataComponentTypes.PROFILE)
+ val profile = item.get(DataComponents.PROFILE)
if (profile == null) {
- lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
+ lastCopiedStack = Pair(item, Component.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
return
}
- val skullTexture = CustomSkyBlockTextures.getSkullTexture(profile)
+ val skullTexture = getSkullId(profile)
if (skullTexture == null) {
- lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture"))
+ lastCopiedStack = Pair(item, Component.translatable("firmament.tooltip.copied.skull-id.fail.no-texture"))
return
}
ClipboardUtils.setTextContent(skullTexture.toString())
lastCopiedStack =
- Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()))
+ Pair(item, Component.translatableEscape("firmament.tooltip.copied.skull-id", skullTexture.toString()))
println("Copied skull id: $skullTexture")
} else if (it.matches(TConfig.copyItemStack)) {
- ClipboardUtils.setTextContent(
- ItemStack.CODEC
- .encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item)
- .orThrow.toPrettyString())
- lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
+ val nbt = ItemStack.CODEC
+ .encodeStart(MC.currentOrDefaultRegistries.createSerializationContext(NbtOps.INSTANCE), item)
+ .orThrow
+ ClipboardUtils.setTextContent(nbt.toPrettyString())
+ lastCopiedStack = Pair(item, Component.translatableEscape("firmament.tooltip.copied.stack"))
+ } else if (it.matches(TConfig.copyTitle) && it.screen is AbstractContainerScreen<*>) {
+ val allTitles = ListTag()
+ val inventoryNames =
+ it.screen.menu.slots
+ .mapNotNullTo(mutableSetOf()) { it.container }
+ .filterIsInstance<Nameable>()
+ .map { it.name }
+ for (it in listOf(it.screen.title) + inventoryNames) {
+ allTitles.add(ComponentSerialization.CODEC.encodeStart(NbtOps.INSTANCE, it).result().getOrNull()!!)
+ }
+ ClipboardUtils.setTextContent(allTitles.toPrettyString())
+ MC.sendChat(tr("firmament.power-user.title.copied", "Copied screen and inventory titles"))
}
}
@@ -168,23 +222,23 @@ object PowerUserTools : FirmamentFeature {
fun onCopyWorldInfo(it: WorldKeyboardEvent) {
if (it.matches(TConfig.copySkullTexture)) {
val p = MC.camera ?: return
- val blockHit = p.raycast(20.0, 0.0f, false) ?: return
+ val blockHit = p.pick(20.0, 0.0f, false) ?: return
if (blockHit.type != HitResult.Type.BLOCK || blockHit !is BlockHitResult) {
- MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
+ MC.sendChat(Component.translatable("firmament.tooltip.copied.skull.fail"))
return
}
- val blockAt = p.world.getBlockState(blockHit.blockPos)?.block
- val entity = p.world.getBlockEntity(blockHit.blockPos)
- if (blockAt !is SkullBlock || entity !is SkullBlockEntity || entity.owner == null) {
- MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
+ val blockAt = p.level.getBlockState(blockHit.blockPos)?.block
+ val entity = p.level.getBlockEntity(blockHit.blockPos)
+ if (blockAt !is SkullBlock || entity !is SkullBlockEntity || entity.ownerProfile == null) {
+ MC.sendChat(Component.translatable("firmament.tooltip.copied.skull.fail"))
return
}
- val id = CustomSkyBlockTextures.getSkullTexture(entity.owner!!)
+ val id = getSkullId(entity.ownerProfile!!)
if (id == null) {
- MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
+ MC.sendChat(Component.translatable("firmament.tooltip.copied.skull.fail"))
} else {
ClipboardUtils.setTextContent(id.toString())
- MC.sendChat(Text.stringifiedTranslatable("firmament.tooltip.copied.skull", id.toString()))
+ MC.sendChat(Component.translatableEscape("firmament.tooltip.copied.skull", id.toString()))
}
}
}
@@ -193,14 +247,14 @@ object PowerUserTools : FirmamentFeature {
fun addItemId(it: ItemTooltipEvent) {
if (TConfig.showItemIds) {
val id = it.stack.skyBlockId ?: return
- it.lines.add(Text.stringifiedTranslatable("firmament.tooltip.skyblockid", id.neuItem))
+ it.lines.add(Component.translatableEscape("firmament.tooltip.skyblockid", id.neuItem).grey())
}
val (item, text) = lastCopiedStack ?: return
- if (!ItemStack.areEqual(item, it.stack)) {
+ if (!ItemStack.matches(item, it.stack)) {
lastCopiedStack = null
return
}
- lastCopiedStackViewTime = true
+ lastCopiedStackViewTime = 0
it.lines.add(text)
}
diff --git a/src/main/kotlin/features/debug/SkinPreviews.kt b/src/main/kotlin/features/debug/SkinPreviews.kt
new file mode 100644
index 0000000..56c63db
--- /dev/null
+++ b/src/main/kotlin/features/debug/SkinPreviews.kt
@@ -0,0 +1,91 @@
+package moe.nea.firmament.features.debug
+
+import com.mojang.authlib.GameProfile
+import kotlinx.serialization.json.JsonPrimitive
+import kotlinx.serialization.json.buildJsonObject
+import kotlinx.serialization.json.put
+import kotlin.time.Duration.Companion.seconds
+import net.minecraft.core.component.DataComponents
+import net.minecraft.world.item.component.ResolvableProfile
+import net.minecraft.world.entity.EquipmentSlot
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.phys.Vec3
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.EntityUpdateEvent
+import moe.nea.firmament.events.IsSlotProtectedEvent
+import moe.nea.firmament.util.ClipboardUtils
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.TimeMark
+import moe.nea.firmament.util.extraAttributes
+import moe.nea.firmament.util.json.toJsonArray
+import moe.nea.firmament.util.math.GChainReconciliation.shortenCycle
+import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.mc.loreAccordingToNbt
+import moe.nea.firmament.util.rawSkyBlockId
+import moe.nea.firmament.util.toTicks
+import moe.nea.firmament.util.tr
+
+
+object SkinPreviews {
+
+ // TODO: add pet support
+ @Subscribe
+ fun onEntityUpdate(event: EntityUpdateEvent) {
+ if (!isRecording) return
+ if (event.entity.position != pos)
+ return
+ val entity = event.entity as? LivingEntity ?: return
+ val stack = entity.getItemBySlot(EquipmentSlot.HEAD) ?: return
+ val profile = stack.get(DataComponents.PROFILE)?.partialProfile() ?: return
+ if (profile == animation.lastOrNull()) return
+ animation.add(profile)
+ val shortened = animation.shortenCycle()
+ if (shortened.size <= (animation.size / 2).coerceAtLeast(1) && lastDiscard.passedTime() > 2.seconds) {
+ val tickEstimation = (lastDiscard.passedTime() / animation.size).toTicks()
+ val skin