From 1272a6e0b52de73d9fc3b6247cfb783c90ab7721 Mon Sep 17 00:00:00 2001 From: Potatoboy9999 <51728317+PotatoPresident@users.noreply.github.com> Date: Sun, 1 Feb 2026 18:23:48 -0800 Subject: [PATCH 1/2] Implement custom page change click actions for search results to bypass dialog --- .../ServerCommonPacketListenerImplMixin.java | 29 +++++++++++++++ .../commands/subcommands/PageCommand.kt | 8 ++--- .../ledger/utility/MessageUtils.kt | 13 +++++-- src/main/resources/ledger.mixins.json | 36 +++++++++---------- 4 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java new file mode 100644 index 00000000..f1eba08f --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java @@ -0,0 +1,29 @@ +package com.github.quiltservertools.ledger.mixin; + +import com.github.quiltservertools.ledger.commands.subcommands.PageCommand; +import com.github.quiltservertools.ledger.utility.MessageUtils; +import net.minecraft.nbt.Tag; +import net.minecraft.network.protocol.common.ServerboundCustomClickActionPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerCommonPacketListenerImpl; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +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(ServerCommonPacketListenerImpl.class) +public class ServerCommonPacketListenerImplMixin { + @Inject(method = "handleCustomClickAction", at = @At("HEAD"), cancellable = true) + private void handleLedgerPageChangeClick(ServerboundCustomClickActionPacket packet, CallbackInfo ci) { + if (!packet.id().equals(MessageUtils.INSTANCE.getPageChangeAction())) return; + if (!((Object) this instanceof ServerGamePacketListenerImpl game)) return; + ServerPlayer player = game.player; + packet.payload().flatMap(Tag::asCompound) + .flatMap(root -> root.getInt("page")) + .ifPresent(page -> + PageCommand.INSTANCE.page(player.createCommandSourceStack(), page) + ); + ci.cancel(); + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PageCommand.kt b/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PageCommand.kt index 67bc9c4b..3ebf0484 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PageCommand.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PageCommand.kt @@ -3,12 +3,12 @@ package com.github.quiltservertools.ledger.commands.subcommands import com.github.quiltservertools.ledger.Ledger import com.github.quiltservertools.ledger.commands.BuildableCommand import com.github.quiltservertools.ledger.database.DatabaseManager -import com.github.quiltservertools.ledger.utility.Context import com.github.quiltservertools.ledger.utility.LiteralNode import com.github.quiltservertools.ledger.utility.MessageUtils import com.github.quiltservertools.ledger.utility.TextColorPallet import com.mojang.brigadier.arguments.IntegerArgumentType import kotlinx.coroutines.launch +import net.minecraft.commands.CommandSourceStack import net.minecraft.commands.Commands.argument import net.minecraft.commands.Commands.literal import net.minecraft.network.chat.Component @@ -18,13 +18,11 @@ object PageCommand : BuildableCommand { literal("page") .then( argument("page", IntegerArgumentType.integer(1)) - .executes { page(it, IntegerArgumentType.getInteger(it, "page")) } + .executes { page(it.source, IntegerArgumentType.getInteger(it, "page")) } ) .build() - private fun page(context: Context, page: Int): Int { - val source = context.source - + fun page(source: CommandSourceStack, page: Int): Int { val params = Ledger.searchCache[source.textName] if (params != null) { Ledger.launch { diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt index 875b2617..af3c4f72 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt @@ -8,19 +8,24 @@ import com.github.quiltservertools.ledger.network.Networking.hasNetworking import com.github.quiltservertools.ledger.network.packet.action.ActionS2CPacket import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.commands.CommandSourceStack +import net.minecraft.nbt.CompoundTag import net.minecraft.network.chat.ClickEvent import net.minecraft.network.chat.Component import net.minecraft.network.chat.HoverEvent import net.minecraft.network.chat.MutableComponent import net.minecraft.network.chat.Style +import net.minecraft.resources.Identifier import java.time.Duration import java.time.Instant import java.time.format.DateTimeFormatter import java.time.format.FormatStyle +import java.util.* import kotlin.time.ExperimentalTime import kotlin.time.toKotlinDuration object MessageUtils { + val pageChangeAction: Identifier = Ledger.identifier("page-change") + @OptIn(ExperimentalTime::class) suspend fun sendSearchResults(source: CommandSourceStack, results: SearchResults, header: Component) { // If the player has a Ledger compatible client, we send results as action packets rather than as chat messages @@ -46,13 +51,15 @@ object MessageUtils { Component.translatable( "text.ledger.footer.page_backward" ).setStyle(TextColorPallet.primaryVariant).withStyle { + val tag: CompoundTag = CompoundTag().apply { this.putInt("page", results.page-1) } + if (results.page > 1) { it.withHoverEvent( HoverEvent.ShowText( Component.translatable("text.ledger.footer.page_backward.hover") ) ).withClickEvent( - ClickEvent.RunCommand("/lg pg ${results.page - 1}") + ClickEvent.Custom(pageChangeAction, Optional.of(tag)) ) } else { Style.EMPTY @@ -63,13 +70,15 @@ object MessageUtils { Component.translatable( "text.ledger.footer.page_forward" ).setStyle(TextColorPallet.primaryVariant).withStyle { + val tag: CompoundTag = CompoundTag().apply { this.putInt("page", results.page+1) } + if (results.page < results.pages) { it.withHoverEvent( HoverEvent.ShowText( Component.translatable("text.ledger.footer.page_forward.hover") ) ).withClickEvent( - ClickEvent.RunCommand("/lg pg ${results.page + 1}") + ClickEvent.Custom(pageChangeAction, Optional.of(tag)) ) } else { Style.EMPTY diff --git a/src/main/resources/ledger.mixins.json b/src/main/resources/ledger.mixins.json index e2e1d398..9980204a 100644 --- a/src/main/resources/ledger.mixins.json +++ b/src/main/resources/ledger.mixins.json @@ -2,37 +2,36 @@ "required": true, "package": "com.github.quiltservertools.ledger.mixin", "mixins": [ - "BlockBehaviourMixin", + "AbstractContainerMenuMixin", "AnvilMenuMixin", "AxeItemMixin", + "BaseContainerBlockEntityMixin", + "BlockBehaviourMixin", "BlockItemMixin", "BlockMixin", "BucketDispenserBehaviorMixin", "BucketItemMixin", "CampfireBlockEntityMixin", "ChestBlockMixin", - "CopperGolemGiveMixin", "CompoundContainerMixin", + "ContainersMixin", + "CopperGolemGiveMixin", "DyeItemMixin", - "ServerExplosionMixin", "FillCommandMixin", "FlintAndSteelItemMixin", "FrostWalkerEnchantmentMixin", "HoeItemMixin", "HoneycombItemMixin", - "ContainersMixin", "JukeboxPlayableMixin", - "WaterlilyBlockMixin", - "BaseContainerBlockEntityMixin", - "TransportItemsBetweenContainersMixin", "PortalShapeMixin", - "AbstractContainerMenuMixin", + "ServerCommonPacketListenerImplMixin", + "ServerExplosionMixin", "ServerPlayerMixin", "SetBlockCommandMixin", "ShovelItemMixin", "SlotMixin", - "blocks.GrowingPlantBlockMixin", - "blocks.GrowingPlantHeadBlockMixin", + "TransportItemsBetweenContainersMixin", + "WaterlilyBlockMixin", "blocks.BambooStalkBlockMixin", "blocks.BedBlockMixin", "blocks.ButtonBlockMixin", @@ -52,13 +51,14 @@ "blocks.FenceGateBlockMixin", "blocks.FireBlockMixin", "blocks.FlowerPotBlockMixin", - "blocks.LiquidBlockMixin", + "blocks.GrowingPlantBlockMixin", + "blocks.GrowingPlantHeadBlockMixin", "blocks.IceBlockMixin", "blocks.JukeboxBlockMixin", "blocks.LeavesBlockMixin", "blocks.LeverBlockMixin", + "blocks.LiquidBlockMixin", "blocks.NoteBlockMixin", - "blocks.WeatheringCopperGolemStatueBlockMixin", "blocks.PointedDripstoneBlockMixin", "blocks.RepeaterBlockMixin", "blocks.RootedDirtBlockMixin", @@ -68,19 +68,20 @@ "blocks.SpreadingSnowyDirtBlockMixin", "blocks.SugarCaneBlockMixin", "blocks.TrapDoorBlockMixin", + "blocks.WeatheringCopperGolemStatueBlockMixin", "blocks.WetSpongeBlockMixin", - "blocks.cauldron.CauldronInteractionMixin", "blocks.cauldron.CauldronBlockMixin", + "blocks.cauldron.CauldronInteractionMixin", "blocks.cauldron.LayeredCauldronBlockMixin", - "blocks.coral.CoralPlantBlockMixin", - "blocks.coral.CoralPlantBlockMixin", "blocks.coral.CoralFanBlockMixin", + "blocks.coral.CoralPlantBlockMixin", "blocks.coral.CoralWallFanBlockMixin", + "blocks.lectern.AbstractContainerMenuMixin", "blocks.lectern.LecternBlockMixin", "blocks.lectern.LecternMenuMixin", - "blocks.lectern.AbstractContainerMenuMixin", - "blocks.sign.SignBlockMixin", "blocks.sign.SignBlockEntityMixin", + "blocks.sign.SignBlockMixin", + "entities.AbstractHurtingProjectileMixin", "entities.ArmorStandMixin", "entities.CatMixin", "entities.CopperGolemMixin", @@ -88,7 +89,6 @@ "entities.EndCrystalMixin", "entities.EnderDragonMixin", "entities.EvokerEntityWololoGoalMixin", - "entities.AbstractHurtingProjectileMixin", "entities.FallingBlockEntityMixin", "entities.ItemEntityMixin", "entities.ItemFrameMixin", From 836184ab9a33f843bd3592c69a7eb28cb014e982 Mon Sep 17 00:00:00 2001 From: Potatoboy9999 <51728317+PotatoPresident@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:40:12 -0700 Subject: [PATCH 2/2] Implement custom teleport click actions --- .../ServerCommonPacketListenerImplMixin.java | 37 +++++++++++++++---- .../ledger/actions/AbstractActionType.kt | 13 +++++-- .../commands/subcommands/TeleportCommand.kt | 12 +++++- .../ledger/utility/MessageUtils.kt | 1 + 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java index f1eba08f..d6ef4c87 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/ServerCommonPacketListenerImplMixin.java @@ -1,12 +1,19 @@ package com.github.quiltservertools.ledger.mixin; import com.github.quiltservertools.ledger.commands.subcommands.PageCommand; +import com.github.quiltservertools.ledger.commands.subcommands.TeleportCommand; import com.github.quiltservertools.ledger.utility.MessageUtils; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.common.ServerboundCustomClickActionPacket; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerCommonPacketListenerImpl; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.Level; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -16,14 +23,30 @@ public class ServerCommonPacketListenerImplMixin { @Inject(method = "handleCustomClickAction", at = @At("HEAD"), cancellable = true) private void handleLedgerPageChangeClick(ServerboundCustomClickActionPacket packet, CallbackInfo ci) { - if (!packet.id().equals(MessageUtils.INSTANCE.getPageChangeAction())) return; if (!((Object) this instanceof ServerGamePacketListenerImpl game)) return; - ServerPlayer player = game.player; - packet.payload().flatMap(Tag::asCompound) - .flatMap(root -> root.getInt("page")) - .ifPresent(page -> - PageCommand.INSTANCE.page(player.createCommandSourceStack(), page) + + if (packet.id().equals(MessageUtils.INSTANCE.getPageChangeAction())) { + ServerPlayer player = game.player; + packet.payload().flatMap(Tag::asCompound) + .flatMap(root -> root.getInt("page")) + .ifPresent(page -> + PageCommand.INSTANCE.page(player.createCommandSourceStack(), page) + ); + ci.cancel(); + } else if (packet.id().equals(MessageUtils.INSTANCE.getTeleportAction())) { + ServerPlayer player = game.player; + packet.payload().flatMap(Tag::asCompound).ifPresent(tag -> { + var pos = new BlockPos( + tag.getIntOr("x", 0), + tag.getIntOr("y", 0), + tag.getIntOr("z", 0) ); - ci.cancel(); + var world = Identifier.parse(tag.getStringOr("world", "")); + ResourceKey resourceKey = ResourceKey.create(Registries.DIMENSION, world); + ServerLevel serverLevel = game.player.level().getServer().getLevel(resourceKey); + TeleportCommand.INSTANCE.teleport(player, serverLevel, pos); + }); + ci.cancel(); + } } } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt index 876cc638..c5562beb 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt @@ -8,6 +8,7 @@ import com.github.quiltservertools.ledger.utility.literal import net.minecraft.ChatFormatting import net.minecraft.commands.CommandSourceStack import net.minecraft.core.BlockPos +import net.minecraft.nbt.CompoundTag import net.minecraft.network.chat.ClickEvent import net.minecraft.network.chat.Component import net.minecraft.network.chat.HoverEvent @@ -18,6 +19,7 @@ import net.minecraft.server.players.NameAndId import net.minecraft.util.Util import net.minecraft.world.level.Level import java.time.Instant +import java.util.* import kotlin.time.ExperimentalTime abstract class AbstractActionType : ActionType { @@ -98,15 +100,20 @@ abstract class AbstractActionType : ActionType { open fun getLocationMessage(): Component = "${pos.x} ${pos.y} ${pos.z}".literal() .setStyle(TextColorPallet.secondary) .withStyle { + val tag: CompoundTag = CompoundTag().apply { + this.putInt("x", pos.x) + this.putInt("y", pos.y) + this.putInt("z", pos.z) + this.putString("world", (world ?: Level.OVERWORLD.identifier()).toString()) + } + it.withHoverEvent( HoverEvent.ShowText( Component.literal(world?.let { "$it\n" } ?: "") .append(Component.translatable("text.ledger.action_message.location.hover")) ) ).withClickEvent( - ClickEvent.RunCommand( - "/lg tp ${world ?: Level.OVERWORLD.identifier()} ${pos.x} ${pos.y} ${pos.z}" - ) + ClickEvent.Custom(MessageUtils.teleportAction, Optional.of(tag)) ) } } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/TeleportCommand.kt b/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/TeleportCommand.kt index 23a933a6..f6aadf32 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/TeleportCommand.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/TeleportCommand.kt @@ -9,7 +9,9 @@ import net.minecraft.commands.Commands import net.minecraft.commands.arguments.DimensionArgument import net.minecraft.commands.arguments.coordinates.Coordinates import net.minecraft.commands.arguments.coordinates.Vec3Argument +import net.minecraft.core.BlockPos import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer object TeleportCommand : BuildableCommand { private const val BLOCK_CENTER_OFFSET = 0.5 @@ -35,6 +37,14 @@ object TeleportCommand : BuildableCommand { val player = context.source.playerOrException val pos = posArg.getBlockPos(context.source) + teleport(player, world, pos) + + return 1 + } + + fun teleport(player: ServerPlayer, world: ServerLevel, pos: BlockPos) { + if (!Permissions.check(player, "ledger.commands.tp", CommandConsts.PERMISSION_LEVEL)) return + val x = pos.x.toDouble() + BLOCK_CENTER_OFFSET val z = pos.z.toDouble() + BLOCK_CENTER_OFFSET @@ -48,7 +58,5 @@ object TeleportCommand : BuildableCommand { player.xRot, true ) - - return 1 } } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt index af3c4f72..8188c68f 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt @@ -25,6 +25,7 @@ import kotlin.time.toKotlinDuration object MessageUtils { val pageChangeAction: Identifier = Ledger.identifier("page-change") + val teleportAction: Identifier = Ledger.identifier("teleport") @OptIn(ExperimentalTime::class) suspend fun sendSearchResults(source: CommandSourceStack, results: SearchResults, header: Component) {