aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/features')
-rw-r--r--src/main/kotlin/features/chat/ChatLinks.kt11
-rw-r--r--src/main/kotlin/features/debug/AnimatedClothingScanner.kt94
-rw-r--r--src/main/kotlin/features/debug/DeveloperFeatures.kt43
-rw-r--r--src/main/kotlin/features/debug/PowerUserTools.kt3
-rw-r--r--src/main/kotlin/features/events/carnival/MinesweeperHelper.kt4
-rw-r--r--src/main/kotlin/features/inventory/REIDependencyWarner.kt3
-rw-r--r--src/main/kotlin/features/inventory/SlotLocking.kt7
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt2
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/VirtualInventory.kt7
-rw-r--r--src/main/kotlin/features/mining/PickaxeAbility.kt4
10 files changed, 144 insertions, 34 deletions
diff --git a/src/main/kotlin/features/chat/ChatLinks.kt b/src/main/kotlin/features/chat/ChatLinks.kt
index f85825b..a084234 100644
--- a/src/main/kotlin/features/chat/ChatLinks.kt
+++ b/src/main/kotlin/features/chat/ChatLinks.kt
@@ -3,6 +3,7 @@ package moe.nea.firmament.features.chat
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsChannel
import io.ktor.utils.io.jvm.javaio.toInputStream
+import java.net.URI
import java.net.URL
import java.util.Collections
import java.util.concurrent.atomic.AtomicInteger
@@ -78,7 +79,7 @@ object ChatLinks : FirmamentFeature {
val texId = Firmament.identifier("dynamic_image_preview${nextTexId.getAndIncrement()}")
MC.textureManager.registerTexture(
texId,
- NativeImageBackedTexture(image)
+ NativeImageBackedTexture({ texId.path }, image)
)
Image(texId, image.width, image.height)
} else
@@ -102,8 +103,8 @@ object ChatLinks : FirmamentFeature {
if (it.screen !is ChatScreen) return
val hoveredComponent =
MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
- val hoverEvent = hoveredComponent.hoverEvent ?: return
- val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
+ val hoverEvent = hoveredComponent.hoverEvent as? HoverEvent.ShowText ?: return
+ val value = hoverEvent.value
val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
if (!isImageUrl(url)) return
val imageFuture = imageCache[url] ?: return
@@ -149,8 +150,8 @@ object ChatLinks : FirmamentFeature {
Text.literal(url).setStyle(
Style.EMPTY.withUnderline(true).withColor(
Formatting.AQUA
- ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
- .withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
+ ).withHoverEvent(HoverEvent.ShowText(Text.literal(url)))
+ .withClickEvent(ClickEvent.OpenUrl(URI(url)))
)
)
if (isImageUrl(url))
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/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/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt
index 8be5d5d..251fc8b 100644
--- a/src/main/kotlin/features/debug/PowerUserTools.kt
+++ b/src/main/kotlin/features/debug/PowerUserTools.kt
@@ -43,6 +43,7 @@ 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
@@ -108,7 +109,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)))
}
}
diff --git a/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt b/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt
index 1824225..cfc05cc 100644
--- a/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt
+++ b/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt
@@ -222,7 +222,7 @@ object MinesweeperHelper {
fun onChat(event: ProcessChatEvent) {
if (CarnivalFeatures.TConfig.displayTutorials && event.unformattedString == startGameQuestion) {
MC.sendChat(Text.translatable("firmament.carnival.tutorial.minesweeper").styled {
- it.withClickEvent(ClickEvent(ClickEvent.Action.RUN_COMMAND, "/firm minesweepertutorial"))
+ it.withClickEvent(ClickEvent.RunCommand("/firm minesweepertutorial"))
})
}
if (!CarnivalFeatures.TConfig.enableBombSolver) {
@@ -259,7 +259,7 @@ object MinesweeperHelper {
val boardPosition = BoardPosition.fromBlockPos(event.blockPos)
log.log { "Breaking block at ${event.blockPos} ($boardPosition)" }
gs.lastClickedPosition = boardPosition
- gs.lastDowsingMode = DowsingMode.fromItem(event.player.inventory.mainHandStack)
+ gs.lastDowsingMode = DowsingMode.fromItem(event.player.mainHandStack)
}
@Subscribe
diff --git a/src/main/kotlin/features/inventory/REIDependencyWarner.kt b/src/main/kotlin/features/inventory/REIDependencyWarner.kt
index 1e9b1b8..7d88dd1 100644
--- a/src/main/kotlin/features/inventory/REIDependencyWarner.kt
+++ b/src/main/kotlin/features/inventory/REIDependencyWarner.kt
@@ -1,5 +1,6 @@
package moe.nea.firmament.features.inventory
+import java.net.URI
import net.fabricmc.loader.api.FabricLoader
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -38,7 +39,7 @@ object REIDependencyWarner {
.white()
.append(Text.literal("[").aqua())
.append(Text.translatable("firmament.download", modName)
- .styled { it.withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, modrinthLink(slug))) }
+ .styled { it.withClickEvent(ClickEvent.OpenUrl(URI (modrinthLink(slug)))) }
.yellow()
.also {
if (alreadyDownloaded)
diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt
index 0083c40..d3348a2 100644
--- a/src/main/kotlin/features/inventory/SlotLocking.kt
+++ b/src/main/kotlin/features/inventory/SlotLocking.kt
@@ -4,6 +4,7 @@ package moe.nea.firmament.features.inventory
import java.util.UUID
import org.lwjgl.glfw.GLFW
+import util.render.CustomRenderLayers
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@@ -17,6 +18,9 @@ import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.int
import kotlinx.serialization.serializer
import net.minecraft.client.gui.screen.ingame.HandledScreen
+import net.minecraft.client.render.RenderLayer
+import net.minecraft.client.render.RenderLayers
+import net.minecraft.client.render.TexturedRenderLayers
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.screen.GenericContainerScreenHandler
import net.minecraft.screen.slot.Slot
@@ -45,7 +49,6 @@ import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex
import moe.nea.firmament.util.mc.SlotUtils.swapWithHotBar
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
-import moe.nea.firmament.util.render.GuiRenderLayers
import moe.nea.firmament.util.render.drawLine
import moe.nea.firmament.util.skyblock.DungeonUtil
import moe.nea.firmament.util.skyblockUUID
@@ -445,7 +448,7 @@ object SlotLocking : FirmamentFeature {
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
if (isSlotLocked || isUUIDLocked) {
it.context.drawGuiTexture(
- GuiRenderLayers.GUI_TEXTURED_NO_DEPTH,
+ RenderLayer::getGuiTexturedOverlay,
when {
isSlotLocked ->
(Identifier.of("firmament:slot_locked"))
diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
index 63a2f54..22f4dab 100644
--- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
+++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
@@ -218,7 +218,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
}
fun drawPlayerInventory(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
- val items = MC.player?.inventory?.main ?: return
+ val items = MC.player?.inventory?.mainStacks ?: return
items.withIndex().forEach { (index, item) ->
val (x, y) = getPlayerInventorySlotPosition(index)
context.drawItem(item, x, y, 0)
diff --git a/src/main/kotlin/features/inventory/storageoverlay/VirtualInventory.kt b/src/main/kotlin/features/inventory/storageoverlay/VirtualInventory.kt
index 3b86184..d99acd7 100644
--- a/src/main/kotlin/features/inventory/storageoverlay/VirtualInventory.kt
+++ b/src/main/kotlin/features/inventory/storageoverlay/VirtualInventory.kt
@@ -11,6 +11,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
+import kotlin.jvm.optionals.getOrNull
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtIo
@@ -42,15 +43,15 @@ data class VirtualInventory(
override fun deserialize(decoder: Decoder): VirtualInventory {
val s = decoder.decodeString()
val n = NbtIo.readCompressed(ByteArrayInputStream(s.decodeBase64Bytes()), NbtSizeTracker.of(100_000_000))
- val items = n.getList(INVENTORY, NbtCompound.COMPOUND_TYPE.toInt())
+ val items = n.getList(INVENTORY).getOrNull()
val ops = getOps()
- return VirtualInventory(items.map {
+ return VirtualInventory(items?.map {
it as NbtCompound
if (it.isEmpty) ItemStack.EMPTY
else ErrorUtil.catch("Could not deserialize item") {
ItemStack.CODEC.parse(ops, it).orThrow
}.or { ItemStack.EMPTY }
- })
+ } ?: listOf())
}
fun getOps() = TolerantRegistriesOps(NbtOps.INSTANCE, MC.currentOrDefaultRegistries)
diff --git a/src/main/kotlin/features/mining/PickaxeAbility.kt b/src/main/kotlin/features/mining/PickaxeAbility.kt
index 8c07e39..d39694a 100644
--- a/src/main/kotlin/features/mining/PickaxeAbility.kt
+++ b/src/main/kotlin/features/mining/PickaxeAbility.kt
@@ -1,6 +1,7 @@
package moe.nea.firmament.features.mining
import java.util.regex.Pattern
+import kotlin.jvm.optionals.getOrNull
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import net.minecraft.client.MinecraftClient
@@ -144,8 +145,7 @@ object PickaxeAbility : FirmamentFeature {
}
} ?: return
val extra = it.item.extraAttributes
- if (!extra.contains("drill_fuel")) return
- val fuel = extra.getInt("drill_fuel")
+ val fuel = extra.getInt("drill_fuel").getOrNull() ?: return
val percentage = fuel / maxFuel.toFloat()
it.barOverride = DurabilityBarEvent.DurabilityBar(
lerp(