Make chat events use fabric events

[no changelog]
This commit is contained in:
nea
2023-09-04 20:22:51 +02:00
parent ee5591684d
commit d202ef5439
16 changed files with 185 additions and 157 deletions

View File

@@ -7,41 +7,22 @@
package moe.nea.firmament.mixins;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import moe.nea.firmament.events.ClientChatLineReceivedEvent;
import moe.nea.firmament.features.fixes.Fixes;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ChatHud.class)
public class MixinChatHud {
@ModifyExpressionValue(method = "render",at = @At(value = "INVOKE",target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
@ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
public boolean onGetChatHud(boolean old) {
return old || Fixes.INSTANCE.shouldPeekChat();
}
@ModifyExpressionValue(method = "getHeight",at = @At(value = "INVOKE",target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
@ModifyExpressionValue(method = "getHeight", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
public boolean onGetChatHudHeight(boolean old) {
return old || Fixes.INSTANCE.shouldPeekChat();
}
@ModifyArg(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;ILnet/minecraft/client/gui/hud/MessageIndicator;Z)V"), method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V")
public Text onAddMessage(Text message) {
var event = new ClientChatLineReceivedEvent(message);
if (ClientChatLineReceivedEvent.Companion.publish(event).getCancelled()) {
return null;
}
return event.getReplaceWith();
}
@Inject(method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;ILnet/minecraft/client/gui/hud/MessageIndicator;Z)V", at = @At("HEAD"), cancellable = true)
public void onAddMessage2(Text message, MessageSignatureData signature, int ticks, MessageIndicator indicator, boolean refresh, CallbackInfo ci) {
if (message == null) ci.cancel();
}
}

View File

@@ -1,14 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.mixins;
import net.minecraft.client.gui.screen.ChatScreen;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(ChatScreen.class)
public class MixinChatScreen {
}

View File

@@ -1,40 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.mixins;
import com.mojang.authlib.GameProfile;
import moe.nea.firmament.events.ServerChatLineReceivedEvent;
import net.minecraft.client.network.message.MessageHandler;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MessageHandler.class)
public class MixinMessageHandler {
@Inject(method = "onChatMessage", cancellable = true, at = @At("HEAD"))
public void onOnChatMessage(SignedMessage message, GameProfile sender, MessageType.Parameters params, CallbackInfo ci) {
var decoratedText = params.applyChatDecoration(message.unsignedContent() != null ? message.unsignedContent() : message.getContent());
var event = new ServerChatLineReceivedEvent(decoratedText);
if (ServerChatLineReceivedEvent.Companion.publish(event).getCancelled()) {
ci.cancel();
}
}
@Inject(method = "onGameMessage", at = @At("HEAD"), cancellable = true)
public void onOnGameMessage(Text message, boolean overlay, CallbackInfo ci) {
if (!overlay) {
var event = new ServerChatLineReceivedEvent(message);
if (ServerChatLineReceivedEvent.Companion.publish(event).getCancelled()) {
ci.cancel();
}
}
}
}

View File

@@ -14,8 +14,19 @@ import io.ktor.client.plugins.compression.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.serialization.kotlinx.json.*
import java.nio.file.Files
import java.nio.file.Path
import kotlinx.coroutines.*
import kotlinx.serialization.json.Json
import moe.nea.firmament.commands.registerFirmamentCommand
import moe.nea.firmament.dbus.FirmamentDbusObject
import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.events.ScreenRenderPostEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.registration.registerFirmamentChatEvents
import moe.nea.firmament.features.FeatureManager
import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.data.IDataHolder
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
@@ -25,30 +36,15 @@ import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents
import net.fabricmc.loader.api.FabricLoader
import net.fabricmc.loader.api.Version
import net.fabricmc.loader.api.metadata.ModMetadata
import net.minecraft.command.CommandRegistryAccess
import net.minecraft.util.Identifier
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder
import org.freedesktop.dbus.exceptions.DBusException
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.plus
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import java.nio.file.Files
import java.nio.file.Path
import kotlin.coroutines.EmptyCoroutineContext
import net.minecraft.command.CommandRegistryAccess
import net.minecraft.util.Identifier
import moe.nea.firmament.commands.registerFirmamentCommand
import moe.nea.firmament.dbus.FirmamentDbusObject
import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.events.ScreenRenderPostEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.features.FeatureManager
import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.data.IDataHolder
object Firmament {
const val MOD_ID = "firmament"
@@ -134,6 +130,7 @@ object Firmament {
globalJob.cancel()
}
})
registerFirmamentChatEvents()
ItemTooltipCallback.EVENT.register { a, b, c ->
ItemTooltipEvent.publish(ItemTooltipEvent(a, b, c))
}
@@ -145,5 +142,6 @@ object Firmament {
})
}
fun identifier(path: String) = Identifier(MOD_ID, path)
}

View File

