From f445ecdd9e298e8b44bc17a244d63ef7d01260a6 Mon Sep 17 00:00:00 2001 From: FlorianMichael <60033407+FlorianMichael@users.noreply.github.com> Date: Sun, 5 Nov 2023 16:30:38 +0100 Subject: [PATCH] Emulate crafting recipe results in <= 1.11.2 Fixed https://github.com/ViaVersion/ViaFabricPlus/issues/60 --- .../definition/ClientsideFixes.java | 29 +++++++++++ .../MixinCraftingScreenHandler.java | 50 +++++++++++++++++++ .../MixinPlayerScreenHandler.java | 26 +++++++--- src/main/resources/viafabricplus.mixins.json | 5 +- 4 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinCraftingScreenHandler.java diff --git a/src/main/java/de/florianmichael/viafabricplus/definition/ClientsideFixes.java b/src/main/java/de/florianmichael/viafabricplus/definition/ClientsideFixes.java index 4a13c9065..c2c735390 100644 --- a/src/main/java/de/florianmichael/viafabricplus/definition/ClientsideFixes.java +++ b/src/main/java/de/florianmichael/viafabricplus/definition/ClientsideFixes.java @@ -28,11 +28,16 @@ import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.FontStorage; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.inventory.RecipeInputInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; +import net.minecraft.recipe.RecipeType; import net.minecraft.registry.Registries; +import net.minecraft.screen.ScreenHandler; import net.raphimc.vialegacy.protocols.classic.protocolc0_28_30toc0_28_30cpe.data.ClassicProtocolExtension; import net.raphimc.vialoader.util.VersionEnum; import net.raphimc.vialoader.util.VersionRange; @@ -175,6 +180,30 @@ public static int getLegacyArmorPoints(final ItemStack itemStack) { return LEGACY_ARMOR_POINTS.get(itemStack.getItem()); } + /** + * Sets the result slot of a crafting screen handler to the correct item stack. In MC <= 1.11.2 the result slot + * is not updated when the input slots change, so we need to update it manually, Spigot and Paper re-syncs the slot, + * so we don't notice this bug on servers that use Spigot or Paper + * + * @param syncId The sync id of the screen handler + * @param screenHandler The screen handler + * @param inventory The inventory of the screen handler + */ + public static void setCraftingResultSlot(final int syncId, final ScreenHandler screenHandler, final RecipeInputInventory inventory) { + final var network = MinecraftClient.getInstance().getNetworkHandler(); + if (network == null) return; + + final var world = MinecraftClient.getInstance().world; + + final var result = network.getRecipeManager(). + getFirstMatch(RecipeType.CRAFTING, inventory, world). // Get the first matching recipe + map(recipe -> recipe.value().craft(inventory, world.getRegistryManager())). // Craft the recipe to get the result + orElse(ItemStack.EMPTY); // If there is no recipe, set the result to air + + // Update the result slot + network.onScreenHandlerSlotUpdate(new ScreenHandlerSlotUpdateS2CPacket(syncId, screenHandler.getRevision(), 0, result)); + } + public static int getCurrentChatLimit() { return currentChatLimit; } diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinCraftingScreenHandler.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinCraftingScreenHandler.java new file mode 100644 index 000000000..883f9c3e6 --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinCraftingScreenHandler.java @@ -0,0 +1,50 @@ +/* + * This file is part of ViaFabricPlus - https://github.com/FlorianMichael/ViaFabricPlus + * Copyright (C) 2021-2023 FlorianMichael/EnZaXD and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.screen.screenhandler; + +import de.florianmichael.viafabricplus.definition.ClientsideFixes; +import de.florianmichael.viafabricplus.protocolhack.ProtocolHack; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.RecipeInputInventory; +import net.minecraft.screen.AbstractRecipeScreenHandler; +import net.minecraft.screen.CraftingScreenHandler; +import net.minecraft.screen.ScreenHandlerType; +import net.raphimc.vialoader.util.VersionEnum; +import org.spongepowered.asm.mixin.Final; +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(CraftingScreenHandler.class) +public abstract class MixinCraftingScreenHandler extends AbstractRecipeScreenHandler { + + @Shadow @Final private RecipeInputInventory input; + + public MixinCraftingScreenHandler(ScreenHandlerType screenHandlerType, int i) { + super(screenHandlerType, i); + } + + @Inject(method = "onContentChanged", at = @At("HEAD")) + public void updateResultSlot(Inventory inventory, CallbackInfo ci) { + if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_11_1to1_11_2)) { + ClientsideFixes.setCraftingResultSlot(syncId, this, input); + } + } +} diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinPlayerScreenHandler.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinPlayerScreenHandler.java index cafbb5d48..b0aca9c63 100644 --- a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinPlayerScreenHandler.java +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/minecraft/screen/screenhandler/MixinPlayerScreenHandler.java @@ -17,8 +17,10 @@ */ package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.screen.screenhandler; +import de.florianmichael.viafabricplus.definition.ClientsideFixes; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.RecipeInputInventory; import net.raphimc.vialoader.util.VersionEnum; -import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import de.florianmichael.viafabricplus.protocolhack.ProtocolHack; import net.minecraft.entity.EquipmentSlot; import net.minecraft.inventory.CraftingInventory; @@ -26,15 +28,17 @@ import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.screen.ScreenHandlerType; import net.minecraft.screen.slot.Slot; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(PlayerScreenHandler.class) public abstract class MixinPlayerScreenHandler extends AbstractRecipeScreenHandler { + @Shadow @Final private RecipeInputInventory craftingInput; + public MixinPlayerScreenHandler(ScreenHandlerType screenHandlerType, int i) { super(screenHandlerType, i); } @@ -51,9 +55,17 @@ private Slot redirectAddOffhandSlot(PlayerScreenHandler screenHandler, Slot slot @SuppressWarnings("InvalidInjectorMethodSignature") @ModifyVariable(method = "quickMove", ordinal = 0, at = @At(value = "STORE", ordinal = 0)) private EquipmentSlot injectTransferSlot(EquipmentSlot slot) { - if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_8) && slot == EquipmentSlot.OFFHAND) + if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_8) && slot == EquipmentSlot.OFFHAND) { return EquipmentSlot.MAINHAND; - else + } else { return slot; + } + } + + @Inject(method = "onContentChanged", at = @At("HEAD")) + public void updateResultSlot(Inventory inventory, CallbackInfo ci) { + if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(VersionEnum.r1_11_1to1_11_2)) { + ClientsideFixes.setCraftingResultSlot(syncId, this, craftingInput); + } } } diff --git a/src/main/resources/viafabricplus.mixins.json b/src/main/resources/viafabricplus.mixins.json index 471d0d7a9..e596dde9d 100644 --- a/src/main/resources/viafabricplus.mixins.json +++ b/src/main/resources/viafabricplus.mixins.json @@ -25,6 +25,7 @@ "compat.ipnext.MixinAutoRefillHandler_ItemSlotMonitor", "compat.sodium.MixinChunkTracker", "fixes.authlib.MixinKeyPairResponse", + "fixes.authlib.MixinYggdrasilUserApiService", "fixes.minecraft.MixinBipedEntityModel", "fixes.minecraft.MixinCamera", "fixes.minecraft.MixinClientPlayerInteractionManager", @@ -167,9 +168,9 @@ "fixes.viaversion.protocol1_9to1_8.MixinEntityPackets_6_1", "fixes.viaversion.protocol1_9to1_8.MixinEntityTracker1_9", "fixes.viaversion.protocol1_9to1_8.MixinMetadataRewriter1_9To1_8", - "fixes.authlib.MixinYggdrasilUserApiService", "jsonwebtoken.MixinClasses", - "jsonwebtoken.MixinDefaultJwtParserBuilder" + "jsonwebtoken.MixinDefaultJwtParserBuilder", + "fixes.minecraft.screen.screenhandler.MixinCraftingScreenHandler" ], "injectors": { "defaultRequire": 1