From dd680c2cd41f11eccd294ca30c38b82565cd1633 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 9 Aug 2019 16:45:41 +0800 Subject: Auto Crafting should work perfectly on vanilla stuff now --- build.gradle | 1 + .../shedaniel/rei/RoughlyEnoughItemsNetwork.java | 3 + .../me/shedaniel/rei/api/AutoTransferHandler.java | 17 +- .../rei/plugin/DefaultAutoCraftingPlugin.java | 11 +- .../plugin/autocrafting/AutoCraftingHandler.java | 116 ++++++++ .../autocrafting/AutoCraftingTableHandler.java | 116 -------- .../me/shedaniel/rei/server/InputSlotCrafter.java | 186 +++++++----- .../java/me/shedaniel/rei/server/RecipeFinder.java | 319 +++++++++++++++++++++ .../me/shedaniel/rei/server/RecipeGridAligner.java | 61 ++++ .../assets/roughlyenoughitems/lang/en_us.json | 1 + 10 files changed, 631 insertions(+), 200 deletions(-) create mode 100644 src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingHandler.java delete mode 100644 src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingTableHandler.java create mode 100644 src/main/java/me/shedaniel/rei/server/RecipeFinder.java create mode 100644 src/main/java/me/shedaniel/rei/server/RecipeGridAligner.java diff --git a/build.gradle b/build.gradle index 2de509dfa..270ff37d7 100755 --- a/build.gradle +++ b/build.gradle @@ -68,6 +68,7 @@ dependencies { modImplementation "io.github.prospector.modmenu:ModMenu:${modmenu_version}" compile "org.lwjgl:lwjgl-jemalloc:3.2.1" compileOnly "com.google.code.findbugs:jsr305:3.0.2" + implementation 'org.jetbrains:annotations:15.0' } task sourcesJar(type: Jar, dependsOn: classes) { diff --git a/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java b/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java index 2001ec5f2..c25a82439 100644 --- a/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java +++ b/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java @@ -92,6 +92,9 @@ public class RoughlyEnoughItemsNetwork implements ModInitializer { } } catch (IllegalStateException e) { player.sendMessage(new TranslatableText(e.getMessage()).formatted(Formatting.RED)); + } catch (Exception e) { + player.sendMessage(new TranslatableText("error.rei.internal.error",e.getMessage()).formatted(Formatting.RED)); + e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java b/src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java index a4a576299..a328c16c8 100644 --- a/src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java +++ b/src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java @@ -5,6 +5,8 @@ package me.shedaniel.rei.api; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import me.shedaniel.rei.client.ScreenHelper; import me.shedaniel.rei.gui.ContainerScreenOverlay; import net.minecraft.client.MinecraftClient; @@ -31,7 +33,11 @@ public interface AutoTransferHandler { } static Result createFailed(String errorKey) { - return new ResultImpl(errorKey); + return new ResultImpl(errorKey, new IntArrayList()); + } + + static Result createFailed(String errorKey, IntList redSlots) { + return new ResultImpl(errorKey, redSlots); } boolean isSuccessful(); @@ -68,6 +74,7 @@ public interface AutoTransferHandler { public final class ResultImpl implements Result { private boolean successful, applicable; private String errorKey; + private IntList integers = new IntArrayList(); private ResultImpl() { this.successful = true; @@ -79,22 +86,24 @@ public interface AutoTransferHandler { this.applicable = applicable; } - public ResultImpl(String errorKey) { + public ResultImpl(String errorKey, IntList integers) { this.successful = false; this.applicable = true; this.errorKey = errorKey; + if (integers != null) + this.integers = integers; } @Override public boolean isSuccessful() { return successful; } - + @Override public boolean isApplicable() { return applicable; } - + @Override public String getErrorKey() { return errorKey; diff --git a/src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java b/src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java index 07e8850e3..8bcbe5083 100644 --- a/src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java +++ b/src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java @@ -8,7 +8,10 @@ package me.shedaniel.rei.plugin; import me.shedaniel.rei.RoughlyEnoughItemsCore; import me.shedaniel.rei.api.RecipeHelper; import me.shedaniel.rei.api.plugins.REIPluginV0; -import me.shedaniel.rei.plugin.autocrafting.*; +import me.shedaniel.rei.plugin.autocrafting.AutoBlastingBookHandler; +import me.shedaniel.rei.plugin.autocrafting.AutoCraftingHandler; +import me.shedaniel.rei.plugin.autocrafting.AutoFurnaceBookHandler; +import me.shedaniel.rei.plugin.autocrafting.AutoSmokerBookHandler; import net.fabricmc.loader.api.SemanticVersion; import net.fabricmc.loader.util.version.VersionParsingException; import net.minecraft.util.Identifier; @@ -32,12 +35,12 @@ public class DefaultAutoCraftingPlugin implements REIPluginV0 { if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLoadingDefaultPlugin()) { return; } - recipeHelper.registerAutoCraftingHandler(new AutoCraftingTableBookHandler()); - recipeHelper.registerAutoCraftingHandler(new AutoInventoryBookHandler()); + // recipeHelper.registerAutoCraftingHandler(new AutoCraftingTableBookHandler()); + // recipeHelper.registerAutoCraftingHandler(new AutoInventoryBookHandler()); recipeHelper.registerAutoCraftingHandler(new AutoFurnaceBookHandler()); recipeHelper.registerAutoCraftingHandler(new AutoSmokerBookHandler()); recipeHelper.registerAutoCraftingHandler(new AutoBlastingBookHandler()); - recipeHelper.registerAutoCraftingHandler(new AutoCraftingTableHandler()); + recipeHelper.registerAutoCraftingHandler(new AutoCraftingHandler()); } } diff --git a/src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingHandler.java b/src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingHandler.java new file mode 100644 index 000000000..f99a8734b --- /dev/null +++ b/src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingHandler.java @@ -0,0 +1,116 @@ +/* + * Roughly Enough Items by Danielshe. + * Licensed under the MIT License. + */ + +package me.shedaniel.rei.plugin.autocrafting; + +import com.google.common.collect.Lists; +import io.netty.buffer.Unpooled; +import me.shedaniel.rei.RoughlyEnoughItemsNetwork; +import me.shedaniel.rei.api.AutoTransferHandler; +import me.shedaniel.rei.listeners.RecipeBookGuiHooks; +import me.shedaniel.rei.plugin.crafting.DefaultCraftingCategory; +import me.shedaniel.rei.plugin.crafting.DefaultCraftingDisplay; +import me.shedaniel.rei.plugin.crafting.DefaultShapedDisplay; +import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.CraftingTableScreen; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider; +import net.minecraft.container.CraftingContainer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DefaultedList; +import net.minecraft.util.PacketByteBuf; + +import java.util.List; + +public class AutoCraftingHandler implements AutoTransferHandler { + @Override + public Result handle(Context context) { + if (!(context.getRecipe() instanceof DefaultCraftingDisplay)) + return Result.createNotApplicable(); + if (!(context.getContainerScreen() instanceof CraftingTableScreen) && !(context.getContainerScreen() instanceof InventoryScreen)) + return Result.createNotApplicable(); + if (context.getContainerScreen() instanceof InventoryScreen && (((DefaultCraftingDisplay) context.getRecipe()).getWidth() > 2 || ((DefaultCraftingDisplay) context.getRecipe()).getHeight() > 2)) + return Result.createFailed("error.rei.transfer.too_small"); + if (!canUseMovePackets()) + return Result.createFailed("error.rei.not.on.server"); + if (!hasItems(context.getRecipe().getInput())) + return Result.createFailed("error.rei.not.enough.materials"); + if (!context.isActuallyCrafting()) + return Result.createSuccessful(); + + context.getMinecraft().openScreen(context.getContainerScreen()); + ((RecipeBookGuiHooks) ((RecipeBookProvider) context.getContainerScreen()).getRecipeBookGui()).rei_getGhostSlots().reset(); + DefaultCraftingDisplay display = (DefaultCraftingDisplay) context.getRecipe(); + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + buf.writeUuid(RoughlyEnoughItemsNetwork.CRAFTING_TABLE_MOVE); + buf.writeBoolean(Screen.hasShiftDown()); + CraftingContainer craftingContainer = (CraftingContainer) context.getContainer(); + + List> ogInput = display.getInput(); + List> input = Lists.newArrayListWithCapacity(craftingContainer.getCraftingWidth() * craftingContainer.getCraftingHeight()); + for (int i = 0; i < craftingContainer.getCraftingWidth() * craftingContainer.getCraftingHeight(); i++) { + input.add(Lists.newArrayList()); + } + for (int i = 0; i < ogInput.size(); i++) { + List ogStacks = ogInput.get(i); + if (display instanceof DefaultShapedDisplay) { + if (!ogInput.get(i).isEmpty()) + input.set(DefaultCraftingCategory.getSlotWithSize(display, i), ogInput.get(i)); + } else if (!ogInput.get(i).isEmpty()) + input.set(i, ogInput.get(i)); + } + + buf.writeInt(input.size()); + for (List stacks : input) { + buf.writeInt(stacks.size()); + for (ItemStack stack : stacks) { + buf.writeItemStack(stack); + } + } + ClientSidePacketRegistry.INSTANCE.sendToServer(RoughlyEnoughItemsNetwork.MOVE_ITEMS_PACKET, buf); + return Result.createSuccessful(); + } + + @Override + public double getPriority() { + return -10; + } + + public boolean canUseMovePackets() { + return ClientSidePacketRegistry.INSTANCE.canServerReceive(RoughlyEnoughItemsNetwork.MOVE_ITEMS_PACKET); + } + + public boolean hasItems(List> inputs) { + // Create a clone of player's inventory, and count + DefaultedList copyMain = DefaultedList.create(); + for (ItemStack stack : MinecraftClient.getInstance().player.inventory.main) { + copyMain.add(stack.copy()); + } + for (List possibleStacks : inputs) { + boolean done = possibleStacks.isEmpty(); + for (ItemStack possibleStack : possibleStacks) { + if (!done) { + int invRequiredCount = possibleStack.getCount(); + for (ItemStack stack : copyMain) { + if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack)) { + while (invRequiredCount > 0 && !stack.isEmpty()) { + invRequiredCount--; + stack.decrement(1); + } + } + } + if (invRequiredCount <= 0) { + done = true; + } + } + } + if (!done) + return false; + } + return true; + } +} diff --git a/src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingTableHandler.java b/src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingTableHandler.java deleted file mode 100644 index d92b40de9..000000000 --- a/src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingTableHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Roughly Enough Items by Danielshe. - * Licensed under the MIT License. - */ - -package me.shedaniel.rei.plugin.autocrafting; - -import com.google.common.collect.Lists; -import io.netty.buffer.Unpooled; -import me.shedaniel.rei.RoughlyEnoughItemsNetwork; -import me.shedaniel.rei.api.AutoTransferHandler; -import me.shedaniel.rei.listeners.RecipeBookGuiHooks; -import me.shedaniel.rei.plugin.crafting.DefaultCraftingCategory; -import me.shedaniel.rei.plugin.crafting.DefaultCraftingDisplay; -import me.shedaniel.rei.plugin.crafting.DefaultShapedDisplay; -import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.CraftingTableScreen; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider; -import net.minecraft.container.CraftingContainer; -import net.minecraft.item.ItemStack; -import net.minecraft.util.DefaultedList; -import net.minecraft.util.PacketByteBuf; - -import java.util.List; - -public class AutoCraftingTableHandler implements AutoTransferHandler { - @Override - public Result handle(Context context) { - if (!(context.getRecipe() instanceof DefaultCraftingDisplay)) - return Result.createNotApplicable(); - if (!(context.getContainerScreen() instanceof CraftingTableScreen) && !(context.getContainerScreen() instanceof InventoryScreen)) - return Result.createNotApplicable(); - if (context.getContainerScreen() instanceof InventoryScreen && (((DefaultCraftingDisplay) context.getRecipe()).getWidth() > 2 || ((DefaultCraftingDisplay) context.getRecipe()).getHeight() > 2)) - return Result.createFailed("error.rei.transfer.too_small"); - if (!canUseMovePackets()) - return Result.createFailed("error.rei.not.on.server"); - if (!hasItems(context.getRecipe().getInput())) - return Result.createFailed("error.rei.not.enough.materials"); - if (!context.isActuallyCrafting()) - return Result.createSuccessful(); - - context.getMinecraft().openScreen(context.getContainerScreen()); - ((RecipeBookGuiHooks) ((RecipeBookProvider) context.getContainerScreen()).getRecipeBookGui()).rei_getGhostSlots().reset(); - DefaultCraftingDisplay display = (DefaultCraftingDisplay) context.getRecipe(); - PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); - buf.writeUuid(RoughlyEnoughItemsNetwork.CRAFTING_TABLE_MOVE); - buf.writeBoolean(Screen.hasShiftDown()); - CraftingContainer craftingContainer = (CraftingContainer) context.getContainer(); - - List> ogInput = display.getInput(); - List> input = Lists.newArrayListWithCapacity(craftingContainer.getCraftingWidth() * craftingContainer.getCraftingHeight()); - for (int i = 0; i < craftingContainer.getCraftingWidth() * craftingContainer.getCraftingHeight(); i++) { - input.add(Lists.newArrayList()); - } - for (int i = 0; i < ogInput.size(); i++) { - List ogStacks = ogInput.get(i); - if (display instanceof DefaultShapedDisplay) { - if (!ogInput.get(i).isEmpty()) - input.set(DefaultCraftingCategory.getSlotWithSize(display, i), ogInput.get(i)); - } else if (!ogInput.get(i).isEmpty()) - input.set(i, ogInput.get(i)); - } - - buf.writeInt(input.size()); - for (List stacks : input) { - buf.writeInt(stacks.size()); - for (ItemStack stack : stacks) { - buf.writeItemStack(stack); - } - } - ClientSidePacketRegistry.INSTANCE.sendToServer(RoughlyEnoughItemsNetwork.MOVE_ITEMS_PACKET, buf); - return Result.createSuccessful(); - } - - @Override - public double getPriority() { - return -10; - } - - public boolean canUseMovePackets() { - return ClientSidePacketRegistry.INSTANCE.canServerReceive(RoughlyEnoughItemsNetwork.MOVE_ITEMS_PACKET); - } - - public boolean hasItems(List> inputs) { - // Create a clone of player's inventory, and count - DefaultedList copyMain = DefaultedList.create(); - for (ItemStack stack : MinecraftClient.getInstance().player.inventory.main) { - copyMain.add(stack.copy()); - } - for (List possibleStacks : inputs) { - boolean done = possibleStacks.isEmpty(); - for (ItemStack possibleStack : possibleStacks) { - if (!done) { - int invRequiredCount = possibleStack.getCount(); - for (ItemStack stack : copyMain) { - if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack)) { - while (invRequiredCount > 0 && !stack.isEmpty()) { - invRequiredCount--; - stack.decrement(1); - } - } - } - if (invRequiredCount <= 0) { - done = true; - } - } - } - if (!done) - return false; - } - return true; - } -} diff --git a/src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java b/src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java index 80e8ee8af..fcab45737 100644 --- a/src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java +++ b/src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java @@ -6,23 +6,27 @@ package me.shedaniel.rei.server; import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntListIterator; import net.minecraft.container.CraftingContainer; import net.minecraft.container.CraftingTableContainer; import net.minecraft.container.PlayerContainer; +import net.minecraft.container.Slot; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; -import net.minecraft.recipe.RecipeFinder; +import net.minecraft.recipe.Ingredient; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.DefaultedList; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; -public class InputSlotCrafter { +public class InputSlotCrafter implements RecipeGridAligner { - protected final RecipeFinder recipeFinder = new RecipeFinder(); protected CraftingContainer craftingContainer; protected PlayerInventory inventory; @@ -35,100 +39,130 @@ public class InputSlotCrafter { } private void fillInputSlots(ServerPlayerEntity player, Map> map, boolean hasShift) { - /* - * Steps: - * Return the already placed items on the grid - * Check if the player have the enough resource to even craft one - * Calculate how many items the player is going to craft - * Move the best suited items for the player to use - * Send container updates to the player - * Profit?? - */ this.inventory = player.inventory; if (this.canReturnInputs() || player.isCreative()) { // Return the already placed items on the grid this.returnInputs(); - // Check if the player have the enough resource to even craft one - if (!isPossibleToCraft(map)) { + RecipeFinder recipeFinder = new RecipeFinder(); + recipeFinder.clear(); + for (ItemStack stack : player.inventory.main) { + recipeFinder.addNormalItem(stack); + } + this.craftingContainer.populateRecipeFinder(new net.minecraft.recipe.RecipeFinder() { + @Override + public void addNormalItem(ItemStack itemStack_1) { + recipeFinder.addNormalItem(itemStack_1); + } + }); + DefaultedList ingredients = DefaultedList.create(); + map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> { + ingredients.add(Ingredient.ofStacks(entry.getValue().toArray(new ItemStack[0]))); + }); + if (recipeFinder.findRecipe(ingredients, (IntList) null)) { + this.fillInputSlots(recipeFinder, ingredients, hasShift); + } else { + this.returnInputs(); craftingContainer.sendContentUpdates(); player.inventory.markDirty(); throw new NullPointerException(); } - // Calculate how many items the player is going to craft - int amountCrafting = hasShift ? 0 : 1; - if (hasShift) { - - } - - // TODO: Rewrite most parts of this - // map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> { - // int id = entry.getKey().intValue(); - // List possibleStacks = entry.getValue(); - // boolean done = false; - // for (ItemStack possibleStack : possibleStacks) { - // int requiredCount = possibleStack.getCount(); - // int invCount = 0; - // for (ItemStack stack : inventory.main) { - // if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack)) - // invCount += stack.getCount(); - // } - // if (invCount >= requiredCount) { - // for (ItemStack stack : inventory.main) { - // if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack)) { - // Slot containerSlot = craftingContainer.getSlot(id + craftingContainer.getCraftingResultSlotIndex() + 1); - // while (containerSlot.getStack().getCount() < requiredCount && !stack.isEmpty()) { - // if (containerSlot.getStack().isEmpty()) { - // containerSlot.setStack(new ItemStack(stack.getItem(), 1)); - // } else { - // containerSlot.getStack().setCount(containerSlot.getStack().getCount() + 1); - // } - // stack.setCount(stack.getCount() - 1); - // } - // if (containerSlot.getStack().getCount() >= requiredCount) - // break; - // } - // } - // break; - // } - // } - // }); - craftingContainer.sendContentUpdates(); player.inventory.markDirty(); } } - private boolean isPossibleToCraft(Map> map) { - // Create a clone of player's inventory, and count - DefaultedList copyMain = DefaultedList.create(); - for (ItemStack stack : inventory.main) { - copyMain.add(stack.copy()); + @Override + public void acceptAlignedInput(Iterator iterator_1, int int_1, int int_2, int int_3, int int_4) { + Slot slot_1 = this.craftingContainer.getSlot(int_1); + ItemStack itemStack_1 = net.minecraft.recipe.RecipeFinder.getStackFromId((Integer) iterator_1.next()); + if (!itemStack_1.isEmpty()) { + for (int int_5 = 0; int_5 < int_2; ++int_5) { + this.fillInputSlot(slot_1, itemStack_1); + } } - for (Map.Entry> entry : map.entrySet()) { - List possibleStacks = entry.getValue(); - boolean done = possibleStacks.isEmpty(); - for (ItemStack possibleStack : possibleStacks) { - if (!done) { - int invRequiredCount = possibleStack.getCount(); - for (ItemStack stack : copyMain) { - if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack)) { - while (invRequiredCount > 0 && !stack.isEmpty()) { - invRequiredCount--; - stack.decrement(1); - } - } + } + + protected void fillInputSlot(Slot slot_1, ItemStack itemStack_1) { + int int_1 = this.inventory.method_7371(itemStack_1); + if (int_1 != -1) { + ItemStack itemStack_2 = this.inventory.getInvStack(int_1).copy(); + if (!itemStack_2.isEmpty()) { + if (itemStack_2.getCount() > 1) { + this.inventory.takeInvStack(int_1, 1); + } else { + this.inventory.removeInvStack(int_1); + } + + itemStack_2.setCount(1); + if (slot_1.getStack().isEmpty()) { + slot_1.setStack(itemStack_2); + } else { + slot_1.getStack().increment(1); + } + + } + } + } + + protected void fillInputSlots(RecipeFinder recipeFinder, DefaultedList ingredients, boolean boolean_1) { + // boolean boolean_2 = this.craftingContainer.matches(recipe_1); + boolean boolean_2 = true; + int int_1 = recipeFinder.countRecipeCrafts(ingredients, (IntList) null); + int int_2; + if (boolean_2) { + for (int_2 = 0; int_2 < this.craftingContainer.getCraftingHeight() * this.craftingContainer.getCraftingWidth() + 1; ++int_2) { + if (int_2 != this.craftingContainer.getCraftingResultSlotIndex()) { + ItemStack itemStack_1 = this.craftingContainer.getSlot(int_2).getStack(); + if (!itemStack_1.isEmpty() && Math.min(int_1, itemStack_1.getMaxCount()) < itemStack_1.getCount() + 1) { + return; } - if (invRequiredCount <= 0) { - done = true; + } + } + } + + int_2 = this.getAmountToFill(boolean_1, int_1, boolean_2); + IntList intList_1 = new IntArrayList(); + if (recipeFinder.findRecipe(ingredients, intList_1, int_2)) { + int int_4 = int_2; + IntListIterator var8 = intList_1.iterator(); + + while (var8.hasNext()) { + int int_5 = (Integer) var8.next(); + int int_6 = RecipeFinder.getStackFromId(int_5).getMaxCount(); + if (int_6 < int_4) { + int_4 = int_6; + } + } + + if (recipeFinder.findRecipe(ingredients, intList_1, int_4)) { + this.returnInputs(); + this.alignRecipeToGrid(this.craftingContainer.getCraftingWidth(), this.craftingContainer.getCraftingHeight(), this.craftingContainer.getCraftingResultSlotIndex(), ingredients, intList_1.iterator(), int_4); + } + } + + } + + protected int getAmountToFill(boolean boolean_1, int int_1, boolean boolean_2) { + int int_2 = 1; + if (boolean_1) { + int_2 = int_1; + } else if (boolean_2) { + int_2 = 64; + for (int int_3 = 0; int_3 < this.craftingContainer.getCraftingWidth() * this.craftingContainer.getCraftingHeight() + 1; ++int_3) { + if (int_3 != this.craftingContainer.getCraftingResultSlotIndex()) { + ItemStack itemStack_1 = this.craftingContainer.getSlot(int_3).getStack(); + if (!itemStack_1.isEmpty() && int_2 > itemStack_1.getCount()) { + int_2 = itemStack_1.getCount(); } } } - if (!done) - return false; + if (int_2 < 64) { + ++int_2; + } } - return true; + return int_2; } protected void returnInputs() { diff --git a/src/main/java/me/shedaniel/rei/server/RecipeFinder.java b/src/main/java/me/shedaniel/rei/server/RecipeFinder.java new file mode 100644 index 000000000..036a6cc84 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/server/RecipeFinder.java @@ -0,0 +1,319 @@ +package me.shedaniel.rei.server; + +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.ints.*; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.Ingredient; +import net.minecraft.util.DefaultedList; +import net.minecraft.util.registry.Registry; + +import javax.annotation.Nullable; +import java.util.BitSet; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +public class RecipeFinder { + public final Int2IntMap idToAmountMap = new Int2IntOpenHashMap(); + + public static int getItemId(ItemStack itemStack_1) { + return Registry.ITEM.getRawId(itemStack_1.getItem()); + } + + public static ItemStack getStackFromId(int int_1) { + return int_1 == 0 ? ItemStack.EMPTY : new ItemStack(Item.byRawId(int_1)); + } + + public void addNormalItem(ItemStack itemStack_1) { + if (!itemStack_1.isDamaged() && !itemStack_1.hasEnchantments() && !itemStack_1.hasCustomName()) { + this.addItem(itemStack_1); + } + + } + + public void addItem(ItemStack itemStack_1) { + this.addItem(itemStack_1, 64); + } + + public void addItem(ItemStack itemStack_1, int int_1) { + if (!itemStack_1.isEmpty()) { + int itemId = getItemId(itemStack_1); + int itemCount = Math.min(int_1, itemStack_1.getCount()); + this.addItem(itemId, itemCount); + } + + } + + private boolean contains(int itemId) { + return this.idToAmountMap.get(itemId) > 0; + } + + /** + * Takes an amount from the finder + * + * @return the amount taken + */ + private int take(int itemId, int amount) { + int mapAmount = this.idToAmountMap.get(itemId); + if (mapAmount >= amount) { + this.idToAmountMap.put(itemId, mapAmount - amount); + return itemId; + } else { + return 0; + } + } + + private void addItem(int itemId, int itemCount) { + this.idToAmountMap.put(itemId, this.idToAmountMap.get(itemId) + itemCount); + } + + public boolean findRecipe(DefaultedList ingredients, @Nullable IntList intList_1) { + return this.findRecipe(ingredients, intList_1, 1); + } + + public boolean findRecipe(DefaultedList ingredients, @Nullable IntList intList_1, int int_1) { + return (new RecipeFinder.Filter(ingredients)).find(int_1, intList_1); + } + + public int countRecipeCrafts(DefaultedList ingredients, @Nullable IntList intList_1) { + return this.countRecipeCrafts(ingredients, Integer.MAX_VALUE, intList_1); + } + + public int countRecipeCrafts(DefaultedList ingredients, int int_1, @Nullable IntList intList_1) { + return (new RecipeFinder.Filter(ingredients)).countCrafts(int_1, intList_1); + } + + public void clear() { + this.idToAmountMap.clear(); + } + + class Filter { + private final List ingredients = Lists.newArrayList(); + private final int ingredientCount; + private final int[] usableIngredientItemIds; + private final int usableIngredientSize; + private final BitSet bitSet; + private final IntList field_7557 = new IntArrayList(); + private final DefaultedList ingredientsInput; + + public Filter(DefaultedList ingredientsInput) { + this.ingredientsInput = ingredientsInput; + this.ingredients.addAll(ingredientsInput.stream().collect(Collectors.toList())); + this.ingredients.removeIf(Ingredient::isEmpty); + this.ingredientCount = this.ingredients.size(); + this.usableIngredientItemIds = this.getUsableIngredientItemIds(); + this.usableIngredientSize = this.usableIngredientItemIds.length; + this.bitSet = new BitSet(this.ingredientCount + this.usableIngredientSize + this.ingredientCount + this.ingredientCount * this.usableIngredientSize); + + for (int ingredientIndex = 0; ingredientIndex < this.ingredients.size(); ++ingredientIndex) { + IntList possibleStacks = ((Ingredient) this.ingredients.get(ingredientIndex)).getIds(); + + // Loops over usable ingredients + for (int usableIngredientIndex = 0; usableIngredientIndex < this.usableIngredientSize; ++usableIngredientIndex) { + if (possibleStacks.contains(this.usableIngredientItemIds[usableIngredientIndex])) { + this.bitSet.set(this.method_7420(true, usableIngredientIndex, ingredientIndex)); + } + } + } + + } + + public boolean find(int int_1, @Nullable IntList intList_1) { + if (int_1 <= 0) { + return true; + } else { + int int_2; + for (int_2 = 0; this.method_7423(int_1); ++int_2) { + RecipeFinder.this.take(this.usableIngredientItemIds[this.field_7557.getInt(0)], int_1); + int int_3 = this.field_7557.size() - 1; + this.method_7421(this.field_7557.getInt(int_3)); + + for (int int_4 = 0; int_4 < int_3; ++int_4) { + this.method_7414((int_4 & 1) == 0, this.field_7557.get(int_4), this.field_7557.get(int_4 + 1)); + } + + this.field_7557.clear(); + this.bitSet.clear(0, this.ingredientCount + this.usableIngredientSize); + } + + boolean boolean_1 = int_2 == this.ingredientCount; + boolean boolean_2 = boolean_1 && intList_1 != null; + if (boolean_2) { + intList_1.clear(); + } + + this.bitSet.clear(0, this.ingredientCount + this.usableIngredientSize + this.ingredientCount); + int int_5 = 0; + List list_1 = ingredientsInput.stream().collect(Collectors.toList()); + + for (int int_6 = 0; int_6 < list_1.size(); ++int_6) { + if (boolean_2 && ((Ingredient) list_1.get(int_6)).isEmpty()) { + intList_1.add(0); + } else { + for (int int_7 = 0; int_7 < this.usableIngredientSize; ++int_7) { + if (this.method_7425(false, int_5, int_7)) { + this.method_7414(true, int_7, int_5); + RecipeFinder.this.addItem(this.usableIngredientItemIds[int_7], int_1); + if (boolean_2) { + intList_1.add(this.usableIngredientItemIds[int_7]); + } + } + } + + ++int_5; + } + } + + return boolean_1; + } + } + + private int[] getUsableIngredientItemIds() { + IntCollection intCollection_1 = new IntAVLTreeSet(); + Iterator var2 = this.ingredients.iterator(); + + while (var2.hasNext()) { + Ingredient ingredient_1 = (Ingredient) var2.next(); + intCollection_1.addAll(ingredient_1.getIds()); + } + + IntIterator intIterator_1 = intCollection_1.iterator(); + + while (intIterator_1.hasNext()) { + if (!RecipeFinder.this.contains(intIterator_1.nextInt())) { + intIterator_1.remove(); + } + } + + return intCollection_1.toIntArray(); + } + + private boolean method_7423(int int_1) { + int usableIngredientSize = this.usableIngredientSize; + + for (int int_3 = 0; int_3 < usableIngredientSize; ++int_3) { + if (RecipeFinder.this.idToAmountMap.get(this.usableIngredientItemIds[int_3]) >= int_1) { + this.method_7413(false, int_3); + + while (!this.field_7557.isEmpty()) { + int int_4 = this.field_7557.size(); + boolean boolean_1 = (int_4 & 1) == 1; + int int_5 = this.field_7557.getInt(int_4 - 1); + if (!boolean_1 && !this.method_7416(int_5)) { + break; + } + + int int_6 = boolean_1 ? this.ingredientCount : usableIngredientSize; + + int int_8; + for (int_8 = 0; int_8 < int_6; ++int_8) { + if (!this.method_7426(boolean_1, int_8) && this.method_7418(boolean_1, int_5, int_8) && this.method_7425(boolean_1, int_5, int_8)) { + this.method_7413(boolean_1, int_8); + break; + } + } + + int_8 = this.field_7557.size(); + if (int_8 == int_4) { + this.field_7557.removeInt(int_8 - 1); + } + } + + if (!this.field_7557.isEmpty()) { + return true; + } + } + } + + return false; + } + + private boolean method_7416(int int_1) { + return this.bitSet.get(this.method_7419(int_1)); + } + + private void method_7421(int int_1) { + this.bitSet.set(this.method_7419(int_1)); + } + + private int method_7419(int int_1) { + return this.ingredientCount + this.usableIngredientSize + int_1; + } + + private boolean method_7418(boolean boolean_1, int int_1, int int_2) { + return this.bitSet.get(this.method_7420(boolean_1, int_1, int_2)); + } + + private boolean method_7425(boolean boolean_1, int int_1, int int_2) { + return boolean_1 != this.bitSet.get(1 + this.method_7420(boolean_1, int_1, int_2)); + } + + private void method_7414(boolean boolean_1, int int_1, int int_2) { + this.bitSet.flip(1 + this.method_7420(boolean_1, int_1, int_2)); + } + + private int method_7420(boolean boolean_1, int int_1, int int_2) { + int int_3 = boolean_1 ? int_1 * this.ingredientCount + int_2 : int_2 * this.ingredientCount + int_1; + return this.ingredientCount + this.usableIngredientSize + this.ingredientCount + 2 * int_3; + } + + private void method_7413(boolean boolean_1, int int_1) { + this.bitSet.set(this.method_7424(boolean_1, int_1)); + this.field_7557.add(int_1); + } + + private boolean method_7426(boolean boolean_1, int int_1) { + return this.bitSet.get(this.method_7424(boolean_1, int_1)); + } + + private int method_7424(boolean boolean_1, int int_1) { + return (boolean_1 ? 0 : this.ingredientCount) + int_1; + } + + public int countCrafts(int int_1, @Nullable IntList intList_1) { + int int_2 = 0; + int int_3 = Math.min(int_1, this.method_7415()) + 1; + + while (true) { + while (true) { + int int_4 = (int_2 + int_3) / 2; + if (this.find(int_4, (IntList) null)) { + if (int_3 - int_2 <= 1) { + if (int_4 > 0) { + this.find(int_4, intList_1); + } + + return int_4; + } + + int_2 = int_4; + } else { + int_3 = int_4; + } + } + } + } + + private int method_7415() { + int int_1 = Integer.MAX_VALUE; + Iterator var2 = this.ingredients.iterator(); + + while (var2.hasNext()) { + Ingredient ingredient_1 = (Ingredient) var2.next(); + int int_2 = 0; + + int int_3; + for (IntListIterator var5 = ingredient_1.getIds().iterator(); var5.hasNext(); int_2 = Math.max(int_2, RecipeFinder.this.idToAmountMap.get(int_3))) { + int_3 = (Integer) var5.next(); + } + + if (int_1 > 0) { + int_1 = Math.min(int_1, int_2); + } + } + + return int_1; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/shedaniel/rei/server/RecipeGridAligner.java b/src/main/java/me/shedaniel/rei/server/RecipeGridAligner.java new file mode 100644 index 000000000..314292e77 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/server/RecipeGridAligner.java @@ -0,0 +1,61 @@ +package me.shedaniel.rei.server; + +import net.minecraft.recipe.Ingredient; +import net.minecraft.util.DefaultedList; +import net.minecraft.util.math.MathHelper; + +import java.util.Iterator; + +public interface RecipeGridAligner { + default void alignRecipeToGrid(int int_1, int int_2, int int_3, DefaultedList recipe_1, Iterator iterator_1, int int_4) { + int int_5 = int_1; + int int_6 = int_2; + // if (recipe_1 instanceof ShapedRecipe) { + // ShapedRecipe shapedRecipe_1 = (ShapedRecipe)recipe_1; + // int_5 = shapedRecipe_1.getWidth(); + // int_6 = shapedRecipe_1.getHeight(); + // } + + int int_7 = 0; + + for (int int_8 = 0; int_8 < int_2; ++int_8) { + if (int_7 == int_3) { + ++int_7; + } + + boolean boolean_1 = (float) int_6 < (float) int_2 / 2.0F; + int int_9 = MathHelper.floor((float) int_2 / 2.0F - (float) int_6 / 2.0F); + if (boolean_1 && int_9 > int_8) { + int_7 += int_1; + ++int_8; + } + + for (int int_10 = 0; int_10 < int_1; ++int_10) { + if (!iterator_1.hasNext()) { + return; + } + + boolean_1 = (float) int_5 < (float) int_1 / 2.0F; + int_9 = MathHelper.floor((float) int_1 / 2.0F - (float) int_5 / 2.0F); + int int_11 = int_5; + boolean boolean_2 = int_10 < int_5; + if (boolean_1) { + int_11 = int_9 + int_5; + boolean_2 = int_9 <= int_10 && int_10 < int_9 + int_5; + } + + if (boolean_2) { + this.acceptAlignedInput(iterator_1, int_7, int_4, int_8, int_10); + } else if (int_11 == int_10) { + int_7 += int_1 - int_10; + break; + } + + ++int_7; + } + } + + } + + void acceptAlignedInput(Iterator var1, int var2, int var3, int var4, int var5); +} \ No newline at end of file diff --git a/src/main/resources/assets/roughlyenoughitems/lang/en_us.json b/src/main/resources/assets/roughlyenoughitems/lang/en_us.json index b11413995..2d5ac8ad8 100755 --- a/src/main/resources/assets/roughlyenoughitems/lang/en_us.json +++ b/src/main/resources/assets/roughlyenoughitems/lang/en_us.json @@ -44,6 +44,7 @@ "error.rei.transfer.too_small": "Unable to move items to a 2x2 grid.", "error.rei.not.on.server": "REI is not on the server.", "error.rei.not.enough.materials": "Not Enough Materials.", + "error.rei.internal.error": "Internal Error: %s", "rei.rei.no.slot.in.inv": "Can't find any space for item in the inventory", "text.rei.showing_craftable": "Showing Craftable", "text.rei.showing_all": "Showing All", -- cgit