@@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events
import moe.nea.firmament.util.unformattedString
import net.minecraft.text.Text
/**
* Filter whether the user should see a chat message altogether. May or may not be called for every chat packet sent by
* the server. When that quality is desired, consider [ProcessChatEvent] instead.
*/
data class AllowChatEvent(val text: Text) : FirmamentEvent.Cancellable() {
val unformattedString = text.unformattedString
companion object : FirmamentEventBus<AllowChatEvent>()
}

View File

@@ -1,21 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events
import net.minecraft.text.Text
import moe.nea.firmament.util.unformattedString
/**
* Published just before a message is added to the chat gui. Cancelling only prevents rendering, not logging to the
* console.
*/
data class ClientChatLineReceivedEvent(val text: Text) : FirmamentEvent.Cancellable() {
val unformattedString = text.unformattedString
var replaceWith: Text = text
companion object : FirmamentEventBus<ClientChatLineReceivedEvent>()
}

View File

@@ -20,10 +20,10 @@ open class FirmamentEventBus<T : FirmamentEvent> {
private val toHandle: MutableList<Handler<T>> = CopyOnWriteArrayList()
fun subscribe(handle: (T) -> Unit) {
subscribe(handle, false)
subscribe(false, handle)
}
fun subscribe(handle: (T) -> Unit, receivesCancelled: Boolean) {
fun subscribe(receivesCancelled: Boolean, handle: (T) -> Unit) {
toHandle.add(Handler(handle, receivesCancelled))
}

View File

@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events
import moe.nea.firmament.util.unformattedString
import net.minecraft.text.Text
/**
* Allow modification of a chat message before it is sent off to the user. Intended for display purposes.
*/
data class ModifyChatEvent(val originalText: Text) : FirmamentEvent() {
var unformattedString = originalText.unformattedString
private set
var replaceWith: Text = originalText
set(value) {
field = value
unformattedString = value.unformattedString
}
companion object : FirmamentEventBus<ModifyChatEvent>()
}

View File

@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events
import moe.nea.firmament.util.unformattedString
import net.minecraft.text.Text
/**
* Behaves like [AllowChatEvent], but is triggered even when cancelled by other mods. Intended for data collection.
* Make sure to subscribe to cancellable events as well when using.
*/
data class ProcessChatEvent(val text: Text, val wasExternallyCancelled: Boolean) : FirmamentEvent.Cancellable() {
val unformattedString = text.unformattedString
init {
if (wasExternallyCancelled)
cancelled = true
}
companion object : FirmamentEventBus<ProcessChatEvent>()
}

View File

@@ -1,19 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events
import net.minecraft.text.Text
import moe.nea.firmament.util.unformattedString
/**
* This event gets published whenever the client receives a chat message from the server.
* This event is cancellable, but should not get cancelled. Use [ClientChatLineReceivedEvent] for that instead. */
data class ServerChatLineReceivedEvent(val text: Text) : FirmamentEvent.Cancellable() {
val unformattedString = text.unformattedString
companion object : FirmamentEventBus<ServerChatLineReceivedEvent>()
}

View File

@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events.registration
import moe.nea.firmament.events.AllowChatEvent
import moe.nea.firmament.events.ModifyChatEvent
import moe.nea.firmament.events.ProcessChatEvent
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents
import net.minecraft.text.Text
private var lastReceivedMessage: Text? = null
fun registerFirmamentChatEvents() {
ClientReceiveMessageEvents.ALLOW_CHAT.register(ClientReceiveMessageEvents.AllowChat { message, signedMessage, sender, params, receptionTimestamp ->
lastReceivedMessage = message
!ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled
&& !AllowChatEvent.publish(AllowChatEvent(message)).cancelled
})
ClientReceiveMessageEvents.ALLOW_GAME.register(ClientReceiveMessageEvents.AllowGame { message, overlay ->
lastReceivedMessage = message
overlay || (!ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled &&
!AllowChatEvent.publish(AllowChatEvent(message)).cancelled)
})
ClientReceiveMessageEvents.MODIFY_GAME.register(ClientReceiveMessageEvents.ModifyGame { message, overlay ->
if (overlay) message
else ModifyChatEvent.publish(ModifyChatEvent(message)).replaceWith
})
ClientReceiveMessageEvents.GAME_CANCELED.register(ClientReceiveMessageEvents.GameCanceled { message, overlay ->
if (!overlay && lastReceivedMessage !== message) {
ProcessChatEvent.publish(ProcessChatEvent(message, true))
}
})
ClientReceiveMessageEvents.CHAT_CANCELED.register(ClientReceiveMessageEvents.ChatCanceled { message, signedMessage, sender, params, receptionTimestamp ->
if (lastReceivedMessage !== message) {
ProcessChatEvent.publish(ProcessChatEvent(message, true))
}
})
}

View File

@@ -12,6 +12,7 @@ import moe.nea.firmament.Firmament
import moe.nea.firmament.features.chat.ChatLinks
import moe.nea.firmament.features.debug.DebugView
import moe.nea.firmament.features.debug.DeveloperFeatures
import moe.nea.firmament.features.debug.MinorTrolling
import moe.nea.firmament.features.fixes.Fixes
import moe.nea.firmament.features.inventory.CraftingOverlay
import moe.nea.firmament.features.inventory.PriceData
@@ -41,6 +42,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
fun autoload() {
synchronized(this) {
if (hasAutoloaded) return
loadFeature(MinorTrolling)
loadFeature(FairySouls)
// TODO: loadFeature(FishingWarning)
loadFeature(SlotLocking)

View File

@@ -26,7 +26,7 @@ import net.minecraft.text.Text
import net.minecraft.util.Formatting
import net.minecraft.util.Identifier
import moe.nea.firmament.Firmament
import moe.nea.firmament.events.ClientChatLineReceivedEvent
import moe.nea.firmament.events.ModifyChatEvent
import moe.nea.firmament.events.ScreenRenderPostEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
@@ -98,9 +98,9 @@ object ChatLinks : FirmamentFeature {
@OptIn(ExperimentalCoroutinesApi::class)
override fun onLoad() {
ClientChatLineReceivedEvent.subscribe {
ModifyChatEvent.subscribe {
if (TConfig.enableLinks)
it.replaceWith = it.text.transformEachRecursively { child ->
it.replaceWith = it.replaceWith.transformEachRecursively { child ->
val text = child.string
if ("://" !in text) return@transformEachRecursively child
val s = Text.empty().setStyle(child.style)

View File

@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.features.debug
import moe.nea.firmament.events.ModifyChatEvent
import moe.nea.firmament.features.FirmamentFeature
import net.minecraft.text.Text
// In memorian Dulkir
object MinorTrolling : FirmamentFeature {
override val identifier: String
get() = "minor-trolling"
val trollers = listOf("nea89o", "lrg89")
val t = "From(?: \\[[^\\]]+])? ([^:]+): (.*)".toRegex()
override fun onLoad() {
ModifyChatEvent.subscribe {
val m = t.matchEntire(it.unformattedString) ?: return@subscribe
val (_, name, text) = m.groupValues
if (name !in trollers) return@subscribe
if (!text.startsWith("c:")) return@subscribe
it.replaceWith = Text.literal(text.substring(2).replace("&", "§"))
}
}
}

View File

@@ -17,7 +17,7 @@ import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import net.minecraft.text.Text
import net.minecraft.util.math.Vec3d
import moe.nea.firmament.events.ServerChatLineReceivedEvent
import moe.nea.firmament.events.AllowChatEvent
import moe.nea.firmament.events.SkyblockServerUpdateEvent
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.features.FirmamentFeature
@@ -125,7 +125,7 @@ object FairySouls : FirmamentFeature {
updateWorldSouls()
updateMissingSouls()
}
ServerChatLineReceivedEvent.subscribe {
AllowChatEvent.subscribe {
when (it.text.unformattedString) {
"You have already found that Fairy Soul!" -> {
markNearestSoul()

View File

@@ -6,18 +6,17 @@
package moe.nea.firmament.util
import java.util.*
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
import moe.nea.firmament.Firmament
import moe.nea.firmament.events.ClientChatLineReceivedEvent
import moe.nea.firmament.events.OutgoingPacketEvent
import moe.nea.firmament.events.ServerChatLineReceivedEvent
import moe.nea.firmament.events.ProcessChatEvent
import moe.nea.firmament.events.SkyblockServerUpdateEvent
import moe.nea.firmament.events.WorldReadyEvent
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
import java.util.*
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
object SBData {
private val profileRegex = "Profile ID: ([a-z0-9\\-]+)".toRegex()
@@ -38,7 +37,7 @@ object SBData {
anyLocrawSent.markNow()
}
}
ServerChatLineReceivedEvent.subscribe { event ->
ProcessChatEvent.subscribe(receivesCancelled = true) { event ->
val profileMatch = profileRegex.matchEntire(event.unformattedString)
if (profileMatch != null) {
try {
@@ -54,23 +53,22 @@ object SBData {
}
if (event.unformattedString.startsWith("{")) {
if (tryReceiveLocraw(event.unformattedString)) {
if (lastLocrawSent.timePassed() < locrawRoundtripTime) {
lastLocrawSent.markFarPast()
event.cancel()
}
if (!hasValidLocraw && !hasSentLocraw && hasReceivedProfile) {
sendLocraw()
}
}
}
}
ClientChatLineReceivedEvent.subscribe { event ->
if (event.unformattedString.startsWith("{") && tryReceiveLocraw(event.unformattedString) && lastLocrawSent.timePassed() < locrawRoundtripTime) {
lastLocrawSent.markFarPast()
event.cancel()
}
}
WorldReadyEvent.subscribe {
locraw = null
hasSentLocraw = false
hasReceivedProfile = false
profileId = null
}
}