Rework Ingredient tracker to track stored items and compare IngredientList against that

This commit is contained in:
Darkere
2022-12-12 22:46:52 +01:00
committed by Raoul
parent 254a86215a
commit aaf528bce2
11 changed files with 308 additions and 178 deletions

View File

@@ -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

View File

@@ -9,6 +9,9 @@ import com.refinedmods.refinedstorage.api.network.grid.handler.IItemGridHandler;
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;
@@ -17,9 +20,6 @@ import com.refinedmods.refinedstorage.container.slot.legacy.LegacyBaseSlot;
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;

View File

@@ -8,8 +8,6 @@ import com.refinedmods.refinedstorage.network.grid.GridCraftingPreviewRequestMes
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;
@@ -20,14 +18,16 @@ import mezz.jei.api.recipe.transfer.IRecipeTransferError;
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> {
@@ -58,26 +58,38 @@ public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridCon
@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,
@@ -86,79 +98,38 @@ public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridCon
)
));
} 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);
}
@@ -185,7 +156,7 @@ public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridCon
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<>();
@@ -194,13 +165,13 @@ public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridCon
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);
}
@@ -213,9 +184,9 @@ public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridCon
}
}
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();

View File

@@ -2,7 +2,7 @@ package com.refinedmods.refinedstorage.integration.jei;
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
import java.util.UUID;
import java.util.*;
class Ingredient {
private final IRecipeSlotView slotView;
@@ -16,7 +16,7 @@ class Ingredient {
}
public boolean isAvailable() {
return getMissingAmount() == 0;
return getMissingAmount() <= 0;
}
public int getMissingAmount() {
@@ -42,4 +42,36 @@ class Ingredient {
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;
}
}
}

View File

