diff --git a/src/api/java/appeng/api/storage/data/IItemList.java b/src/api/java/appeng/api/storage/data/IItemList.java index 4d37ce012..4292f49b7 100644 --- a/src/api/java/appeng/api/storage/data/IItemList.java +++ b/src/api/java/appeng/api/storage/data/IItemList.java @@ -39,7 +39,7 @@ import appeng.api.storage.IStorageChannel; * - For fluids: AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class).createList() * - Replace with the corresponding {@link IStorageChannel} type for non native channels */ -public interface IItemList> extends IItemContainer, Iterable +public interface IItemList> extends IItemContainer, Iterable, Cloneable { /** @@ -85,4 +85,9 @@ public interface IItemList> extends IItemContainer, Ite * resets stack sizes to 0. */ void resetStatus(); -} \ No newline at end of file + + /** + * create a copy of this list. + */ + IItemList clone(); +} diff --git a/src/main/java/appeng/client/me/ItemRepo.java b/src/main/java/appeng/client/me/ItemRepo.java index 2065dc865..3b4fc6d03 100644 --- a/src/main/java/appeng/client/me/ItemRepo.java +++ b/src/main/java/appeng/client/me/ItemRepo.java @@ -39,19 +39,20 @@ import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; public class ItemRepo { private final IItemList list = AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createList(); - private final ArrayList view = new ArrayList<>(); + private final List view; private final IScrollSource src; private final ISortSource sortSrc; private int rowSize = 9; - private String searchString = ""; + private volatile String searchString = ""; private IPartitionList myPartitionList; private String innerSearch = ""; private boolean hasPower; @@ -59,6 +60,9 @@ public class ItemRepo { public ItemRepo(final IScrollSource src, final ISortSource sortSrc) { this.src = src; this.sortSrc = sortSrc; + + this.view = Collections.synchronizedList(new ArrayList<>()); + list.forEach(this.view::add); } public IAEItemStack getReferenceItem(int idx) { @@ -95,111 +99,123 @@ public class ItemRepo { this.updateView(); } + private CompletableFuture searchTask = null; + public void updateView() { - this.view.clear(); - - this.view.ensureCapacity(this.list.size()); - - final Enum viewMode = this.sortSrc.getSortDisplay(); - final Enum searchMode = AEConfig.instance().getConfigManager().getSetting(Settings.SEARCH_MODE); - final boolean needsZeroCopy = viewMode == ViewItems.CRAFTABLE; - - if (searchMode == SearchBoxMode.JEI_AUTOSEARCH || searchMode == SearchBoxMode.JEI_MANUAL_SEARCH || searchMode == SearchBoxMode.JEI_AUTOSEARCH_KEEP || searchMode == SearchBoxMode.JEI_MANUAL_SEARCH_KEEP) { - this.updateJEI(this.searchString); + if (searchTask != null) { + searchTask.cancel(true); } - final boolean terminalSearchToolTips = AEConfig.instance().getConfigManager().getSetting(Settings.SEARCH_TOOLTIPS) != YesNo.NO; + // Since sortSrc is final, so we can safely call it inside lambda + searchTask = CompletableFuture.supplyAsync(() -> { + IItemList list = this.list.clone(); + List view = new ArrayList<>(list.size()); + Enum viewMode = this.sortSrc.getSortDisplay(); - boolean searchMod = false; + boolean needsZeroCopy = viewMode == ViewItems.CRAFTABLE; - this.innerSearch = searchString.toLowerCase(); - if (this.innerSearch.startsWith("@")) { - searchMod = true; - this.innerSearch = this.innerSearch.substring(1); - } + boolean terminalSearchToolTips = AEConfig.instance().getConfigManager().getSetting(Settings.SEARCH_TOOLTIPS) != YesNo.NO; - Pattern m = null; - try { - m = Pattern.compile(this.innerSearch, Pattern.CASE_INSENSITIVE); - } catch (final Throwable ignore) { - try { - m = Pattern.compile(Pattern.quote(this.innerSearch), Pattern.CASE_INSENSITIVE); - } catch (final Throwable __) { - return; + boolean searchMod = false; + + String innerSearch = searchString.toLowerCase(); + if (innerSearch.startsWith("@")) { + searchMod = true; + innerSearch = innerSearch.substring(1); } - } - boolean notDone = false; - for (IAEItemStack is : this.list) { - if (this.myPartitionList != null) { - if (!this.myPartitionList.isListed(is)) { - continue; + Pattern m = null; + try { + m = Pattern.compile(innerSearch, Pattern.CASE_INSENSITIVE); + } catch (final Throwable ignore) { + try { + m = Pattern.compile(Pattern.quote(innerSearch), Pattern.CASE_INSENSITIVE); + } catch (final Throwable __) { + return Collections.emptyList(); } } - if (viewMode == ViewItems.CRAFTABLE && !is.isCraftable()) { - continue; - } - if (viewMode == ViewItems.STORED && is.getStackSize() == 0) { - continue; - } + for (IAEItemStack is : list) { + if (this.myPartitionList != null) { + if (!this.myPartitionList.isListed(is)) { + continue; + } + } - final String dspName = (searchMod ? Platform.getModId(is) : Platform.getItemDisplayName(is)).toLowerCase(); - boolean foundMatchingItemStack = true; + if (viewMode == ViewItems.CRAFTABLE && !is.isCraftable()) { + continue; + } - for (String term : innerSearch.split(" ")) { - if (term.length() > 1 && (term.startsWith("-") || term.startsWith("!"))) { - term = term.substring(1); - if (dspName.contains(term)) { + if (viewMode == ViewItems.STORED && is.getStackSize() == 0) { + continue; + } + + final String dspName = (searchMod ? Platform.getModId(is) : Platform.getItemDisplayName(is)).toLowerCase(); + boolean foundMatchingItemStack = true; + + for (String term : innerSearch.split(" ")) { + if (term.length() > 1 && (term.startsWith("-") || term.startsWith("!"))) { + term = term.substring(1); + if (dspName.contains(term)) { + foundMatchingItemStack = false; + break; + } + } else if (!dspName.contains(term)) { foundMatchingItemStack = false; break; } - } else if (!dspName.contains(term)) { - foundMatchingItemStack = false; - break; } - } - if (terminalSearchToolTips && !foundMatchingItemStack) { - final List tooltip = Platform.getTooltip(is); - for (final String line : tooltip) { - if (m.matcher(line).find()) { - foundMatchingItemStack = true; - break; + if (terminalSearchToolTips && !foundMatchingItemStack) { + final List tooltip = Platform.getTooltip(is); + for (final String line : tooltip) { + if (m.matcher(line).find()) { + foundMatchingItemStack = true; + break; + } } } - } - if (foundMatchingItemStack) { - if (needsZeroCopy) { - is = is.copy(); - is.setStackSize(0); + if (foundMatchingItemStack) { + if (needsZeroCopy) { + is = is.copy(); + is.setStackSize(0); + } + + view.add(is); } - - this.view.add(is); } - } - final Enum SortBy = this.sortSrc.getSortBy(); - final Enum SortDir = this.sortSrc.getSortDir(); + final Enum SortBy = this.sortSrc.getSortBy(); + final Enum SortDir = this.sortSrc.getSortDir(); - ItemSorters.setDirection((appeng.api.config.SortDir) SortDir); - ItemSorters.init(); + ItemSorters.setDirection((appeng.api.config.SortDir) SortDir); + ItemSorters.init(); - if (SortBy == SortOrder.MOD) { - Collections.sort(this.view, ItemSorters.CONFIG_BASED_SORT_BY_MOD); - } else if (SortBy == SortOrder.AMOUNT) { - Collections.sort(this.view, ItemSorters.CONFIG_BASED_SORT_BY_SIZE); - } else if (SortBy == SortOrder.INVTWEAKS) { - if (InventoryBogoSortModule.isLoaded()) { - Collections.sort(this.view, InventoryBogoSortModule.COMPARATOR); + if (SortBy == SortOrder.MOD) { + view.sort(ItemSorters.CONFIG_BASED_SORT_BY_MOD); + } else if (SortBy == SortOrder.AMOUNT) { + view.sort(ItemSorters.CONFIG_BASED_SORT_BY_SIZE); + } else if (SortBy == SortOrder.INVTWEAKS) { + if (InventoryBogoSortModule.isLoaded()) { + Collections.sort(this.view, InventoryBogoSortModule.COMPARATOR); + } else { + Collections.sort(this.view, ItemSorters.CONFIG_BASED_SORT_BY_INV_TWEAKS); + } } else { - Collections.sort(this.view, ItemSorters.CONFIG_BASED_SORT_BY_INV_TWEAKS); + view.sort(ItemSorters.CONFIG_BASED_SORT_BY_NAME); } - } else { - Collections.sort(this.view, ItemSorters.CONFIG_BASED_SORT_BY_NAME); - } + + return view; + }).thenAcceptAsync(view -> { + this.view.clear(); + this.view.addAll(view); + }).thenRunAsync(() -> { + this.searchTask = null; // Prevent redundant cancellation + }); + + } private void updateJEI(String filter) { @@ -211,6 +227,8 @@ public class ItemRepo { } public void clear() { + searchTask.cancel(true); + searchTask = null; this.list.resetStatus(); } @@ -236,5 +254,16 @@ public class ItemRepo { public void setSearchString(@Nonnull final String searchString) { this.searchString = searchString; + + if (searchTask != null) { + searchTask.cancel(true); + searchTask = null; + } + + // Passive JEI auto search + final Enum searchMode = AEConfig.instance().getConfigManager().getSetting(Settings.SEARCH_MODE); + if (searchMode == SearchBoxMode.JEI_AUTOSEARCH || searchMode == SearchBoxMode.JEI_MANUAL_SEARCH || searchMode == SearchBoxMode.JEI_AUTOSEARCH_KEEP || searchMode == SearchBoxMode.JEI_MANUAL_SEARCH_KEEP) { + this.updateJEI(this.searchString); + } } } diff --git a/src/main/java/appeng/fluids/util/FluidList.java b/src/main/java/appeng/fluids/util/FluidList.java index 7a8e8485a..d04917548 100644 --- a/src/main/java/appeng/fluids/util/FluidList.java +++ b/src/main/java/appeng/fluids/util/FluidList.java @@ -161,6 +161,13 @@ public final class FluidList implements IItemList { } } + @Override + public FluidList clone() { + FluidList list = new FluidList(); + list.records.putAll(records); + return list; + } + private IAEFluidStack getFluidRecord(final IAEFluidStack fluid) { return this.records.get(fluid); } diff --git a/src/main/java/appeng/util/inv/ItemListIgnoreCrafting.java b/src/main/java/appeng/util/inv/ItemListIgnoreCrafting.java index 6b39463fe..370955cf9 100644 --- a/src/main/java/appeng/util/inv/ItemListIgnoreCrafting.java +++ b/src/main/java/appeng/util/inv/ItemListIgnoreCrafting.java @@ -94,4 +94,9 @@ public class ItemListIgnoreCrafting> implements IItemList< public void resetStatus() { this.target.resetStatus(); } + + @Override + public ItemListIgnoreCrafting clone() { + return new ItemListIgnoreCrafting<>(target); + } } diff --git a/src/main/java/appeng/util/item/ItemList.java b/src/main/java/appeng/util/item/ItemList.java index 0c0223e7d..04e8e58f7 100644 --- a/src/main/java/appeng/util/item/ItemList.java +++ b/src/main/java/appeng/util/item/ItemList.java @@ -140,6 +140,14 @@ public final class ItemList implements IItemList { } } + @Override + public ItemList clone() { + ItemList list = new ItemList(); + list.records.putAll(records); + list.version.set(version.get()); + return list; + } + private ItemVariantList getOrCreateRecord(Item item) { return this.records.computeIfAbsent(item, this::makeRecordMap); }