From a31f7254d76add6c34f134713c131463602f8cef Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 4 Sep 2024 16:20:39 +0900 Subject: New plugin reload setup 1. Plugin reloads will now interrupt existing reloading tasks if a new plugin reload has been requested 2. Plugin reloads will be automatically interrupted when the player leaves the level / the level is removed 3. More logging in DisplayRegistryImpl showing the stats of displays 4. Failure in filling recipes will now not stop the caching of display lookup 5. Slightly improve performance of checking display visibility on display lookup --- .../gui/config/options/AllREIConfigOptions.java | 2 - .../impl/client/registry/display/DisplayCache.java | 4 ++ .../client/registry/display/DisplayCacheImpl.java | 16 ++++- .../registry/display/DisplayRegistryImpl.java | 78 ++++++++++++++++------ .../shedaniel/rei/impl/client/view/ViewsImpl.java | 8 +-- 5 files changed, 78 insertions(+), 30 deletions(-) (limited to 'runtime/src/main/java/me/shedaniel/rei/impl/client') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java index 30340bfba..b9596c200 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java @@ -24,7 +24,6 @@ package me.shedaniel.rei.impl.client.gui.config.options; import me.shedaniel.clothconfig2.api.ModifierKeyCode; -import me.shedaniel.rei.RoughlyEnoughItemsCore; import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; import me.shedaniel.rei.api.client.config.entry.EntryStackProvider; import me.shedaniel.rei.api.client.gui.config.*; @@ -248,7 +247,6 @@ public interface AllREIConfigOptions { .enabledDisabled(); CompositeOption RELOAD_PLUGINS = make("reset.reload_plugins", i -> null, (i, v) -> new Object()) .reload((access, option, onClose) -> { - RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear(); RoughlyEnoughItemsCoreClient.reloadPlugins(null, null); while (!PluginManager.areAnyReloading()) { try { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java index b65aba97c..9c725efc4 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java @@ -34,6 +34,10 @@ import java.util.List; import java.util.Set; public interface DisplayCache { + int cachedSize(); + + int notCachedSize(); + boolean doesCache(); boolean isCached(Display display); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java index 80a73f82e..4c995c9b0 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java @@ -53,6 +53,16 @@ public class DisplayCacheImpl implements DisplayCache { this.displaysByOutput = createSetMultimap(); } + @Override + public int cachedSize() { + return this.displaysCached.size(); + } + + @Override + public int notCachedSize() { + return this.displaysNotCached.size(); + } + @Override public boolean doesCache() { return this.cache; @@ -66,7 +76,7 @@ public class DisplayCacheImpl implements DisplayCache { @Override public void add(Display display) { if (this.cache) { - if (!preprocessed) { + if (!this.preprocessed) { this.displaysNotCached.add(display); } else { this.process(display); @@ -80,7 +90,7 @@ public class DisplayCacheImpl implements DisplayCache { @Override public boolean remove(Display display) { if (this.cache) { - if (!preprocessed) { + if (!this.preprocessed) { return this.displaysNotCached.remove(display); } else { boolean removed = this.displaysCached.remove(display); @@ -106,7 +116,7 @@ public class DisplayCacheImpl implements DisplayCache { @Override public void endReload() { if (this.cache) { - if (preprocessed) { + if (this.preprocessed) { InternalLogger.getInstance().error("DisplayCache#endReload called after preprocessed!"); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java index d0d36130c..7ec01a9f2 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java @@ -23,7 +23,9 @@ package me.shedaniel.rei.impl.client.registry.display; -import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import dev.architectury.event.EventResult; import me.shedaniel.rei.api.client.plugins.REIClientPlugin; import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; @@ -36,6 +38,7 @@ import me.shedaniel.rei.api.client.registry.display.visibility.DisplayVisibility import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.display.Display; import me.shedaniel.rei.api.common.plugins.PluginManager; +import me.shedaniel.rei.api.common.registry.ReloadStage; import me.shedaniel.rei.impl.common.InternalLogger; import me.shedaniel.rei.impl.common.registry.RecipeManagerContextImpl; import net.minecraft.world.item.crafting.Recipe; @@ -55,10 +58,6 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl category = (DisplayCategory) CategoryRegistry.getInstance().get(display.getCategoryIdentifier()).getCategory(); - Preconditions.checkNotNull(category, "Failed to resolve category: " + display.getCategoryIdentifier()); + return isDisplayVisible(category, display); + } + + public boolean isDisplayVisible(DisplayCategory category, Display display) { + if (category == null) throw new NullPointerException("Failed to resolve category: " + display.getCategoryIdentifier()); for (DisplayVisibilityPredicate predicate : visibilityPredicates) { try { EventResult result = predicate.handleDisplay(category, display); @@ -187,13 +190,8 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl> allSortedRecipes = getAllSortedRecipes(); - for (int i = allSortedRecipes.size() - 1; i >= 0; i--) { - Recipe recipe = allSortedRecipes.get(i); - addWithReason(recipe, DisplayAdditionReason.RECIPE_MANAGER); - } - } + InternalLogger.getInstance().debug("Found preliminary %d displays", displaySize()); + fillSortedRecipes(); for (CategoryIdentifier identifier : getAll().keySet()) { if (CategoryRegistry.getInstance().tryGet(identifier).isEmpty()) { @@ -201,21 +199,59 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl failedDisplays = new ArrayList<>(); + removeFailedDisplays(); + this.displaysHolder.endReload(); + InternalLogger.getInstance().debug("%d displays registration have completed", displaySize()); + } + + private void fillSortedRecipes() { + Stopwatch stopwatch = Stopwatch.createStarted(); + int lastSize = displaySize(); + if (!fillers.isEmpty()) { + List> allSortedRecipes = getAllSortedRecipes(); + for (int i = allSortedRecipes.size() - 1; i >= 0; i--) { + Recipe recipe = allSortedRecipes.get(i); + try { + addWithReason(recipe, DisplayAdditionReason.RECIPE_MANAGER); + } catch (Throwable e) { + InternalLogger.getInstance().error("Failed to fill display for recipe: %s [%s]", recipe, recipe.getId(), e); + } + } + } + InternalLogger.getInstance().debug("Filled %d displays from recipe manager in %s", displaySize() - lastSize, stopwatch.stop()); + } + + private void removeFailedDisplays() { + Multimap, Display> failedDisplays = Multimaps.newListMultimap(new HashMap<>(), ArrayList::new); for (List displays : getAll().values()) { for (Display display : displays) { if (!DisplayValidator.validate(display)) { - failedDisplays.add(display); + failedDisplays.put(display.getCategoryIdentifier(), display); } } } - for (Display display : failedDisplays) { - this.displaysHolder.remove(display); - } - - this.displaysHolder.endReload(); - InternalLogger.getInstance().debug("Registered %d displays", displaySize()); + InternalLogger.getInstance().debug("Removing %d failed displays" + (!failedDisplays.isEmpty() ? ":" : ""), failedDisplays.size()); + failedDisplays.asMap().entrySet().stream() + .sorted(Comparator.comparing(entry -> entry.getKey().toString())) + .forEach(entry -> { + InternalLogger.getInstance().debug("- %s: %d failed display" + (entry.getValue().size() == 1 ? "" : "s"), entry.getKey(), entry.getValue().size()); + for (Display display : entry.getValue()) { + this.displaysHolder.remove(display); + } + }); + } + + @Override + public void postStage(ReloadStage stage) { + if (stage != ReloadStage.END) return; + InternalLogger.getInstance().debug("Registered displays report (%d displays, %d cached / %d not cached)" + (displaySize() > 0 ? ":" : ""), + displaySize(), displaysHolder().cache().cachedSize(), displaysHolder().cache().notCachedSize()); + getAll().entrySet().stream() + .sorted(Comparator.comparing(entry -> entry.getKey().toString())) + .forEach(entry -> { + InternalLogger.getInstance().debug("- %s: %d display" + (entry.getValue().size() == 1 ? "" : "s"), entry.getKey(), entry.getValue().size()); + }); } public DisplaysHolder displaysHolder() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java index 2d2e6fada..0047638aa 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java @@ -111,7 +111,7 @@ public class ViewsImpl implements Views { forCategories(processingVisibilityHandlers, filteringCategories, displayRegistry, result, (configuration, categoryId, displays, set) -> { if (categories.contains(categoryId)) { // If the category is in the search, add all displays for (Display display : displays) { - if (!processingVisibilityHandlers || displayRegistry.isDisplayVisible(display)) { + if (!processingVisibilityHandlers || ((DisplayRegistryImpl) displayRegistry).isDisplayVisible(configuration.getCategory(), display)) { set.add(display); } } @@ -121,7 +121,7 @@ public class ViewsImpl implements Views { return; } for (Display display : displays) { - if (processingVisibilityHandlers && !displayRegistry.isDisplayVisible(display)) continue; + if (processingVisibilityHandlers && !((DisplayRegistryImpl) displayRegistry).isDisplayVisible(configuration.getCategory(), display)) continue; if (!recipesForStacks.isEmpty()) { if (isRecipesFor(displaysHolder, recipesForStacks, display)) { set.add(display); @@ -171,7 +171,7 @@ public class ViewsImpl implements Views { forCategories(processingVisibilityHandlers, filteringCategories, displayRegistry, result, (configuration, categoryId, displays, set) -> { if (categories.contains(categoryId)) return; for (Display display : displays) { - if (processingVisibilityHandlers && !displayRegistry.isDisplayVisible(display)) continue; + if (processingVisibilityHandlers && !((DisplayRegistryImpl) displayRegistry).isDisplayVisible(configuration.getCategory(), display)) continue; if (!recipesForStacksWildcard.isEmpty()) { if (isRecipesFor(displaysHolder, recipesForStacksWildcard, display)) { set.add(display); @@ -193,7 +193,7 @@ public class ViewsImpl implements Views { if (isStackWorkStationOfCategory(configuration, usagesFor)) { categories.add(categoryId); if (processingVisibilityHandlers) { - set.addAll(CollectionUtils.filterToSet(displays, displayRegistry::isDisplayVisible)); + set.addAll(CollectionUtils.filterToSet(displays, display -> ((DisplayRegistryImpl) displayRegistry).isDisplayVisible(configuration.getCategory(), display))); } else { set.addAll(displays); } -- cgit From 428d5359f0aff3b9922c7a80058f5214cf7f8340 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Fri, 6 Sep 2024 20:49:27 +0900 Subject: New Display Registry Analysis in REI config, with options to sort the entries --- .../gui/config/options/AllREIConfigGroups.java | 3 +- .../gui/config/options/AllREIConfigOptions.java | 4 + .../gui/performance/DisplayRegistryInfoScreen.java | 173 +++++++++++++++++++++ .../client/gui/performance/PerformanceScreen.java | 38 ++++- .../performance/entry/SubCategoryListEntry.java | 4 + .../rei/impl/client/gui/screen/ScreenWithMenu.java | 89 +++++++++++ 6 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/DisplayRegistryInfoScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ScreenWithMenu.java (limited to 'runtime/src/main/java/me/shedaniel/rei/impl/client') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java index 28b83e639..b206fd6e5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java @@ -116,7 +116,8 @@ public interface AllREIConfigGroups { OptionGroup DEBUG_PERFORMANCE = make("debug.performance") .add(PLUGINS_PERFORMANCE) .add(SEARCH_PERFORMANCE) - .add(ENTRY_LIST_PERFORMANCE); + .add(ENTRY_LIST_PERFORMANCE) + .add(DISPLAY_REGISTRY_ANALYSIS); OptionGroup RESET_RELOAD = make("reset.reload") .add(RELOAD_PLUGINS) .add(RELOAD_SEARCH); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java index b9596c200..97119df2e 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java @@ -36,6 +36,7 @@ import me.shedaniel.rei.impl.client.config.entries.ConfigureCategoriesScreen; import me.shedaniel.rei.impl.client.config.entries.FilteringEntry; import me.shedaniel.rei.impl.client.gui.config.REIConfigScreen; import me.shedaniel.rei.impl.client.gui.config.options.configure.PanelBoundariesConfiguration; +import me.shedaniel.rei.impl.client.gui.performance.DisplayRegistryInfoScreen; import me.shedaniel.rei.impl.client.gui.performance.PerformanceScreen; import me.shedaniel.rei.impl.client.gui.screen.ConfigReloadingScreen; import me.shedaniel.rei.impl.client.gui.screen.collapsible.CollapsibleEntriesScreen; @@ -245,6 +246,9 @@ public interface AllREIConfigOptions { .enabledDisabled(); CompositeOption ENTRY_LIST_PERFORMANCE = make("debug.entry_list_performance", i -> i.advanced.layout.debugRenderTimeRequired, (i, v) -> i.advanced.layout.debugRenderTimeRequired = v) .enabledDisabled(); + CompositeOption DISPLAY_REGISTRY_ANALYSIS = make("debug.display_registry_analysis", i -> null, (i, v) -> new Object()) + .details((access, option, onClose) -> Minecraft.getInstance().setScreen(new DisplayRegistryInfoScreen(onClose))) + .requiresLevel(); CompositeOption RELOAD_PLUGINS = make("reset.reload_plugins", i -> null, (i, v) -> new Object()) .reload((access, option, onClose) -> { RoughlyEnoughItemsCoreClient.reloadPlugins(null, null); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/DisplayRegistryInfoScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/DisplayRegistryInfoScreen.java new file mode 100644 index 000000000..289a1c4fd --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/DisplayRegistryInfoScreen.java @@ -0,0 +1,173 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.performance; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.gui.modules.Menu; +import me.shedaniel.rei.impl.client.gui.modules.entries.ToggleMenuEntry; +import me.shedaniel.rei.impl.client.gui.screen.ScreenWithMenu; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import net.minecraft.util.FormattedCharSequence; + +import java.util.*; +import java.util.stream.Stream; + +@Environment(EnvType.CLIENT) +public class DisplayRegistryInfoScreen extends ScreenWithMenu { + private Runnable onClose; + + public DisplayRegistryInfoScreen(Runnable onClose) { + super(Component.translatable("text.rei.display_registry_analysis")); + this.onClose = onClose; + } + + private ListWidget list; + private SortType sortType = SortType.ID; + + @Override + public void init() { + { + Component backText = Component.literal("↩ ").append(Component.translatable("gui.back")); + addRenderableWidget(new Button(4, 4, Minecraft.getInstance().font.width(backText) + 10, 20, backText, button -> { + this.onClose.run(); + this.onClose = null; + })); + } + { + Component text = Component.translatable("text.rei.sort"); + Rectangle bounds = new Rectangle(this.width - 4 - Minecraft.getInstance().font.width(text) - 10, 4, Minecraft.getInstance().font.width(text) + 10, 20); + addRenderableWidget(new Button(bounds.x, bounds.y, bounds.width, bounds.height, text, button -> { + this.setMenu(new Menu(bounds, CollectionUtils.map(SortType.values(), type -> { + return ToggleMenuEntry.of(Component.translatable("text.rei.sort.by", type.name().toLowerCase(Locale.ROOT)), () -> false, o -> { + this.closeMenu(); + this.sortType = type; + this.init(this.minecraft, this.width, this.height); + }); + }), false)); + })); + } + list = new ListWidget(); + list.addItem(new EntryImpl(Component.literal("Total Displays"), DisplayRegistry.getInstance().displaySize())); + sort(DisplayRegistry.getInstance().getAll().entrySet().stream()) + .forEach(entry -> { + list.addItem(new EntryImpl(entry.getKey(), entry.getValue().size())); + }); + addWidget(list); + } + + private Stream, List>> sort(Stream, List>> stream) { + return switch (sortType) { + case COUNT -> stream.sorted(Comparator., List>>comparingInt(value -> value.getValue().size()).reversed()); + case ID -> stream.sorted(Comparator.comparing(value -> value.getKey().toString())); + }; + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + renderDirtBackground(0); + list.render(poses, mouseX, mouseY, delta); + this.font.drawShadow(poses, this.title.getVisualOrderText(), this.width / 2.0F - this.font.width(this.title) / 2.0F, 12.0F, -1); + super.render(poses, mouseX, mouseY, delta); + } + + public static abstract class ListEntry extends DynamicElementListWidget.ElementEntry { + } + + private class ListWidget extends DynamicElementListWidget { + public ListWidget() { + super(DisplayRegistryInfoScreen.this.minecraft, DisplayRegistryInfoScreen.this.width, DisplayRegistryInfoScreen.this.height, 30, DisplayRegistryInfoScreen.this.height, GuiComponent.BACKGROUND_LOCATION); + } + + @Override + public int getItemWidth() { + return width; + } + + @Override + public int addItem(ListEntry item) { + return super.addItem(item); + } + + @Override + protected int getScrollbarPosition() { + return width - 6; + } + } + + public static class EntryImpl extends ListEntry { + private final Component component; + public final int count; + + public EntryImpl(CategoryIdentifier identifier, int count) { + this(Component.literal(identifier.getIdentifier().toString()), count); + } + + public EntryImpl(Component component, int count) { + this.component = component; + this.count = count; + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + Minecraft.getInstance().font.drawShadow(matrices, this.component.getVisualOrderText(), (float) x + 4, (float) (y + 6), -1); + FormattedCharSequence rightText = Component.translatable("text.rei.display_registry_analysis.displays", count).getVisualOrderText(); + Minecraft.getInstance().font.drawShadow(matrices, rightText, (float) x + entryWidth - 6 - 8 - Minecraft.getInstance().font.width(rightText), (float) (y + 6), -1); + } + + @Override + public int getItemHeight() { + return 24; + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public List narratables() { + return Collections.emptyList(); + } + } + + private enum SortType { + COUNT, + ID + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java index 239489a71..29d170779 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java @@ -26,17 +26,21 @@ package me.shedaniel.rei.impl.client.gui.performance; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.datafixers.util.Pair; import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget; +import me.shedaniel.math.Rectangle; import me.shedaniel.rei.RoughlyEnoughItemsCore; import me.shedaniel.rei.api.common.plugins.REIPlugin; import me.shedaniel.rei.api.common.plugins.REIPluginProvider; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.gui.modules.Menu; +import me.shedaniel.rei.impl.client.gui.modules.entries.ToggleMenuEntry; import me.shedaniel.rei.impl.client.gui.performance.entry.PerformanceEntryImpl; import me.shedaniel.rei.impl.client.gui.performance.entry.SubCategoryListEntry; +import me.shedaniel.rei.impl.client.gui.screen.ScreenWithMenu; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiComponent; import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextColor; import net.minecraft.util.FormattedCharSequence; @@ -48,7 +52,7 @@ import java.util.stream.Collectors; import static java.util.concurrent.TimeUnit.*; @Environment(EnvType.CLIENT) -public class PerformanceScreen extends Screen { +public class PerformanceScreen extends ScreenWithMenu { private Runnable onClose; public PerformanceScreen(Runnable onClose) { @@ -57,6 +61,7 @@ public class PerformanceScreen extends Screen { } private PerformanceEntryListWidget list; + private SortType sortType = SortType.ORDER; /* * Copyright (C) 2008 The Guava Authors @@ -142,8 +147,22 @@ public class PerformanceScreen extends Screen { this.onClose = null; })); } + { + Component text = Component.translatable("text.rei.sort"); + Rectangle bounds = new Rectangle(this.width - 4 - Minecraft.getInstance().font.width(text) - 10, 4, Minecraft.getInstance().font.width(text) + 10, 20); + addRenderableWidget(new Button(bounds.x, bounds.y, bounds.width, bounds.height, text, button -> { + this.setMenu(new Menu(bounds, CollectionUtils.map(SortType.values(), type -> { + return ToggleMenuEntry.of(Component.translatable("text.rei.sort.by", type.name().toLowerCase(Locale.ROOT)), () -> false, o -> { + this.closeMenu(); + this.sortType = type; + this.init(this.minecraft, this.width, this.height); + }); + }), false)); + })); + } list = new PerformanceEntryListWidget(); long[] totalTime = {0}; + List subCategories = new ArrayList<>(); RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.getStages().forEach((stage, inner) -> { List entries = new ArrayList<>(); inner.times().forEach((obj, time) -> { @@ -158,9 +177,15 @@ public class PerformanceScreen extends Screen { entries.add(new PerformanceEntryImpl(Component.literal("Miscellaneous Operations"), inner.totalNano() - separateTime)); } totalTime[0] += Math.max(inner.totalNano(), separateTime); - entries.sort(Comparator.comparingLong(value -> value.time).reversed()); - list.addItem(new SubCategoryListEntry(Component.literal(stage), (List) (List) entries, Math.max(inner.totalNano(), separateTime), false)); + if (this.sortType == SortType.DURATION) { + entries.sort(Comparator.comparingLong(value -> value.time).reversed()); + } + subCategories.add(new SubCategoryListEntry(Component.literal(stage), (List) (List) entries, Math.max(inner.totalNano(), separateTime), false)); }); + if (this.sortType == SortType.DURATION) { + subCategories.sort(Comparator.comparingLong(SubCategoryListEntry::getTotalTime).reversed()); + } + subCategories.forEach(list::addItem); list.children().add(0, new PerformanceEntryImpl(Component.literal("Total Load Time"), totalTime[0])); addWidget(list); } @@ -212,4 +237,9 @@ public class PerformanceScreen extends Screen { return width - 6; } } + + private enum SortType { + ORDER, + DURATION + } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java index 368bd5a3a..9596fc51d 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java @@ -157,6 +157,10 @@ public class SubCategoryListEntry extends PerformanceScreen.PerformanceEntry imp return Collections.emptyList(); } + public long getTotalTime() { + return totalTime; + } + public class CategoryLabelWidget implements GuiEventListener { private final Rectangle rectangle = new Rectangle(); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ScreenWithMenu.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ScreenWithMenu.java new file mode 100644 index 000000000..ab8df5df8 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ScreenWithMenu.java @@ -0,0 +1,89 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.screen; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.rei.impl.client.gui.modules.Menu; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Nullable; + +public class ScreenWithMenu extends Screen { + @Nullable + private Menu menu; + + protected ScreenWithMenu(Component component) { + super(component); + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + super.render(poses, mouseX, mouseY, delta); + if (this.menu != null) { + poses.pushPose(); + poses.translate(0, 0, 400); + this.menu.render(poses, mouseX, mouseY, delta); + poses.popPose(); + } + } + + protected void setMenu(@Nullable Menu menu) { + this.menu = menu; + } + + protected void closeMenu() { + this.menu = null; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (this.menu != null) { + if (!this.menu.mouseClicked(mouseX, mouseY, button)) + this.menu = null; + return true; + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (this.menu != null && this.menu.mouseReleased(mouseX, mouseY, button)) + return true; + return super.mouseReleased(mouseX, mouseY, button); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (this.menu != null && this.menu.mouseScrolled(mouseX, mouseY, amount)) + return true; + return super.mouseScrolled(mouseX, mouseY, amount); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (this.menu != null && this.menu.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) + return true; + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } +} -- cgit From b8d0975527d86b79a1f9c160415baf8a6e6cf5cb Mon Sep 17 00:00:00 2001 From: shedaniel Date: Tue, 17 Sep 2024 13:46:46 +0800 Subject: New Slot.withEntriesListener --- .../rei/impl/client/gui/widget/EntryWidget.java | 7 ++++ .../client/util/OriginalRetainingCyclingList.java | 42 ++++++++++++++++++---- 2 files changed, 42 insertions(+), 7 deletions(-) (limited to 'runtime/src/main/java/me/shedaniel/rei/impl/client') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java index 58e745e34..874e94087 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java @@ -84,6 +84,7 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.Consumer; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -271,6 +272,12 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { return this; } + @Override + public Slot withEntriesListener(Consumer listener) { + this.getCyclingEntries().addListener($ -> listener.accept(this)); + return this; + } + public Slot entries(CyclingList> stacks) { this.getCyclingEntries().setBacking(stacks); if (removeTagMatch) tagMatch = null; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/util/OriginalRetainingCyclingList.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/util/OriginalRetainingCyclingList.java index aeb557d45..50627c2a1 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/util/OriginalRetainingCyclingList.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/util/OriginalRetainingCyclingList.java @@ -26,16 +26,15 @@ package me.shedaniel.rei.impl.client.util; import com.google.common.collect.Iterables; import org.jetbrains.annotations.Nullable; -import java.util.AbstractList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; +import java.util.*; +import java.util.function.Consumer; import java.util.function.Supplier; public class OriginalRetainingCyclingList implements CyclingList.Mutable { private final Supplier empty; @Nullable private CyclingList backing = null; + private List>> listeners = List.of(); public OriginalRetainingCyclingList(Supplier empty) { this.empty = empty; @@ -56,7 +55,9 @@ public class OriginalRetainingCyclingList implements CyclingList.Mutable { @Override public T previous() { if (this.backing == null) return empty.get(); - return this.backing.previous(); + T previous = this.backing.previous(); + notifyListeners(); + return previous; } @Override @@ -72,7 +73,9 @@ public class OriginalRetainingCyclingList implements CyclingList.Mutable { @Override public T next() { if (this.backing == null) return empty.get(); - return this.backing.next(); + T next = this.backing.next(); + notifyListeners(); + return next; } @Override @@ -86,11 +89,16 @@ public class OriginalRetainingCyclingList implements CyclingList.Mutable { mutable.add(entry); this.backing = mutable; } + + notifyListeners(); } @Override public void resetToStart() { - if (this.backing != null) this.backing.resetToStart(); + if (this.backing != null) { + this.backing.resetToStart(); + notifyListeners(); + }; } @Override @@ -118,6 +126,8 @@ public class OriginalRetainingCyclingList implements CyclingList.Mutable { mutable.addAll(entries); this.backing = mutable; } + + notifyListeners(); } } @@ -128,10 +138,13 @@ public class OriginalRetainingCyclingList implements CyclingList.Mutable { } else { this.backing = null; } + + notifyListeners(); } public void setBacking(@Nullable CyclingList backing) { this.backing = backing; + notifyListeners(); } private static AbstractList getListFromCollection(Collection entries) { @@ -173,4 +186,19 @@ public class OriginalRetainingCyclingList implements CyclingList.Mutable { if (this.backing == null) return CyclingList.of(this.empty); return this.backing; } + + public void addListener(Consumer> listener) { + if (this.listeners instanceof ArrayList>> list) { + list.add(listener); + } else { + this.listeners = new ArrayList<>(this.listeners); + this.listeners.add(listener); + } + } + + private void notifyListeners() { + for (Consumer> listener : this.listeners) { + listener.accept(this); + } + } } -- cgit