Skip to content

Commit

Permalink
Rework Ingredient tracker to track stored items and compare Ingredien…
Browse files Browse the repository at this point in the history
…tList against that
  • Loading branch information
Darkere authored and raoulvdberge committed Dec 17, 2022
1 parent 254a862 commit aaf528b
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 166 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Available items indicator in JEI now updates while JEI is open

### Fixed

- Fixed chained crafters not taking over the name of the root crafter.
- Fixed lag when opening JEI in large system

## [v1.11.1] - 2022-10-30

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
import com.refinedmods.refinedstorage.apiimpl.network.node.GridNetworkNode;
import com.refinedmods.refinedstorage.blockentity.BaseBlockEntity;
import com.refinedmods.refinedstorage.blockentity.config.IType;
import com.refinedmods.refinedstorage.blockentity.grid.portable.IPortableGrid;
import com.refinedmods.refinedstorage.container.slot.filter.FilterSlot;
import com.refinedmods.refinedstorage.container.slot.filter.FluidFilterSlot;
import com.refinedmods.refinedstorage.container.slot.grid.CraftingGridSlot;
Expand All @@ -17,9 +20,6 @@
import com.refinedmods.refinedstorage.container.slot.legacy.LegacyDisabledSlot;
import com.refinedmods.refinedstorage.container.slot.legacy.LegacyFilterSlot;
import com.refinedmods.refinedstorage.screen.IScreenInfoProvider;
import com.refinedmods.refinedstorage.blockentity.BaseBlockEntity;
import com.refinedmods.refinedstorage.blockentity.config.IType;
import com.refinedmods.refinedstorage.blockentity.grid.portable.IPortableGrid;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import com.refinedmods.refinedstorage.network.grid.GridProcessingTransferMessage;
import com.refinedmods.refinedstorage.network.grid.GridTransferMessage;
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.stack.ItemGridStack;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.forge.ForgeTypes;
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
Expand All @@ -20,14 +18,16 @@
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingRecipe;
import net.minecraftforge.fluids.FluidStack;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridContainerMenu, Object> {
Expand Down Expand Up @@ -58,26 +58,38 @@ public RecipeType<Object> getRecipeType() {

@Override
public @Nullable IRecipeTransferError transferRecipe(GridContainerMenu container, Object recipe, IRecipeSlotsView recipeSlots, Player player, boolean maxTransfer, boolean doTransfer) {
if (!(container.getScreenInfoProvider() instanceof GridScreen)) {
if (!(container.getScreenInfoProvider() instanceof GridScreen gridScreen)) {
return null;
}

//When JEI is open the screen no longer ticks and doesn't run Actions. However, we do still want to run the actions that update the grid to keep the stored items up to date
gridScreen.runActions();

Ingredient.IngredientList ingredientList = new Ingredient.IngredientList();
for (IRecipeSlotView slotView : recipeSlots.getSlotViews(RecipeIngredientRole.INPUT)) {
Optional<ItemStack> firstStack = slotView.getIngredients(VanillaTypes.ITEM_STACK).findAny();
ingredientList.add(new Ingredient(slotView, firstStack.map(ItemStack::getCount).orElse(0)));
}

IngredientTracker tracker = IngredientTracker.getTracker(container);
tracker.updateAvailability(ingredientList, container, player);

GridType type = container.getGrid().getGridType();

if (type == GridType.CRAFTING) {
return transferRecipeForCraftingGrid(container, recipe, recipeSlots, player, doTransfer);
return transferRecipeForCraftingGrid(container, recipe, recipeSlots, player, doTransfer, ingredientList);
} else if (type == GridType.PATTERN) {
return transferRecipeForPatternGrid(container, recipe, recipeSlots, player, doTransfer);
return transferRecipeForPatternGrid(container, recipe, recipeSlots, player, doTransfer, ingredientList);
}

return null;
}

private RecipeTransferCraftingGridError transferRecipeForCraftingGrid(GridContainerMenu container, Object recipe, IRecipeSlotsView recipeLayout, Player player, boolean doTransfer) {
IngredientTracker tracker = createTracker(container, recipeLayout, player, doTransfer);
private RecipeTransferCraftingGridError transferRecipeForCraftingGrid(GridContainerMenu container, Object recipe, IRecipeSlotsView recipeLayout, Player player, boolean doTransfer, Ingredient.IngredientList ingredientList) {

if (doTransfer) {
if (tracker.hasMissingButAutocraftingAvailable() && Screen.hasControlDown()) {
tracker.createCraftingRequests().forEach((id, count) -> RS.NETWORK_HANDLER.sendToServer(
if (ingredientList.hasMissingButAutocraftingAvailable() && Screen.hasControlDown()) {
ingredientList.createCraftingRequests().forEach((id, count) -> RS.NETWORK_HANDLER.sendToServer(
new GridCraftingPreviewRequestMessage(
id,
count,
Expand All @@ -86,79 +98,38 @@ private RecipeTransferCraftingGridError transferRecipeForCraftingGrid(GridContai
)
));
} else {
moveItems(container, recipe, recipeLayout, tracker);
moveItems(container, recipe, recipeLayout, player);
}
} else {
if (tracker.hasMissing()) {
return new RecipeTransferCraftingGridError(tracker);
if (ingredientList.hasMissing()) {
return new RecipeTransferCraftingGridError(ingredientList);
}
}

return null;
}

private IRecipeTransferError transferRecipeForPatternGrid(GridContainerMenu container, Object recipe, IRecipeSlotsView recipeLayout, Player player, boolean doTransfer) {
IngredientTracker tracker = createTracker(container, recipeLayout, player, doTransfer);

private IRecipeTransferError transferRecipeForPatternGrid(GridContainerMenu container, Object recipe, IRecipeSlotsView recipeLayout, Player player, boolean doTransfer, Ingredient.IngredientList ingredientList) {
if (doTransfer) {
moveItems(container, recipe, recipeLayout, tracker);
moveItems(container, recipe, recipeLayout, player);
} else {
if (tracker.isAutocraftingAvailable()) {
return new RecipeTransferPatternGridError(tracker);
if (ingredientList.isAutocraftingAvailable()) {
return new RecipeTransferPatternGridError(ingredientList);
}
}

return null;
}

private IngredientTracker createTracker(GridContainerMenu container, IRecipeSlotsView recipeLayout, Player player, boolean doTransfer) {
IngredientTracker tracker = new IngredientTracker(recipeLayout, doTransfer);

// Using IGridView#getStacks will return a *filtered* list of items in the view,
// which will cause problems - especially if the user uses JEI synchronised searching.
// Instead, we will use IGridView#getAllStacks which provides an unordered view of all GridStacks.
Collection<IGridStack> gridStacks = ((GridScreen) container.getScreenInfoProvider()).getView().getAllStacks();

// Check grid
if (container.getGrid().isGridActive()) {
for (IGridStack gridStack : gridStacks) {
if (gridStack instanceof ItemGridStack) {
tracker.addAvailableStack(((ItemGridStack) gridStack).getStack(), gridStack);
}
}
}

// Check inventory
for (int inventorySlot = 0; inventorySlot < player.getInventory().getContainerSize(); inventorySlot++) {
if (!player.getInventory().getItem(inventorySlot).isEmpty()) {
tracker.addAvailableStack(player.getInventory().getItem(inventorySlot), null);
}
}

// Check grid crafting slots
if (container.getGrid().getGridType().equals(GridType.CRAFTING)) {
CraftingContainer craftingMatrix = container.getGrid().getCraftingMatrix();
if (craftingMatrix != null) {
for (int matrixSlot = 0; matrixSlot < craftingMatrix.getContainerSize(); matrixSlot++) {
if (!craftingMatrix.getItem(matrixSlot).isEmpty()) {
tracker.addAvailableStack(craftingMatrix.getItem(matrixSlot), null);
}
}
}
}

return tracker;
}

public boolean hasTransferredRecently() {
return System.currentTimeMillis() - lastTransferTimeMs <= TRANSFER_SCROLLBAR_DELAY_MS;
}

private void moveItems(GridContainerMenu gridContainer, Object recipe, IRecipeSlotsView recipeLayout, IngredientTracker tracker) {
private void moveItems(GridContainerMenu gridContainer, Object recipe, IRecipeSlotsView recipeLayout, Player player) {
this.lastTransferTimeMs = System.currentTimeMillis();

if (gridContainer.getGrid().getGridType() == GridType.PATTERN && !(recipe instanceof CraftingRecipe)) {
moveForProcessing(recipeLayout, tracker);
moveForProcessing(recipeLayout, gridContainer, player);
} else {
move(recipeLayout);
}
Expand All @@ -185,7 +156,7 @@ private void move(IRecipeSlotsView recipeSlotsView) {
RS.NETWORK_HANDLER.sendToServer(new GridTransferMessage(inputs));
}

private void moveForProcessing(IRecipeSlotsView recipeLayout, IngredientTracker tracker) {
private void moveForProcessing(IRecipeSlotsView recipeLayout, GridContainerMenu gridContainer, Player player) {
List<ItemStack> inputs = new LinkedList<>();
List<ItemStack> outputs = new LinkedList<>();

Expand All @@ -194,13 +165,13 @@ private void moveForProcessing(IRecipeSlotsView recipeLayout, IngredientTracker

List<IRecipeSlotView> inputSlots = recipeLayout.getSlotViews(RecipeIngredientRole.INPUT);
for (IRecipeSlotView view : inputSlots) {
handleItemIngredient(inputs, view, tracker);
handleItemIngredient(inputs, view, gridContainer, player);
handleFluidIngredient(fluidInputs, view);
}

List<IRecipeSlotView> outputSlots = recipeLayout.getSlotViews(RecipeIngredientRole.OUTPUT);
for (IRecipeSlotView view : outputSlots) {
handleItemIngredient(outputs, view, tracker);
handleItemIngredient(outputs, view, gridContainer, player);
handleFluidIngredient(fluidOutputs, view);
}

Expand All @@ -213,9 +184,9 @@ private void handleFluidIngredient(List<FluidStack> list, IRecipeSlotView slotVi
}
}

private void handleItemIngredient(List<ItemStack> list, IRecipeSlotView slotView, IngredientTracker tracker) {
private void handleItemIngredient(List<ItemStack> list, IRecipeSlotView slotView, GridContainerMenu gridContainer, Player player) {
if (slotView != null && slotView.getIngredients(VanillaTypes.ITEM_STACK).findAny().isPresent()) {
ItemStack stack = tracker.findBestMatch(slotView.getIngredients(VanillaTypes.ITEM_STACK).toList());
ItemStack stack = IngredientTracker.getTracker(gridContainer).findBestMatch(gridContainer, player, slotView.getIngredients(VanillaTypes.ITEM_STACK).toList());

if (stack.isEmpty() && slotView.getDisplayedIngredient(VanillaTypes.ITEM_STACK).isPresent()) {
stack = slotView.getDisplayedIngredient(VanillaTypes.ITEM_STACK).get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import mezz.jei.api.gui.ingredient.IRecipeSlotView;

import java.util.UUID;
import java.util.*;

class Ingredient {
private final IRecipeSlotView slotView;
Expand All @@ -16,7 +16,7 @@ public Ingredient(IRecipeSlotView view, int count) {
}

public boolean isAvailable() {
return getMissingAmount() == 0;
return getMissingAmount() <= 0;
}

public int getMissingAmount() {
Expand All @@ -42,4 +42,36 @@ public void setCraftStackId(UUID craftStackId) {
public void fulfill(int amount) {
fulfilled += amount;
}

static class IngredientList {
List<Ingredient> ingredients = new ArrayList<>();

void add(Ingredient ingredient) {
ingredients.add(ingredient);
}

public boolean hasMissing() {
return ingredients.stream().anyMatch(ingredient -> !ingredient.isAvailable());
}

public boolean hasMissingButAutocraftingAvailable() {
return ingredients.stream().anyMatch(ingredient -> !ingredient.isAvailable() && ingredient.isCraftable());
}

public boolean isAutocraftingAvailable() {
return ingredients.stream().anyMatch(Ingredient::isCraftable);
}

public Map<UUID, Integer> createCraftingRequests() {
Map<UUID, Integer> toRequest = new HashMap<>();

for (Ingredient ingredient : ingredients) {
if (!ingredient.isAvailable() && ingredient.isCraftable()) {
toRequest.merge(ingredient.getCraftStackId(), ingredient.getMissingAmount(), Integer::sum);
}
}

return toRequest;
}
}
}
Loading

0 comments on commit aaf528b

Please sign in to comment.