Refactor recipe profiler (#911)

This commit is contained in:
slprime authored and GitHub committed 2026-04-07 13:10:39 +03:00
1 parent d25365942a
commit 8f949bc1d1
7 files changed
+226 -92

No files matched your search

@@ -73,6 +73,7 @@ public class IMCForNEI {
API.registerRecipeHandler(new ProfilerRecipeHandler(true));
API.registerUsageHandler(new ProfilerRecipeHandler(false));
sendProfilerRecipeHandler();
}
private static void sendHandler(String aName, String aBlock) {
@@ -136,6 +137,19 @@ public class IMCForNEI {
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendProfilerRecipeHandler() {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handler", "codechicken.nei.recipe.ProfilerRecipeHandler");
aNBT.setString("modName", "Not Enough Items");
aNBT.setString("modId", "nei");
aNBT.setString("itemName", "minecraft:clock");
aNBT.setInteger("handlerHeight", 16);
aNBT.setInteger("handlerWidth", 166);
aNBT.setBoolean("multipleWidgetsAllowed", true);
aNBT.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendCatalyst(String aName, String aStack) {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handlerID", aName);
@@ -91,10 +91,6 @@ public class PositionedStack {
return null;
}
public String getCustomBadge() {
return null;
}
public int getChance() {
return this.chance;
}
@@ -22,8 +22,8 @@ public class Badge {
this.text = text;
this.tooltip = Arrays.stream(tooltip).map(t -> EnumChatFormatting.GRAY + t).collect(Collectors.toList());
this.badgeTextColor = getHexValue(
NEIClientUtils.getTextColorOrDefault("recipe.badge.color", "0xFFAA00"),
0xFFAA00);
NEIClientUtils.getTextColorOrDefault("recipe.badge.color", "0xFDD835"),
0xFDD835);
}
public String getText() {
@@ -23,12 +23,12 @@ import net.minecraftforge.common.MinecraftForge;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.Widget;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.event.NEIRegisterHandlerInfosEvent;
@@ -139,18 +139,16 @@ public abstract class GuiRecipeTab extends Widget {
public void addTooltips(List<String> tooltip) {
tooltip.add(handler.getRecipeTabName().trim());
String handlerMod = getHandlerMod(handlerName, handlerID);
tooltip.add(EnumChatFormatting.BLUE + handlerMod);
boolean shiftHeld = Keyboard.getEventKeyState()
&& (Keyboard.getEventKey() == Keyboard.KEY_LSHIFT || Keyboard.getEventKey() == Keyboard.KEY_RSHIFT);
if (handlerMod.equals("Unknown") || shiftHeld) {
if (handlerMod.equals("Unknown") || NEIClientUtils.shiftKey()) {
tooltip.add("");
tooltip.add("HandlerName: " + handlerName);
tooltip.add("HandlerID: " + handlerID);
tooltip.add("HandlerOrder: " + NEIClientConfig.getHandlerOrder(handler));
tooltip.add(EnumChatFormatting.GRAY + "HandlerName: " + handlerName);
tooltip.add(EnumChatFormatting.GRAY + "HandlerID: " + handlerID);
tooltip.add(EnumChatFormatting.GRAY + "HandlerOrder: " + NEIClientConfig.getHandlerOrder(handler));
}
tooltip.add(EnumChatFormatting.BLUE + handlerMod);
}
public abstract boolean onButtonPress(boolean rightclick);
@@ -263,12 +263,6 @@ public class NEIRecipeWidget extends Widget {
final List<Badge> badges = k.getBadges();
if (badges == null) {
final String customBadge = k.getCustomBadge();
if (customBadge != null) {
return customBadge.isEmpty() ? Collections.emptyList()
: Collections.singletonList(new Badge(customBadge));
}
if (input) {
if (StackInfo.getAmount(pStack.item) == 0) {
@@ -1,16 +1,24 @@
package codechicken.nei.recipe;
import static codechicken.lib.gui.GuiDraw.drawString;
import static codechicken.lib.gui.GuiDraw.getStringWidth;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Container;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import codechicken.core.TaskProfiler.ProfilerResult;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
@@ -20,12 +28,104 @@ import codechicken.nei.util.AsyncTaskProfiler;
public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
private static final AsyncTaskProfiler profiler = new AsyncTaskProfiler();
public static class RecipeProfiler {
private final AsyncTaskProfiler profiler = new AsyncTaskProfiler();
private final Map<String, IRecipeHandler> handlerMap = new ConcurrentHashMap<>();
private final AtomicInteger idCounter = new AtomicInteger(0);
private boolean isProfiling = false;
public <T extends IRecipeHandler> ArrayList<T> start(String profilerSection, Supplier<ArrayList<T>> callback) {
profilerInfos.clear();
this.handlerMap.clear();
this.idCounter.set(0);
if (NEIClientConfig.isProfileRecipeEnabled()) {
this.profiler.clear();
this.profiler.start(EnumChatFormatting.getTextWithoutFormattingCodes(profilerSection));
this.isProfiling = true;
try {
return callback.get();
} finally {
this.profiler.end();
this.isProfiling = false;
final List<ProfilerResult> results = this.profiler.getResults();
results.sort(
Comparator.comparingLong((ProfilerResult profilerResult) -> profilerResult.time)
.reversed());
for (ProfilerResult profilerResult : results) {
profilerInfos.add(new ProfilerInfo(handlerMap.get(profilerResult.name), profilerResult));
}
}
} else {
return callback.get();
}
}
public <T extends IRecipeHandler> T profile(T handler, Supplier<T> callback) {
if (this.isProfiling && !(handler instanceof ProfilerRecipeHandler)) {
final String id = String.valueOf(idCounter.incrementAndGet());
this.handlerMap.put(id, handler);
this.profiler.clearCurrent();
this.profiler.start(id);
try {
T result = callback.get();
if (result != null) {
this.handlerMap.put(id, result);
}
return result;
} finally {
this.profiler.end();
}
} else {
return callback.get();
}
}
public static AsyncTaskProfiler getProfiler() {
return profiler;
}
protected static class ProfilerInfo {
public final String modName;
public final IRecipeHandler handler;
public final List<String> tooltip = new ArrayList<>();
public final String title;
public final String time;
public ProfilerInfo(IRecipeHandler handler, ProfilerResult result) {
this.handler = handler;
this.time = (result.time < 1_000_000 ? (result.time / 1_000) + "us" : (result.time / 1_000_000) + "ms");
if (handler == null) {
this.title = result.name;
this.modName = "Unknown";
} else {
final HandlerInfo handlerInfo = GuiRecipeTab.getHandlerInfo(handler);
final String handlerName = handlerInfo != null ? handlerInfo.getHandlerName() : "Unknown";
this.title = EnumChatFormatting.getTextWithoutFormattingCodes(handler.getRecipeName());
this.modName = handlerInfo != null ? handlerInfo.getModName() : "Unknown";
this.tooltip.add(EnumChatFormatting.GRAY + "HandlerName: " + handlerName);
this.tooltip.add(EnumChatFormatting.GRAY + "HandlerID: " + handler.getOverlayIdentifier());
this.tooltip.add(EnumChatFormatting.GRAY + "HandlerOrder: " + NEIClientConfig.getHandlerOrder(handler));
}
}
}
public static final RecipeProfiler recipeProfiler = new RecipeProfiler();
protected static final List<ProfilerInfo> profilerInfos = new ArrayList<>();
private static final int WIDTH = 166;
private final boolean crafting;
public ProfilerRecipeHandler(boolean crafting) {
@@ -39,11 +139,7 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public int numRecipes() {
if (!NEIClientConfig.isProfileRecipeEnabled()) return 0;
return (int) Math.ceil(
((crafting ? GuiCraftingRecipe.craftinghandlers.size() : GuiUsageRecipe.usagehandlers.size()) - 1)
/ 6D);
return NEIClientConfig.isProfileRecipeEnabled() ? Math.max(1, profilerInfos.size()) : 0;
}
@Override
@@ -51,21 +147,32 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public void drawForeground(int recipe) {
List<ProfilerResult> results = profiler.getResults();
results.removeIf(profilerResult -> getRecipeName().equals(profilerResult.name));
if (recipe >= profilerInfos.size()) return;
results.sort((o1, o2) -> o1.time < o2.time ? 1 : -1);
final ProfilerInfo info = profilerInfos.get(recipe);
final int valueWidth = getStringWidth(info.time);
final IRecipeHandler handler = info.handler;
final boolean hovered = handler != null && handler.numRecipes() > 0
&& isHandlerTitleHovered(recipe, GuiDraw.getMousePosition());
for (int i = recipe * 6; i < results.size() && i < (recipe + 1) * 6; i++) {
ProfilerResult r = results.get(i);
int y = (i % 6) * 20 + 6;
drawString(r.name, 8, y, 0xFF808080, false);
GuiDraw.drawString(
NEIClientUtils.cropText(GuiDraw.fontRenderer, info.title, WIDTH - valueWidth - 2),
0,
3,
hovered ? 0xFFFFDD00 : 0xFF808080,
false);
GuiDraw.drawString(info.time, WIDTH - valueWidth, 3, 0xFF404040, false);
}
String s;
if (r.time < 1000000L) s = (r.time / 1000) + "us";
else s = (r.time / 1000000) + "ms";
private boolean isHandlerTitleHovered(int recipe, Point mouse) {
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
drawString(s, 156 - getStringWidth(s), y + 10, 0xFF404040, false);
if (guiContainer instanceof GuiRecipe guiRecipe) {
final Point recipePosition = guiRecipe.getRecipePosition(recipe);
return new Rectangle(recipePosition.x, recipePosition.y, WIDTH, 16)
.contains(mouse.x - guiContainer.guiLeft, mouse.y - guiContainer.guiTop);
} else {
return false;
}
}
@@ -104,6 +211,22 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public List<String> handleTooltip(GuiRecipe<?> gui, List<String> currenttip, int recipe) {
if (recipe >= profilerInfos.size()) return currenttip;
final ProfilerInfo r = profilerInfos.get(recipe);
currenttip.add(r.title + ": " + r.time);
if (r.handler != null) {
currenttip.add(EnumChatFormatting.GRAY + "Recipes: " + r.handler.numRecipes() + GuiDraw.TOOLTIP_LINESPACE);
if (NEIClientUtils.shiftKey()) {
currenttip.addAll(r.tooltip);
}
currenttip.add(EnumChatFormatting.BLUE + r.modName);
}
return currenttip;
}
@@ -119,6 +242,23 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
@Override
public boolean mouseClicked(GuiRecipe<?> gui, int button, int recipe) {
if (recipe >= profilerInfos.size()) return false;
final ProfilerInfo r = profilerInfos.get(recipe);
if (r.handler != null) {
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
if (guiContainer instanceof GuiRecipe guiRecipe) {
int index = guiRecipe.currenthandlers.indexOf(r.handler);
if (index != -1) {
guiRecipe.setRecipePage(index);
return true;
}
}
}
return false;
}
@@ -12,10 +12,8 @@ import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import codechicken.core.TaskProfiler;
import codechicken.nei.ItemList;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.util.AsyncTaskProfiler;
public class RecipeHandlerQuery<T extends IRecipeHandler> {
@@ -34,30 +32,22 @@ public class RecipeHandlerQuery<T extends IRecipeHandler> {
}
public ArrayList<T> runWithProfiling(String profilerSection) {
TaskProfiler profiler = ProfilerRecipeHandler.getProfiler();
return ProfilerRecipeHandler.recipeProfiler.start(profilerSection, () -> {
// Save the current config state here, as it may be altered
// if getRecipeHandlesParallel took so long and player accidentally changed config.
boolean profileRecipes = NEIClientConfig.isProfileRecipeEnabled();
if (profileRecipes) {
profiler.clear();
profiler.start(profilerSection);
}
try {
ArrayList<T> handlers = getRecipeHandlersParallel();
try {
ArrayList<T> handlers = getRecipeHandlersParallel();
if (error) {
if (error) {
displayRecipeLookupError();
}
return handlers;
} catch (InterruptedException | ExecutionException e) {
printLog(e);
displayRecipeLookupError();
return new ArrayList<>(0);
}
return handlers;
} catch (InterruptedException | ExecutionException e) {
printLog(e);
displayRecipeLookupError();
return new ArrayList<>(0);
} finally {
if (profileRecipes) profiler.end();
}
});
}
private ArrayList<T> getRecipeHandlersParallel() throws InterruptedException, ExecutionException {
@@ -77,40 +67,42 @@ public class RecipeHandlerQuery<T extends IRecipeHandler> {
private ArrayList<T> getSerialHandlersWithRecipes() {
return serialRecipeHandlers.stream().map(handler -> {
AsyncTaskProfiler profiler = ProfilerRecipeHandler.getProfiler();
profiler.clearCurrent();
profiler.start(handler.getRecipeName());
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
} finally {
profiler.end();
}
}).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
return serialRecipeHandlers.stream()
.map(handler -> ProfilerRecipeHandler.recipeProfiler.profile(handler, () -> {
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
}
}))
.filter(
h -> h != null && h.numRecipes() > 0
&& SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new));
}
private ArrayList<T> getHandlersWithRecipes() throws InterruptedException, ExecutionException {
return ItemList.forkJoinPool.submit(() -> recipeHandlers.parallelStream().map(handler -> {
AsyncTaskProfiler profiler = ProfilerRecipeHandler.getProfiler();
profiler.clearCurrent();
profiler.start(handler.getRecipeName());
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
} finally {
profiler.end();
}
}).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new))).get();
return ItemList.forkJoinPool.submit(
() -> recipeHandlers.parallelStream()
.map(handler -> ProfilerRecipeHandler.recipeProfiler.profile(handler, () -> {
try {
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
}
}))
.filter(
h -> h != null && h.numRecipes() > 0
&& SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new)))
.get();
}
private boolean isHidden(T handler) {