From 33ba5271aad8c0ef8acc249736525e98112ec32b Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 30 Apr 2022 03:15:43 +0200 Subject: [PATCH 1/2] Sketch for an item sync module --- .../mixin/player/MixinPlayerControllerMP.java | 12 +++++ .../module/modules/player/NoGhostItems.kt | 54 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt diff --git a/src/main/java/com/lambda/mixin/player/MixinPlayerControllerMP.java b/src/main/java/com/lambda/mixin/player/MixinPlayerControllerMP.java index c0e173f53..14a2b112f 100644 --- a/src/main/java/com/lambda/mixin/player/MixinPlayerControllerMP.java +++ b/src/main/java/com/lambda/mixin/player/MixinPlayerControllerMP.java @@ -2,12 +2,15 @@ import com.lambda.client.event.LambdaEventBus; import com.lambda.client.event.events.PlayerAttackEvent; +import com.lambda.client.module.modules.player.NoGhostItems; import com.lambda.client.module.modules.player.TpsSync; import com.lambda.client.util.TpsCalculator; import net.minecraft.block.state.IBlockState; import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.ClickType; +import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; @@ -15,6 +18,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(PlayerControllerMP.class) public class MixinPlayerControllerMP { @@ -33,4 +37,12 @@ public void attackEntity(EntityPlayer playerIn, Entity targetEntity, CallbackInf ci.cancel(); } } + + @Inject(method = "windowClick", at = @At("HEAD"), cancellable = true) + public void onWindowClick(int windowId, int slotId, int mouseButton, ClickType type, EntityPlayer player, CallbackInfoReturnable cir) { + if (NoGhostItems.INSTANCE.isEnabled()) { + NoGhostItems.INSTANCE.handleWindowClick(windowId, slotId, mouseButton, type, player); + cir.cancel(); + } + } } diff --git a/src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt b/src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt new file mode 100644 index 000000000..c84998275 --- /dev/null +++ b/src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt @@ -0,0 +1,54 @@ +package com.lambda.client.module.modules.player + +import com.lambda.client.LambdaMod +import com.lambda.client.event.events.PacketEvent +import com.lambda.client.event.listener.listener +import com.lambda.client.module.Category +import com.lambda.client.module.Module +import com.lambda.client.util.threads.runSafe +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.ClickType +import net.minecraft.item.ItemStack +import net.minecraft.network.play.client.CPacketClickWindow +import net.minecraft.network.play.server.SPacketConfirmTransaction +import java.util.concurrent.ConcurrentHashMap + +object NoGhostItems : Module( + name = "NoGhostItems", + description = "Syncs inventory transactions for strict environments", + category = Category.PLAYER +) { + private val pendingTransactions = ConcurrentHashMap() + + init { + listener { event -> + if (event.packet is SPacketConfirmTransaction) { + pendingTransactions[event.packet.actionNumber]?.let { + it.player.openContainer.slotClick(it.slotId, it.mouseButton, it.type, it.player) + pendingTransactions.remove(event.packet.actionNumber) + LambdaMod.LOG.info("Accepted transaction: ${event.packet.actionNumber}") + } + } + } + } + + fun handleWindowClick(windowId: Int, slotId: Int, mouseButton: Int, type: ClickType, player: EntityPlayer) { + val transaction = InventoryTransaction(windowId, slotId, mouseButton, type, player) + + if (!pendingTransactions.values.contains(transaction)) { + val transactionID = transaction.player.openContainer.getNextTransactionID(transaction.player.inventory) + pendingTransactions[transactionID] = transaction + + LambdaMod.LOG.info("Started transaction: transactionID: $transactionID transaction: $transaction") + runSafe { + connection.sendPacket(CPacketClickWindow(transaction.windowId, transaction.slotId, transaction.mouseButton, transaction.type, ItemStack.EMPTY, transactionID)) + } + } + } + + data class InventoryTransaction(val windowId: Int, val slotId: Int, val mouseButton: Int, val type: ClickType, val player: EntityPlayer) { + override fun toString(): String { + return "windowId: $windowId slotId: $slotId mouseButton: $mouseButton type: ${type.name} player: ${player.name}" + } + } +} \ No newline at end of file From 26845e3daaec76f2fa0b54ac037d3cc2210322f1 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 1 May 2022 07:21:57 +0200 Subject: [PATCH 2/2] Super strict works now. --- .../module/modules/player/NoGhostItems.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt b/src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt index c84998275..b7b373a50 100644 --- a/src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt +++ b/src/main/kotlin/com/lambda/client/module/modules/player/NoGhostItems.kt @@ -1,6 +1,5 @@ package com.lambda.client.module.modules.player -import com.lambda.client.LambdaMod import com.lambda.client.event.events.PacketEvent import com.lambda.client.event.listener.listener import com.lambda.client.module.Category @@ -11,22 +10,23 @@ import net.minecraft.inventory.ClickType import net.minecraft.item.ItemStack import net.minecraft.network.play.client.CPacketClickWindow import net.minecraft.network.play.server.SPacketConfirmTransaction -import java.util.concurrent.ConcurrentHashMap object NoGhostItems : Module( name = "NoGhostItems", description = "Syncs inventory transactions for strict environments", category = Category.PLAYER ) { - private val pendingTransactions = ConcurrentHashMap() + private val timeout by setting("Timeout in ticks", 5, 1..50, 1) + + private var pendingTransaction: InventoryTransaction? = null + private var lastPending = System.currentTimeMillis() init { listener { event -> if (event.packet is SPacketConfirmTransaction) { - pendingTransactions[event.packet.actionNumber]?.let { + pendingTransaction?.let { it.player.openContainer.slotClick(it.slotId, it.mouseButton, it.type, it.player) - pendingTransactions.remove(event.packet.actionNumber) - LambdaMod.LOG.info("Accepted transaction: ${event.packet.actionNumber}") + pendingTransaction = null } } } @@ -35,11 +35,11 @@ object NoGhostItems : Module( fun handleWindowClick(windowId: Int, slotId: Int, mouseButton: Int, type: ClickType, player: EntityPlayer) { val transaction = InventoryTransaction(windowId, slotId, mouseButton, type, player) - if (!pendingTransactions.values.contains(transaction)) { + if (pendingTransaction == null || System.currentTimeMillis() - lastPending > timeout * 50L) { val transactionID = transaction.player.openContainer.getNextTransactionID(transaction.player.inventory) - pendingTransactions[transactionID] = transaction + pendingTransaction = transaction + lastPending = System.currentTimeMillis() - LambdaMod.LOG.info("Started transaction: transactionID: $transactionID transaction: $transaction") runSafe { connection.sendPacket(CPacketClickWindow(transaction.windowId, transaction.slotId, transaction.mouseButton, transaction.type, ItemStack.EMPTY, transactionID)) }