diff options
Diffstat (limited to 'src/main/kotlin')
-rw-r--r-- | src/main/kotlin/features/debug/AnimatedClothingScanner.kt | 94 | ||||
-rw-r--r-- | src/main/kotlin/util/MC.kt | 18 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/NbtPrism.kt | 91 |
3 files changed, 186 insertions, 17 deletions
diff --git a/src/main/kotlin/features/debug/AnimatedClothingScanner.kt b/src/main/kotlin/features/debug/AnimatedClothingScanner.kt index 11b47a9..d0db252 100644 --- a/src/main/kotlin/features/debug/AnimatedClothingScanner.kt +++ b/src/main/kotlin/features/debug/AnimatedClothingScanner.kt @@ -1,49 +1,109 @@ 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.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 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.util.ClipboardUtils import moe.nea.firmament.util.MC +import moe.nea.firmament.util.mc.NbtPrism import moe.nea.firmament.util.skyBlockId import moe.nea.firmament.util.tr object AnimatedClothingScanner { - var observedEntity: Entity? = null + data class SubjectOfFashionTheft<T>( + val observedEntity: Entity, + 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 subject: SubjectOfFashionTheft<*>? = null @OptIn(ExperimentalStdlibApi::class) @Subscribe fun onUpdate(event: EntityUpdateEvent) { - if (event.entity != observedEntity) return + val s = subject ?: return + if (event.entity != s.observedEntity) return if (event is EntityUpdateEvent.EquipmentUpdate) { + val lines = mutableListOf<String>() 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 = (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 EXPORT_WATERMARK = "[CLOTHES EXPORT]" + @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."), - ) + thenArgument( + "component", + RegistryKeyArgumentType.registryKey(RegistryKeys.DATA_COMPONENT_TYPE) + ) { component -> + thenArgument("path", NbtPrism.Argument) { path -> + thenExecute { + subject = + if (subject == null) run { + val entity = MC.instance.targetedEntity ?: return@run null + val clipboard = ClipboardUtils.getTextContents() + MC.instance.entit + 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 + + MC.sendChat( + subject?.let { + tr( + "firmament.fitstealer.targeted", + "Observing the equipment of ${it.observedEntity.name}." + ) + } ?: tr("firmament.fitstealer.targetlost", "No longer logging equipment."), + ) + } + } } } } diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt index 0f45e13..a31d181 100644 --- a/src/main/kotlin/util/MC.kt +++ b/src/main/kotlin/util/MC.kt @@ -2,6 +2,7 @@ package moe.nea.firmament.util import io.github.moulberry.repo.data.Coordinate import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.jvm.optionals.getOrNull import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.hud.InGameHud import net.minecraft.client.gui.screen.Screen @@ -16,10 +17,14 @@ import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket import net.minecraft.registry.BuiltinRegistries +import net.minecraft.registry.Registry +import net.minecraft.registry.RegistryKey import net.minecraft.registry.RegistryKeys import net.minecraft.registry.RegistryWrapper +import net.minecraft.registry.entry.RegistryEntry import net.minecraft.resource.ReloadableResourceManagerImpl import net.minecraft.text.Text +import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.world.World import moe.nea.firmament.events.TickEvent @@ -120,6 +125,19 @@ object MC { return field } private set + + + fun <T> unsafeGetRegistryEntry(registry: RegistryKey<out Registry<T>>, identifier: Identifier) = + unsafeGetRegistryEntry(RegistryKey.of(registry, identifier)) + + + fun <T> unsafeGetRegistryEntry(registryKey: RegistryKey<T>): T? { + return currentOrDefaultRegistries + .getOrThrow(registryKey.registryRef) + .getOptional(registryKey) + .getOrNull() + ?.value() + } } diff --git a/src/main/kotlin/util/mc/NbtPrism.kt b/src/main/kotlin/util/mc/NbtPrism.kt new file mode 100644 index 0000000..f034210 --- /dev/null +++ b/src/main/kotlin/util/mc/NbtPrism.kt @@ -0,0 +1,91 @@ +package moe.nea.firmament.util.mc + +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonPrimitive +import com.mojang.brigadier.StringReader +import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.arguments.StringArgumentType +import com.mojang.brigadier.context.CommandContext +import com.mojang.brigadier.suggestion.Suggestions +import com.mojang.brigadier.suggestion.SuggestionsBuilder +import com.mojang.serialization.JsonOps +import java.util.concurrent.CompletableFuture +import kotlin.collections.indices +import kotlin.collections.map +import kotlin.jvm.optionals.getOrNull +import net.minecraft.nbt.NbtCompound +import net.minecraft.nbt.NbtElement +import net.minecraft.nbt.NbtList +import net.minecraft.nbt.NbtOps +import net.minecraft.nbt.NbtString +import moe.nea.firmament.util.Base64Util + +class NbtPrism(val path: List<String>) { + companion object { + fun fromElement(path: JsonElement): NbtPrism? { + if (path is JsonArray) { + return NbtPrism(path.map { (it as JsonPrimitive).asString }) + } else if (path is JsonPrimitive && path.isString) { + return NbtPrism(path.asString.split(".")) + } + return null + } + } + + object Argument : ArgumentType<NbtPrism> { + override fun parse(reader: StringReader): NbtPrism? { + return fromElement(JsonPrimitive(StringArgumentType.string().parse(reader))) + } + + override fun getExamples(): Collection<String?>? { + return listOf("some.nbt.path", "some.other.*", "some.path.*json.in.a.json.string") + } + } + + override fun toString(): String { + return "Prism($path)" + } + + fun access(root: NbtElement): Collection<NbtElement> { + var rootSet = mutableListOf(root) + var switch = mutableListOf<NbtElement>() + for (pathSegment in path) { + if (pathSegment == ".") continue + if (pathSegment != "*" && pathSegment.startsWith("*")) { + if (pathSegment == "*json") { + for (element in rootSet) { + val eString = element.asString().getOrNull() ?: continue + val element = Gson().fromJson(eString, JsonElement::class.java) + switch.add(JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, element)) + } + } else if (pathSegment == "*base64") { + for (element in rootSet) { + val string = element.asString().getOrNull() ?: continue + switch.add(NbtString.of(Base64Util.decodeString(string))) + } + } + } + for (element in rootSet) { + if (element is NbtList) { + if (pathSegment == "*") + switch.addAll(element) + val index = pathSegment.toIntOrNull() ?: continue + if (index !in element.indices) continue + switch.add(element[index]) + } + if (element is NbtCompound) { + if (pathSegment == "*") + element.keys.mapTo(switch) { element.get(it)!! } + switch.add(element.get(pathSegment) ?: continue) + } + } + val temp = switch + switch = rootSet + rootSet = temp + switch.clear() + } + return rootSet + } +} |