feat: Add more complex entity equipment scraper

This commit is contained in:
Linnea Gräf
2025-05-07 23:09:10 +02:00
parent 38fd61fdcc
commit 63669bc28b
5 changed files with 349 additions and 56 deletions

View File

@@ -2,13 +2,12 @@ package moe.nea.firmament.features.debug
import net.minecraft.command.argument.RegistryKeyArgumentType
import net.minecraft.component.ComponentType
import net.minecraft.component.DataComponentTypes
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 net.minecraft.util.Identifier
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.commands.get
import moe.nea.firmament.commands.thenArgument
@@ -16,16 +15,17 @@ 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.skyBlockId
import moe.nea.firmament.util.tr
object AnimatedClothingScanner {
data class SubjectOfFashionTheft<T>(
val observedEntity: Entity,
data class LensOfFashionTheft<T>(
val prism: NbtPrism,
val component: ComponentType<T>,
) {
@@ -36,75 +36,158 @@ object AnimatedClothingScanner {
}
}
var subject: SubjectOfFashionTheft<*>? = null
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.observedEntity) return
if (event.entity != s) return
val l = lens ?: return
if (event is EntityUpdateEvent.EquipmentUpdate) {
val lines = mutableListOf<String>()
event.newEquipment.forEach {
val formatted = (s.observe(it.second)).joinToString()
lines.add(formatted)
MC.sendChat(
tr(
"firmament.fitstealer.update",
"[FIT CHECK][${MC.currentTick}] ${it.first.asString()} => $formatted"
)
)
}
if (lines.isNotEmpty()) {
val contents = ClipboardUtils.getTextContents()
if (contents.startsWith(EXPORT_WATERMARK))
ClipboardUtils.setTextContent(
contents + "\n" + lines.joinToString("\n")
)
val formatted = (l.observe(it.second)).joinToString()
history.add(formatted)
// TODO: add a slot filter
}
}
}
val EXPORT_WATERMARK = "[CLOTHES EXPORT]"
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") {
thenArgument(
"component",
RegistryKeyArgumentType.registryKey(RegistryKeys.DATA_COMPONENT_TYPE)
) { component ->
thenArgument("path", NbtPrism.Argument) { path ->
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 {
subject =
if (subject == null) run {
val entity = MC.instance.targetedEntity ?: return@run null
val clipboard = ClipboardUtils.getTextContents()
if (!clipboard.startsWith(EXPORT_WATERMARK)) {
ClipboardUtils.setTextContent(EXPORT_WATERMARK)
} else {
ClipboardUtils.setTextContent("$clipboard\n\n[NEW SCANNER]")
}
SubjectOfFashionTheft(
entity,
get(path),
MC.unsafeGetRegistryEntry(get(component))!!,
)
} else null
val history = reduceHistory { a, b ->
(a.toMutableSet() + b).toList()
}
copyHistory(history)
MC.sendChat(
subject?.let {
tr(
"firmament.fitstealer.targeted",
"Observing the equipment of ${it.observedEntity.name}."
)
} ?: tr("firmament.fitstealer.targetlost", "No longer logging equipment."),
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."),
)
}
}