summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2025-01-25 00:40:58 +0100
committerLinnea Gräf <nea@nea.moe>2025-01-25 00:40:58 +0100
commitaf991c28062e405a02c139d3d67c4f86a9043e35 (patch)
tree29bd1d1568b707be04915f47a1bf558e923cd3fe /src/main
parente4f585c173ca0a5d09130ab97c18c48f91fe5ad7 (diff)
downloadultra-notifier-af991c28062e405a02c139d3d67c4f86a9043e35.tar.gz
ultra-notifier-af991c28062e405a02c139d3d67c4f86a9043e35.tar.bz2
ultra-notifier-af991c28062e405a02c139d3d67c4f86a9043e35.zip
.
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/moe/nea/ultranotifier/mixin/AccessorChatHud.java11
-rw-r--r--src/main/java/moe/nea/ultranotifier/mixin/ChatHudCategoryTracker.java42
-rw-r--r--src/main/java/moe/nea/ultranotifier/mixin/ChatScreenTabRenderer.java61
-rw-r--r--src/main/kotlin/datamodel/ChatType.kt100
-rw-r--r--src/main/kotlin/gui/ChatUi.kt89
-rw-r--r--src/main/kotlin/util/minecrat/MC.kt11
-rw-r--r--src/main/kotlin/util/minecrat/TextUtil.kt1
-rw-r--r--src/main/kotlin/util/minecrat/infer.kt15
-rw-r--r--src/main/kotlin/util/render/ScreenRenderUtils.kt33
-rw-r--r--src/main/resources/fabric.mod.json3
10 files changed, 365 insertions, 1 deletions
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"]
},