Skip to content

Commit

Permalink
fix: Swap only (#6)
Browse files Browse the repository at this point in the history
* fix: `MASS_CRAFT_SWAPS` use inventory, not only hotbars

* debug

* style

* fix

* fix: make sure the fixes are client side

* fix: sakura-ryoko#5

* fix: clientbound packet buffer

* debug
  • Loading branch information
zly2006 authored Jul 23, 2024
1 parent dc9648b commit 0be120c
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 30 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ repositories {
maven { url 'https://jitpack.io' }
}

loom {
accessWidenerPath = file("src/main/resources/minihud.accesswidener")
}

dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.mappings_version}:v2"
Expand Down
37 changes: 33 additions & 4 deletions src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.inventory.RecipeInputInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.recipe.Recipe;
import net.minecraft.screen.CraftingScreenHandler;
import net.minecraft.screen.slot.Slot;
import fi.dy.masa.malilib.config.options.ConfigHotkey;
import fi.dy.masa.malilib.gui.GuiBase;
Expand Down Expand Up @@ -179,6 +175,18 @@ else if (key == Hotkeys.SORT_INVENTORY.getKeybind())
return false;
}

private static void debugPrintInv(RecipeInputInventory inv)
{
for (int i = 0; i < inv.getHeight(); i++)
{
for (int j = 0; j < inv.getWidth(); j++)
{
System.out.print(inv.getStack(i * inv.getWidth() + j) + " ");
}
System.out.println();
}
}

@Override
public void onClientTick(MinecraftClient mc)
{
Expand All @@ -204,6 +212,7 @@ public void onClientTick(MinecraftClient mc)
return;
}

InventoryUtils.bufferInvUpdates = true;
Slot outputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui);

if (outputSlot != null)
Expand All @@ -224,16 +233,31 @@ public void onClientTick(MinecraftClient mc)
InventoryUtils.setInhibitCraftingOutputUpdate(true);
InventoryUtils.throwAllCraftingResultsToGround(recipe, gui);
InventoryUtils.throwAllNonRecipeItemsToGround(recipe, gui);
RecipeInputInventory inv = ((IMixinCraftingResultSlot) (outputSlot)).itemscroller_getCraftingInventory();
System.out.println("Before:");
debugPrintInv(inv);
try
{
Thread.sleep(0);
} catch (InterruptedException e)
{
}
InventoryUtils.setCraftingGridContentsUsingSwaps(gui, mc.player.getInventory(), recipe, outputSlot);
System.out.println("After:");
debugPrintInv(inv);
InventoryUtils.setInhibitCraftingOutputUpdate(false);
InventoryUtils.updateCraftingOutputSlot(outputSlot);

System.out.printf("Output slot: %s\n", outputSlot.getStack());

if (InventoryUtils.areStacksEqual(outputSlot.getStack(), recipe.getResult()) == false)
{
break;
}

InventoryUtils.shiftClickSlot(gui, outputSlot.id);
System.out.println("Shift clicked");
debugPrintInv(inv);
}
}
else
Expand Down Expand Up @@ -270,6 +294,11 @@ public void onClientTick(MinecraftClient mc)
}

this.massCraftTicker = 0;
InventoryUtils.bufferInvUpdates = false;
InventoryUtils.invUpdatesBuffer.removeIf(packet -> {
packet.apply(mc.getNetworkHandler());
return true;
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import fi.dy.masa.itemscroller.util.InventoryUtils;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket;
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.network.packet.s2c.play.StatisticsS2CPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
Expand All @@ -19,4 +21,39 @@ private void onPong(StatisticsS2CPacket packet, CallbackInfo ci)
ci.cancel();
}
}

@Inject(
method = "onInventory",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V",
shift = At.Shift.AFTER
),
cancellable = true)
private void onInventory(InventoryS2CPacket packet, CallbackInfo ci)
{
if (InventoryUtils.bufferInvUpdates)
{
InventoryUtils.invUpdatesBuffer.add(packet);
ci.cancel();
}
}

