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.kt180
-rw-r--r--src/main/kotlin/features/debug/DebugLogger.kt2
-rw-r--r--src/main/kotlin/features/debug/DeveloperFeatures.kt43
-rw-r--r--src/main/kotlin/features/debug/ExportedTestConstantMeta.kt19
-rw-r--r--src/main/kotlin/features/debug/PowerUserTools.kt35
5 files changed, 247 insertions, 32 deletions
diff --git a/src/main/kotlin/features/debug/AnimatedClothingScanner.kt b/src/main/kotlin/features/debug/AnimatedClothingScanner.kt
index 11b47a9..9f9f135 100644
--- a/src/main/kotlin/features/debug/AnimatedClothingScanner.kt
+++ b/src/main/kotlin/features/debug/AnimatedClothingScanner.kt
@@ -1,51 +1,193 @@
package moe.nea.firmament.features.debug
-import net.minecraft.component.DataComponentTypes
+import net.minecraft.command.argument.RegistryKeyArgumentType
+import net.minecraft.component.ComponentType
import net.minecraft.entity.Entity
+import net.minecraft.entity.decoration.ArmorStandEntity
+import net.minecraft.item.ItemStack
+import net.minecraft.nbt.NbtElement
+import net.minecraft.nbt.NbtOps
+import net.minecraft.registry.RegistryKeys
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.skyBlockId
+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 {
- var observedEntity: Entity? = null
+ data class LensOfFashionTheft<T>(
+ val prism: NbtPrism,
+ val component: ComponentType<T>,
+ ) {
+ fun observe(itemStack: ItemStack): Collection<NbtElement> {
+ 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) {
- if (event.entity != observedEntity) return
+ val s = subject ?: return
+ if (event.entity != s) return
+ val l = lens ?: return
if (event is EntityUpdateEvent.EquipmentUpdate) {
event.newEquipment.forEach {
- val id = it.second.skyBlockId?.neuItem
- val colour = it.second.get(DataComponentTypes.DYED_COLOR)
- ?.rgb?.toHexString(HexFormat.UpperCase)
- ?.let { " #$it" } ?: ""
- MC.sendChat(tr("firmament.fitstealer.update",
- "[FIT CHECK][${MC.currentTick}] ${it.first.asString()} => ${id}${colour}"))
+ 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("dev") {
thenLiteral("stealthisfit") {
- thenExecute {
- observedEntity =
- if (observedEntity == null) MC.instance.targetedEntity else null
-
- MC.sendChat(
- observedEntity?.let {
- tr("firmament.fitstealer.targeted", "Observing the equipment of ${it.name}.")
- } ?: tr("firmament.fitstealer.targetlost", "No longer logging equipment."),
- )
+ 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.world.getEntitiesByClass(
+ ArmorStandEntity::class.java,
+ p.boundingBox.expand(10.0),
+ { it.isMarker })
+ .minBy { it.squaredDistanceTo(p) }
+ toggleObserve(nearestPet)
+ }
+ }
+ thenExecute {
+ val ent = MC.instance.targetedEntity
+ if (ent == null) {
+ source.sendFeedback(
+ tr(
+ "firmament.fitstealer.notargetundercursor",
+ "No entity under cursor"
+ )
+ )
+ } else {
+ toggleObserve(ent)
+ }
+ }
+ }
+ thenLiteral("path") {
+ thenArgument(
+ "component",
+ RegistryKeyArgumentType.registryKey(RegistryKeys.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).value}"
+ )
+ )
+ }
+ }
+ }
}
}
}
}
+
+ 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..9115956 100644
--- a/src/main/kotlin/features/debug/DebugLogger.kt
+++ b/src/main/kotlin/features/debug/DebugLogger.kt
@@ -10,6 +10,7 @@ class DebugLogger(val tag: String) {
companion object {
val allInstances = InstanceList<DebugLogger>("DebugLogger")
}
+
object EnabledLogs : DataHolder<MutableSet<String>>(serializer(), "DebugLogs", ::mutableSetOf)
init {
@@ -17,6 +18,7 @@ class DebugLogger(val tag: String) {
}
fun isEnabled() = DeveloperFeatures.isEnabled && EnabledLogs.data.contains(tag)
+ fun log(text: String) = log { text }
fun log(text: () -> String) {
if (!isEnabled()) return
MC.sendChat(Text.literal(text()))
diff --git a/src/main/kotlin/features/debug/DeveloperFeatures.kt b/src/main/kotlin/features/debug/DeveloperFeatures.kt
index 8f0c25c..af1e92e 100644
--- a/src/main/kotlin/features/debug/DeveloperFeatures.kt
+++ b/src/main/kotlin/features/debug/DeveloperFeatures.kt
@@ -3,6 +3,10 @@ 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
@@ -10,11 +14,14 @@ import net.minecraft.client.MinecraftClient
import net.minecraft.text.Text
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.iterate
object DeveloperFeatures : FirmamentFeature {
@@ -42,6 +49,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
diff --git a/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt b/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt
new file mode 100644
index 0000000..a817dd6
--- /dev/null
+++ b/src/main/kotlin/features/debug/ExportedTestConstantMeta.kt
@@ -0,0 +1,19 @@
+package moe.nea.firmament.features.debug
+
+import com.mojang.serialization.Codec
+import com.mojang.serialization.codecs.RecordCodecBuilder
+import java.util.Optional
+
+data class ExportedTestConstantMeta(
+ val dataVersion: Int,
+ val modVersion: Optional<String>,
+) {
+ companion object {
+ 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)
+ }
+ }
+}
diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt
index 8be5d5d..893b176 100644
--- a/src/main/kotlin/features/debug/PowerUserTools.kt
+++ b/src/main/kotlin/features/debug/PowerUserTools.kt
@@ -1,31 +1,25 @@
package moe.nea.firmament.features.debug
-import com.mojang.serialization.Codec
-import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
-import com.mojang.serialization.codecs.RecordCodecBuilder
import kotlin.jvm.optionals.getOrNull
import net.minecraft.block.SkullBlock
import net.minecraft.block.entity.SkullBlockEntity
import net.minecraft.component.DataComponentTypes
import net.minecraft.component.type.ProfileComponent
import net.minecraft.entity.Entity
-import net.minecraft.entity.EntityType
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
-import net.minecraft.nbt.NbtCompound
+import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtOps
-import net.minecraft.nbt.NbtString
import net.minecraft.predicate.NbtPredicate
import net.minecraft.text.Text
import net.minecraft.text.TextCodecs
import net.minecraft.util.Identifier
+import net.minecraft.util.Nameable
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.hit.HitResult
-import net.minecraft.util.math.Position
-import net.minecraft.util.math.Vec3d
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.CustomItemModelEvent
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
@@ -43,8 +37,10 @@ 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.skyBlockId
+import moe.nea.firmament.util.tr
object PowerUserTools : FirmamentFeature {
override val identifier: String
@@ -59,6 +55,7 @@ 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")
}
override val config
@@ -108,7 +105,7 @@ object PowerUserTools : FirmamentFeature {
MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.position", target.pos))
if (target is LivingEntity) {
MC.sendChat(Text.translatable("firmament.poweruser.entity.armor"))
- for (armorItem in target.armorItems) {
+ for ((slot, armorItem) in target.iterableArmorItems) {
MC.sendChat(Text.translatable("firmament.poweruser.entity.armor.item", debugFormat(armorItem)))
}
}
@@ -179,11 +176,23 @@ object PowerUserTools : FirmamentFeature {
Pair(item, Text.stringifiedTranslatable("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())
+ val nbt = ItemStack.CODEC
+ .encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item)
+ .orThrow
+ ClipboardUtils.setTextContent(nbt.toPrettyString())
lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
+ } else if (it.matches(TConfig.copyTitle)) {
+ val allTitles = NbtList()
+ val inventoryNames =
+ it.screen.screenHandler.slots
+ .mapNotNullTo(mutableSetOf()) { it.inventory }
+ .filterIsInstance<Nameable>()
+ .map { it.name }
+ for (it in listOf(it.screen.title) + inventoryNames) {
+ allTitles.add(TextCodecs.CODEC.encodeStart(NbtOps.INSTANCE, it).result().getOrNull()!!)
+ }
+ ClipboardUtils.setTextContent(allTitles.toPrettyString())
+ MC.sendChat(tr("firmament.power-user.title.copied", "Copied screen and inventory titles"))
}
}