Add support for other mods using /locraw

This commit is contained in:
nea
2023-06-09 18:08:38 +02:00
parent e7a7b04d8c
commit 428056ff80
7 changed files with 105 additions and 20 deletions

View File

@@ -0,0 +1,21 @@
package moe.nea.firmament.mixins;
import moe.nea.firmament.events.ClientChatLineReceivedEvent;
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.callback.CallbackInfo;
@Mixin(ChatHud.class)
public class MixinChatHud {
@Inject(at = @At("HEAD"), method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;ILnet/minecraft/client/gui/hud/MessageIndicator;Z)V", cancellable = true)
public void onAddMessage(Text message, MessageSignatureData signature, int ticks, MessageIndicator indicator, boolean refresh, CallbackInfo ci) {
if (ClientChatLineReceivedEvent.Companion.publish(new ClientChatLineReceivedEvent(message)).getCancelled()) {
ci.cancel();
}
}
}

View File

@@ -18,8 +18,10 @@
package moe.nea.firmament.mixins; package moe.nea.firmament.mixins;
import moe.nea.firmament.events.OutgoingPacketEvent;
import moe.nea.firmament.events.ParticleSpawnEvent; import moe.nea.firmament.events.ParticleSpawnEvent;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; import net.minecraft.network.packet.s2c.play.ParticleS2CPacket;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -38,4 +40,11 @@ public class MixinClientPacketHandler {
packet.isLongDistance() packet.isLongDistance()
)); ));
} }
@Inject(method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V", at = @At("HEAD"), cancellable = true)
public void onSendPacket(Packet<?> packet, CallbackInfo ci) {
if (OutgoingPacketEvent.Companion.publish(new OutgoingPacketEvent(packet)).getCancelled()) {
ci.cancel();
}
}
} }

View File

@@ -0,0 +1,14 @@
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
companion object : FirmamentEventBus<ClientChatLineReceivedEvent>()
}

View File

@@ -0,0 +1,7 @@
package moe.nea.firmament.events
import net.minecraft.network.packet.Packet
data class OutgoingPacketEvent(val packet: Packet<*>) : FirmamentEvent.Cancellable() {
companion object : FirmamentEventBus<OutgoingPacketEvent>()
}

View File