@Inject(
method = "onScreenHandlerSlotUpdate",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V",
shift = At.Shift.AFTER
),
cancellable = true
)
private void onScreenHandlerSlotUpdate(ScreenHandlerSlotUpdateS2CPacket packet, CallbackInfo ci)
{
if (InventoryUtils.bufferInvUpdates)
{
InventoryUtils.invUpdatesBuffer.add(packet);
ci.cancel();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package fi.dy.masa.itemscroller.mixin;

import net.minecraft.client.MinecraftClient;
import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket;
import net.minecraft.screen.ScreenHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
Expand All @@ -26,6 +29,13 @@ private void cancelWindowClicksWhileReplayingBufferedPackets(CallbackInfo ci)
target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V"))
private void bufferClickPacketsAndCancel(ClientPlayNetworkHandler netHandler, Packet<?> packet)
{
if (packet instanceof ClickSlotC2SPacket clickPacket)
{
MinecraftClient mc = MinecraftClient.getInstance();
System.out.printf("clickPacket: type: %s button: %d, slot: %d, (after) cursor item: %s\n", clickPacket.getActionType(), clickPacket.getButton(), clickPacket.getSlot(), clickPacket.getStack());
clickPacket.getModifiedStacks().forEach((integer, stack) -> System.out.printf("%d = %s, ", integer, stack));
System.out.println();
}
if (ClickPacketBuffer.shouldBufferClickPackets())
{
ClickPacketBuffer.bufferPacket(packet);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fi.dy.masa.itemscroller.mixin;

import net.minecraft.client.MinecraftClient;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -25,7 +26,10 @@ public abstract class MixinCraftingScreenHandler
@Inject(method = "onContentChanged", at = @At("RETURN"))
private void onSlotChangedCraftingGrid(net.minecraft.inventory.Inventory inventory, CallbackInfo ci)
{
InventoryUtils.onSlotChangedCraftingGrid(this.player, this.input, this.result);
if (MinecraftClient.getInstance().isOnThread())
{
InventoryUtils.onSlotChangedCraftingGrid(this.player, this.input, this.result);
}
}

@Inject(method = "updateResult", at = @At("RETURN"))
Expand All @@ -37,6 +41,9 @@ private static void onUpdateResult(
CraftingResultInventory resultInv,
RecipeEntry<CraftingRecipe> recipeEntry, CallbackInfo ci)
{
InventoryUtils.onSlotChangedCraftingGrid(player, craftingInventory, resultInv);
if (MinecraftClient.getInstance().isOnThread())
{
InventoryUtils.onSlotChangedCraftingGrid(player, craftingInventory, resultInv);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package fi.dy.masa.itemscroller.mixin.debug;

import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket;
import net.minecraft.screen.slot.Slot;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(ServerPlayNetworkHandler.class)
public class MixinServerPlayNetworkHandler
{
@Shadow public ServerPlayerEntity player;

@Inject(method = "onClickSlot", at = @At(value = "JUMP", opcode = Opcodes.IFEQ, ordinal = 2))
private void beforeSendUpdates(ClickSlotC2SPacket packet, CallbackInfo ci) {
for (Slot slot : this.player.currentScreenHandler.slots)
{
ItemStack clientStack = this.player.currentScreenHandler.trackedStacks.get(slot.id);
if (!ItemStack.areItemsEqual(slot.getStack(), clientStack))
{
System.err.printf("Slot %d desync! Server: %s, Client: %s\n", slot.id, slot.getStack(), clientStack);
}
}
}
}
16 changes: 15 additions & 1 deletion src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,21 @@ public void ensureRecipeSizeAndClearRecipe(int size)
private void lookupVanillaRecipe(World world) {
this.vanillaRecipe = null;
var mc = MinecraftClient.getInstance();
for (RecipeEntry<CraftingRecipe> match : mc.world.getRecipeManager().getAllMatches(RecipeType.CRAFTING, CraftingRecipeInput.create(3, 3, Arrays.asList(recipe)), world))
int recipeSize;
if (recipe.length == 4)
{
recipeSize = 2;
}
else if (recipe.length == 9)
{
recipeSize = 3;
}
else
{
return;
}

for (RecipeEntry<CraftingRecipe> match : mc.world.getRecipeManager().getAllMatches(RecipeType.CRAFTING, CraftingRecipeInput.create(recipeSize, recipeSize, Arrays.asList(recipe)), world))
{
if (InventoryUtils.areStacksEqual(result, match.value().getResult(world.getRegistryManager())))
{
Expand Down
45 changes: 26 additions & 19 deletions src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import net.minecraft.client.gui.screen.ingame.MerchantScreen;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.component.ComponentMap;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.ContainerComponent;
import net.minecraft.entity.player.PlayerEntity;
Expand All @@ -27,12 +26,11 @@
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.RecipeInputInventory;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.c2s.play.ClientStatusC2SPacket;
import net.minecraft.network.packet.c2s.query.QueryPingC2SPacket;
import net.minecraft.network.packet.s2c.play.StatisticsS2CPacket;
import net.minecraft.network.packet.s2c.query.PingResultS2CPacket;
import net.minecraft.recipe.CraftingRecipe;
import net.minecraft.recipe.RecipeEntry;
import net.minecraft.recipe.RecipeType;
Expand All @@ -45,7 +43,6 @@
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.screen.slot.TradeOutputSlot;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.village.TradeOffer;
import net.minecraft.village.TradeOfferList;
import net.minecraft.world.GameRules;
Expand Down Expand Up @@ -79,6 +76,8 @@ public class InventoryUtils
private static boolean inhibitCraftResultUpdate;
private static Runnable selectedSlotUpdateTask;
public static boolean assumeEmptyShulkerStacking = false;
public static boolean bufferInvUpdates = false;
public static List<Packet<ClientPlayPacketListener>> invUpdatesBuffer = new ArrayList<>();

public static void setInhibitCraftingOutputUpdate(boolean inhibitUpdate)
{
Expand All @@ -89,10 +88,10 @@ public static void onSlotChangedCraftingGrid(PlayerEntity player,
RecipeInputInventory craftMatrix,
CraftingResultInventory inventoryCraftResult)
{
if (inhibitCraftResultUpdate && Configs.Generic.MASS_CRAFT_INHIBIT_MID_UPDATES.getBooleanValue())
{
return;
}
// if (inhibitCraftResultUpdate && Configs.Generic.MASS_CRAFT_INHIBIT_MID_UPDATES.getBooleanValue())
// {
// return;
// }

if (Configs.Generic.CLIENT_CRAFTING_FIX.getBooleanValue())
{
Expand Down Expand Up @@ -1688,31 +1687,39 @@ public static void setCraftingGridContentsUsingSwaps(HandledScreen<? extends Scr

for (int i = 0, slotNum = range.getFirst(); i < rangeSlots && slotNum < invSlots; i++, slotNum++)
{
Slot slotTmp = gui.getScreenHandler().getSlot(slotNum);
Slot craftingTableSlot = gui.getScreenHandler().getSlot(slotNum);
ItemStack recipeStack = recipeItems[i];
ItemStack slotStack = slotTmp.getStack();
boolean recipeHasItem = isStackEmpty(recipeStack) == false;
ItemStack slotStack = craftingTableSlot.getStack();

if (areStacksEqual(recipeStack, slotStack) == false)
{
if (recipeHasItem == false)
if (recipeStack.isEmpty())
{
toRemove.add(slotNum);
}
else
{
int index = getPlayerInventoryIndexWithItem(recipeStack, inv);
int index = getSlotNumberOfLargestMatchingStackFromDifferentInventory(gui.getScreenHandler(), craftingTableSlot, recipeStack);

if (index >= 0)
{
clickSlot(gui, slotNum, index, SlotActionType.SWAP);
Slot ingredientSlot = gui.getScreenHandler().getSlot(index);
if (ingredientSlot.inventory instanceof PlayerInventory && ingredientSlot.getIndex() < 9)
{
// hotbar
clickSlot(gui, slotNum, ingredientSlot.getIndex(), SlotActionType.SWAP);
}
else
{
swapSlots(gui, slotNum, index);
}
movedSomething = true;
}
}
}
}

movedSomething |= (toRemove.isEmpty() == false);
movedSomething |= !toRemove.isEmpty();

for (int slotNum : toRemove)
{
Expand Down Expand Up @@ -3034,9 +3041,9 @@ public static void dropStack(HandledScreen<? extends ScreenHandler> gui, int slo

public static void swapSlots(HandledScreen<? extends ScreenHandler> gui, int slotNum, int otherSlot)
{
clickSlot(gui, slotNum, 0, SlotActionType.SWAP);
clickSlot(gui, otherSlot, 0, SlotActionType.SWAP);
clickSlot(gui, slotNum, 0, SlotActionType.SWAP);
clickSlot(gui, slotNum, 8, SlotActionType.SWAP);
clickSlot(gui, otherSlot, 8, SlotActionType.SWAP);
clickSlot(gui, slotNum, 8, SlotActionType.SWAP);
}

private static void dragSplitItemsIntoSlots(HandledScreen<? extends ScreenHandler> gui,
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"mixins": [
"mixins.itemscroller.json"
],

"accessWidener": "minihud.accesswidener",
"depends": {
"minecraft": ">=1.21",
"malilib": ">=0.20.0"
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/minihud.accesswidener
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
accessWidener v1 named
accessible field net/minecraft/screen/ScreenHandler trackedStacks Lnet/minecraft/util/collection/DefaultedList;
9 changes: 6 additions & 3 deletions src/main/resources/mixins.itemscroller.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
"MixinClientPlayerInteractionManager",
"MixinClientPlayNetworkHandler",
"MixinCraftingScreenHandler",
"MixinItemStack",
"MixinMerchantScreen",
"MixinMerchantScreenHandler",
"MixinRecipeBookWidget",
"MixinScreen",
"MixinItemStack"
"MixinScreen"
],
"injectors": {
"defaultRequire": 1
}
},
"mixins": [
"debug.MixinServerPlayNetworkHandler"
]
}

0 comments on commit 0be120c

Please sign in to comment.