Skip to content

Commit

Permalink
New Display Registry Analysis in REI config, with options to sort the…
Browse files Browse the repository at this point in the history
… entries
  • Loading branch information
shedaniel committed Sep 17, 2024
1 parent 1de901a commit bfe5865
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ static <T> OptionGroup make(String id) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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;
Expand Down Expand Up @@ -246,6 +247,9 @@ static <T> CompositeOption<T> make(String id, Function<ConfigObjectImpl, T> bind
.enabledDisabled();
CompositeOption<Boolean> ENTRY_LIST_PERFORMANCE = make("debug.entry_list_performance", i -> i.advanced.layout.debugRenderTimeRequired, (i, v) -> i.advanced.layout.debugRenderTimeRequired = v)
.enabledDisabled();
CompositeOption<Object> 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<Object> RELOAD_PLUGINS = make("reset.reload_plugins", i -> null, (i, v) -> new Object())
.reload((access, option, onClose) -> {
RoughlyEnoughItemsCoreClient.reloadPlugins(null, null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* 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.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
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(new TranslatableComponent("text.rei.display_registry_analysis"));
this.onClose = onClose;
}

private ListWidget list;
private SortType sortType = SortType.ID;

@Override
public void init() {
{
Component backText = new TextComponent("↩ ").append(new TranslatableComponent("gui.back"));
addRenderableWidget(new Button(4, 4, Minecraft.getInstance().font.width(backText) + 10, 20, backText, button -> {
this.onClose.run();
this.onClose = null;
}));
}
{
Component text = new TranslatableComponent("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(new TranslatableComponent("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(new TextComponent("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<Map.Entry<CategoryIdentifier<?>, List<Display>>> sort(Stream<Map.Entry<CategoryIdentifier<?>, List<Display>>> stream) {
return switch (sortType) {
case COUNT -> stream.sorted(Comparator.<Map.Entry<CategoryIdentifier<?>, List<Display>>>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<ListEntry> {
}

private class ListWidget extends DynamicElementListWidget<ListEntry> {
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(new TextComponent(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 = new TranslatableComponent("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<? extends GuiEventListener> children() {
return Collections.emptyList();
}

@Override
public List<? extends NarratableEntry> narratables() {
return Collections.emptyList();
}
}

private enum SortType {
COUNT,
ID
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,21 @@
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.network.chat.TextComponent;
Expand All @@ -50,7 +54,7 @@
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) {
Expand All @@ -59,6 +63,7 @@ public PerformanceScreen(Runnable onClose) {
}

private PerformanceEntryListWidget list;
private SortType sortType = SortType.ORDER;

/*
* Copyright (C) 2008 The Guava Authors
Expand Down Expand Up @@ -144,8 +149,22 @@ public void init() {
this.onClose = null;
}));
}
{
Component text = new TranslatableComponent("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(new TranslatableComponent("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<SubCategoryListEntry> subCategories = new ArrayList<>();
RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.getStages().forEach((stage, inner) -> {
List<PerformanceEntryImpl> entries = new ArrayList<>();
inner.times().forEach((obj, time) -> {
Expand All @@ -160,9 +179,15 @@ public void init() {
entries.add(new PerformanceEntryImpl(new TextComponent("Miscellaneous Operations"), inner.totalNano() - separateTime));
}
totalTime[0] += Math.max(inner.totalNano(), separateTime);
entries.sort(Comparator.<PerformanceEntryImpl>comparingLong(value -> value.time).reversed());
list.addItem(new SubCategoryListEntry(new TextComponent(stage), (List<PerformanceScreen.PerformanceEntry>) (List<? extends PerformanceScreen.PerformanceEntry>) entries, Math.max(inner.totalNano(), separateTime), false));
if (this.sortType == SortType.DURATION) {
entries.sort(Comparator.<PerformanceEntryImpl>comparingLong(value -> value.time).reversed());
}
subCategories.add(new SubCategoryListEntry(new TextComponent(stage), (List<PerformanceScreen.PerformanceEntry>) (List<? extends PerformanceScreen.PerformanceEntry>) 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(new TextComponent("Total Load Time"), totalTime[0]));
addWidget(list);
}
Expand Down Expand Up @@ -214,4 +239,9 @@ protected int getScrollbarPosition() {
return width - 6;
}
}

private enum SortType {
ORDER,
DURATION
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ public List<? extends NarratableEntry> narratables() {
return Collections.emptyList();
}

public long getTotalTime() {
return totalTime;
}

public class CategoryLabelWidget implements GuiEventListener {
private final Rectangle rectangle = new Rectangle();

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit bfe5865

Please sign in to comment.