@@ -2,124 +2,214 @@ package com.refinedmods.refinedstorage.integration.jei;
import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern;
import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternProvider;
import com.refinedmods.refinedstorage.api.network.grid.GridType;
import com.refinedmods.refinedstorage.api.util.IComparer;
import com.refinedmods.refinedstorage.apiimpl.API;
import com.refinedmods.refinedstorage.container.GridContainerMenu;
import com.refinedmods.refinedstorage.item.PatternItem;
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.stack.ItemGridStack;
import com.refinedmods.refinedstorage.screen.grid.view.IGridView;
import com.refinedmods.refinedstorage.util.ItemStackKey;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.recipe.RecipeIngredientRole;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable;
import java.util.*;
public class IngredientTracker {
private final List<Ingredient> ingredients = new ArrayList<>();
private final Map<ResourceLocation, Integer> storedItems = new HashMap<>();
private boolean doTransfer;
public IngredientTracker(IRecipeSlotsView recipeLayout, boolean doTransfer) {
for (IRecipeSlotView slotView : recipeLayout.getSlotViews(RecipeIngredientRole.INPUT)) {
Optional<ItemStack> optionalItemStack = slotView.getIngredients(VanillaTypes.ITEM_STACK).findAny();
private static IngredientTracker INSTANCE;
optionalItemStack.ifPresent(stack -> ingredients.add(new Ingredient(slotView, stack.getCount())));
private final Map<ItemStackKey, Integer> storedItems = new HashMap<>();
private final Map<ItemStackKey, Integer> patternItems = new HashMap<>();
private final Map<ItemStackKey, UUID> craftableItems = new HashMap<>();
public static IngredientTracker getTracker(GridContainerMenu gridContainer) {
if (INSTANCE == null) {
INSTANCE = new IngredientTracker(gridContainer);
}
return INSTANCE;
}
public static void invalidate() {
INSTANCE = null;
}
public IngredientTracker(GridContainerMenu gridContainer) {
// 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.
IGridView view = ((GridScreen) gridContainer.getScreenInfoProvider()).getView();
//Existing stacks are synced by the fact that they are both referencing the same object. However, new stacks need to be added.
view.addDeltaListener((iGridStack -> {
if (iGridStack instanceof ItemGridStack stack) {
if (stack.isCraftable()) {
craftableItems.put(new ItemStackKey(stack.getStack()), stack.getId());
} else {
addStack(stack.getStack());
}
}
}));
Collection<IGridStack> gridStacks = view.getAllStacks();
// Check grid
if (gridContainer.getGrid().isGridActive()) {
for (IGridStack gridStack : gridStacks) {
if (gridStack instanceof ItemGridStack stackInGrid) {
// for craftables we should easily be able to take the hit from hashing the nbt
if (stackInGrid.isCraftable()) {
craftableItems.put(new ItemStackKey(stackInGrid.getStack()), gridStack.getId());
} else { // for non-craftables we don't hash nbt to avoid the performance hit
addStack(stackInGrid.getStack());
}
}
}
}
}
public void addStack(ItemStack stack) {
if (stack.isEmpty()) {
return;
}
if (stack.getItem() instanceof ICraftingPatternProvider) {
ICraftingPattern pattern = PatternItem.fromCache(Minecraft.getInstance().level, stack);
if (pattern.isValid()) {
for (ItemStack outputStack : pattern.getOutputs()) {
patternItems.merge(new ItemStackKey(outputStack), 1, Integer::sum);
}
}
} else {
storedItems.merge(new ItemStackKey(stack), stack.getCount(), Integer::sum);
}
this.doTransfer = doTransfer;
}
public Collection<Ingredient> getIngredients() {
return ingredients;
public ItemStack findBestMatch(GridContainerMenu gridContainer, Player player, List<ItemStack> list) {
ItemStack resultStack = ItemStack.EMPTY;
int count = 0;
for (ItemStack listStack : list) {
//check crafting matrix
if (gridContainer.getGrid().getGridType().equals(GridType.CRAFTING)) {
CraftingContainer craftingMatrix = gridContainer.getGrid().getCraftingMatrix();
if (craftingMatrix != null) {
for (int matrixSlot = 0; matrixSlot < craftingMatrix.getContainerSize(); matrixSlot++) {
ItemStack stackInSlot = craftingMatrix.getItem(matrixSlot);
if (API.instance().getComparer().isEqual(listStack, stackInSlot, IComparer.COMPARE_NBT)) {
if (stackInSlot.getCount() > count) {
count = stackInSlot.getCount();
resultStack = stackInSlot;
}
}
}
}
}
//check inventory
for (int inventorySlot = 0; inventorySlot < player.getInventory().getContainerSize(); inventorySlot++) {
ItemStack stackInSlot = player.getInventory().getItem(inventorySlot);
if (API.instance().getComparer().isEqual(listStack, stackInSlot, IComparer.COMPARE_NBT)) {
if (stackInSlot.getCount() > count) {
count = stackInSlot.getCount();
resultStack = stackInSlot;
}
}
}
//check storage
var stored = storedItems.get(new ItemStackKey(listStack));
if (stored != null) {
if (stored > count) {
resultStack = listStack;
count = stored;
}
}
}
//If the item is not in storage check patterns autocrafting
if (count == 0) {
for (ItemStack itemStack : list) {
ItemStackKey key = new ItemStackKey(itemStack);
if (craftableItems.get(key) != null) {
resultStack = itemStack;
break;
} else if (patternItems.get(key) != null) {
resultStack = itemStack;
break;
}
}
}
return resultStack;
}
public void addAvailableStack(ItemStack stack, @Nullable IGridStack gridStack) {
int available = stack.getCount();
if (doTransfer) {
if (stack.getItem() instanceof ICraftingPatternProvider) {
ICraftingPattern pattern = PatternItem.fromCache(Minecraft.getInstance().level, stack);
if (pattern.isValid()) {
for (ItemStack outputStack : pattern.getOutputs()) {
storedItems.merge(registryName(outputStack.getItem()), outputStack.getCount(), Integer::sum);
public void updateAvailability(Ingredient.IngredientList ingredientList, GridContainerMenu gridContainer, Player player) {
Map<Integer, Integer> usedMatrixStacks = new HashMap<>();
Map<Integer, Integer> usedInventoryStacks = new HashMap<>();
Map<ItemStackKey, Integer> usedStoredStacks = new HashMap<>();
//Gather available Stacks
for (Ingredient ingredient : ingredientList.ingredients) {
ingredient.getSlotView().getIngredients(VanillaTypes.ITEM_STACK).takeWhile(stack -> !ingredient.isAvailable()).forEach(stack -> {
ingredient.setCraftStackId(craftableItems.get(new ItemStackKey(stack)));
// Check grid crafting slots
if (gridContainer.getGrid().getGridType().equals(GridType.CRAFTING)) {
CraftingContainer craftingMatrix = gridContainer.getGrid().getCraftingMatrix();
if (craftingMatrix != null) {
for (int matrixSlot = 0; matrixSlot < craftingMatrix.getContainerSize(); matrixSlot++) {
if (checkStack(usedMatrixStacks, ingredient, stack, matrixSlot, craftingMatrix.getItem(matrixSlot))) {
return;
}
}
}
}
} else {
storedItems.merge(registryName(stack.getItem()), available, Integer::sum);
}
}
for (Ingredient ingredient : ingredients) {
if (available == 0) {
return;
}
Optional<ItemStack> match = ingredient
.getSlotView()
.getIngredients(VanillaTypes.ITEM_STACK)
.filter(s -> API.instance().getComparer().isEqual(stack, s, IComparer.COMPARE_NBT))
.findFirst();
if (match.isPresent()) {
// Craftables and non-craftables are 2 different gridstacks
// As such we need to ignore craftable stacks as they are not actual items
if (gridStack != null && gridStack.isCraftable()) {
ingredient.setCraftStackId(gridStack.getId());
} else if (!ingredient.isAvailable()) {
int needed = ingredient.getMissingAmount();
int used = Math.min(available, needed);
ingredient.fulfill(used);
available -= used;
//read inventory
for (int inventorySlot = 0; inventorySlot < player.getInventory().getContainerSize(); inventorySlot++) {
if (checkStack(usedInventoryStacks, ingredient, stack, inventorySlot, player.getInventory().getItem(inventorySlot))) {
return;
}
}
}
//Check Stored Stacks
ItemStackKey key = new ItemStackKey(stack);
Integer stored = storedItems.get(key);
if (stored != null) {
Integer used = usedStoredStacks.get(key);
int amount = Math.min(ingredient.getMissingAmount(), used == null ? stored : stored - used);
if (amount > 0) {
ingredient.fulfill(amount);
usedStoredStacks.put(key, used == null ? amount : used + amount);
}
}
});
}
}
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);
private boolean checkStack(Map<Integer, Integer> usedMatrixStacks, Ingredient ingredient, ItemStack stack, int slot, ItemStack stackInSlot) {
if (API.instance().getComparer().isEqual(stack, stackInSlot, IComparer.COMPARE_NBT)) {
Integer used = usedMatrixStacks.get(slot);
int amount = Math.min(ingredient.getMissingAmount(), used == null ? stackInSlot.getCount() : stackInSlot.getCount() - used);
if (amount > 0) {
ingredient.fulfill(amount);
usedMatrixStacks.put(slot, amount);
}
return ingredient.isAvailable();
}
return toRequest;
return false;
}
public ItemStack findBestMatch(List<ItemStack> list) {
ItemStack stack = ItemStack.EMPTY;
int count = 0;
for (ItemStack itemStack : list) {
Integer stored = storedItems.get(registryName(itemStack.getItem()));
if (stored != null && stored > count) {
stack = itemStack;
count = stored;
}
}
return stack;
}
private ResourceLocation registryName(final Item item) {
return ForgeRegistries.ITEMS.getKey(item);
}
}

View File

@@ -8,7 +8,6 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@@ -17,10 +16,11 @@ public class RecipeTransferCraftingGridError implements IRecipeTransferError {
protected static final Color AUTOCRAFTING_HIGHLIGHT_COLOR = new Color(0.0f, 0.0f, 1.0f, 0.4f);
private static final Color MISSING_HIGHLIGHT_COLOR = new Color(1.0f, 0.0f, 0.0f, 0.4f);
private static final boolean HOST_OS_IS_MACOS = System.getProperty("os.name").equals("Mac OS X");
protected final IngredientTracker tracker;
protected final Ingredient.IngredientList ingredientList;
public RecipeTransferCraftingGridError(IngredientTracker tracker) {
this.tracker = tracker;
public RecipeTransferCraftingGridError(Ingredient.IngredientList ingredientList) {
this.ingredientList = ingredientList;
}
@Override
@@ -28,6 +28,16 @@ public class RecipeTransferCraftingGridError implements IRecipeTransferError {
return Type.COSMETIC;
}
@Override
public int getButtonHighlightColor() {
if (ingredientList.hasMissingButAutocraftingAvailable()) {
return AUTOCRAFTING_HIGHLIGHT_COLOR.getRGB();
} else if (ingredientList.hasMissing()) {
return MISSING_HIGHLIGHT_COLOR.getRGB();
}
return IRecipeTransferError.super.getButtonHighlightColor();
}
@Override
public void showError(PoseStack poseStack, int mouseX, int mouseY, IRecipeSlotsView recipeSlotsView, int recipeX, int recipeY) {
poseStack.translate(recipeX, recipeY, 0);
@@ -44,7 +54,7 @@ public class RecipeTransferCraftingGridError implements IRecipeTransferError {
boolean craftMessage = false;
boolean missingMessage = false;
for (Ingredient ingredient : tracker.getIngredients()) {
for (Ingredient ingredient : ingredientList.ingredients) {
if (!ingredient.isAvailable()) {
if (ingredient.isCraftable()) {
ingredient.getSlotView().drawHighlight(stack, AUTOCRAFTING_HIGHLIGHT_COLOR.getRGB());

View File

@@ -4,13 +4,12 @@ import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import java.util.ArrayList;
import java.util.List;
public class RecipeTransferPatternGridError extends RecipeTransferCraftingGridError {
public RecipeTransferPatternGridError(IngredientTracker tracker) {
super(tracker);
public RecipeTransferPatternGridError(Ingredient.IngredientList ingredients) {
super(ingredients);
}
@Override
@@ -20,7 +19,7 @@ public class RecipeTransferPatternGridError extends RecipeTransferCraftingGridEr
boolean craftMessage = false;
for (Ingredient ingredient : tracker.getIngredients()) {
for (Ingredient ingredient : ingredientList.ingredients) {
if (ingredient.isCraftable()) {
ingredient.getSlotView().drawHighlight(stack, AUTOCRAFTING_HIGHLIGHT_COLOR.getRGB());
craftMessage = true;

View File

@@ -73,7 +73,7 @@ public abstract class BaseScreen<T extends AbstractContainerMenu> extends Abstra
executeLater(AbstractContainerScreen.class, callback);
}
private void runActions() {
public void runActions() {
runActions(getClass());
runActions(AbstractContainerScreen.class);
}

View File

@@ -10,6 +10,12 @@ import com.refinedmods.refinedstorage.api.network.grid.IGrid;
import com.refinedmods.refinedstorage.api.network.grid.handler.IItemGridHandler;
import com.refinedmods.refinedstorage.apiimpl.network.node.GridNetworkNode;
import com.refinedmods.refinedstorage.apiimpl.render.ElementDrawers;
import com.refinedmods.refinedstorage.blockentity.NetworkNodeBlockEntity;
import com.refinedmods.refinedstorage.blockentity.config.IType;
import com.refinedmods.refinedstorage.blockentity.data.BlockEntitySynchronizationManager;
import com.refinedmods.refinedstorage.blockentity.grid.GridBlockEntity;
import com.refinedmods.refinedstorage.blockentity.grid.portable.IPortableGrid;
import com.refinedmods.refinedstorage.blockentity.grid.portable.PortableGridBlockEntity;
import com.refinedmods.refinedstorage.container.GridContainerMenu;
import com.refinedmods.refinedstorage.network.grid.*;
import com.refinedmods.refinedstorage.screen.BaseScreen;
@@ -24,12 +30,6 @@ import com.refinedmods.refinedstorage.screen.widget.ScrollbarWidget;
import com.refinedmods.refinedstorage.screen.widget.SearchWidget;
import com.refinedmods.refinedstorage.screen.widget.TabListWidget;
import com.refinedmods.refinedstorage.screen.widget.sidebutton.*;
import com.refinedmods.refinedstorage.blockentity.NetworkNodeBlockEntity;
import com.refinedmods.refinedstorage.blockentity.config.IType;
import com.refinedmods.refinedstorage.blockentity.data.BlockEntitySynchronizationManager;
import com.refinedmods.refinedstorage.blockentity.grid.GridBlockEntity;
import com.refinedmods.refinedstorage.blockentity.grid.portable.IPortableGrid;
import com.refinedmods.refinedstorage.blockentity.grid.portable.PortableGridBlockEntity;
import com.refinedmods.refinedstorage.util.RenderUtils;
import com.refinedmods.refinedstorage.util.TimeUtils;
import net.minecraft.ChatFormatting;
@@ -37,7 +37,6 @@ import net.minecraft.client.resources.language.I18n;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
@@ -658,6 +657,7 @@ public class GridScreen extends BaseScreen<GridContainerMenu> implements IScreen
if (!RS.CLIENT_CONFIG.getGrid().getRememberSearchQuery()) {
searchQuery = "";
}
getView().removed();
}
public SearchWidget getSearchField() {

View File

@@ -1,6 +1,7 @@
package com.refinedmods.refinedstorage.screen.grid.view;
import com.refinedmods.refinedstorage.api.network.grid.IGrid;
import com.refinedmods.refinedstorage.integration.jei.IngredientTracker;
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.filtering.GridFilterParser;
import com.refinedmods.refinedstorage.screen.grid.sorting.IGridSorter;
@@ -9,6 +10,7 @@ import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -22,6 +24,8 @@ public class GridViewImpl implements IGridView {
private boolean active = false;
private List<IGridStack> stacks = new ArrayList<>();
private List<Consumer<IGridStack>> deltaListeners = new ArrayList<>();
public GridViewImpl(GridScreen screen, IGridSorter defaultSorter, List<IGridSorter> sorters) {
this.screen = screen;
this.defaultSorter = defaultSorter;
@@ -73,6 +77,17 @@ public class GridViewImpl implements IGridView {
this.screen.updateScrollbar();
}
@Override
public void addDeltaListener(Consumer<IGridStack> listener) {
deltaListeners.add(listener);
}
@Override
public void removed() {
deltaListeners.clear();
IngredientTracker.invalidate();
}
private Comparator<IGridStack> getActiveSort() {
IGrid grid = screen.getGrid();
SortingDirection sortingDirection = grid.getSortingDirection() == IGrid.SORTING_DIRECTION_DESCENDING ? SortingDirection.DESCENDING : SortingDirection.ASCENDING;
@@ -153,6 +168,9 @@ public class GridViewImpl implements IGridView {
if (craftingStack != null && shouldSort && activeFilters.test(existing)) {
stacks.remove(craftingStack);
}
deltaListeners.forEach(consumer -> consumer.accept(stack));
} else {
if (shouldSort) {
stacks.remove(existing);

View File

@@ -6,6 +6,7 @@ import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
public interface IGridView {
List<IGridStack> getStacks();
@@ -24,4 +25,8 @@ public interface IGridView {
boolean canCraft();
void sort();
void addDeltaListener(Consumer<IGridStack> listener);
void removed();
}