diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..b1c79229 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,46 @@ +name: step.build + +on: + workflow_call: + inputs: + release: + type: boolean + required: false + default: false + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3.6.0 + with: + distribution: "temurin" + java-version: 17 + + - name: Cache gradle files + uses: actions/cache@v3.0.11 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ./.gradle/loom-cache + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle.properties', '**/*.accesswidener') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Build with Gradle + run: | + chmod +x gradlew + ./gradlew classes --parallel + ./gradlew build + env: + BUILD_ID: ${{ github.run_number }} + BUILD_RELEASE: ${{ inputs.release }} + + - uses: actions/upload-artifact@v3.1.1 + with: + name: build-artifacts + path: build/libs/* diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 00000000..2db0a243 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,16 @@ +name: Dev Builds + +on: + push: + paths: + - "*.gradle" + - "gradle.properties" + - "src/**" + - "versions/**" + - ".github/**" + pull_request: + workflow_dispatch: + +jobs: + build: + uses: ./.github/workflows/build.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..dc696dd1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,104 @@ +name: Release + +on: + release: + types: + - published + workflow_dispatch: + inputs: + target_release_tag: + description: The tag of the release you want to append the artifact to + type: string + required: true + + +jobs: + prepare: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.setmatrix.outputs.matrix }} + steps: + - uses: actions/checkout@v2.5.0 + + - name: Display context + run: | + echo ref_name = ${{ github.ref_name }} + echo target_release_tag = ${{ github.event.inputs.target_release_tag }} + + build: + uses: ./.github/workflows/build.yml + with: + release: true + + release: + needs: + - build + - prepare + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Download build artifacts + uses: actions/download-artifact@v3 + with: + name: build-artifacts + path: build-artifacts + + - name: Get github release information + if: ${{ github.event_name == 'workflow_dispatch' }} + id: get_release + uses: cardinalby/git-get-release-action@1.2.4 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tag: ${{ github.event.inputs.target_release_tag }} + + - name: Generate publish related infomation + id: release_info + run: | + if [ $GITHUB_EVENT_NAME == 'release' ] + then + echo "::set-output name=tag_name::" # leave an empty value here so softprops/action-gh-release will use the default value + elif [ $GITHUB_EVENT_NAME == 'workflow_dispatch' ] + then + echo "::set-output name=tag_name::${{ github.event.inputs.target_release_tag }}" + else + echo Unknown github event name $GITHUB_EVENT_NAME + exit 1 + fi + + - name: Read Properties mod_version + id: mod_version + uses: christian-draeger/read-properties@1.1.1 + with: + path: gradle.properties + property: mod_version + + - name: Read Properties minecraft_version + id: minecraft_version + uses: christian-draeger/read-properties@1.1.1 + with: + path: gradle.properties + property: minecraft_version + + - name: Publish Minecraft Mods + uses: Kir-Antipov/mc-publish@v3.2 + with: + github-tag: ${{ steps.release_info.outputs.tag_name }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + files-primary: ${{'build-artifacts/!(*-@(dev|sources)).jar'}} + files-secondary: '' + + name: ${{ format('Itemscroller v {0} for mc{1}', steps.mod_version.outputs.value, steps.minecraft_version.outputs.value) }} + version: ${{ format('mc{0}-v{1}', steps.minecraft_version.outputs.value, steps.mod_version.outputs.value) }} + version-type: release + changelog: ${{ format('{0}{1}', github.event.release.body, steps.get_release.outputs.body) }} # one of them should be an empty string (null) + + loaders: fabric + game-versions: ${{ matrix.game_versions }} + version-resolver: exact + + retry-attempts: 3 + retry-delay: 10000 diff --git a/.gitignore b/.gitignore index 47cd4121..655898ea 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,9 @@ build/ eclipse/ .classpath .project +.idea +run/ .vscode build.number -run/ .DS_Store -launch.json \ No newline at end of file +launch.json diff --git a/README.md b/README.md index d00b0972..9812327c 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,24 @@ ## Note -This is a customized version of Masa's itemscroller mod that fixes crafting features for 1.18. Masa's original mod can be found [here](https://github.com/maruohon/itemscroller) +This is a customized version of Andrew54757's itemscroller-crafting-fix, which is a customized version of Masa's itemscroller mod that fixes crafting features for 1.18. -Customizations: -* More accurate/faster crafting through recipe book protocol -* Toggleable crafting (so you can keep crafting without holding down a key, eg for crafting millions of pistons) -* Honey crafting +Masa's original mod can be found [here](https://github.com/maruohon/itemscroller) -- Removed carpetControlQ crafting option as it causes a "slow crafting issue" -- Removed packetRateLimit as it may lead to problems. +Andrew54757's mod can be found [here](https://github.com/Andrews54757/itemscroller-crafting-fix) -## This is not Masa's original itemscroller. If you have issues with this mod, please contact Andrews54757 (or open a bug report here). +Customizations: +Beside the customizations offered by crafting-fix, we also provide some features that are not present in the original mod: +* Apply crafting features over anvil, grindstone(could help with building overstacked items), stonecutter. +* Renaming multiple items automatically over anvil +* `reserveCrafting` to make it possible to pick up every kind of recipe items after each crafting -### What's different? -Post 1.13, Mojang has changed the crafting mechanics of the game. Before 1.13, crafting was very fast as much of the logic was handled client-side. In 1.13, most of the crafting logic was moved to the server. This broke Itemscroller's fast crafting features, since every ingredient now had to be moved one slot at a time to the crafting grid for it to work. This drastically worsened server-client desync, a compounding problem, leading to an increasing number of failed crafting attempts and accidental ingredient leaks which made afk crafting impossible. +- Fix rendering bug when recipes do not have the length of a square number -This customized version of the mod, fixes the problem by handling ingredient movement server-side using the recipe book protocols when it can. +除crafting-fix提供的功能外,还提供以下功能: +* 可以在铁砧/砂轮(可用于合成堆叠附魔书等)/切石机上使用快速合成功能 +* 可以在铁砧上批量重命名物品,只需快速合成前在铁砧上预先写好名字并调用对应的快速合成功能即可,注意仅能用于`合成而不扔出` +* `reserveCrafting` 选项可以在合成后保留物品栏中合成材料的槽位,无论地上合成材料扔出的顺序,总有物品栏位置能从地上捡起合成材料 -**Note: Some recipes like fireworks rockets that are not in the recipe book do not take advantage of this protocol, in those cases old itemscroller methods will be used** +## This is not Masa's original itemscroller. If you have issues with this mod, please contact WenDavid552 (or open a bug report here). Item Scroller ============== diff --git a/gradle.properties b/gradle.properties index 5fdafdfd..ec18b82d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ author = masa mod_file_name = itemscroller-fabric # Current mod version -mod_version = craftfix-1.2.6 +mod_version = craftaddon-1.0.0 # Required malilib version malilib_version = 0.14.0 @@ -20,4 +20,4 @@ minecraft_version = 1.19.3 mappings_version = 1.19.3+build.2 fabric_loader_version = 0.14.11 -mod_menu_version = 5.0.1 \ No newline at end of file +mod_menu_version = 5.0.1 diff --git a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java index 6613c20f..e6aaa064 100644 --- a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java +++ b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java @@ -7,8 +7,10 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import net.minecraft.client.gui.screen.ingame.CraftingScreen; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.gui.screen.ingame.*; +import net.minecraft.screen.ForgingScreenHandler; +import net.minecraft.screen.GrindstoneScreenHandler; +import net.minecraft.screen.StonecutterScreenHandler; import net.minecraft.screen.slot.CraftingResultSlot; import fi.dy.masa.malilib.config.ConfigUtils; import fi.dy.masa.malilib.config.IConfigHandler; @@ -61,6 +63,7 @@ public static class Generic public static class Toggles { public static final ConfigBoolean CRAFTING_FEATURES = new ConfigBoolean("enableCraftingFeatures", true, "Enables scrolling items to and from crafting grids,\nwith a built-in 18 recipe memory.\nHold down the Recipe key to see the stored recipes and\nto change the selection. While holding the Recipe key,\nyou can either scroll or press a number key to change the selection.\nA recipe is stored to the currently selected \"recipe slot\"\n by clicking pick block over a configured crafting output slot.\nThe supported crafting grids must be added to the scrollableCraftingGrids list."); + public static final ConfigBoolean RESERVED_CRAFTING = new ConfigBoolean("enableReservedCrafting", false, "Enables to reserve slots of items of recipes in inventory,\nso items on the ground could be picked up for further crafting"); public static final ConfigBoolean DROP_MATCHING = new ConfigBoolean("enableDropkeyDropMatching", true, "Enables dropping all matching items from the same\ninventory with the hotkey"); public static final ConfigBoolean RIGHT_CLICK_CRAFT_STACK = new ConfigBoolean("enableRightClickCraftingOneStack", true, "Enables crafting up to one full stack when right clicking on\na slot that has been configured as a crafting output slot."); public static final ConfigBoolean SCROLL_EVERYTHING = new ConfigBoolean("enableScrollingEverything", true, "Enables scroll moving all items at once while\nholding the modifierMoveEverything keybind"); @@ -75,6 +78,7 @@ public static class Toggles public static final ImmutableList OPTIONS = ImmutableList.of( CRAFTING_FEATURES, + RESERVED_CRAFTING, DROP_MATCHING, RIGHT_CLICK_CRAFT_STACK, SCROLL_EVERYTHING, @@ -119,6 +123,12 @@ public static void loadFromFile() CraftingHandler.addCraftingGridDefinition(CraftingScreen.class.getName(), CraftingResultSlot.class.getName(), 0, new SlotRange(1, 9)); //"net.minecraft.client.gui.inventory.PlayerInventoryScreen,net.minecraft.inventory.SlotCrafting,0,1-4", // vanilla player inventory crafting grid CraftingHandler.addCraftingGridDefinition(InventoryScreen.class.getName(), CraftingResultSlot.class.getName(), 0, new SlotRange(1, 4)); + // vanilla anvil + CraftingHandler.addCraftingGridDefinition(AnvilScreen.class.getName(), ForgingScreenHandler.class.getName()+"$2", 2, new SlotRange(0, 2)); + // vanilla grindstone + CraftingHandler.addCraftingGridDefinition(GrindstoneScreen.class.getName(), GrindstoneScreenHandler.class.getName()+"$4", 2, new SlotRange(0, 2)); + // vanilla stonecutter + CraftingHandler.addCraftingGridDefinition(StonecutterScreen.class.getName(), StonecutterScreenHandler.class.getName()+"$2", 1, new SlotRange(0, 1)); } public static void saveToFile() @@ -170,7 +180,7 @@ private static void getStrings(JsonObject obj, Set outputSet, String arr private static void writeStrings(JsonObject obj, Set inputSet, String arrayName) { - if (inputSet.isEmpty() == false) + if (!inputSet.isEmpty()) { JsonArray arr = new JsonArray(); diff --git a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java index 3a15da24..e246ec28 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java @@ -4,7 +4,10 @@ import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.StonecutterScreen; import net.minecraft.recipe.CraftingRecipe; +import net.minecraft.recipe.StonecuttingRecipe; +import net.minecraft.screen.StonecutterScreenHandler; import net.minecraft.screen.slot.Slot; import fi.dy.masa.malilib.config.options.ConfigHotkey; import fi.dy.masa.malilib.gui.GuiBase; @@ -159,12 +162,24 @@ public void onClientTick(MinecraftClient mc) { RecipePattern recipe = RecipeStorage.getInstance().getSelectedRecipe(); CraftingRecipe bookRecipe = InventoryUtils.getBookRecipeFromPattern(recipe); - if (bookRecipe != null && !bookRecipe.isIgnoredInRecipeBook()) { // Use recipe book if possible + if (!(gui instanceof StonecutterScreen) && bookRecipe != null && !bookRecipe.isIgnoredInRecipeBook()) { // Use recipe book if possible // System.out.println("recipe"); - mc.interactionManager.clickRecipe(gui.getScreenHandler().syncId, bookRecipe, true); + if(Configs.Toggles.RESERVED_CRAFTING.getBooleanValue()) { + int option = InventoryUtils.checkRecipeEnough(recipe, gui); + if (option > 0) { + mc.interactionManager.clickRecipe(gui.getScreenHandler().syncId, bookRecipe, option > 1); + } + } + else{ + mc.interactionManager.clickRecipe(gui.getScreenHandler().syncId, bookRecipe, true); + } } else { // System.out.println("move"); InventoryUtils.tryMoveItemsToFirstCraftingGrid(recipe, gui, true); + int stonecuttingRecipeIndex = InventoryUtils.getStonecuttingRecipeFromPattern(recipe); + if(stonecuttingRecipeIndex != -1 && gui instanceof StonecutterScreen) { + mc.interactionManager.clickButton((gui.getScreenHandler()).syncId, stonecuttingRecipeIndex); + } } for (int i = 0; i < recipe.getMaxCraftAmount(); i++) { diff --git a/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java b/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java index 63a66a26..b3e883d4 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java @@ -234,7 +234,7 @@ private void renderRecipeItems(RecipePattern recipe, int recipeCountPerPage, Han for (int i = 0, row = 0; row < recipeDimensions; row++) { - for (int col = 0; col < recipeDimensions; col++, i++) + for (int col = 0; col < recipeDimensions && i < recipe.getRecipeLength(); col++, i++) { int xOff = col * 17; int yOff = row * 17; @@ -257,7 +257,7 @@ private ItemStack getHoveredRecipeIngredient(int mouseX, int mouseY, RecipePatte { for (int i = 0, row = 0; row < recipeDimensions; row++) { - for (int col = 0; col < recipeDimensions; col++, i++) + for (int col = 0; col < recipeDimensions && i < recipe.getRecipeLength(); col++, i++) { int xOff = col * scaledGridEntry; int yOff = row * scaledGridEntry; diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinAnvilScreen.java b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinAnvilScreen.java new file mode 100644 index 00000000..43e3ba12 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinAnvilScreen.java @@ -0,0 +1,18 @@ +package fi.dy.masa.itemscroller.mixin; + +import net.minecraft.client.gui.screen.ingame.AnvilScreen; +import net.minecraft.client.gui.widget.TextFieldWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.awt.*; + +@Mixin(AnvilScreen.class) +public interface IMixinAnvilScreen { + @Invoker("onRenamed") + void itemscroller_setItemName(String newName); + + @Accessor("nameField") + TextFieldWidget itemscroller_getNameField(); +} diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java index 4a155c93..b6a84abb 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java @@ -9,6 +9,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtList; import net.minecraft.recipe.CraftingRecipe; +import net.minecraft.recipe.StonecuttingRecipe; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; @@ -20,6 +21,8 @@ public class RecipePattern private ItemStack result = InventoryUtils.EMPTY_STACK; private ItemStack[] recipe = new ItemStack[9]; public CraftingRecipe cachedRecipeFromBook = null; + + public int cachedRecipeFromStonecutting = -1; private int maxCraftAmount = 64; private HashSet recipeRemainders = new HashSet(); diff --git a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java index c2a22ac4..f8222d0f 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java @@ -13,6 +13,7 @@ import fi.dy.masa.itemscroller.ItemScroller; import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.config.Hotkeys; +import fi.dy.masa.itemscroller.mixin.IMixinAnvilScreen; import fi.dy.masa.itemscroller.recipes.CraftingHandler; import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; import fi.dy.masa.itemscroller.recipes.RecipePattern; @@ -20,24 +21,24 @@ import fi.dy.masa.itemscroller.villager.VillagerDataStorage; import fi.dy.masa.itemscroller.villager.VillagerUtils; import fi.dy.masa.malilib.util.GuiUtils; +import org.apache.commons.lang3.StringUtils; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntComparator; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.client.gui.screen.ingame.*; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.recipe.CraftingRecipe; import net.minecraft.recipe.RecipeManager; import net.minecraft.recipe.RecipeType; +import net.minecraft.recipe.StonecuttingRecipe; import net.minecraft.screen.Generic3x3ContainerScreenHandler; import net.minecraft.registry.Registries; import net.minecraft.screen.MerchantScreenHandler; @@ -1187,7 +1188,7 @@ private static boolean tryMoveItemsToCraftingGridSlots(RecipePattern recipe, // This slot is used to check that we get items from a DIFFERENT inventory than // where this slot is in - Slot slotGridFirst = container.getSlot(range.getFirst()); + Slot[] slotGrids = {container.getSlot(range.getFirst()), slot}; Map ingredientSlots = ItemType.getSlotsPerItem(recipe.getRecipeItems()); for (Map.Entry entry : ingredientSlots.entrySet()) { @@ -1202,9 +1203,9 @@ private static boolean tryMoveItemsToCraftingGridSlots(RecipePattern recipe, } if (fillStacks) { - fillCraftingGrid(gui, slotGridFirst, ingredientReference, targetSlots); + fillCraftingGrid(gui, slotGrids, ingredientReference, targetSlots); } else { - moveOneRecipeItemIntoCraftingGrid(gui, slotGridFirst, ingredientReference, targetSlots); + moveOneRecipeItemIntoCraftingGrid(gui, slotGrids, ingredientReference, targetSlots); } } } @@ -1213,7 +1214,7 @@ private static boolean tryMoveItemsToCraftingGridSlots(RecipePattern recipe, } private static void fillCraftingGrid(HandledScreen gui, - Slot slotGridFirst, + Slot[] slotGrids, ItemStack ingredientReference, IntArrayList targetSlots) { ScreenHandler container = gui.getScreenHandler(); @@ -1226,7 +1227,7 @@ private static void fillCraftingGrid(HandledScreen gui, } while (true) { - slotNum = getSlotNumberOfLargestMatchingStackFromDifferentInventory(container, slotGridFirst, + slotNum = getSlotNumberOfLargestMatchingStackFromDifferentInventory(container, slotGrids, ingredientReference); // Didn't find ingredient items @@ -1333,6 +1334,32 @@ public static CraftingRecipe getBookRecipeFromPattern(RecipePattern recipe) { return null; } + public static int getStonecuttingRecipeFromPattern(RecipePattern recipe) { + if(recipe.cachedRecipeFromStonecutting != -1) { + return recipe.cachedRecipeFromStonecutting; + } + else { + MinecraftClient mc = MinecraftClient.getInstance(); + RecipeManager recipeManager = mc.world.getRecipeManager(); + + ItemStack[] items = recipe.getRecipeItems(); + SimpleInventory search = new SimpleInventory(items.length); + // Set dummy slots with recipe pattern + for (int i = 0; i < items.length; i++) { + search.setStack(i, items[i]); + } + List inputRecipes = recipeManager.getAllMatches(RecipeType.STONECUTTING, search, mc.world); + for(int i=0; i gui) { Slot slot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); @@ -1342,19 +1369,31 @@ public static void craftEverythingPossibleWithCurrentRecipe(RecipePattern recipe SlotRange range = CraftingHandler.getCraftingGridSlots(gui, slot); if (range != null) { - + String cacheName = null; for (int i = 0; i < 36; i++) { CraftingRecipe bookRecipe = getBookRecipeFromPattern(recipe); - if (bookRecipe != null && !bookRecipe.isIgnoredInRecipeBook()) { // Use recipe book if possible + if (!(gui instanceof StonecutterScreen) && bookRecipe != null && !bookRecipe.isIgnoredInRecipeBook()) { // Use recipe book if possible MinecraftClient mc = MinecraftClient.getInstance(); mc.interactionManager.clickRecipe(gui.getScreenHandler().syncId, bookRecipe, true); } else { + if(cacheName == null && gui instanceof AnvilScreen) { + cacheName = ((IMixinAnvilScreen)gui).itemscroller_getNameField().getText(); + } // Clear all items from the grid first, to avoid unbalanced stacks if (clearCraftingGridOfItems(recipe, gui, range, false) == false) { continue; } tryMoveItemsToCraftingGridSlots(recipe, slot, gui, true); + + int stonecuttingRecipeIndex = InventoryUtils.getStonecuttingRecipeFromPattern(recipe); + if(stonecuttingRecipeIndex != -1 && gui instanceof StonecutterScreen) { + MinecraftClient mc = MinecraftClient.getInstance(); + mc.interactionManager.clickButton((gui.getScreenHandler()).syncId, stonecuttingRecipeIndex); + } + } + if(!StringUtils.isBlank(cacheName) && gui instanceof AnvilScreen) { + ((IMixinAnvilScreen)gui).itemscroller_setItemName(cacheName); } shiftClickSlot(gui, slot.id); } @@ -1476,6 +1515,46 @@ public static void setCraftingGridContentsUsingSwaps(HandledScreen gui) { + Slot craftingOutputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); + ScreenHandler container = gui.getScreenHandler(); + int numSlots = container.slots.size(); + SlotRange range = CraftingHandler.getCraftingGridSlots(gui, craftingOutputSlot); + + // Check that the slot range is valid and that the recipe can fit into this type + // of crafting grid + if (range != null && range.getLast() < numSlots && recipe.getRecipeLength() <= range.getSlotCount()) { + // This slot is used to check that we get items from a DIFFERENT inventory than + // where this slot is in + Map ingredientSlots = ItemType.getSlotsPerItem(recipe.getRecipeItems()); + Slot[] slotReference = {container.getSlot(range.getFirst()), craftingOutputSlot}; + for(Map.Entry entry : ingredientSlots.entrySet()) { + int countItems = 0; + final int numSlotsWithItem = entry.getValue().size(); + final ItemStack stackReference = entry.getKey().getStack(); + final int itemsCraftOnce = numSlotsWithItem * stackReference.getMaxCount(); + for(Slot slot : container.slots) { + if(!areSlotsInSameInventory(slot, slotReference) && slot.hasStack() + && areStacksEqual(stackReference, slot.getStack())){ + countItems += slot.getStack().getCount(); + } + } + if(countItems <= itemsCraftOnce && countItems % numSlotsWithItem == 0) { + return countItems == numSlotsWithItem ? 0 : 1; + } + } + return 2; + } + return 0; + } + private static int putSingleItemIntoSlots(HandledScreen gui, IntArrayList targetSlots, int startIndex) { @@ -1522,7 +1601,7 @@ public static void moveOneSetOfItemsFromSlotToPlayerInventory(HandledScreen gui, - Slot slotGridFirst, + Slot[] slotGrids, ItemStack ingredientReference, IntArrayList targetSlots) { ScreenHandler container = gui.getScreenHandler(); @@ -1531,7 +1610,7 @@ private static void moveOneRecipeItemIntoCraftingGrid(HandledScreen largest) { + // 这个stack应该是legal的,也就是我们强行忽略掉overstacked items + if (stackSize > largest && stackSize <= slot.getStack().getMaxCount()) { slotNum = slot.id; largest = stackSize; } @@ -1630,7 +1709,7 @@ && areStacksEqual(stackReference, slot.getStack())) { * then the largest one is selected. */ private static int getSlotNumberOfSmallestStackFromDifferentInventory(ScreenHandler container, - Slot slotReference, + Slot[] slotReference, ItemStack stackReference, int idealSize) { int slotNumSmallest = -1; @@ -1785,6 +1864,14 @@ public static boolean areStacksEqual(ItemStack stack1, ItemStack stack2) { return stack1.isEmpty() == false && stack1.isItemEqual(stack2) && ItemStack.areNbtEqual(stack1, stack2); } + private static boolean areSlotsInSameInventory(Slot slot1, Slot[] slots) { + for (Slot slot: slots) { + if(areSlotsInSameInventory(slot, slot1)) { + return true; + } + } + return false; + } private static boolean areSlotsInSameInventory(Slot slot1, Slot slot2) { return areSlotsInSameInventory(slot1, slot2, false); } @@ -2344,17 +2431,19 @@ private static void dragSplitItemsIntoSlots(HandledScreen= numSlots) { break; } - - clickSlot(gui, slotNum, 1, SlotActionType.QUICK_CRAFT); + ItemStack item = container.slots.get(slotNum).getStack(); + if(item.getCount() < item.getMaxCount()) { + clickSlot(gui, slotNum, 1, SlotActionType.QUICK_CRAFT); + } } // End the drag diff --git a/src/main/resources/mixins.itemscroller.json b/src/main/resources/mixins.itemscroller.json index 7251579c..c27c7eda 100644 --- a/src/main/resources/mixins.itemscroller.json +++ b/src/main/resources/mixins.itemscroller.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_17", "minVersion": "0.8", "client": [ + "IMixinAnvilScreen", "IMixinCraftingResultSlot", "IMixinMerchantScreen", "IMixinScreenWithHandler",