[MC1.16] [Client] Performance improvements for grid view (#2705)

* Make Crafting Manager AND search terms, not OR them

Plus refactor getFilters to avoid passing raw lists of filters around
and instead use GridFilter objects that combine conditions through OR
and AND.

* Don't make all items vanish if query ends in `|`

* Split out Comparator and Predicate generation for GridView into 2 separate functions

* Move common code up into BaseGridView; remove identical subclasses

This also follows the Law of Demeter a litle better.

* Avoid sorting entire grid view on single item update

Instead, use binary search and insert.

* Ensure crafting stack is removed/inserted to complement original stack
This commit is contained in:
Score_Under
2021-01-02 10:26:39 +00:00
committed by GitHub
parent 6b8daf11fb
commit 74ab430728
20 changed files with 285 additions and 289 deletions

View File

@@ -74,7 +74,7 @@ public class CrafterManagerContainer extends BaseContainer {
int y = 19 + 18 - screenInfoProvider.getCurrentOffset() * 18; int y = 19 + 18 - screenInfoProvider.getCurrentOffset() * 18;
int x = 8; int x = 8;
List<Predicate<IGridStack>> filters = GridFilterParser.getFilters(null, screenInfoProvider.getSearchFieldText(), Collections.emptyList()); Predicate<IGridStack> filters = GridFilterParser.getFilters(null, screenInfoProvider.getSearchFieldText(), Collections.emptyList());
for (Map.Entry<String, Integer> category : containerData.entrySet()) { for (Map.Entry<String, Integer> category : containerData.entrySet()) {
IItemHandlerModifiable dummy; IItemHandlerModifiable dummy;
@@ -124,11 +124,9 @@ public class CrafterManagerContainer extends BaseContainer {
for (ItemStack output : pattern.getOutputs()) { for (ItemStack output : pattern.getOutputs()) {
ItemGridStack outputConverted = new ItemGridStack(output); ItemGridStack outputConverted = new ItemGridStack(output);
for (Predicate<IGridStack> filter : filters) { if (filters.test(outputConverted)) {
if (filter.test(outputConverted)) { visible = true;
visible = true; break;
break;
}
} }
} }
} }

View File

@@ -63,8 +63,6 @@ public class GridFluidDeltaMessage {
public static void handle(GridFluidDeltaMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(GridFluidDeltaMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight())); message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight()));
grid.getView().sort();
}); });
ctx.get().setPacketHandled(true); ctx.get().setPacketHandled(true);

View File

@@ -6,7 +6,7 @@ import com.refinedmods.refinedstorage.api.util.StackListEntry;
import com.refinedmods.refinedstorage.screen.BaseScreen; import com.refinedmods.refinedstorage.screen.BaseScreen;
import com.refinedmods.refinedstorage.screen.grid.GridScreen; import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack; import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.view.FluidGridView; import com.refinedmods.refinedstorage.screen.grid.view.GridViewImpl;
import com.refinedmods.refinedstorage.util.StackUtils; import com.refinedmods.refinedstorage.util.StackUtils;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
@@ -68,7 +68,7 @@ public class GridFluidUpdateMessage {
public static void handle(GridFluidUpdateMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(GridFluidUpdateMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
grid.setView(new FluidGridView(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters())); grid.setView(new GridViewImpl(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters()));
grid.getView().setCanCraft(message.canCraft); grid.getView().setCanCraft(message.canCraft);
grid.getView().setStacks(message.stacks); grid.getView().setStacks(message.stacks);
grid.getView().sort(); grid.getView().sort();

View File

@@ -63,8 +63,6 @@ public class GridItemDeltaMessage {
public static void handle(GridItemDeltaMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(GridItemDeltaMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight())); message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight()));
grid.getView().sort();
}); });
ctx.get().setPacketHandled(true); ctx.get().setPacketHandled(true);

View File

