Skip to content

Commit 064f2c3

Browse files
committed
Greatly improve performance of removing ingredients at runtime
1 parent 5f3b41c commit 064f2c3

File tree

8 files changed

+142
-104
lines changed

8 files changed

+142
-104
lines changed

Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java

Lines changed: 14 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import mezz.jei.api.ingredients.IIngredientHelper;
66
import mezz.jei.api.ingredients.IIngredientType;
77
import mezz.jei.api.ingredients.ITypedIngredient;
8-
import mezz.jei.api.ingredients.subtypes.UidContext;
98
import mezz.jei.api.runtime.IIngredientManager;
109
import mezz.jei.api.runtime.IIngredientVisibility;
1110
import mezz.jei.common.config.DebugConfig;
@@ -33,7 +32,6 @@
3332
import java.util.List;
3433
import java.util.Optional;
3534
import java.util.Set;
36-
import java.util.function.Function;
3735
import java.util.regex.Matcher;
3836
import java.util.regex.Pattern;
3937
import java.util.stream.Stream;
@@ -94,7 +92,7 @@ public IngredientFilter(
9492
}
9593

9694
this.filterTextSource.addListener(filterText -> {
97-
ingredientListCached = null;
95+
invalidateCache();
9896
notifyListenersOfChange();
9997
});
10098

@@ -113,7 +111,7 @@ public <V> void addIngredient(IListElementInfo<V> info) {
113111
IListElement<V> element = info.getElement();
114112
updateHiddenState(element);
115113

116-
this.elementSearch.add(info);
114+
this.elementSearch.add(info, ingredientManager);
117115

118116
invalidateCache();
119117
}
@@ -127,25 +125,7 @@ public void rebuildItemFilter() {
127125
Collection<IListElement<?>> ingredients = this.elementSearch.getAllIngredients();
128126
this.elementSearch = createElementSearch(this.clientConfig, this.elementPrefixParser);
129127
List<IListElementInfo<?>> elementInfos = IngredientListElementFactory.rebuildList(ingredientManager, ingredients, modIdHelper);
130-
this.elementSearch.addAll(elementInfos);
131-
}
132-
133-
public <V> Optional<IListElement<V>> searchForMatchingElement(
134-
IIngredientHelper<V> ingredientHelper,
135-
ITypedIngredient<V> typedIngredient
136-
) {
137-
V ingredient = typedIngredient.getIngredient();
138-
IIngredientType<V> type = typedIngredient.getType();
139-
Function<ITypedIngredient<V>, Object> uidFunction = (i) -> ingredientHelper.getUid(i, UidContext.Ingredient);
140-
Object ingredientUid = uidFunction.apply(typedIngredient);
141-
String lowercaseDisplayName = DisplayNameUtil.getLowercaseDisplayNameForSearch(ingredient, ingredientHelper);
142-
143-
ElementPrefixParser.TokenInfo tokenInfo = new ElementPrefixParser.TokenInfo(lowercaseDisplayName, ElementPrefixParser.NO_PREFIX);
144-
return this.elementSearch.getSearchResults(tokenInfo)
145-
.stream()
146-
.map(elementInfo -> checkForMatch(elementInfo, type, ingredientUid, uidFunction))
147-
.flatMap(Optional::stream)
148-
.findFirst();
128+
this.elementSearch.addAll(elementInfos, ingredientManager);
149129
}
150130

151131
@Override
@@ -159,12 +139,12 @@ public void updateHidden() {
159139
changed |= updateHiddenState(element);
160140
}
161141
if (changed) {
162-
ingredientListCached = null;
142+
invalidateCache();
163143
notifyListenersOfChange();
164144
}
165145
}
166146

