aboutsummaryrefslogtreecommitdiff
path: root/runtime-engine/plugins
diff options
context:
space:
mode:
authorshedaniel <daniel@shedaniel.me>2022-08-05 01:30:08 +0800
committershedaniel <daniel@shedaniel.me>2022-08-26 10:53:55 +0900
commit8c13c015031a0de865d2e767cd8e879754f803e2 (patch)
tree2bb6fa6578b63d1c216b863a6c4206295c044b36 /runtime-engine/plugins
parent8f5d3ef632f3d1a733c98ce5607c9fd5a0fd7567 (diff)
downloadRoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.tar.gz
RoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.tar.bz2
RoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.zip
More work
Diffstat (limited to 'runtime-engine/plugins')
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java199
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceConfigEntries.java37
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java93
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntryImpl.java71
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java177
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java153
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginReloaderImpl.java290
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/common/plugins/performance/PerformanceLogger.java56
-rw-r--r--runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/common/plugins/performance/PerformanceLoggerImpl.java99
-rw-r--r--runtime-engine/plugins/src/main/resources/META-INF/services/me.shedaniel.rei.impl.client.config.ConfigManagerInternal$SystemSetup1
10 files changed, 1176 insertions, 0 deletions
diff --git a/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java
new file mode 100644
index 000000000..bdbd7f02e
--- /dev/null
+++ b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/PerformanceScreen.java
@@ -0,0 +1,199 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.vertex.PoseStack;
+import com.mojang.datafixers.util.Pair;
+import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget;
+import me.shedaniel.rei.api.common.plugins.REIPlugin;
+import me.shedaniel.rei.api.common.plugins.REIPluginProvider;
+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.common.plugins.PluginReloaderImpl;
+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;
+import net.minecraft.network.chat.TranslatableComponent;
+import net.minecraft.util.FormattedCharSequence;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.TimeUnit.*;
+
+@Environment(EnvType.CLIENT)
+public class PerformanceScreen extends Screen {
+ private Screen parent;
+
+ public PerformanceScreen(Screen parent) {
+ super(new TranslatableComponent("text.rei.performance"));
+ this.parent = parent;
+ }
+
+ private PerformanceEntryListWidget list;
+
+ /*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ public static FormattedCharSequence formatTime(long time, boolean total) {
+ TimeUnit unit = chooseUnit(time);
+ double value = (double) time / NANOSECONDS.convert(1, unit);
+ return new TextComponent(String.format(Locale.ROOT, "%.4g", value) + " " + abbreviate(unit))
+ .withStyle(style -> style.withColor(TextColor.fromRgb(chooseColor(MILLISECONDS.convert(time, NANOSECONDS), total))))
+ .getVisualOrderText();
+ }
+
+ private static int chooseColor(long time, boolean total) {
+ if (time > (total ? 2500 : 1000)) {
+ return 0xff5555;
+ } else if (time > (total ? 700 : 300)) {
+ return 0xffa600;
+ } else if (time > (total ? 200 : 100)) {
+ return 0xfff017;
+ }
+ return 0x12ff22;
+ }
+
+ private static TimeUnit chooseUnit(long nanos) {
+ if (DAYS.convert(nanos, NANOSECONDS) > 0) {
+ return DAYS;
+ }
+ if (HOURS.convert(nanos, NANOSECONDS) > 0) {
+ return HOURS;
+ }
+ if (MINUTES.convert(nanos, NANOSECONDS) > 0) {
+ return MINUTES;
+ }
+ if (SECONDS.convert(nanos, NANOSECONDS) > 0) {
+ return SECONDS;
+ }
+ if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
+ return MILLISECONDS;
+ }
+ if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
+ return MICROSECONDS;
+ }
+ return NANOSECONDS;
+ }
+
+ private static String abbreviate(TimeUnit unit) {
+ switch (unit) {
+ case NANOSECONDS:
+ return "ns";
+ case MICROSECONDS:
+ return "\u03bcs"; // μs
+ case MILLISECONDS:
+ return "ms";
+ case SECONDS:
+ return "s";
+ case MINUTES:
+ return "min";
+ case HOURS:
+ return "h";
+ case DAYS:
+ return "d";
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @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 -> {
+ minecraft.setScreen(parent);
+ this.parent = null;
+ }));
+ }
+ list = new PerformanceEntryListWidget();
+ long[] totalTime = {0};
+ PluginReloaderImpl.PERFORMANCE_LOGGER.getStages().forEach((stage, inner) -> {
+ List<PerformanceEntryImpl> entries = new ArrayList<>();
+ inner.times().forEach((obj, time) -> {
+ entries.add(new PerformanceEntryImpl(new TextComponent(obj instanceof Pair ? ((Pair<REIPluginProvider<?>, REIPlugin<?>>) obj).getFirst().getPluginProviderName() : Objects.toString(obj)), time));
+ });
+ Collection<Long> values = inner.times().values();
+ long separateTime;
+ synchronized (inner.times()) {
+ separateTime = values.stream().collect(Collectors.summarizingLong(value -> value)).getSum();
+ }
+ if ((inner.totalNano() - separateTime) > 1000000) {
+ 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));
+ });
+ list.children().add(0, new PerformanceEntryImpl(new TextComponent("Total Load Time"), totalTime[0]));
+ addWidget(list);
+ }
+
+ @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 PerformanceEntry extends DynamicElementListWidget.ElementEntry<PerformanceEntry> {
+ }
+
+ private class PerformanceEntryListWidget extends DynamicElementListWidget<PerformanceEntry> {
+ public PerformanceEntryListWidget() {super(PerformanceScreen.this.minecraft, PerformanceScreen.this.width, PerformanceScreen.this.height, 30, PerformanceScreen.this.height, GuiComponent.BACKGROUND_LOCATION);}
+
+ @Override
+ public int getItemWidth() {
+ return width;
+ }
+
+ @Override
+ public int addItem(PerformanceEntry item) {
+ return super.addItem(item);
+ }
+
+ @Override
+ protected int getScrollbarPosition() {
+ return width - 6;
+ }
+ }
+}
diff --git a/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceConfigEntries.java b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceConfigEntries.java
new file mode 100644
index 000000000..56c47c7d9
--- /dev/null
+++ b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceConfigEntries.java
@@ -0,0 +1,37 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.entry;
+
+import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
+import me.shedaniel.rei.impl.client.config.ConfigManagerInternal;
+
+import java.util.Collection;
+import java.util.List;
+
+public class PerformanceConfigEntries implements ConfigManagerInternal.SystemSetup {
+ @Override
+ public Collection<? extends AbstractConfigListEntry<?>> collectAdvanced() {
+ return List.of(new PerformanceEntry(200));
+ }
+}
diff --git a/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java
new file mode 100644
index 000000000..8f328b0bb
--- /dev/null
+++ b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntry.java
@@ -0,0 +1,93 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.entry;
+
+import com.google.common.collect.ImmutableList;
+import com.mojang.blaze3d.platform.Window;
+import com.mojang.blaze3d.vertex.PoseStack;
+import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
+import me.shedaniel.rei.impl.client.gui.performance.PerformanceScreen;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.chat.NarratorChatListener;
+import net.minecraft.client.gui.components.AbstractWidget;
+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.TranslatableComponent;
+import net.minecraft.util.Unit;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.List;
+import java.util.Optional;
+
+@ApiStatus.Internal
+public class PerformanceEntry extends AbstractConfigListEntry<Unit> {
+ private int width;
+ private AbstractWidget buttonWidget = new Button(0, 0, 0, 20, NarratorChatListener.NO_TITLE, button -> {
+ Minecraft.getInstance().setScreen(new PerformanceScreen(Minecraft.getInstance().screen));
+ });
+ private List<AbstractWidget> children = ImmutableList.of(buttonWidget);
+
+ public PerformanceEntry(int width) {
+ super(NarratorChatListener.NO_TITLE, false);
+ this.width = width;
+ buttonWidget.setMessage(new TranslatableComponent("text.rei.performance"));
+ }
+
+ @Override
+ public Unit getValue() {
+ return Unit.INSTANCE;
+ }
+
+ @Override
+ public Optional<Unit> getDefaultValue() {
+ return Optional.of(Unit.INSTANCE);
+ }
+
+ @Override
+ public void save() {
+
+ }
+
+ @Override
+ public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
+ super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
+ Window window = Minecraft.getInstance().getWindow();
+ this.buttonWidget.active = this.isEditable();
+ this.buttonWidget.y = y;
+ this.buttonWidget.x = x + entryWidth / 2 - width / 2;
+ this.buttonWidget.setWidth(width);
+ this.buttonWidget.render(matrices, mouseX, mouseY, delta);
+ }
+
+ @Override
+ public List<? extends GuiEventListener> children() {
+ return children;
+ }
+
+ @Override
+ public List<? extends NarratableEntry> narratables() {
+ return children;
+ }
+}
diff --git a/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntryImpl.java b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntryImpl.java
new file mode 100644
index 000000000..4c08eac0c
--- /dev/null
+++ b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/PerformanceEntryImpl.java
@@ -0,0 +1,71 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.entry;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.PoseStack;
+import me.shedaniel.rei.impl.client.gui.performance.PerformanceScreen;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.Minecraft;
+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.Collections;
+import java.util.List;
+
+@Environment(EnvType.CLIENT)
+public class PerformanceEntryImpl extends PerformanceScreen.PerformanceEntry {
+ private final Component name;
+ public final long time;
+
+ public PerformanceEntryImpl(Component name, long time) {
+ this.name = name;
+ this.time = time;
+ }
+
+ 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.name.getVisualOrderText(), (float) x, (float) (y + 6), -1);
+ FormattedCharSequence timeText = PerformanceScreen.formatTime(time, false);
+ Minecraft.getInstance().font.drawShadow(matrices, timeText, (float) x + entryWidth - 6 - 4 - Minecraft.getInstance().font.width(timeText), (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();
+ }
+}
diff --git a/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java
new file mode 100644
index 000000000..029e8155a
--- /dev/null
+++ b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/client/gui/performance/entry/SubCategoryListEntry.java
@@ -0,0 +1,177 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.entry;
+
+import com.google.common.collect.Lists;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.PoseStack;
+import me.shedaniel.clothconfig2.api.Expandable;
+import me.shedaniel.math.Rectangle;
+import me.shedaniel.rei.impl.client.gui.performance.PerformanceScreen;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.components.events.GuiEventListener;
+import net.minecraft.client.gui.narration.NarratableEntry;
+import net.minecraft.client.resources.sounds.SimpleSoundInstance;
+import net.minecraft.network.chat.Component;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.sounds.SoundEvents;
+import net.minecraft.util.FormattedCharSequence;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+@Environment(EnvType.CLIENT)
+public class SubCategoryListEntry extends PerformanceScreen.PerformanceEntry implements Expandable {
+ private static final ResourceLocation CONFIG_TEX = new ResourceLocation("cloth-config2", "textures/gui/cloth_config.png");
+ private final List<PerformanceScreen.PerformanceEntry> entries;
+ private final CategoryLabelWidget widget;
+ private final List<GuiEventListener> children;
+ private final Component name;
+ private final long totalTime;
+ private boolean expanded;
+
+ public SubCategoryListEntry(Component name, List<PerformanceScreen.PerformanceEntry> entries, long totalTime, boolean defaultExpanded) {
+ this.name = name;
+ this.entries = entries;
+ this.totalTime = totalTime;
+ this.expanded = defaultExpanded;
+ this.widget = new CategoryLabelWidget();
+ this.children = Lists.newArrayList(new GuiEventListener[]{this.widget});
+ this.children.addAll(entries);
+ }
+
+ @Override
+ public boolean isExpanded() {
+ return this.expanded;
+ }
+
+ @Override
+ public void setExpanded(boolean expanded) {
+ this.expanded = expanded;
+ }
+
+ @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.setShaderTexture(0, CONFIG_TEX);
+ RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
+ this.widget.rectangle.x = x + 3;
+ this.widget.rectangle.y = y;
+ this.widget.rectangle.width = entryWidth - 6;
+ this.widget.rectangle.height = 24;
+ this.blit(matrices, x + 3, y + 5, 24, (this.widget.rectangle.contains(mouseX, mouseY) ? 18 : 0) + (this.expanded ? 9 : 0), 9, 9);
+ Minecraft.getInstance().font.drawShadow(matrices, this.name.getVisualOrderText(), (float) x + 3 + 15, (float) (y + 6), this.widget.rectangle.contains(mouseX, mouseY) ? -1638890 : -1);
+
+ for (PerformanceScreen.PerformanceEntry performanceEntry : this.entries) {
+ performanceEntry.setParent(this.getParent());
+ }
+
+ if (this.expanded) {
+ int yy = y + 24;
+
+ PerformanceScreen.PerformanceEntry entry;
+ for (Iterator<PerformanceScreen.PerformanceEntry> iterator = this.entries.iterator(); iterator.hasNext(); yy += entry.getItemHeight()) {
+ entry = iterator.next();
+ entry.render(matrices, -1, yy, x + 3 + 15, entryWidth - 15 - 3, entry.getItemHeight(), mouseX, mouseY, isHovered && this.getFocused() == entry, delta);
+ }
+ }
+ FormattedCharSequence timeText = PerformanceScreen.formatTime(totalTime, true);
+ Minecraft.getInstance().font.drawShadow(matrices, timeText, (float) x + entryWidth - 6 - 4 - Minecraft.getInstance().font.width(timeText), (float) (y + 6), -1);
+ }
+
+ @Override
+ public int getMorePossibleHeight() {
+ if (!this.expanded) {
+ return -1;
+ } else {
+ List<Integer> list = new ArrayList();
+ int i = 24;
+
+ for (PerformanceScreen.PerformanceEntry entry : this.entries) {
+ i += entry.getItemHeight();
+ if (entry.getMorePossibleHeight() >= 0) {
+ list.add(i + entry.getMorePossibleHeight());
+ }
+ }
+
+ list.add(i);
+ return list.stream().max(Integer::compare).orElse(0) - this.getItemHeight();
+ }
+ }
+
+ public Rectangle getEntryArea(int x, int y, int entryWidth, int entryHeight) {
+ this.widget.rectangle.x = x;
+ this.widget.rectangle.y = y;
+ this.widget.rectangle.width = entryWidth;
+ this.widget.rectangle.height = 24;
+ return new Rectangle(this.getParent().left, y, this.getParent().right - this.getParent().left, 20);
+ }
+
+ @Override
+ public int getItemHeight() {
+ if (!this.expanded) {
+ return 24;
+ } else {
+ int i = 24;
+
+ PerformanceScreen.PerformanceEntry entry;
+ for (Iterator<PerformanceScreen.PerformanceEntry> iterator = this.entries.iterator(); iterator.hasNext(); i += entry.getItemHeight()) {
+ entry = iterator.next();
+ }
+
+ return i;
+ }
+ }
+
+ @Override
+ public List<? extends GuiEventListener> children() {
+ return this.expanded ? this.children : Collections.singletonList(this.widget);
+ }
+
+ @Override
+ public List<? extends NarratableEntry> narratables() {
+ return Collections.emptyList();
+ }
+
+ public class CategoryLabelWidget implements GuiEventListener {
+ private final Rectangle rectangle = new Rectangle();
+
+ public CategoryLabelWidget() {
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (this.rectangle.contains(mouseX, mouseY)) {
+ SubCategoryListEntry.this.expanded = !SubCategoryListEntry.this.expanded;
+ Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+}
diff --git a/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java
new file mode 100644
index 000000000..a4af54642
--- /dev/null
+++ b/runtime-engine/plugins/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java
@@ -0,0 +1,153 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.common.plugins;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import me.shedaniel.rei.api.common.plugins.PluginManager;
+import me.shedaniel.rei.api.common.plugins.PluginView;
+import me.shedaniel.rei.api.common.plugins.REIPlugin;
+import me.shedaniel.rei.api.common.plugins.REIPluginProvider;
+import me.shedaniel.rei.api.common.registry.ReloadStage;
+import me.shedaniel.rei.api.common.registry.Reloadable;
+import me.shedaniel.rei.impl.common.InternalLogger;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.UnaryOperator;
+
+@ApiStatus.Internal
+public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager<P>, PluginView<P> {
+ private final List<Reloadable<P>> reloadables = new ArrayList<>();
+ private final Map<Class<? extends Reloadable<P>>, Reloadable<? super P>> cache = new ConcurrentHashMap<>();
+ private final Class<P> pluginClass;
+ private final UnaryOperator<PluginView<P>> view;
+ private final List<REIPluginProvider<P>> plugins = new ArrayList<>();
+ private final PluginReloaderImpl<P> reloader = new PluginReloaderImpl<>();
+
+ @SafeVarargs
+ public PluginManagerImpl(Class<P> pluginClass, UnaryOperator<PluginView<P>> view, Class<? extends Reloadable<? extends P>>... reloadables) {
+ this.pluginClass = pluginClass;
+ this.view = view;
+ for (Class<? extends Reloadable<? extends P>> reloadable : reloadables) {
+ registerReloadable(reloadable);
+ }
+ }
+
+ @Override
+ public void registerReloadable(Reloadable<? extends P> reloadable) {
+ this.reloadables.add((Reloadable<P>) reloadable);
+ InternalLogger.getInstance().info("Registered reloadable into plugin manager []: " + reloadable.getClass().getName(), pluginClass.getSimpleName());
+ }
+
+ @Override
+ public boolean isReloading() {
+ return this.reloader.isReloading();
+ }
+
+ @Override
+ public <T extends Reloadable<? super P>> T get(Class<T> reloadableClass) {
+ Reloadable<? super P> reloadable = this.cache.get(reloadableClass);
+ if (reloadable != null) return (T) reloadable;
+
+ for (Reloadable<P> r : reloadables) {
+ if (reloadableClass.isInstance(r)) {
+ this.cache.put((Class<? extends Reloadable<P>>) reloadableClass, r);
+ return (T) r;
+ }
+ }
+ throw new IllegalArgumentException("Unknown reloadable type! " + reloadableClass.getName());
+ }
+
+ @Override
+ public List<Reloadable<P>> getReloadables() {
+ return Collections.unmodifiableList(reloadables);
+ }
+
+ @Override
+ public PluginView<P> view() {
+ return view.apply(this);
+ }
+
+ @Override
+ public void registerPlugin(REIPluginProvider<? extends P> plugin) {
+ plugins.add((REIPluginProvider<P>) plugin);
+ InternalLogger.getInstance().info("Registered plugin provider %s for %s", plugin.getPluginProviderName(), PluginReloaderImpl.name(pluginClass));
+ }
+
+ @Override
+ public void pre(ReloadStage stage) {
+ this.reloader.pre(this, pluginClass, getPluginWrapped().toList(), stage);
+ }
+
+ @Override
+ public void post(ReloadStage stage) {
+ this.reloader.post(this, pluginClass, getPluginWrapped().toList(), stage);
+ }
+
+ @Override
+ public void startReload(ReloadStage stage) {
+ this.reloader.startReload(this, pluginClass, getPluginWrapped().toList(), stage);
+ }
+
+ @Override
+ public List<REIPluginProvider<P>> getPluginProviders() {