Skip to content

Commit 943473b

Browse files
authored
Added NoGhostItems. Makes sure item transactions got accepted before doing client side inventory change (#305)
* Sketch for an item sync module * Super strict works now.
1 parent 02e6f0e commit 943473b

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

src/main/java/com/lambda/mixin/player/MixinPlayerControllerMP.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@
22

33
import com.lambda.client.event.LambdaEventBus;
44
import com.lambda.client.event.events.PlayerAttackEvent;
5+
import com.lambda.client.module.modules.player.NoGhostItems;
56
import com.lambda.client.module.modules.player.TpsSync;
67
import com.lambda.client.util.TpsCalculator;
78
import net.minecraft.block.state.IBlockState;
89
import net.minecraft.client.multiplayer.PlayerControllerMP;
910
import net.minecraft.entity.Entity;
1011
import net.minecraft.entity.player.EntityPlayer;
12+
import net.minecraft.inventory.ClickType;
13+
import net.minecraft.item.ItemStack;
1114
import net.minecraft.util.math.BlockPos;
1215
import net.minecraft.world.World;
1316
import org.spongepowered.asm.mixin.Mixin;
1417
import org.spongepowered.asm.mixin.injection.At;
1518
import org.spongepowered.asm.mixin.injection.Inject;
1619
import org.spongepowered.asm.mixin.injection.Redirect;
1720
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
21+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
1822

1923
@Mixin(PlayerControllerMP.class)
2024
public class MixinPlayerControllerMP {
@@ -33,4 +37,12 @@ public void attackEntity(EntityPlayer playerIn, Entity targetEntity, CallbackInf
3337
ci.cancel();
3438
}
3539
}
40+
41+
@Inject(method = "windowClick", at = @At("HEAD"), cancellable = true)
42+
public void onWindowClick(int windowId, int slotId, int mouseButton, ClickType type, EntityPlayer player, CallbackInfoReturnable<ItemStack> cir) {
43+
if (NoGhostItems.INSTANCE.isEnabled()) {
44+
NoGhostItems.INSTANCE.handleWindowClick(windowId, slotId, mouseButton, type, player);
45+
cir.cancel();
46+
}
47+
}
3648
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.lambda.client.module.modules.player
2+
3+
import com.lambda.client.event.events.PacketEvent
4+
import com.lambda.client.event.listener.listener
5+
import com.lambda.client.module.Category
6+
import com.lambda.client.module.Module
7+
import com.lambda.client.util.threads.runSafe
8+
import net.minecraft.entity.player.EntityPlayer
9+
import net.minecraft.inventory.ClickType
10+
import net.minecraft.item.ItemStack
11+
import net.minecraft.network.play.client.CPacketClickWindow
12+
import net.minecraft.network.play.server.SPacketConfirmTransaction
13+
14+
object NoGhostItems : Module(
15+
name = "NoGhostItems",
16+
description = "Syncs inventory transactions for strict environments",
17+
category = Category.PLAYER
18+
) {
19+
private val timeout by setting("Timeout in ticks", 5, 1..50, 1)
20+
21+
private var pendingTransaction: InventoryTransaction? = null
22+
private var lastPending = System.currentTimeMillis()
23+
24+
init {
25+
listener<PacketEvent.Receive> { event ->
26+
if (event.packet is SPacketConfirmTransaction) {
27+
pendingTransaction?.let {
28+
it.player.openContainer.slotClick(it.slotId, it.mouseButton, it.type, it.player)
29+
pendingTransaction = null
30+
}
31+
}
32+
}
33+
}
34+
35+
fun handleWindowClick(windowId: Int, slotId: Int, mouseButton: Int, type: ClickType, player: EntityPlayer) {
36+
val transaction = InventoryTransaction(windowId, slotId, mouseButton, type, player)
37+
38+
if (pendingTransaction == null || System.currentTimeMillis() - lastPending > timeout * 50L) {
39+
val transactionID = transaction.player.openContainer.getNextTransactionID(transaction.player.inventory)
40+
pendingTransaction = transaction
41+
lastPending = System.currentTimeMillis()
42+
43+
runSafe {
44+
connection.sendPacket(CPacketClickWindow(transaction.windowId, transaction.slotId, transaction.mouseButton, transaction.type, ItemStack.EMPTY, transactionID))
45+
}
46+
}
47+
}
48+
49+
data class InventoryTransaction(val windowId: Int, val slotId: Int, val mouseButton: Int, val type: ClickType, val player: EntityPlayer) {
50+
override fun toString(): String {
51+
return "windowId: $windowId slotId: $slotId mouseButton: $mouseButton type: ${type.name} player: ${player.name}"
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)