Skip to content

Commit 4ff5cdd

Browse files
committed
anti desync measures for inventory packets
1 parent 9ee4e53 commit 4ff5cdd

File tree

2 files changed

+67
-21
lines changed

2 files changed

+67
-21
lines changed

src/main/kotlin/com/lambda/context/AutomationConfig.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ object AutomationConfig : Configurable(LambdaConfig), Automated {
5959
override val hotbarConfig = HotbarSettings(this, Group.Hotbar)
6060
override val eatConfig = EatSettings(this, Group.Eat)
6161

62+
val avoidDesync by setting("Avoid Desync", true, "Cancels incoming inventory update packets if they match previous actions").group(Group.Debug)
63+
val maxDesyncCache by setting("Max Desync Cache", 10, 1..30, 1, "Maximum cached previous inventory actions")
64+
val desyncTimeout by setting("Desync Timeout", 10, 1..30, 1, unit = " ticks", description = "Time to store previous inventory actions before dropping the cache").group(Group.Debug)
6265
val showAllEntries by setting("Show All Entries", false, "Show all entries in the task tree").group(Group.Debug)
6366
val shrinkFactor by setting("Shrink Factor", 0.001, 0.0..1.0, 0.001).group(Group.Debug)
6467
val ignoreItemDropWarnings by setting("Ignore Drop Warnings", false, "Hides the item drop warnings from the break manager").group(Group.Debug)

src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,47 @@
1818
package com.lambda.interaction.request.inventory
1919

2020
import com.lambda.context.AutomatedSafeContext
21+
import com.lambda.context.AutomationConfig
22+
import com.lambda.context.AutomationConfig.avoidDesync
2123
import com.lambda.context.SafeContext
2224
import com.lambda.event.EventFlow.post
25+
import com.lambda.event.events.PacketEvent
2326
import com.lambda.event.events.TickEvent
2427
import com.lambda.event.events.UpdateManagerEvent
2528
import com.lambda.event.listener.SafeListener.Companion.listen
2629
import com.lambda.interaction.request.Logger
2730
import com.lambda.interaction.request.RequestHandler
28-
import com.lambda.interaction.request.inventory.InventoryManager.activeRequest
29-
import com.lambda.interaction.request.inventory.InventoryManager.processRequest
3031
import com.lambda.interaction.request.placing.PlaceManager
3132
import com.lambda.module.hud.ManagerDebugLoggers.inventoryManagerLogger
32-
import com.lambda.threading.runSafeAutomated
33+
import com.lambda.util.Communication.info
34+
import com.lambda.util.collections.LimitedDecayQueue
35+
import com.lambda.util.item.ItemStackUtils.equal
36+
import net.minecraft.item.ItemStack
37+
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket
38+
import net.minecraft.screen.ScreenHandler
39+
import net.minecraft.screen.slot.Slot
3340

3441
object InventoryManager : RequestHandler<InventoryRequest>(
3542
1,
3643
TickEvent.Pre,
3744
TickEvent.Input.Pre,
3845
TickEvent.Input.Post,
39-
TickEvent.Player.Post,
40-
onOpen = { activeRequest?.let { it.runSafeAutomated { processRequest(it) } } }
46+
TickEvent.Player.Post
4147
), Logger {
42-
private var activeRequest: InventoryRequest? = null
4348
private var actions = mutableListOf<SafeContext.() -> Unit>()
4449

50+
private var slots = listOf<ItemStack>()
51+
private var alteredSlots = LimitedDecayQueue<Pair<Int, Pair<ItemStack, ItemStack>>>(
52+
AutomationConfig.maxDesyncCache, AutomationConfig.desyncTimeout * 50L
53+
)
54+
55+
private var screenHandler: ScreenHandler? = null
56+
set(value) {
57+
if (value != null && field !== value)
58+
slots = getStacks(value.slots)
59+
field = value
60+
}
61+
4562
private var maxActionsThisSecond = 0
4663
private var actionsThisSecond = 0
4764
private var secondCounter = 0
@@ -52,36 +69,55 @@ object InventoryManager : RequestHandler<InventoryRequest>(
5269
override fun load(): String {
5370
super.load()
5471

72+
listen<PacketEvent.Receive.Pre>(priority = Int.MIN_VALUE) { event ->
73+
if (!avoidDesync) return@listen
74+
val packet = event.packet as? InventoryS2CPacket ?: return@listen
75+
screenHandler = player.currentScreenHandler
76+
val packetScreenHandler =
77+
if (packet.syncId == 0) player.playerScreenHandler
78+
else player.currentScreenHandler
79+
event.cancel()
80+
val alteredContents = mutableListOf<ItemStack>()
81+
packet.contents.forEachIndexed { index, incomingStack ->
82+
val matches = alteredSlots.removeIf { cached ->
83+
incomingStack.equal(cached.second.second)
84+
}
85+
if (matches) alteredContents.add(packetScreenHandler.slots[index].stack)
86+
else alteredContents.add(incomingStack)
87+
if (matches) info(matches.toString())
88+
}
89+
mc.executeSync {
90+
packetScreenHandler.updateSlotStacks(packet.revision(), alteredContents, packet.cursorStack())
91+
}
92+
}
93+
5594
listen<TickEvent.Post>(priority = Int.MIN_VALUE) {
95+
if (avoidDesync) {
96+
alteredSlots.addAll(gatherInventoryChanges())
97+
slots = getStacks(player.currentScreenHandler.slots)
98+
}
5699
if (++secondCounter >= 20) {
57100
secondCounter = 0
58101
actionsThisSecond = 0
59102
}
60103
actionsThisTick = 0
61-
activeRequest = null
62104
actions.clear()
63105
}
64106

65107
return "Loaded Inventory Manager"
66108
}
67109

68110
override fun AutomatedSafeContext.handleRequest(request: InventoryRequest) {
69-
if (activeRequest != null) return
70111
if (request.actions.size >= request.inventoryConfig.actionsPerSecond - actionsThisSecond &&
71112
!request.settleForLess &&
72113
!request.mustPerform) return
73-
activeRequest = request
74-
processRequest(request)
75-
if (actionsThisTick > 0) activeThisTick = true
76-
}
114+
if (tickStage !in inventoryConfig.tickStageMask) return
77115

78-
private fun AutomatedSafeContext.processRequest(request: InventoryRequest) {
79116
if (request.fresh) populateFrom(request)
80117

81-
if (tickStage !in inventoryConfig.tickStageMask) return
82-
83118
PlaceManager.logger.debug("Processing request", request)
84119

120+
screenHandler = player.currentScreenHandler
85121
val iterator = actions.iterator()
86122
while (iterator.hasNext()) {
87123
if (actionsThisSecond + 1 > maxActionsThisSecond && !request.mustPerform) break
@@ -92,13 +128,11 @@ object InventoryManager : RequestHandler<InventoryRequest>(
92128
}
93129

94130
if (actions.isEmpty()) {
95-
activeRequest?.let { request ->
96-
logger.debug("Clearing active request", activeRequest)
97-
request.done = true
98-
request.onComplete?.invoke(this)
99-
activeRequest = null
100-
}
131+
request.done = true
132+
request.onComplete?.invoke(this)
101133
}
134+
135+
if (actionsThisTick > 0) activeThisTick = true
102136
}
103137

104138
private fun populateFrom(request: InventoryRequest) {
@@ -107,5 +141,14 @@ object InventoryManager : RequestHandler<InventoryRequest>(
107141
maxActionsThisSecond = request.inventoryConfig.actionsPerSecond
108142
}
109143

144+
private fun SafeContext.gatherInventoryChanges() =
145+
if (player.currentScreenHandler !== screenHandler) emptyList()
146+
else screenHandler?.slots
147+
?.filter { it.stack != slots[it.id] }
148+
?.map { Pair(it.id, Pair(slots[it.id], it.stack.copy())) }
149+
?: emptyList()
150+
151+
private fun getStacks(slots: Collection<Slot>) = slots.map { it.stack.copy() }
152+
110153
override fun preEvent() = UpdateManagerEvent.Inventory.post()
111154
}

0 commit comments

Comments
 (0)