diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | build.gradle.kts | 3 | ||||
-rw-r--r-- | src/main/java/moe/nea/ultranotifier/mixin/AccessorChatHud.java | 11 | ||||
-rw-r--r-- | src/main/java/moe/nea/ultranotifier/mixin/ChatHudCategoryTracker.java | 42 | ||||
-rw-r--r-- | src/main/java/moe/nea/ultranotifier/mixin/ChatScreenTabRenderer.java | 61 | ||||
-rw-r--r-- | src/main/kotlin/datamodel/ChatType.kt | 100 | ||||
-rw-r--r-- | src/main/kotlin/gui/ChatUi.kt | 89 | ||||
-rw-r--r-- | src/main/kotlin/util/minecrat/MC.kt | 11 | ||||
-rw-r--r-- | src/main/kotlin/util/minecrat/TextUtil.kt | 1 | ||||
-rw-r--r-- | src/main/kotlin/util/minecrat/infer.kt | 15 | ||||
-rw-r--r-- | src/main/kotlin/util/render/ScreenRenderUtils.kt | 33 | ||||
-rw-r--r-- | src/main/resources/fabric.mod.json | 3 | ||||
-rw-r--r-- | versions/mapping-1.16.2-forge-1.12.2.txt | 2 |
13 files changed, 369 insertions, 3 deletions
@@ -6,3 +6,4 @@ versions/*/build/ versions/*/run/ .env .properties +.kotlin diff --git a/build.gradle.kts b/build.gradle.kts index 5d1ad4d..8a6a887 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -79,6 +79,7 @@ dependencies { modImplementation("net.fabricmc.fabric-api:fabric-api:${version.fabricVersion!!}") runtimeOnly("me.djtheredstoner:DevAuth-fabric:$devauthVersion") } + runtimeOnly("com.google.guava:guava:17.0") shadowImpl("com.github.therealbush:eventbus:1.0.2") include(version.universalCraft) shadowImpl("io.github.juuxel:libninepatch:1.2.0") @@ -91,7 +92,7 @@ dependencies { } annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT") annotationProcessor("com.google.code.gson:gson:2.10.1") - annotationProcessor("com.google.guava:guava:17.0") + annotationProcessor("com.google.guava:guava:33.4.0-jre") } } diff --git a/src/main/java/moe/nea/ultranotifier/mixin/AccessorChatHud.java b/src/main/java/moe/nea/ultranotifier/mixin/AccessorChatHud.java new file mode 100644 index 0000000..25efbfe --- /dev/null +++ b/src/main/java/moe/nea/ultranotifier/mixin/AccessorChatHud.java @@ -0,0 +1,11 @@ +package moe.nea.ultranotifier.mixin; + +import net.minecraft.client.gui.hud.ChatHud; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ChatHud.class) +public interface AccessorChatHud { + @Invoker("getLineHeight") + int getLineHeight_ultranotifier(); +} diff --git a/src/main/java/moe/nea/ultranotifier/mixin/ChatHudCategoryTracker.java b/src/main/java/moe/nea/ultranotifier/mixin/ChatHudCategoryTracker.java new file mode 100644 index 0000000..4d31bde --- /dev/null +++ b/src/main/java/moe/nea/ultranotifier/mixin/ChatHudCategoryTracker.java @@ -0,0 +1,42 @@ +package moe.nea.ultranotifier.mixin; + +import moe.nea.ultranotifier.datamodel.CategorizedChatLine; +import moe.nea.ultranotifier.datamodel.ChatCategoryArbiter; +import moe.nea.ultranotifier.datamodel.HasCategorizedChatLine; +import net.minecraft.client.gui.hud.ChatHudLine; +import net.minecraft.client.gui.hud.MessageIndicator; +import net.minecraft.network.message.MessageSignatureData; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChatHudLine.class) +public class ChatHudCategoryTracker implements HasCategorizedChatLine { + @Shadow + @Final + private Text content; + + @Unique + CategorizedChatLine categorizedSelf; + + @Inject(method = "<init>", at = @At("TAIL")) + private void onInit( + int creationTick, + Text content, + MessageSignatureData signature, MessageIndicator tag, + CallbackInfo ci + ) { + categorizedSelf = ChatCategoryArbiter.INSTANCE.categorize(content); + } + + @Override + public @NotNull CategorizedChatLine getCategorizedChatLine_ultraNotifier() { + return categorizedSelf; + } +} diff --git a/src/main/java/moe/nea/ultranotifier/mixin/ChatScreenTabRenderer.java b/src/main/java/moe/nea/ultranotifier/mixin/ChatScreenTabRenderer.java new file mode 100644 index 0000000..7a5a127 --- /dev/null +++ b/src/main/java/moe/nea/ultranotifier/mixin/ChatScreenTabRenderer.java @@ -0,0 +1,61 @@ +package moe.nea.ultranotifier.mixin; + +import moe.nea.ultranotifier.gui.ChatUi; +import moe.nea.ultranotifier.util.render.ScreenRenderUtils; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ChatScreen; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +//#endif + +@Mixin(ChatScreen.class) +public abstract class ChatScreenTabRenderer { + + private ChatUi chatUi_ultraNotifier = new ChatUi((ChatScreen) (Object) this); + + @Unique + ChatUi chatUi() {return chatUi_ultraNotifier;} + + @Inject( +//#if MC > 1.16 + method = "render", +//#else +//$$ method="drawScreen", +//#endif + at = @At("HEAD")) + private void onRender( +//#if MC >1.20 + DrawContext context, +//#elseif MC > 1.16 +//$$ MatrixStack context, +//#endif + int mouseX, int mouseY, + float delta, + CallbackInfo ci) { + chatUi().renderButtons( + ScreenRenderUtils.umatrix( + //#if MC > 1.16 + context + //#endif + ), + mouseX, mouseY + ); + } + + @Inject( + method = "mouseClicked", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z", opcode = Opcodes.INVOKESPECIAL) + ) + private void onMouseClick( + double mouseX, double mouseY, + int button, + CallbackInfoReturnable<Boolean> cir + ) { + chatUi().clickMouse(mouseX, mouseY, button); + } +} diff --git a/src/main/kotlin/datamodel/ChatType.kt b/src/main/kotlin/datamodel/ChatType.kt new file mode 100644 index 0000000..1fcc522 --- /dev/null +++ b/src/main/kotlin/datamodel/ChatType.kt @@ -0,0 +1,100 @@ +package moe.nea.ultranotifier.datamodel + +import net.minecraft.text.Text +import java.util.regex.Pattern + +data class ChatTypeId( + val id: String +) + +data class ChatType( + val id: ChatTypeId, + val name: String, + val patterns: List<ChatPattern>, +) + +data class ChatPattern( + val text: String +) { + val pattern = Pattern.compile(text) + val predicate = pattern.asMatchPredicate() +} + +data class ChatCategory( + val label: String, + val chatTypes: Set<ChatTypeId>, +) + +data class ChatUniverse( + val name: String, + val types: List<ChatType>, + val categories: List<ChatCategory>, +) { + fun categorize( + text: String + ): CategorizedChatLine { + val types = this.types + .asSequence() + .filter { + it.patterns.any { + it.predicate.test(text) + } + } + .map { + it.id + } + .toSet() + // TODO: potentially allow recalculating categories on the fly + val categories = categories.filterTo(mutableSetOf()) { it.chatTypes.any { it in types } } + return CategorizedChatLine( + text, types, categories + ) + } +} + +data class CategorizedChatLine( + val text: String, + val types: Set<ChatTypeId>, + val categories: Set<ChatCategory>, +) + +interface HasCategorizedChatLine { + val categorizedChatLine_ultraNotifier: CategorizedChatLine +} + +object ChatCategoryArbiter { + val universe = ChatUniverse( + "Hypixel SkyBlock", + listOf( + ChatType( + ChatTypeId("bazaar"), + "Bazaar", + listOf( + ChatPattern("(?i).*Bazaar.*") + ) + ), + ChatType( + ChatTypeId("auction-house"), + "Auction House", + listOf( + ChatPattern("(?i).*Auction House.*") + ) + ), + ), + listOf( + ChatCategory( + "Economy", + setOf(ChatTypeId("bazaar"), ChatTypeId("auction-house")) + ) + ) + ) + + fun categorize(content: Text): CategorizedChatLine { + universe.categorize(content.lit) + } +} + + + + + diff --git a/src/main/kotlin/gui/ChatUi.kt b/src/main/kotlin/gui/ChatUi.kt new file mode 100644 index 0000000..0ec3063 --- /dev/null +++ b/src/main/kotlin/gui/ChatUi.kt @@ -0,0 +1,89 @@ +package moe.nea.ultranotifier.gui + +import gg.essential.universal.UGraphics +import gg.essential.universal.UMatrixStack +import moe.nea.ultranotifier.util.minecrat.MC +import moe.nea.ultranotifier.util.minecrat.accessor +import moe.nea.ultranotifier.util.render.ScreenRenderUtils +import net.minecraft.client.gui.screen.ChatScreen +import java.awt.Color + +class ChatUi(val chatScreen: ChatScreen) { + + fun getChatBgOpacity(opacityMultiplier: Double = 1.0): Color { + return Color((MC.instance.options.textBackgroundOpacity.value * opacityMultiplier * 255).toInt() shl 24, true) + } + + fun calculateChatTop(): Double { + val ch = MC.chatHud + ch.accessor() + val chatOffset = + 40 + val chatTop = + (chatScreen.height - chatOffset) / ch.chatScale - ch.visibleLineCount * ch.lineHeight_ultranotifier + return chatTop + } + + var selectedTab = "Bazaar" + fun iterateButtons( + onEach: ( + label: String, + isSelected: Boolean, + pos: Rect, + ) -> Unit + ) { + val chatTop = calculateChatTop() + var xOffset = 5 + val top = chatTop - 16.0 + for (button in listOf("Socials", "Bazaar", "Slayer", "Guild", "Party", "Dungeon", "Fishing","Socials", "Bazaar", "Slayer", "Guild", "Party", "Dungeon", "Fishing")) { + val w = ScreenRenderUtils.getTextWidth(button) + 3 + val isSelected = button == selectedTab + onEach(button, isSelected, Rect(xOffset.toDouble(), top, w.toDouble(), 16.0)) + xOffset += w + 5 + } + } + + data class Rect( + val x: Double, val y: Double, + val w: Double, val h: Double, + ) { + fun contains(mouseX: Double, mouseY: Double): Boolean { + return mouseX in (l..<r) && mouseY in (t..<b) + } + + val l get() = x + val t get() = y + val r get() = x + w + val b get() = y + h + val cy get() = y + h / 2 + val cx get() = x + w / 2 + } + + fun renderButtons( + matrixStack: UMatrixStack, + mouseX: Double, mouseY: Double, + ) { + iterateButtons { text, isSelected, pos -> + val w = ScreenRenderUtils.getTextWidth(text) + UGraphics.enableBlend() + ScreenRenderUtils.fillRect(matrixStack, + pos.l, pos.t, pos.r, pos.b, + if (isSelected) getChatBgOpacity() else getChatBgOpacity(0.75)) + UGraphics.disableBlend() + ScreenRenderUtils.renderText(matrixStack, + pos.l + 2, pos.cy - 9 / 2, + if (isSelected) "§a$text" else "§f$text") + } + } + + fun clickMouse(mouseX: Double, mouseY: Double, button: Int) { + iterateButtons { label, isSelected, pos -> + if (pos.contains(mouseX, mouseY)) { + if (button == 0) { + selectedTab = label + } + // TODO: right click options or something + } + } + } +} diff --git a/src/main/kotlin/util/minecrat/MC.kt b/src/main/kotlin/util/minecrat/MC.kt new file mode 100644 index 0000000..d942982 --- /dev/null +++ b/src/main/kotlin/util/minecrat/MC.kt @@ -0,0 +1,11 @@ +package moe.nea.ultranotifier.util.minecrat + +import net.minecraft.client.MinecraftClient + +object MC { + val font get() = instance.textRenderer + val instance get() = MinecraftClient.getInstance() + val inGameHud get() = instance.inGameHud!! + val chatHud get() = inGameHud.chatHud!! + +} diff --git a/src/main/kotlin/util/minecrat/TextUtil.kt b/src/main/kotlin/util/minecrat/TextUtil.kt new file mode 100644 index 0000000..2a09e13 --- /dev/null +++ b/src/main/kotlin/util/minecrat/TextUtil.kt @@ -0,0 +1 @@ +package moe.nea.ultranotifier.util.minecrat diff --git a/src/main/kotlin/util/minecrat/infer.kt b/src/main/kotlin/util/minecrat/infer.kt new file mode 100644 index 0000000..a109bbd --- /dev/null +++ b/src/main/kotlin/util/minecrat/infer.kt @@ -0,0 +1,15 @@ +@file:OptIn(ExperimentalContracts::class) + +package moe.nea.ultranotifier.util.minecrat + +import moe.nea.ultranotifier.mixin.AccessorChatHud +import net.minecraft.client.gui.hud.ChatHud +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +fun ChatHud.accessor(): AccessorChatHud { + contract { + returns() implies (this@accessor is AccessorChatHud) + } + return this as AccessorChatHud +} diff --git a/src/main/kotlin/util/render/ScreenRenderUtils.kt b/src/main/kotlin/util/render/ScreenRenderUtils.kt index 224ccb4..af8424a 100644 --- a/src/main/kotlin/util/render/ScreenRenderUtils.kt +++ b/src/main/kotlin/util/render/ScreenRenderUtils.kt @@ -1,13 +1,36 @@ package moe.nea.ultranotifier.util.render +//#if MC > 1.16 import gg.essential.universal.UGraphics import gg.essential.universal.UMatrixStack import juuxel.libninepatch.NinePatch import juuxel.libninepatch.TextureRenderer +import moe.nea.ultranotifier.util.minecrat.MC +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.Identifier import java.awt.Color +//#endif + object ScreenRenderUtils { + //#if MC > 1.16 + @JvmStatic + fun umatrix( + matrixStack: MatrixStack + ) = UMatrixStack(matrixStack) + + //#endif + //#if MC >= 1.20 + @JvmStatic + fun umatrix( + context: DrawContext + ) = UMatrixStack(context.matrices) + //#endif + + @JvmStatic + fun umatrix() = UMatrixStack() + fun fillRect( matrixStack: UMatrixStack, left: Double, top: Double, @@ -82,7 +105,7 @@ object ScreenRenderUtils { ) { val x1 = left + x.toDouble() val y1 = top + y.toDouble() - val x2 = x1+ width + val x2 = x1 + width val y2 = y1 + height graphics.pos(matrixStack, x1, y1, 0.0) .tex(u1.toDouble(), v1.toDouble()) @@ -101,4 +124,12 @@ object ScreenRenderUtils { graphics.drawDirect() } + fun getTextWidth(text: String): Int { + return MC.font.getWidth(text) + } + + fun renderText(matrixStack: UMatrixStack, x: Double, y: Double, text: String) { + UGraphics.drawString(matrixStack, text, x.toFloat(), y.toFloat(), -1, false) + } + } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index df30b70..3d7d0bd 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -4,6 +4,9 @@ "name": "${modName}", "description": "${description}", "version": "${version}", + "mixins": [ + "mixins.ultranotifier.json" + ], "entrypoints": { "main": ["moe.nea.ultranotifier.UltraNotifierEntryPoint"] }, diff --git a/versions/mapping-1.16.2-forge-1.12.2.txt b/versions/mapping-1.16.2-forge-1.12.2.txt index a3546a8..434dca3 100644 --- a/versions/mapping-1.16.2-forge-1.12.2.txt +++ b/versions/mapping-1.16.2-forge-1.12.2.txt @@ -3,6 +3,6 @@ net.minecraft.network.chat.Component getString() getUnformattedText() net.minecraftforge.eventbus.api.Event net.minecraftforge.fml.common.eventhandler.Event net.minecraftforge.eventbus.api.SubscribeEvent net.minecraftforge.fml.common.eventhandler.SubscribeEvent net.minecraft.client.gui.screens.Screen net.minecraft.client.gui.GuiScreen - +net.minecraft.client.gui.components.ChatComponent.render() drawChat() net.minecraftforge.event.TickEvent net.minecraftforge.fml.common.gameevent.TickEvent net.minecraftforge.event.TickEvent$ClientTickEvent net.minecraftforge.fml.common.gameevent.TickEvent$ClientTickEvent |