Skip to content

Commit 683aadb

Browse files
committed
accurately track when the player's screen handler changes
1 parent e8a5c6f commit 683aadb

File tree

3 files changed

+68
-9
lines changed

3 files changed

+68
-9
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.mixin.entity;
19+
20+
import com.lambda.interaction.request.inventory.InventoryManager;
21+
import net.minecraft.client.MinecraftClient;
22+
import net.minecraft.client.gui.screen.ingame.HandledScreens;
23+
import net.minecraft.screen.ScreenHandler;
24+
import net.minecraft.screen.ScreenHandlerType;
25+
import net.minecraft.text.Text;
26+
import org.spongepowered.asm.mixin.Mixin;
27+
import org.spongepowered.asm.mixin.injection.At;
28+
import org.spongepowered.asm.mixin.injection.Inject;
29+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
30+
31+
@Mixin(HandledScreens.Provider.class)
32+
public interface HandledScreensMixin {
33+
@Inject(method = "open", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setScreen(Lnet/minecraft/client/gui/screen/Screen;)V"))
34+
private void injectOpen(Text name, ScreenHandlerType<ScreenHandler> type, MinecraftClient client, int id, CallbackInfo ci) {
35+
if (client.player == null) return;
36+
InventoryManager.onSetScreenHandler(client.player.currentScreenHandler);
37+
}
38+
}

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

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717

1818
package com.lambda.interaction.request.inventory
1919

20-
import com.lambda.context.AutomatedSafeContext
2120
import com.lambda.config.AutomationConfig.Companion.DEFAULT
21+
import com.lambda.context.AutomatedSafeContext
2222
import com.lambda.context.SafeContext
2323
import com.lambda.event.EventFlow.post
24+
import com.lambda.event.events.PacketEvent
2425
import com.lambda.event.events.TickEvent
2526
import com.lambda.event.events.UpdateManagerEvent
2627
import com.lambda.event.listener.SafeListener.Companion.listen
@@ -38,6 +39,7 @@ import com.lambda.util.item.ItemStackUtils.equal
3839
import com.llamalad7.mixinextras.injector.wrapoperation.Operation
3940
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen
4041
import net.minecraft.item.ItemStack
42+
import net.minecraft.network.packet.c2s.play.CloseHandledScreenC2SPacket
4143
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket
4244
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket
4345
import net.minecraft.screen.PlayerScreenHandler
@@ -60,14 +62,16 @@ object InventoryManager : RequestHandler<InventoryRequest>(
6062
private var actions = mutableListOf<InventoryAction>()
6163

6264
private var slots = listOf<ItemStack>()
63-
private var alteredSlots = LimitedDecayQueue<Pair<Int, Pair<ItemStack, ItemStack>>>(
65+
private var alteredSlots = LimitedDecayQueue<InventoryChange>(
6466
Int.MAX_VALUE, DEFAULT.desyncTimeout * 50L
6567
)
6668

6769
private var screenHandler: ScreenHandler? = null
6870
set(value) {
69-
if (value != null && field?.syncId != value.syncId)
71+
if (value != null && field?.syncId != value.syncId) {
72+
alteredSlots.clear()
7073
slots = getStacks(value.slots)
74+
}
7175
field = value
7276
}
7377

@@ -92,6 +96,14 @@ object InventoryManager : RequestHandler<InventoryRequest>(
9296
actions = mutableListOf()
9397
}
9498

99+
listen<PacketEvent.Send.Pre> { event ->
100+
if (event.packet is CloseHandledScreenC2SPacket &&
101+
event.packet.syncId != player.currentScreenHandler.syncId
102+
) {
103+
screenHandler = player.playerScreenHandler
104+
}
105+
}
106+
95107
return "Loaded Inventory Manager"
96108
}
97109

@@ -136,7 +148,6 @@ object InventoryManager : RequestHandler<InventoryRequest>(
136148
activeRequest?.let { active ->
137149
PlaceManager.logger.debug("Processing request", active)
138150
if (tickStage !in active.inventoryConfig.tickStageMask && active.nowOrNothing) return
139-
screenHandler = player.currentScreenHandler
140151
val iterator = actions.iterator()
141152
while (iterator.hasNext()) {
142153
val action = iterator.next()
@@ -169,7 +180,7 @@ object InventoryManager : RequestHandler<InventoryRequest>(
169180
if (player.currentScreenHandler.syncId != screenHandler?.syncId) return
170181
val changes = screenHandler?.slots
171182
?.filter { !it.stack.equal(slots[it.id]) }
172-
?.map { Pair(it.id, Pair(slots[it.id], it.stack.copy())) }
183+
?.map { InventoryChange(it.id, slots[it.id], it.stack.copy()) }
173184
?: emptyList()
174185
alteredSlots.addAll(changes)
175186
slots = getStacks(player.currentScreenHandler.slots)
@@ -189,7 +200,6 @@ object InventoryManager : RequestHandler<InventoryRequest>(
189200
original.call(packet)
190201
return
191202
}
192-
screenHandler = player.currentScreenHandler
193203
val packetScreenHandler =
194204
when (packet.syncId) {
195205
0 -> player.playerScreenHandler
@@ -199,7 +209,7 @@ object InventoryManager : RequestHandler<InventoryRequest>(
199209
val alteredContents = mutableListOf<ItemStack>()
200210
packet.contents.forEachIndexed { index, incomingStack ->
201211
val matches = alteredSlots.removeIf { cached ->
202-
incomingStack.equal(cached.second.second)
212+
incomingStack.equal(cached.after)
203213
}
204214
if (matches) alteredContents.add(packetScreenHandler.slots[index].stack)
205215
else alteredContents.add(incomingStack)
@@ -218,7 +228,6 @@ object InventoryManager : RequestHandler<InventoryRequest>(
218228
@JvmStatic
219229
fun onSlotUpdate(packet: ScreenHandlerSlotUpdateS2CPacket, original: Operation<Void>) {
220230
runSafe {
221-
screenHandler = player.currentScreenHandler
222231
if (!mc.isOnThread || !DEFAULT.avoidDesync) {
223232
original.call(packet)
224233
return
@@ -231,7 +240,7 @@ object InventoryManager : RequestHandler<InventoryRequest>(
231240
} ?: false
232241

233242
val matches = alteredSlots.removeIf {
234-
it.first == packet.slot && it.second.second.equal(itemStack)
243+
it.syncId == packet.slot && it.after.equal(itemStack)
235244
}
236245

237246
if (packet.syncId == 0) {
@@ -255,5 +264,16 @@ object InventoryManager : RequestHandler<InventoryRequest>(
255264
original.call(packet)
256265
}
257266

267+
@JvmStatic
268+
fun onSetScreenHandler(screenHandler: ScreenHandler) {
269+
this.screenHandler = screenHandler
270+
}
271+
272+
private data class InventoryChange(
273+
val syncId: Int,
274+
val before: ItemStack,
275+
val after: ItemStack
276+
)
277+
258278
override fun preEvent() = UpdateManagerEvent.Inventory.post()
259279
}

src/main/resources/lambda.mixins.common.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"entity.ClientPlayInteractionManagerMixin",
1414
"entity.EntityMixin",
1515
"entity.FireworkRocketEntityMixin",
16+
"entity.HandledScreensMixin",
1617
"entity.LivingEntityMixin",
1718
"entity.PlayerEntityMixin",
1819
"entity.PlayerInventoryMixin",

0 commit comments

Comments
 (0)