@@ -6,7 +6,7 @@ import com.refinedmods.refinedstorage.api.util.StackListEntry;
import com.refinedmods.refinedstorage.screen.BaseScreen; import com.refinedmods.refinedstorage.screen.BaseScreen;
import com.refinedmods.refinedstorage.screen.grid.GridScreen; import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack; import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.view.ItemGridView; import com.refinedmods.refinedstorage.screen.grid.view.GridViewImpl;
import com.refinedmods.refinedstorage.util.StackUtils; import com.refinedmods.refinedstorage.util.StackUtils;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
@@ -68,7 +68,7 @@ public class GridItemUpdateMessage {
public static void handle(GridItemUpdateMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(GridItemUpdateMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
grid.setView(new ItemGridView(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters())); grid.setView(new GridViewImpl(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters()));
grid.getView().setCanCraft(message.canCraft); grid.getView().setCanCraft(message.canCraft);
grid.getView().setStacks(message.stacks); grid.getView().setStacks(message.stacks);
grid.getView().sort(); grid.getView().sort();

View File

@@ -57,8 +57,6 @@ public class PortableGridFluidDeltaMessage {
public static void handle(PortableGridFluidDeltaMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(PortableGridFluidDeltaMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight())); message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight()));
grid.getView().sort();
}); });
ctx.get().setPacketHandled(true); ctx.get().setPacketHandled(true);

View File

@@ -4,7 +4,7 @@ import com.refinedmods.refinedstorage.api.util.StackListEntry;
import com.refinedmods.refinedstorage.screen.BaseScreen; import com.refinedmods.refinedstorage.screen.BaseScreen;
import com.refinedmods.refinedstorage.screen.grid.GridScreen; import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack; import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.view.FluidGridView; import com.refinedmods.refinedstorage.screen.grid.view.GridViewImpl;
import com.refinedmods.refinedstorage.tile.grid.portable.IPortableGrid; import com.refinedmods.refinedstorage.tile.grid.portable.IPortableGrid;
import com.refinedmods.refinedstorage.util.StackUtils; import com.refinedmods.refinedstorage.util.StackUtils;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
@@ -52,7 +52,7 @@ public class PortableGridFluidUpdateMessage {
public static void handle(PortableGridFluidUpdateMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(PortableGridFluidUpdateMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
grid.setView(new FluidGridView(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters())); grid.setView(new GridViewImpl(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters()));
grid.getView().setStacks(message.stacks); grid.getView().setStacks(message.stacks);
grid.getView().sort(); grid.getView().sort();
}); });

View File

@@ -59,8 +59,6 @@ public class PortableGridItemDeltaMessage {
public static void handle(PortableGridItemDeltaMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(PortableGridItemDeltaMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight())); message.clientDeltas.forEach(p -> grid.getView().postChange(p.getLeft(), p.getRight()));
grid.getView().sort();
}); });
ctx.get().setPacketHandled(true); ctx.get().setPacketHandled(true);

View File