167-
public <V> boolean updateHiddenState(IListElement<V> element) {
147+
private <V> boolean updateHiddenState(IListElement<V> element) {
168148
ITypedIngredient<V> typedIngredient = element.getTypedIngredient();
169149
boolean visible = this.ingredientVisibility.isIngredientVisible(typedIngredient);
170150
if (element.isVisible() != visible) {
@@ -178,13 +158,12 @@ public <V> boolean updateHiddenState(IListElement<V> element) {
178158
public <V> void onIngredientVisibilityChanged(ITypedIngredient<V> ingredient, boolean visible) {
179159
IIngredientType<V> ingredientType = ingredient.getType();
180160
IIngredientHelper<V> ingredientHelper = ingredientManager.getIngredientHelper(ingredientType);
181-
searchForMatchingElement(ingredientHelper, ingredient)
182-
.ifPresent(element -> {
183-
if (element.isVisible() != visible) {
184-
element.setVisible(visible);
185-
notifyListenersOfChange();
186-
}
187-
});
161+
IListElement<V> match = this.elementSearch.findElement(ingredient, ingredientHelper);
162+
if (match != null && match.isVisible() != visible) {
163+
match.setVisible(visible);
164+
invalidateCache();
165+
notifyListenersOfChange();
166+
}
188167
}
189168

190169
@Override
@@ -232,31 +211,11 @@ private Stream<ITypedIngredient<?>> getIngredientListUncached(String filterText)
232211
.map(IListElement::getTypedIngredient);
233212
}
234213

235-
private static <T> Optional<IListElement<T>> checkForMatch(IListElement<?> element, IIngredientType<T> ingredientType, Object uid, Function<ITypedIngredient<T>, Object> uidFunction) {
236-
return optionalCast(element, ingredientType)
237-
.filter(cast -> {
238-
ITypedIngredient<T> typedIngredient = cast.getTypedIngredient();
239-
Object elementUid = uidFunction.apply(typedIngredient);
240-
return uid.equals(elementUid);
241-
});
242-
}
243-
244-
private static <T> Optional<IListElement<T>> optionalCast(IListElement<?> element, IIngredientType<T> ingredientType) {
245-
ITypedIngredient<?> typedIngredient = element.getTypedIngredient();
246-
if (typedIngredient.getType() == ingredientType) {
247-
@SuppressWarnings("unchecked")
248-
IListElement<T> cast = (IListElement<T>) element;
249-
return Optional.of(cast);
250-
}
251-
return Optional.empty();
252-
}
253-
254214
@Override
255215
public <V> void onIngredientsAdded(IIngredientHelper<V> ingredientHelper, Collection<ITypedIngredient<V>> ingredients) {
256216
for (ITypedIngredient<V> value : ingredients) {
257-
Optional<IListElement<V>> matchingElementOptional = searchForMatchingElement(ingredientHelper, value);
258-
if (matchingElementOptional.isPresent()) {
259-
IListElement<V> matchingElement = matchingElementOptional.get();
217+
IListElement<V> matchingElement = this.elementSearch.findElement(value, ingredientHelper);
218+
if (matchingElement != null) {
260219
updateHiddenState(matchingElement);
261220
if (DebugConfig.isDebugModeEnabled()) {
262221
LOGGER.debug("Updated ingredient: {}", ingredientHelper.getErrorInfo(value.getIngredient()));
@@ -276,21 +235,7 @@ public <V> void onIngredientsAdded(IIngredientHelper<V> ingredientHelper, Collec
276235

277236
@Override
278237
public <V> void onIngredientsRemoved(IIngredientHelper<V> ingredientHelper, Collection<ITypedIngredient<V>> ingredients) {
279-
for (ITypedIngredient<V> typedIngredient : ingredients) {
280-
Optional<IListElement<V>> matchingElementOptional = searchForMatchingElement(ingredientHelper, typedIngredient);
281-
if (matchingElementOptional.isEmpty()) {
282-
String errorInfo = ingredientHelper.getErrorInfo(typedIngredient.getIngredient());
283-
LOGGER.error("Could not find a matching ingredient to remove: {}", errorInfo);
284-
} else {
285-
if (DebugConfig.isDebugModeEnabled()) {
286-
LOGGER.debug("Removed ingredient: {}", ingredientHelper.getErrorInfo(typedIngredient.getIngredient()));
287-
}
288-
IListElement<V> matchingElement = matchingElementOptional.get();
289-
matchingElement.setVisible(false);
290-
}
291-
}
292-
293-
invalidateCache();
238+
// ignore this, it's handled by onIngredientVisibilityChanged
294239
}
295240

296241
private record SearchTokens(List<ElementPrefixParser.TokenInfo> toSearch, List<ElementPrefixParser.TokenInfo> toRemove) {

Gui/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public class IngredientGridWithNavigation implements IRecipeFocusSource {
6969
private ImmutableRect2i backgroundArea = ImmutableRect2i.EMPTY;
7070
private ImmutableRect2i slotBackgroundArea = ImmutableRect2i.EMPTY;
7171
private Set<ImmutableRect2i> guiExclusionAreas = Set.of();
72+
private boolean active;
7273

7374
public IngredientGridWithNavigation(
7475
String debugName,
@@ -98,11 +99,17 @@ public IngredientGridWithNavigation(
9899
this.ghostIngredientDragManager = new GhostIngredientDragManager(this.ingredientGrid, screenHelper, ingredientManager, toggleState);
99100

100101
this.ingredientSource.addSourceListChangedListener(() -> {
101-
boolean resetToFirstPage = clientConfig.isAddingBookmarksToFrontEnabled();
102-
updateLayout(resetToFirstPage);
102+
if (isActive()) {
103+
boolean resetToFirstPage = clientConfig.isAddingBookmarksToFrontEnabled();
104+
updateLayout(resetToFirstPage);
105+
}
103106
});
104107
}
105108

109+
private boolean isActive() {
110+
return active;
111+
}
112+
106113
public boolean hasRoom() {
107114
return this.ingredientGrid.hasRoom();
108115
}
@@ -186,6 +193,7 @@ private void updateGridBounds(final ImmutableRect2i availableArea, @Nullable Imm
186193
}
187194

188195
public void updateBounds(final ImmutableRect2i availableArea, Set<ImmutableRect2i> guiExclusionAreas, @Nullable ImmutablePoint2i mouseExclusionPoint) {
196+
this.active = true;
189197
this.guiExclusionAreas = guiExclusionAreas;
190198

191199
final boolean navigationEnabled =
@@ -201,6 +209,7 @@ public void updateBounds(final ImmutableRect2i availableArea, Set<ImmutableRect2
201209
updateGridBounds(availableArea, mouseExclusionPoint, true);
202210
}
203211
if (!hasRoom()) {
212+
this.active = false;
204213
return;
205214
}
206215

@@ -302,6 +311,7 @@ public boolean isEmpty() {
302311
}
303312

304313
public void close() {
314+
this.active = false;
305315
this.ghostIngredientDragManager.stopDrag();
306316
}
307317

Gui/src/main/java/mezz/jei/gui/overlay/IngredientListRenderer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
import net.minecraft.client.renderer.RenderType;
1818

1919
import java.util.ArrayList;
20+
import java.util.Iterator;
2021
import java.util.List;
21-
import java.util.ListIterator;
2222
import java.util.Map;
2323
import java.util.Set;
2424
import java.util.stream.Stream;
@@ -81,7 +81,7 @@ public void set(final int startIndex, List<IElement<?>> ingredientList) {
8181
renderElementsByType.clear();
8282
renderOverlays.clear();
8383

84-
ListIterator<IElement<?>> elementIterator = ingredientList.listIterator(startIndex);
84+
Iterator<IElement<?>> elementIterator = ingredientList.listIterator(startIndex);
8585

8686
for (IngredientListSlot ingredientListSlot : slots) {
8787
if (ingredientListSlot.isBlocked()) {

Gui/src/main/java/mezz/jei/gui/search/ElementSearch.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package mezz.jei.gui.search;
22

3+
import mezz.jei.api.ingredients.IIngredientHelper;
4+
import mezz.jei.api.ingredients.ITypedIngredient;
5+
import mezz.jei.api.ingredients.subtypes.UidContext;
6+
import mezz.jei.api.runtime.IIngredientManager;
37
import mezz.jei.core.search.CombinedSearchables;
48
import mezz.jei.core.search.ISearchStorage;
59
import mezz.jei.core.search.ISearchable;
@@ -10,9 +14,11 @@
1014
import mezz.jei.gui.ingredients.IListElementInfo;
1115
import org.apache.logging.log4j.LogManager;
1216
import org.apache.logging.log4j.Logger;
17+
import org.jetbrains.annotations.Nullable;
1318

1419
import java.util.Collection;
1520
import java.util.Collections;
21+
import java.util.HashMap;
1622
import java.util.IdentityHashMap;
1723
import java.util.Map;
1824
import java.util.Set;
@@ -22,7 +28,7 @@ public class ElementSearch implements IElementSearch {
2228

2329
private final Map<PrefixInfo<IListElementInfo<?>, IListElement<?>>, PrefixedSearchable<IListElementInfo<?>, IListElement<?>>> prefixedSearchables = new IdentityHashMap<>();
2430
private final CombinedSearchables<IListElement<?>> combinedSearchables = new CombinedSearchables<>();
25-
private final Set<IListElement<?>> allElements = Collections.newSetFromMap(new IdentityHashMap<>());
31+
private final Map<Object, IListElement<?>> allElements = new HashMap<>();
2632

2733
public ElementSearch(ElementPrefixParser elementPrefixParser) {
2834
for (PrefixInfo<IListElementInfo<?>, IListElement<?>> prefixInfo : elementPrefixParser.allPrefixInfos()) {
@@ -57,24 +63,34 @@ public Set<IListElement<?>> getSearchResults(ElementPrefixParser.TokenInfo token
5763
}
5864

5965
@Override
60-
public void add(IListElementInfo<?> info) {
61-
this.allElements.add(info.getElement());
66+
public <T> void add(IListElementInfo<T> info, IIngredientManager ingredientManager) {
67+
IListElement<T> element = info.getElement();
68+
Object uid = getUid(element.getTypedIngredient(), ingredientManager);
69+
this.allElements.put(uid, element);
70+
6271
for (PrefixedSearchable<IListElementInfo<?>, IListElement<?>> prefixedSearchable : this.prefixedSearchables.values()) {
6372
SearchMode searchMode = prefixedSearchable.getMode();
6473
if (searchMode != SearchMode.DISABLED) {
6574
Collection<String> strings = prefixedSearchable.getStrings(info);
6675
ISearchStorage<IListElement<?>> storage = prefixedSearchable.getSearchStorage();
6776
for (String string : strings) {
68-
storage.put(string, info.getElement());
77+
storage.put(string, element);
6978
}
7079
}
7180
}
7281
}
7382

83+
private static <T> Object getUid(ITypedIngredient<T> typedIngredient, IIngredientManager ingredientManager) {
84+
IIngredientHelper<T> ingredientHelper = ingredientManager.getIngredientHelper(typedIngredient.getType());
85+
return ingredientHelper.getUid(typedIngredient.getIngredient(), UidContext.Ingredient);
86+
}
87+
7488
@Override
75-
public void addAll(Collection<IListElementInfo<?>> infos) {
89+
public void addAll(Collection<IListElementInfo<?>> infos, IIngredientManager ingredientManager) {
7690
for (IListElementInfo<?> info : infos) {
77-
this.allElements.add(info.getElement());
91+
IListElement<?> element = info.getElement();
92+
Object uid = getUid(info.getTypedIngredient(), ingredientManager);
93+
this.allElements.put(uid, element);
7894
}
7995
for (PrefixedSearchable<IListElementInfo<?>, IListElement<?>> prefixedSearchable : this.prefixedSearchables.values()) {
8096
SearchMode searchMode = prefixedSearchable.getMode();
@@ -91,8 +107,20 @@ public void addAll(Collection<IListElementInfo<?>> infos) {
91107
}
92108

93109
@Override
94-
public Set<IListElement<?>> getAllIngredients() {
95-
return Collections.unmodifiableSet(allElements);
110+
public @Nullable <T> IListElement<T> findElement(ITypedIngredient<T> ingredient, IIngredientHelper<T> ingredientHelper) {
111+
Object ingredientUid = ingredientHelper.getUid(ingredient.getIngredient(), UidContext.Ingredient);
112+
IListElement<?> listElement = allElements.get(ingredientUid);
113+
if (listElement != null && listElement.getTypedIngredient().getType().equals(ingredient.getType())) {
114+
@SuppressWarnings("unchecked")
115+
IListElement<T> cast = (IListElement<T>) listElement;
116+
return cast;
117+
}
118+
return null;
119+
}
120+
121+
@Override
122+
public Collection<IListElement<?>> getAllIngredients() {
123+
return Collections.unmodifiableCollection(allElements.values());
96124
}
97125

98126
@Override

0 commit comments

Comments
 (0)