diff --git a/src/main/java/io/github/homchom/recode/event/EventTypes.kt b/src/main/java/io/github/homchom/recode/event/EventTypes.kt index f7d7b25e..9dc19e94 100644 --- a/src/main/java/io/github/homchom/recode/event/EventTypes.kt +++ b/src/main/java/io/github/homchom/recode/event/EventTypes.kt @@ -133,4 +133,4 @@ interface Requester : Sender, Detector { * * @see Requester.request */ -suspend fun Requester.request(): R = request(Unit) \ No newline at end of file +suspend fun Requester.request() = request(Unit) \ No newline at end of file diff --git a/src/main/java/io/github/homchom/recode/event/trial/ToggleRequesterGroup.kt b/src/main/java/io/github/homchom/recode/event/trial/ToggleRequesterGroup.kt deleted file mode 100644 index ad423647..00000000 --- a/src/main/java/io/github/homchom/recode/event/trial/ToggleRequesterGroup.kt +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.homchom.recode.event.trial - -import io.github.homchom.recode.Power -import io.github.homchom.recode.event.Listenable -import io.github.homchom.recode.event.Requester -import io.github.homchom.recode.util.std.unitOrNull -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -/** - * Creates a [ToggleRequesterGroup] with the given [trial] for toggling. - * - * The returned toggle is *naive*, meaning it assumes the request will yield the desired state and, if not, - * runs the request again. Prefer explicit toggle requests without false positives when possible. - * - * If this function is given a trial with a [io.github.homchom.recode.event.Validated] basis, the base context - * should be hidden when needed to avoid duplicate notifications (see [TrialScope.hidden]). - * - * @param trial Should yield a true result if the state is enabled, and false if it is disabled. - * - * @see requester - */ -fun toggleRequesterGroup( - name: String, - lifecycle: Listenable<*>, - trial: RequesterTrial -): ToggleRequesterGroup { - return NaiveToggle(name, lifecycle, trial) -} - -/** - * A specialized group of [Requester] objects for a toggleable state. - * - * Each requester returns a [Boolean], representing the actual detected state (as opposed to the expected state). - */ -sealed interface ToggleRequesterGroup { - val toggle: Requester - val enable: Requester - val disable: Requester -} - -private class NaiveToggle( - name: String, - lifecycle: Listenable<*>, - trial: RequesterTrial -) : ToggleRequesterGroup { - override val toggle: Requester - override val enable: Requester - override val disable: Requester - - private val power = Power() - - init { - fun impl(desiredState: Boolean?, reference: () -> Requester) = - requester(name, lifecycle, shortCircuitTrial( - trial.results, - trial.defaultInput, - start = { input -> - val isDesired = trial.start(input) == desiredState - isDesired.unitOrNull() - }, - tests = { supplier, input, isRequest -> - suspending { - fun retry(result: Boolean, isRequest: Boolean) = - isRequest && (desiredState == true && !result || desiredState == false && result) - - val state = supplier.supplyIn(this, input, isRequest, ::retry)?.await() - if (state != null && retry(state, isRequest)) { - power.launch(Dispatchers.Default) { - reference().request(input!!, isRequest) - } - } - } - } - )) - - toggle = impl(null, ::toggle) - enable = impl(true, ::enable) - disable = impl(false, ::disable) - - toggle.use(power) - enable.use(power) - disable.use(power) - } -} \ No newline at end of file diff --git a/src/main/java/io/github/homchom/recode/feature/automation/AutoCommands.kt b/src/main/java/io/github/homchom/recode/feature/automation/AutoCommands.kt index e060f7fd..afa0fd79 100644 --- a/src/main/java/io/github/homchom/recode/feature/automation/AutoCommands.kt +++ b/src/main/java/io/github/homchom/recode/feature/automation/AutoCommands.kt @@ -23,13 +23,15 @@ object AutoCommands { register("lagslayer", "autolagslayer", DFStateDetectors.ChangeMode) { (new) -> if (new.mode is PlotMode.Dev && !currentDFState.isInMode(PlotMode.Dev)) { - launch { CommandSenders.LagSlayer.enable.send() } + launch { + if (!CommandSenders.LagSlayer.isLagSlayerEnabled) CommandSenders.LagSlayer.send() + } } } register("nightvis", "autonightvis", DFStateDetectors.ChangeMode) { (new) -> if (new.session != SupportSession.Helping && new.mode != PlotMode.Play) { - launch { CommandSenders.NightVision.enable.send() } + launch { CommandSenders.NightVision.send() } } } diff --git a/src/main/java/io/github/homchom/recode/hypercube/CommandAliasGroup.kt b/src/main/java/io/github/homchom/recode/hypercube/CommandAliasGroup.kt index fd79b99c..fff1980e 100644 --- a/src/main/java/io/github/homchom/recode/hypercube/CommandAliasGroup.kt +++ b/src/main/java/io/github/homchom/recode/hypercube/CommandAliasGroup.kt @@ -17,5 +17,5 @@ enum class CommandAliasGroup(vararg aliases: String) : Set by aliases.to PLOT_NAME("plot name", "p name"), // legacy - RELORE("relore"); + RELORE("relore") } \ No newline at end of file diff --git a/src/main/java/io/github/homchom/recode/hypercube/CommandSenders.kt b/src/main/java/io/github/homchom/recode/hypercube/CommandSenders.kt index ba86a618..ed086bef 100644 --- a/src/main/java/io/github/homchom/recode/hypercube/CommandSenders.kt +++ b/src/main/java/io/github/homchom/recode/hypercube/CommandSenders.kt @@ -1,16 +1,19 @@ package io.github.homchom.recode.hypercube +import io.github.homchom.recode.Power +import io.github.homchom.recode.event.Requester +import io.github.homchom.recode.event.listenEach import io.github.homchom.recode.event.trial.requester -import io.github.homchom.recode.event.trial.toggleRequesterGroup import io.github.homchom.recode.event.trial.trial import io.github.homchom.recode.hypercube.state.DFStateDetectors import io.github.homchom.recode.multiplayer.ReceiveMessageEvent +import io.github.homchom.recode.multiplayer.SendPacketEvent import io.github.homchom.recode.multiplayer.Sender import io.github.homchom.recode.ui.text.equalsPlain import io.github.homchom.recode.ui.text.matchesPlain import io.github.homchom.recode.ui.text.plainText -import io.github.homchom.recode.util.regex.dynamicRegex import io.github.homchom.recode.util.regex.regex +import net.minecraft.network.protocol.game.ServerboundChatCommandPacket // TODO: replace more requesters with senders and update documentation object CommandSenders { @@ -25,62 +28,53 @@ object CommandSenders { } )) - private val timeRegex = dynamicRegex { time: Long? -> - str("$MAIN_ARROW Set your player time to ") - if (time == null) digit.oneOrMore() else str(time.toString()) - period + // TODO: support time keywords through command suggestions (not enum) + val ClientTime = Sender(DFStateDetectors) { time: Long -> + Sender.sendCommand("time $time") } - // TODO: support time keywords through command suggestions (not enum) - val ClientTime = requester("/time", DFStateDetectors, trial( - ReceiveMessageEvent.Chat, - null as Long?, - start = { time -> Sender.sendCommand("time $time") }, - tests = { context, time, _: Boolean -> - timeRegex(time).matchesPlain(context.value).instantUnitOrNull() - } - )) + private val lsRegex = regex { + str(LAGSLAYER_PREFIX); space + group { + str("Now monitoring plot ") + digit.oneOrMore() + str(". Type /lagslayer to stop monitoring.") - val Flight = toggleRequesterGroup("/fly", DFStateDetectors, trial( - ReceiveMessageEvent.Chat, - Unit, - start = { Sender.sendCommand("fly") }, - tests = t@{ message, _, _ -> - val enabled = when (message().plainText) { - "$MAIN_ARROW Flight enabled." -> true - "$MAIN_ARROW Flight disabled." -> false - else -> return@t null - } - instant(enabled) - } - )) + or - private val lsEnabledRegex = regex { - str("$LAGSLAYER_PREFIX Now monitoring plot ") - val plot by digit.oneOrMore() - str(". Type /lagslayer to stop monitoring.") - } - private val lsDisabledRegex = regex { - str("$LAGSLAYER_PREFIX Stopped monitoring plot ") - val plot by digit.oneOrMore() - period + str("Stopped monitoring plot ") + digit.oneOrMore() + period + } } - val LagSlayer = toggleRequesterGroup("/lagslayer", DFStateDetectors, trial( + private val lsDelegate = requester("/lagslayer", DFStateDetectors, trial( ReceiveMessageEvent.Chat, Unit, start = { Sender.sendCommand("lagslayer") }, tests = t@{ (message), _, _ -> - val enabled = when { - lsEnabledRegex.matchesPlain(message) -> true - lsDisabledRegex.matchesPlain(message) -> false - else -> return@t null - } - instant(enabled) + lsRegex.matchesPlain(message).instantUnitOrNull() } )) - val NightVision = toggleRequesterGroup("/nightvis", DFStateDetectors, trial( + object LagSlayer : Requester by lsDelegate { + var isLagSlayerEnabled = false + private set + + // this does not extend delegate because isLagSlayerEnabled should not desync + private val power = Power(startEnabled = true) + + init { + power.listenEach(SendPacketEvent) { packet -> + if (packet !is ServerboundChatCommandPacket) return@listenEach + if (!packet.command.equals("lagslayer", true)) return@listenEach + + isLagSlayerEnabled = !isLagSlayerEnabled + } + } + } + + val NightVision = requester("/nightvis", DFStateDetectors, trial( ReceiveMessageEvent.Chat, Unit, start = { Sender.sendCommand("nightvis") }, diff --git a/src/main/java/io/github/homchom/recode/hypercube/state/DFStateDetectors.kt b/src/main/java/io/github/homchom/recode/hypercube/state/DFStateDetectors.kt index 8a757f2f..77ab9537 100644 --- a/src/main/java/io/github/homchom/recode/hypercube/state/DFStateDetectors.kt +++ b/src/main/java/io/github/homchom/recode/hypercube/state/DFStateDetectors.kt @@ -42,7 +42,7 @@ private val eventGroup = GroupListenable>() object DFStateDetectors : StateListenable> by eventGroup { private val scoreboardNodeRegex = Regex("""(?.+) - .+""") - private val teleportEvent = ReceiveGamePacketEvent.filterIsInstance() + private val teleportEvent = ReceivePacketEvent.filterIsInstance() val EnterSpawn = eventGroup.add(detector("spawn", trial(teleportEvent, Unit) t@{ _, _ -> diff --git a/src/main/java/io/github/homchom/recode/mixin/multiplayer/MConnection.java b/src/main/java/io/github/homchom/recode/mixin/multiplayer/MConnection.java index 634c94ef..d37d4535 100644 --- a/src/main/java/io/github/homchom/recode/mixin/multiplayer/MConnection.java +++ b/src/main/java/io/github/homchom/recode/mixin/multiplayer/MConnection.java @@ -4,8 +4,8 @@ import net.minecraft.client.Minecraft; import net.minecraft.network.Connection; import net.minecraft.network.PacketListener; +import net.minecraft.network.PacketSendListener; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientGamePacketListener; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -14,13 +14,21 @@ @Mixin(Connection.class) public abstract class MConnection { @Inject(method = "genericsFtw", at = @At("HEAD")) - private static void interceptPacketHandling( + private static void runReceivePacketEvent( Packet packet, PacketListener packetListener, CallbackInfo ci ) { - if (packetListener instanceof ClientGamePacketListener) { - Minecraft.getInstance().execute(() -> MultiplayerEvents.getReceiveGamePacketEvent().run(packet)); - } + Minecraft.getInstance().execute(() -> MultiplayerEvents.getReceivePacketEvent().run(packet)); + } + + @Inject(method = "sendPacket", at = @At("HEAD")) + private void runSendPacketEvent( + Packet packet, + PacketSendListener packetSendListener, + boolean flush, + CallbackInfo ci + ) { + Minecraft.getInstance().execute(() -> MultiplayerEvents.getSendPacketEvent().run(packet)); } } diff --git a/src/main/java/io/github/homchom/recode/multiplayer/MultiplayerEvents.kt b/src/main/java/io/github/homchom/recode/multiplayer/MultiplayerEvents.kt index 638f34ce..8c23f285 100644 --- a/src/main/java/io/github/homchom/recode/multiplayer/MultiplayerEvents.kt +++ b/src/main/java/io/github/homchom/recode/multiplayer/MultiplayerEvents.kt @@ -26,7 +26,9 @@ val DisconnectFromServerEvent = wrapFabricEvent(ClientPlayConnectionEvents.DISCO data class ServerJoinContext(val handler: ClientPacketListener, val sender: PacketSender, val client: Minecraft) data class ServerDisconnectContext(val handler: ClientPacketListener, val client: Minecraft) -val ReceiveGamePacketEvent = createEvent>() +val ReceivePacketEvent = createEvent>() + +val SendPacketEvent = createEvent>() sealed class ReceiveMessageEvent : CustomEvent, Boolean> by createValidatedEvent() { data object Chat : ReceiveMessageEvent() diff --git a/src/main/java/io/github/homchom/recode/ui/text/StyleWrapper.kt b/src/main/java/io/github/homchom/recode/ui/text/StyleWrapper.kt index 9c56f887..baf6c762 100644 --- a/src/main/java/io/github/homchom/recode/ui/text/StyleWrapper.kt +++ b/src/main/java/io/github/homchom/recode/ui/text/StyleWrapper.kt @@ -7,6 +7,7 @@ import net.kyori.adventure.text.Component import net.kyori.adventure.text.event.ClickEvent import net.kyori.adventure.text.event.HoverEvent import net.kyori.adventure.text.format.Style +import net.kyori.adventure.text.format.TextColor import net.kyori.adventure.text.format.TextDecoration /** @@ -55,7 +56,7 @@ value class StyleWrapper(private val builder: Style.Builder) { fun white() = color(ColorPalette.WHITE) fun color(color: RGB) = apply { - builder.color { color.hex } + builder.color(TextColor.color(color)) } fun bold() = apply {