@@ -4,7 +4,7 @@ import com.refinedmods.refinedstorage.api.util.StackListEntry;
import com.refinedmods.refinedstorage.screen.BaseScreen; import com.refinedmods.refinedstorage.screen.BaseScreen;
import com.refinedmods.refinedstorage.screen.grid.GridScreen; import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack; import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.view.ItemGridView; import com.refinedmods.refinedstorage.screen.grid.view.GridViewImpl;
import com.refinedmods.refinedstorage.tile.grid.portable.IPortableGrid; import com.refinedmods.refinedstorage.tile.grid.portable.IPortableGrid;
import com.refinedmods.refinedstorage.util.StackUtils; import com.refinedmods.refinedstorage.util.StackUtils;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -52,7 +52,7 @@ public class PortableGridItemUpdateMessage {
public static void handle(PortableGridItemUpdateMessage message, Supplier<NetworkEvent.Context> ctx) { public static void handle(PortableGridItemUpdateMessage message, Supplier<NetworkEvent.Context> ctx) {
BaseScreen.executeLater(GridScreen.class, grid -> { BaseScreen.executeLater(GridScreen.class, grid -> {
grid.setView(new ItemGridView(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters())); grid.setView(new GridViewImpl(grid, GridScreen.getDefaultSorter(), GridScreen.getSorters()));
grid.getView().setStacks(message.stacks); grid.getView().setStacks(message.stacks);
grid.getView().sort(); grid.getView().sort();
}); });

View File

@@ -17,9 +17,8 @@ import com.refinedmods.refinedstorage.screen.IScreenInfoProvider;
import com.refinedmods.refinedstorage.screen.grid.sorting.*; import com.refinedmods.refinedstorage.screen.grid.sorting.*;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack; import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.stack.ItemGridStack; import com.refinedmods.refinedstorage.screen.grid.stack.ItemGridStack;
import com.refinedmods.refinedstorage.screen.grid.view.FluidGridView; import com.refinedmods.refinedstorage.screen.grid.view.GridViewImpl;
import com.refinedmods.refinedstorage.screen.grid.view.IGridView; import com.refinedmods.refinedstorage.screen.grid.view.IGridView;
import com.refinedmods.refinedstorage.screen.grid.view.ItemGridView;
import com.refinedmods.refinedstorage.screen.widget.CheckboxWidget; import com.refinedmods.refinedstorage.screen.widget.CheckboxWidget;
import com.refinedmods.refinedstorage.screen.widget.ScrollbarWidget; import com.refinedmods.refinedstorage.screen.widget.ScrollbarWidget;
import com.refinedmods.refinedstorage.screen.widget.SearchWidget; import com.refinedmods.refinedstorage.screen.widget.SearchWidget;
@@ -70,7 +69,7 @@ public class GridScreen extends BaseScreen<GridContainer> implements IScreenInfo
super(container, 227, 0, inventory, title); super(container, 227, 0, inventory, title);
this.grid = grid; this.grid = grid;
this.view = grid.getGridType() == GridType.FLUID ? new FluidGridView(this, getDefaultSorter(), getSorters()) : new ItemGridView(this, getDefaultSorter(), getSorters()); this.view = new GridViewImpl(this, getDefaultSorter(), getSorters());
this.wasConnected = this.grid.isGridActive(); this.wasConnected = this.grid.isGridActive();
this.tabs = new TabListWidget<>(this, new ElementDrawers<>(this), grid::getTabs, grid::getTotalTabPages, grid::getTabPage, grid::getTabSelected, IGrid.TABS_PER_PAGE); this.tabs = new TabListWidget<>(this, new ElementDrawers<>(this), grid::getTabs, grid::getTotalTabPages, grid::getTabPage, grid::getTabSelected, IGrid.TABS_PER_PAGE);
this.tabs.addListener(new TabListWidget.ITabListListener() { this.tabs.addListener(new TabListWidget.ITabListListener() {

View File

@@ -0,0 +1,35 @@
package com.refinedmods.refinedstorage.screen.grid.filtering;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import java.util.List;
import java.util.function.Predicate;
public class AndGridFilter implements Predicate<IGridStack> {
private final List<Predicate<IGridStack>> andPartFilters;
private AndGridFilter(List<Predicate<IGridStack>> andPartFilters) {
this.andPartFilters = andPartFilters;
}
@Override
public boolean test(IGridStack gridStack) {
for (Predicate<IGridStack> part : andPartFilters) {
if (!part.test(gridStack)) {
return false;
}
}
return true;
}
public static Predicate<IGridStack> of(List<Predicate<IGridStack>> filters) {
if (filters.isEmpty()) {
return t -> true;
}
if (filters.size() == 1) {
return filters.get(0);
}
return new AndGridFilter(filters);
}
}

View File

@@ -13,22 +13,22 @@ public final class GridFilterParser {
private GridFilterParser() { private GridFilterParser() {
} }
public static List<Predicate<IGridStack>> getFilters(@Nullable IGrid grid, String query, List<IFilter> filters) { public static Predicate<IGridStack> getFilters(@Nullable IGrid grid, String query, List<IFilter> filters) {
List<Predicate<IGridStack>> gridFilters; List<Predicate<IGridStack>> gridFilters;
String[] orParts = query.split("\\|"); String[] orParts = query.split("\\|");
if (orParts.length == 1) { if (orParts.length == 1) {
gridFilters = getFilters(query); gridFilters = getFilters(orParts[0]);
} else { } else {
List<List<Predicate<IGridStack>>> orPartFilters = new LinkedList<>(); List<Predicate<IGridStack>> orPartFilters = new LinkedList<>();
for (String orPart : orParts) { for (String orPart : orParts) {
orPartFilters.add(getFilters(orPart)); orPartFilters.add(AndGridFilter.of(getFilters(orPart)));
} }
gridFilters = new LinkedList<>(); gridFilters = new LinkedList<>();
gridFilters.add(new OrGridFilter(orPartFilters)); gridFilters.add(OrGridFilter.of(orPartFilters));
} }
if (grid != null) { if (grid != null) {
@@ -43,7 +43,7 @@ public final class GridFilterParser {
gridFilters.add(new FilterGridFilter(filters)); gridFilters.add(new FilterGridFilter(filters));
} }
return gridFilters; return AndGridFilter.of(gridFilters);
} }
private static List<Predicate<IGridStack>> getFilters(String query) { private static List<Predicate<IGridStack>> getFilters(String query) {

View File

@@ -6,22 +6,30 @@ import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
public class OrGridFilter implements Predicate<IGridStack> { public class OrGridFilter implements Predicate<IGridStack> {
private final List<List<Predicate<IGridStack>>> orPartFilters; private final List<Predicate<IGridStack>> orPartFilters;
public OrGridFilter(List<List<Predicate<IGridStack>>> orPartFilters) { private OrGridFilter(List<Predicate<IGridStack>> orPartFilters) {
this.orPartFilters = orPartFilters; this.orPartFilters = orPartFilters;
} }
@Override @Override
public boolean test(IGridStack gridStack) { public boolean test(IGridStack gridStack) {
for (List<Predicate<IGridStack>> orPart : orPartFilters) { for (Predicate<IGridStack> part : orPartFilters) {
for (Predicate<IGridStack> part : orPart) { if (part.test(gridStack)) {
if (part.test(gridStack)) { return true;
return true;
}
} }
} }
return false; return false;
} }
public static Predicate<IGridStack> of(List<Predicate<IGridStack>> filters) {
if (filters.isEmpty()) {
return t -> false;
}
if (filters.size() == 1) {
return filters.get(0);
}
return new OrGridFilter(filters);
}
} }

View File

@@ -155,7 +155,16 @@ public class FluidGridStack implements IGridStack {
@Override @Override
public int getQuantity() { public int getQuantity() {
// The isCraftable check is needed so sorting is applied correctly // The isCraftable check is needed so sorting is applied correctly
return isCraftable() ? 0 : stack.getAmount(); return isCraftable() || zeroed ? 0 : stack.getAmount();
}
@Override
public void setQuantity(int amount) {
if (amount <= 0) {
setZeroed(true);
} else {
stack.setAmount(amount);
}
} }
@Override @Override

View File

@@ -30,6 +30,8 @@ public interface IGridStack {
int getQuantity(); int getQuantity();
void setQuantity(int amount);
String getFormattedFullQuantity(); String getFormattedFullQuantity();
void draw(MatrixStack matrixStack, BaseScreen<?> screen, int x, int y); void draw(MatrixStack matrixStack, BaseScreen<?> screen, int x, int y);

View File

@@ -168,7 +168,16 @@ public class ItemGridStack implements IGridStack {
@Override @Override
public int getQuantity() { public int getQuantity() {
// The isCraftable check is needed so sorting is applied correctly // The isCraftable check is needed so sorting is applied correctly
return isCraftable() ? 0 : stack.getCount(); return isCraftable() || zeroed ? 0 : stack.getCount();
}
@Override
public void setQuantity(int amount) {
if (amount <= 0) {
setZeroed(true);
} else {
stack.setCount(amount);
}
} }
@Override @Override

View File

@@ -1,111 +0,0 @@
package com.refinedmods.refinedstorage.screen.grid.view;
import com.refinedmods.refinedstorage.api.network.grid.IGrid;
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.filtering.GridFilterParser;
import com.refinedmods.refinedstorage.screen.grid.sorting.IGridSorter;
import com.refinedmods.refinedstorage.screen.grid.sorting.SortingDirection;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Predicate;
public abstract class BaseGridView implements IGridView {
private final GridScreen screen;
private boolean canCraft;
private final IGridSorter defaultSorter;
private final List<IGridSorter> sorters;
private List<IGridStack> stacks = new ArrayList<>();
protected final Map<UUID, IGridStack> map = new HashMap<>();
protected BaseGridView(GridScreen screen, IGridSorter defaultSorter, List<IGridSorter> sorters) {
this.screen = screen;
this.defaultSorter = defaultSorter;
this.sorters = sorters;
}
@Override
public List<IGridStack> getStacks() {
return stacks;
}
@Override
public Collection<IGridStack> getAllStacks() {
return map.values();
}
@Nullable
@Override
public IGridStack get(UUID id) {
return map.get(id);
}
@Override
public void sort() {
if (!screen.canSort()) {
return;
}
List<IGridStack> newStacks = new ArrayList<>();
if (screen.getGrid().isGridActive()) {
newStacks.addAll(map.values());
IGrid grid = screen.getGrid();
List<Predicate<IGridStack>> filters = GridFilterParser.getFilters(
grid,
screen.getSearchFieldText(),
(grid.getTabSelected() >= 0 && grid.getTabSelected() < grid.getTabs().size()) ? grid.getTabs().get(grid.getTabSelected()).getFilters() : grid.getFilters()
);
newStacks.removeIf(stack -> {
// If this is a crafting stack,
// and there is a regular matching stack in the view too,
// and we aren't in "view only craftables" mode,
// we don't want the duplicate stacks and we will remove this stack.
if (screen.getGrid().getViewType() != IGrid.VIEW_TYPE_CRAFTABLES &&
stack.isCraftable() &&
stack.getOtherId() != null &&
map.containsKey(stack.getOtherId())) {
return true;
}
for (Predicate<IGridStack> filter : filters) {
if (!filter.test(stack)) {
return true;
}
}
return false;
});
SortingDirection sortingDirection = grid.getSortingDirection() == IGrid.SORTING_DIRECTION_DESCENDING ? SortingDirection.DESCENDING : SortingDirection.ASCENDING;
newStacks.sort((left, right) -> defaultSorter.compare(left, right, sortingDirection));
for (IGridSorter sorter : sorters) {
if (sorter.isApplicable(grid)) {
newStacks.sort((left, right) -> sorter.compare(left, right, sortingDirection));
}
}
}
this.stacks = newStacks;
this.screen.updateScrollbar();
}
@Override
public void setCanCraft(boolean canCraft) {
this.canCraft = canCraft;
}
@Override
public boolean canCraft() {
return canCraft;
}
}

View File

@@ -1,69 +0,0 @@
package com.refinedmods.refinedstorage.screen.grid.view;
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.sorting.IGridSorter;
import com.refinedmods.refinedstorage.screen.grid.stack.FluidGridStack;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import java.util.List;
public class FluidGridView extends BaseGridView {
public FluidGridView(GridScreen screen, IGridSorter defaultSorter, List<IGridSorter> sorters) {
super(screen, defaultSorter, sorters);
}
@Override
public void setStacks(List<IGridStack> stacks) {
map.clear();
for (IGridStack stack : stacks) {
map.put(stack.getId(), stack);
}
}
@Override
public void postChange(IGridStack stack, int delta) {
if (!(stack instanceof FluidGridStack)) {
return;
}
// COMMENT 1 (about this if check in general)
// Update the other id reference if needed.
// Taking a stack out - and then re-inserting it - gives the new stack a new ID
// With that new id, the reference for the crafting stack would be outdated.
// COMMENT 2 (about map.containsKey(stack.getOtherId()))
// This check is needed or the .updateOtherId() call will crash with a NPE in high-update environments.
// This is because we might have scenarios where we process "old" delta packets from another session when we haven't received any initial update packet from the new session.
// (This is because of the executeLater system)
// This causes the .updateOtherId() to fail with a NPE because the map is still empty or the IDs mismatch.
// We could use !map.isEmpty() here too. But if we have 2 "old" delta packets, it would rightfully ignore the first one. But this method mutates the map and would put an entry.
// This means that on the second delta packet it would still crash because the map wouldn't be empty anymore.
if (!stack.isCraftable() &&
stack.getOtherId() != null &&
map.containsKey(stack.getOtherId())) {
IGridStack craftingStack = map.get(stack.getOtherId());
craftingStack.updateOtherId(stack.getId());
craftingStack.setTrackerEntry(stack.getTrackerEntry());
}
FluidGridStack existing = (FluidGridStack) map.get(stack.getId());
if (existing == null) {
((FluidGridStack) stack).getStack().setAmount(delta);
map.put(stack.getId(), stack);
} else {
if (existing.getStack().getAmount() + delta <= 0) {
existing.setZeroed(true);
map.remove(existing.getId());
} else {
existing.getStack().grow(delta);
}
existing.setTrackerEntry(stack.getTrackerEntry());
}
}
}

View File

@@ -0,0 +1,193 @@
package com.refinedmods.refinedstorage.screen.grid.view;
import com.refinedmods.refinedstorage.api.network.grid.IGrid;
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.filtering.GridFilterParser;
import com.refinedmods.refinedstorage.screen.grid.sorting.IGridSorter;
import com.refinedmods.refinedstorage.screen.grid.sorting.SortingDirection;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GridViewImpl implements IGridView {
private final GridScreen screen;
private boolean canCraft;
private final IGridSorter defaultSorter;
private final List<IGridSorter> sorters;
private List<IGridStack> stacks = new ArrayList<>();
protected final Map<UUID, IGridStack> map = new HashMap<>();
public GridViewImpl(GridScreen screen, IGridSorter defaultSorter, List<IGridSorter> sorters) {
this.screen = screen;
this.defaultSorter = defaultSorter;
this.sorters = sorters;
}
@Override
public List<IGridStack> getStacks() {
return stacks;
}
@Override
public Collection<IGridStack> getAllStacks() {
return map.values();
}
@Nullable
@Override
public IGridStack get(UUID id) {
return map.get(id);
}
@Override
public void sort() {
if (!screen.canSort()) {
return;
}
if (screen.getGrid().isGridActive()) {
this.stacks = map.values().stream()
.filter(getActiveFilters())
.sorted(getActiveSort())
.collect(Collectors.toList());
} else {
this.stacks = Collections.emptyList();
}
this.screen.updateScrollbar();
}
private Comparator<IGridStack> getActiveSort() {
IGrid grid = screen.getGrid();
SortingDirection sortingDirection = grid.getSortingDirection() == IGrid.SORTING_DIRECTION_DESCENDING ? SortingDirection.DESCENDING : SortingDirection.ASCENDING;
return Stream.concat(Stream.of(defaultSorter), sorters.stream().filter(s -> s.isApplicable(grid)))
.map(sorter -> (Comparator<IGridStack>) (o1, o2) -> sorter.compare(o1, o2, sortingDirection))
.reduce((l, r) -> r.thenComparing(l))
.orElseThrow(IllegalStateException::new); // There is at least 1 value in the stream (i.e. defaultSorter)
}
private Predicate<IGridStack> getActiveFilters() {
IGrid grid = screen.getGrid();
Predicate<IGridStack> filters = GridFilterParser.getFilters(
grid,
screen.getSearchFieldText(),
(grid.getTabSelected() >= 0 && grid.getTabSelected() < grid.getTabs().size()) ? grid.getTabs().get(grid.getTabSelected()).getFilters() : grid.getFilters()
);
if (screen.getGrid().getViewType() != IGrid.VIEW_TYPE_CRAFTABLES) {
return stack -> {
// If this is a crafting stack,
// and there is a regular matching stack in the view too,
// and we aren't in "view only craftables" mode,
// we don't want the duplicate stacks and we will remove this stack.
if (stack.isCraftable() &&
stack.getOtherId() != null &&
map.containsKey(stack.getOtherId())) {
return false;
}
return filters.test(stack);
};
} else {
return filters;
}
}
@Override
public void setStacks(List<IGridStack> stacks) {
map.clear();
for (IGridStack stack : stacks) {
map.put(stack.getId(), stack);
}
}
@Override
public void postChange(IGridStack stack, int delta) {
// COMMENT 1 (about this if check in general)
// Update the other id reference if needed.
// Taking a stack out - and then re-inserting it - gives the new stack a new ID
// With that new id, the reference for the crafting stack would be outdated.
// COMMENT 2 (about map.containsKey(stack.getOtherId()))
// This check is needed or the .updateOtherId() call will crash with a NPE in high-update environments.
// This is because we might have scenarios where we process "old" delta packets from another session when we haven't received any initial update packet from the new session.
// (This is because of the executeLater system)
// This causes the .updateOtherId() to fail with a NPE because the map is still empty or the IDs mismatch.
// We could use !map.isEmpty() here too. But if we have 2 "old" delta packets, it would rightfully ignore the first one. But this method mutates the map and would put an entry.
// This means that on the second delta packet it would still crash because the map wouldn't be empty anymore.
IGridStack craftingStack;
if (!stack.isCraftable() &&
stack.getOtherId() != null &&
map.containsKey(stack.getOtherId())) {
craftingStack = map.get(stack.getOtherId());
craftingStack.updateOtherId(stack.getId());
craftingStack.setTrackerEntry(stack.getTrackerEntry());
} else {
craftingStack = null;
}
IGridStack existing = map.get(stack.getId());
boolean stillExists = true;
boolean shouldSort = screen.canSort();
if (existing == null) {
stack.setQuantity(delta);
map.put(stack.getId(), stack);
existing = stack;
if (craftingStack != null && shouldSort) {
stacks.remove(craftingStack);
}
} else {
if (shouldSort) {
stacks.remove(existing);
}
existing.setQuantity(existing.getQuantity() + delta);
if (existing.getQuantity() <= 0) {
map.remove(existing.getId());
stillExists = false;
if (craftingStack != null && shouldSort && getActiveFilters().test(craftingStack)) {
addStack(craftingStack);
}
}
existing.setTrackerEntry(stack.getTrackerEntry());
}
if (shouldSort) {
if (stillExists && getActiveFilters().test(existing)) {
addStack(existing);
}
this.screen.updateScrollbar();
}
}
private void addStack(IGridStack stack) {
int insertionPos = Collections.binarySearch(stacks, stack, getActiveSort());
if (insertionPos < 0) {
insertionPos = -insertionPos - 1;
}
stacks.add(insertionPos, stack);
}
@Override
public void setCanCraft(boolean canCraft) {
this.canCraft = canCraft;
}
@Override
public boolean canCraft() {
return canCraft;
}
}

View File

@@ -1,69 +0,0 @@
package com.refinedmods.refinedstorage.screen.grid.view;
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
import com.refinedmods.refinedstorage.screen.grid.sorting.IGridSorter;
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
import com.refinedmods.refinedstorage.screen.grid.stack.ItemGridStack;
import java.util.List;
public class ItemGridView extends BaseGridView {
public ItemGridView(GridScreen screen, IGridSorter defaultSorter, List<IGridSorter> sorters) {
super(screen, defaultSorter, sorters);
}
@Override
public void setStacks(List<IGridStack> stacks) {
map.clear();
for (IGridStack stack : stacks) {
map.put(stack.getId(), stack);
}
}
@Override
public void postChange(IGridStack stack, int delta) {
if (!(stack instanceof ItemGridStack)) {
return;
}
// COMMENT 1 (about this if check in general)
// Update the other id reference if needed.
// Taking a stack out - and then re-inserting it - gives the new stack a new ID
// With that new id, the reference for the crafting stack would be outdated.
// COMMENT 2 (about map.containsKey(stack.getOtherId()))
// This check is needed or the .updateOtherId() call will crash with a NPE in high-update environments.
// This is because we might have scenarios where we process "old" delta packets from another session when we haven't received any initial update packet from the new session.
// (This is because of the executeLater system)
// This causes the .updateOtherId() to fail with a NPE because the map is still empty or the IDs mismatch.
// We could use !map.isEmpty() here too. But if we have 2 "old" delta packets, it would rightfully ignore the first one. But this method mutates the map and would put an entry.
// This means that on the second delta packet it would still crash because the map wouldn't be empty anymore.
if (!stack.isCraftable() &&
stack.getOtherId() != null &&
map.containsKey(stack.getOtherId())) {
IGridStack craftingStack = map.get(stack.getOtherId());
craftingStack.updateOtherId(stack.getId());
craftingStack.setTrackerEntry(stack.getTrackerEntry());
}
ItemGridStack existing = (ItemGridStack) map.get(stack.getId());
if (existing == null) {
((ItemGridStack) stack).getStack().setCount(delta);
map.put(stack.getId(), stack);
} else {
if (existing.getStack().getCount() + delta <= 0) {
existing.setZeroed(true);
map.remove(existing.getId());
} else {
existing.getStack().grow(delta);
}
existing.setTrackerEntry(stack.getTrackerEntry());
}
}
}