@@ -23,9 +23,9 @@ import moe.nea.firmament.util.unformattedString
/** /**
* This event gets published whenever the client receives a chat message from the server. * 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() { data class ServerChatLineReceivedEvent(val text: Text) : FirmamentEvent.Cancellable() {
companion object : FirmamentEventBus<ServerChatLineReceivedEvent>()
val unformattedString = text.unformattedString val unformattedString = text.unformattedString
companion object : FirmamentEventBus<ServerChatLineReceivedEvent>()
} }

View File

@@ -18,49 +18,81 @@
package moe.nea.firmament.util package moe.nea.firmament.util
import java.util.UUID
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
import moe.nea.firmament.Firmament 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.ServerChatLineReceivedEvent
import moe.nea.firmament.events.SkyblockServerUpdateEvent import moe.nea.firmament.events.SkyblockServerUpdateEvent
import moe.nea.firmament.events.WorldReadyEvent import moe.nea.firmament.events.WorldReadyEvent
object SBData { object SBData {
val profileRegex = "(?:Your profile was changed to: |You are playing on profile: )(.+)".toRegex() private val profileRegex = "Profile ID: ([a-z0-9\\-]+)".toRegex()
var profileCuteName: String? = null var profileId: UUID? = null
private var lastLocrawSent = Timer() private var lastLocrawSent = Timer()
private val anyLocrawSent = Timer()
private val locrawRoundtripTime: Duration = 5.seconds private val locrawRoundtripTime: Duration = 5.seconds
private var hasReceivedProfile = false
private var hasSentLocraw = false
var locraw: Locraw? = null var locraw: Locraw? = null
val skyblockLocation get() = locraw?.skyblockLocation val skyblockLocation: String? get() = locraw?.skyblockLocation
val hasValidLocraw get() = locraw?.server !in listOf("limbo", null)
fun init() { fun init() {
OutgoingPacketEvent.subscribe { event ->
if (event.packet is CommandExecutionC2SPacket && event.packet.command == "locraw") {
anyLocrawSent.markNow()
}
}
ServerChatLineReceivedEvent.subscribe { event -> ServerChatLineReceivedEvent.subscribe { event ->
val profileMatch = profileRegex.matchEntire(event.unformattedString) val profileMatch = profileRegex.matchEntire(event.unformattedString)
if (profileMatch != null) { if (profileMatch != null) {
profileCuteName = profileMatch.groupValues[1] try {
profileId = UUID.fromString(profileMatch.groupValues[1])
hasReceivedProfile = true
if (!hasValidLocraw && !hasSentLocraw && anyLocrawSent.timePassed() > locrawRoundtripTime) {
sendLocraw()
}
} catch (e: IllegalArgumentException) {
profileId = null
e.printStackTrace()
}
} }
if (event.unformattedString.startsWith("{")) { if (event.unformattedString.startsWith("{")) {
if (tryReceiveLocraw(event.unformattedString) && lastLocrawSent.timePassed() < locrawRoundtripTime) { if (tryReceiveLocraw(event.unformattedString)) {
lastLocrawSent.markFarPast() if (!hasValidLocraw && !hasSentLocraw && hasReceivedProfile) {
event.cancel() sendLocraw()
}
} }
} }
} }
ClientChatLineReceivedEvent.subscribe { event ->
if (event.unformattedString.startsWith("{") && tryReceiveLocraw(event.unformattedString) && lastLocrawSent.timePassed() < locrawRoundtripTime) {
lastLocrawSent.markFarPast()
event.cancel()
}
}
WorldReadyEvent.subscribe { WorldReadyEvent.subscribe {
sendLocraw()
locraw = null locraw = null
hasSentLocraw = false
hasReceivedProfile = false
} }
} }
private fun tryReceiveLocraw(unformattedString: String): Boolean = try { private fun tryReceiveLocraw(unformattedString: String, update: Boolean = true): Boolean = try {
val lastLocraw = locraw val lastLocraw = locraw
locraw = Firmament.json.decodeFromString<Locraw>(unformattedString) val n = Firmament.json.decodeFromString<Locraw>(unformattedString)
SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw)) if (update) {
locraw = n
SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw))
}
true true
} catch (e: SerializationException) { } catch (e: SerializationException) {
e.printStackTrace() e.printStackTrace()
@@ -71,6 +103,7 @@ object SBData {
} }
fun sendLocraw() { fun sendLocraw() {
hasSentLocraw = true
lastLocrawSent.markNow() lastLocrawSent.markNow()
val nh = MC.player?.networkHandler ?: return val nh = MC.player?.networkHandler ?: return
nh.sendChatCommand("locraw") nh.sendChatCommand("locraw")

View File

@@ -19,6 +19,7 @@
package moe.nea.firmament.util.data package moe.nea.firmament.util.data
import java.nio.file.Path import java.nio.file.Path
import java.util.UUID
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlin.io.path.createDirectories import kotlin.io.path.createDirectories
import kotlin.io.path.deleteExisting import kotlin.io.path.deleteExisting
@@ -37,10 +38,10 @@ abstract class ProfileSpecificDataHolder<S>(
private val configDefault: () -> S private val configDefault: () -> S
) : IDataHolder<S?> { ) : IDataHolder<S?> {
var allConfigs: MutableMap<String, S> var allConfigs: MutableMap<UUID, S>
override val data: S? override val data: S?
get() = SBData.profileCuteName?.let { get() = SBData.profileId?.let {
allConfigs.computeIfAbsent(it) { configDefault() } allConfigs.computeIfAbsent(it) { configDefault() }
} }
@@ -52,7 +53,7 @@ abstract class ProfileSpecificDataHolder<S>(
private val configDirectory: Path get() = Firmament.CONFIG_DIR.resolve("profiles").resolve(configName) private val configDirectory: Path get() = Firmament.CONFIG_DIR.resolve("profiles").resolve(configName)
private fun readValues(): MutableMap<String, S> { private fun readValues(): MutableMap<UUID, S> {
if (!configDirectory.exists()) { if (!configDirectory.exists()) {
configDirectory.createDirectories() configDirectory.createDirectories()
} }
@@ -61,7 +62,7 @@ abstract class ProfileSpecificDataHolder<S>(
.filter { it.extension == "json" } .filter { it.extension == "json" }
.mapNotNull { .mapNotNull {
try { try {
it.nameWithoutExtension to Firmament.json.decodeFromString(dataSerializer, it.readText()) UUID.fromString(it.nameWithoutExtension) to Firmament.json.decodeFromString(dataSerializer, it.readText())
} catch (e: Exception) { /* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/ } catch (e: Exception) { /* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
IDataHolder.badLoads.add(configName) IDataHolder.badLoads.add(configName)
Firmament.logger.error( Firmament.logger.error(
@@ -79,7 +80,7 @@ abstract class ProfileSpecificDataHolder<S>(
} }
val c = allConfigs val c = allConfigs
configDirectory.listDirectoryEntries().forEach { configDirectory.listDirectoryEntries().forEach {
if (it.nameWithoutExtension !in c) { if (it.nameWithoutExtension !in c.mapKeys { it.toString() }) {
it.deleteExisting() it.deleteExisting()
} }
} }