From 21595e86f2b945c28468cc838b77cd700a679e25 Mon Sep 17 00:00:00 2001 From: AlexIIL Date: Sat, 17 Feb 2024 21:07:29 +0000 Subject: [PATCH] Re-add some of fabric loader's gui classes, as some mods use them. --- .../loader/impl/gui/FabricGuiEntry.java | 85 +++ .../loader/impl/gui/FabricStatusTree.java | 614 ++++++++++++++++++ .../loader/impl/gui/PluginIconImpl.java | 5 + .../loader/impl/gui/QuiltStatusNode.java | 2 - 4 files changed, 704 insertions(+), 2 deletions(-) create mode 100644 src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricGuiEntry.java create mode 100644 src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricStatusTree.java diff --git a/src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricGuiEntry.java b/src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricGuiEntry.java new file mode 100644 index 000000000..43178be20 --- /dev/null +++ b/src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricGuiEntry.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016 FabricMC + * Copyright 2022-2023 QuiltMC + * + * 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. + */ + +package net.fabricmc.loader.impl.gui; + +import java.awt.GraphicsEnvironment; +import java.util.function.Consumer; + +import org.quiltmc.loader.api.gui.LoaderGuiException; +import org.quiltmc.loader.api.gui.QuiltLoaderGui; +import org.quiltmc.loader.api.gui.QuiltLoaderText; +import org.quiltmc.loader.impl.QuiltLoaderImpl; +import org.quiltmc.loader.impl.game.GameProvider; +import org.quiltmc.loader.impl.gui.QuiltGuiEntry; +import org.quiltmc.loader.impl.util.log.Log; +import org.quiltmc.loader.impl.util.log.LogCategory; + +import net.fabricmc.loader.impl.gui.FabricStatusTree.FabricBasicButtonType; +import net.fabricmc.loader.impl.gui.FabricStatusTree.FabricStatusTab; + +/** @deprecated Replaced by the public APIs in {@link QuiltLoaderGui} */ +@Deprecated +public class FabricGuiEntry { + + /** Opens the given {@link FabricStatusTree} in a new swing window. + * + * @throws Exception if something went wrong while opening the window. */ + public static void open(FabricStatusTree tree) throws Exception { + QuiltLoaderGui.open(tree.toQuiltWindow()); + } + + /** @param exitAfter If true then this will call {@link System#exit(int)} after showing the gui, otherwise this will + * return normally. */ + public static void displayCriticalError(Throwable exception, boolean exitAfter) { + displayError(QuiltLoaderText.translate("").toString(), exception, exitAfter); + } + + public static void displayError(String mainText, Throwable exception, boolean exitAfter) { + QuiltGuiEntry.displayError(mainText, exception, true, exitAfter); + } + + public static void displayError(String mainText, Throwable exception, Consumer treeCustomiser, boolean exitAfter) { + GameProvider provider = QuiltLoaderImpl.INSTANCE.tryGetGameProvider(); + + if ((provider == null || provider.canOpenGui()) && !GraphicsEnvironment.isHeadless()) { + FabricStatusTree tree = new FabricStatusTree("Quilt Loader " + QuiltLoaderImpl.VERSION, mainText); + FabricStatusTab crashTab = tree.addTab(QuiltLoaderText.translate("tab.messages").toString()); + + if (exception == null) { + exception = new Error("Missing exception!"); + } + crashTab.node.addCleanedException(exception); + + tree.addButton(QuiltLoaderText.translate("button.exit").toString(), FabricBasicButtonType.CLICK_ONCE).makeClose(); + treeCustomiser.accept(tree); + try { + QuiltLoaderGui.open(tree.toQuiltWindow()); + } catch (LoaderGuiException e) { + if (exitAfter) { + Log.warn(LogCategory.GENERAL, "Failed to open the error gui!", e); + } else { + throw new RuntimeException("Failed to open the error gui!", e); + } + } + } + + if (exitAfter) { + System.exit(1); + } + } +} diff --git a/src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricStatusTree.java b/src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricStatusTree.java new file mode 100644 index 000000000..0d521bc2f --- /dev/null +++ b/src/fabric/impl/java/net/fabricmc/loader/impl/gui/FabricStatusTree.java @@ -0,0 +1,614 @@ +/* + * Copyright 2016 FabricMC + * Copyright 2022-2023 QuiltMC + * + * 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. + */ + +package net.fabricmc.loader.impl.gui; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Set; +import java.util.StringJoiner; +import java.util.function.UnaryOperator; + +import org.quiltmc.loader.api.gui.QuiltBasicWindow; +import org.quiltmc.loader.api.gui.QuiltDisplayedError.QuiltErrorButton; +import org.quiltmc.loader.api.gui.QuiltGuiTab; +import org.quiltmc.loader.api.gui.QuiltGuiTreeTab; +import org.quiltmc.loader.api.gui.QuiltLoaderGui; +import org.quiltmc.loader.api.gui.QuiltLoaderIcon; +import org.quiltmc.loader.api.gui.QuiltLoaderText; +import org.quiltmc.loader.api.gui.QuiltTreeNode; +import org.quiltmc.loader.api.gui.QuiltWarningLevel; +import org.quiltmc.loader.impl.FormattedException; +import org.quiltmc.loader.impl.gui.PluginIconImpl; +import org.quiltmc.loader.impl.gui.QuiltStatusNode; + +/** @deprecated Replaced by the public APIs {@link QuiltLoaderGui}, notably {@link QuiltLoaderGui#createBasicWindow()} + * and {@link QuiltLoaderGui#createTreeNode()} */ +@Deprecated +public final class FabricStatusTree { + /** @deprecated Replaced by {@link org.quiltmc.loader.api.gui.QuiltWarningLevel} */ + @Deprecated + public enum FabricTreeWarningLevel { + ERROR, + WARN, + INFO, + NONE; + + public final String lowerCaseName = name().toLowerCase(Locale.ROOT); + + public boolean isHigherThan(FabricTreeWarningLevel other) { + return ordinal() < other.ordinal(); + } + + public boolean isAtLeast(FabricTreeWarningLevel other) { + return ordinal() <= other.ordinal(); + } + + public static FabricTreeWarningLevel getHighest(FabricTreeWarningLevel a, FabricTreeWarningLevel b) { + return a.isHigherThan(b) ? a : b; + } + } + + @Deprecated + public enum FabricBasicButtonType { + /** Sends the status message to the main application, then disables itself. */ + CLICK_ONCE, + /** Sends the status message to the main application, remains enabled. */ + CLICK_MANY; + } + + /** No icon is displayed. */ + public static final String ICON_TYPE_DEFAULT = ""; + /** Generic folder. */ + public static final String ICON_TYPE_FOLDER = "folder"; + /** Generic (unknown contents) file. */ + public static final String ICON_TYPE_UNKNOWN_FILE = "file"; + /** Generic non-Fabric jar file. */ + public static final String ICON_TYPE_JAR_FILE = "jar"; + /** Generic Fabric-related jar file. */ + public static final String ICON_TYPE_FABRIC_JAR_FILE = "jar+fabric"; + /** Something related to Fabric (It's not defined what exactly this is for, but it uses the main Fabric logo). */ + public static final String ICON_TYPE_FABRIC = "fabric"; + /** Generic JSON file. */ + public static final String ICON_TYPE_JSON = "json"; + /** A file called "fabric.mod.json". */ + public static final String ICON_TYPE_FABRIC_JSON = "json+fabric"; + /** Java bytecode class file. */ + public static final String ICON_TYPE_JAVA_CLASS = "java_class"; + /** A folder inside of a Java JAR. */ + public static final String ICON_TYPE_PACKAGE = "package"; + /** A folder that contains Java class files. */ + public static final String ICON_TYPE_JAVA_PACKAGE = "java_package"; + /** A tick symbol, used to indicate that something matched. */ + public static final String ICON_TYPE_TICK = "tick"; + /** A cross symbol, used to indicate that something didn't match (although it's not an error). Used as the opposite + * of {@link #ICON_TYPE_TICK} */ + public static final String ICON_TYPE_LESSER_CROSS = "lesser_cross"; + + public final String title; + public final String mainText; + public final List tabs = new ArrayList<>(); + public final List buttons = new ArrayList<>(); + + public FabricStatusTree(String title, String mainText) { + Objects.requireNonNull(title, "null title"); + Objects.requireNonNull(mainText, "null mainText"); + + this.title = title; + this.mainText = mainText; + } + + public FabricStatusTree(DataInputStream is) throws IOException { + title = is.readUTF(); + mainText = is.readUTF(); + + for (int i = is.readInt(); i > 0; i--) { + tabs.add(new FabricStatusTab(is)); + } + + for (int i = is.readInt(); i > 0; i--) { + buttons.add(new FabricStatusButton(is)); + } + } + + public void writeTo(DataOutputStream os) throws IOException { + os.writeUTF(title); + os.writeUTF(mainText); + os.writeInt(tabs.size()); + + for (FabricStatusTab tab : tabs) { + tab.writeTo(os); + } + + os.writeInt(buttons.size()); + + for (FabricStatusButton button : buttons) { + button.writeTo(os); + } + } + + public QuiltBasicWindow toQuiltWindow() { + QuiltBasicWindow window = QuiltLoaderGui.createBasicWindow(); + window.title(QuiltLoaderText.of(title)); + window.mainText(QuiltLoaderText.of(mainText)); + for (FabricStatusTab ftab : this.tabs) { + window.addTreeTab(QuiltLoaderText.of(ftab.node.name), ftab.node.toQuilt(null)); + } + for (FabricStatusButton button : this.buttons) { + button.toQuilt(window); + } + return window; + } + + public FabricStatusTab addTab(String name) { + FabricStatusTab tab = new FabricStatusTab(name); + tabs.add(tab); + return tab; + } + + public FabricStatusButton addButton(String text, FabricBasicButtonType type) { + FabricStatusButton button = new FabricStatusButton(text, type); + buttons.add(button); + return button; + } + + /** @deprecated Replaced by {@link QuiltErrorButton} */ + @Deprecated + public static final class FabricStatusButton { + public final String text; + public final FabricBasicButtonType type; + public String clipboard; + public boolean shouldClose, shouldContinue; + + public FabricStatusButton(String text, FabricBasicButtonType type) { + Objects.requireNonNull(text, "null text"); + + this.text = text; + this.type = type; + } + + public FabricStatusButton(DataInputStream is) throws IOException { + text = is.readUTF(); + type = FabricBasicButtonType.valueOf(is.readUTF()); + shouldClose = is.readBoolean(); + shouldContinue = is.readBoolean(); + + if (is.readBoolean()) clipboard = is.readUTF(); + } + + public void writeTo(DataOutputStream os) throws IOException { + os.writeUTF(text); + os.writeUTF(type.name()); + os.writeBoolean(shouldClose); + os.writeBoolean(shouldContinue); + + if (clipboard != null) { + os.writeBoolean(true); + os.writeUTF(clipboard); + } else { + os.writeBoolean(false); + } + } + + public void toQuilt(QuiltBasicWindow window) { + final QuiltErrorButton button; + if (clipboard != null) { + button = window.addCopyTextToClipboardButton(QuiltLoaderText.of(text), clipboard); + } else { + button = window.addContinueButton(); + } + + if (shouldClose) { + button.icon(QuiltLoaderGui.iconLevelError()); + button.text(QuiltLoaderText.translate("button.exit")); + } + } + + public FabricStatusButton makeClose() { + shouldClose = true; + return this; + } + + public FabricStatusButton makeContinue() { + this.shouldContinue = true; + return this; + } + + public FabricStatusButton withClipboard(String clipboard) { + this.clipboard = clipboard; + return this; + } + } + + /** @deprecated Replaced by {@link QuiltGuiTab} and (more directly) by {@link QuiltGuiTreeTab} */ + @Deprecated + public static final class FabricStatusTab { + public final FabricStatusNode node; + + /** The minimum warning level to display for this tab. */ + public FabricTreeWarningLevel filterLevel = FabricTreeWarningLevel.NONE; + + public FabricStatusTab(String name) { + this.node = new FabricStatusNode(null, name); + } + + public FabricStatusTab(DataInputStream is) throws IOException { + node = new FabricStatusNode(null, is); + filterLevel = FabricTreeWarningLevel.valueOf(is.readUTF()); + } + + public void writeTo(DataOutputStream os) throws IOException { + node.writeTo(os); + os.writeUTF(filterLevel.name()); + } + + public FabricStatusNode addChild(String name) { + return node.addChild(name); + } + } + + /** @deprecated Replaced by {@link QuiltTreeNode} */ + @Deprecated + public static final class FabricStatusNode { + private FabricStatusNode parent; + public String name; + /** The icon type. There can be a maximum of 2 decorations (added with "+" symbols), or 3 if the + * {@link #setWarningLevel(FabricTreeWarningLevel) warning level} is set to + * {@link FabricTreeWarningLevel#NONE } */ + public String iconType = ICON_TYPE_DEFAULT; + private FabricTreeWarningLevel warningLevel = FabricTreeWarningLevel.NONE; + public boolean expandByDefault = false; + /** Extra text for more information. Lines should be separated by "\n". */ + public String details; + public final List children = new ArrayList<>(); + + private FabricStatusNode(FabricStatusNode parent, String name) { + Objects.requireNonNull(name, "null name"); + + this.parent = parent; + this.name = name; + } + + public FabricStatusNode(FabricStatusNode parent, DataInputStream is) throws IOException { + this.parent = parent; + + name = is.readUTF(); + iconType = is.readUTF(); + warningLevel = FabricTreeWarningLevel.valueOf(is.readUTF()); + expandByDefault = is.readBoolean(); + if (is.readBoolean()) details = is.readUTF(); + + for (int i = is.readInt(); i > 0; i--) { + children.add(new FabricStatusNode(this, is)); + } + } + + public void writeTo(DataOutputStream os) throws IOException { + os.writeUTF(name); + os.writeUTF(iconType); + os.writeUTF(warningLevel.name()); + os.writeBoolean(expandByDefault); + os.writeBoolean(details != null); + if (details != null) os.writeUTF(details); + os.writeInt(children.size()); + + for (FabricStatusNode child : children) { + child.writeTo(os); + } + } + + public QuiltTreeNode toQuilt(QuiltTreeNode qParent) { + QuiltTreeNode node = qParent == null ? QuiltLoaderGui.createTreeNode() : qParent.addChild(); + node.text(QuiltLoaderText.of(name)); + final QuiltWarningLevel level; + switch (warningLevel) { + case NONE: + level = QuiltWarningLevel.NONE; + break; + case INFO: + level = QuiltWarningLevel.INFO; + break; + case WARN: + level = QuiltWarningLevel.WARN; + break; + case ERROR: + level = QuiltWarningLevel.ERROR; + break; + default: + level = QuiltWarningLevel.CONCERN; + break; + } + node.level(level); + // Details? + ((QuiltStatusNode) node).setExpandByDefault(expandByDefault); + + String[] splitIcon = iconType.split("\\+"); + if (splitIcon.length == 1 && splitIcon[0].isEmpty()) { + splitIcon = new String[0]; + } + + if (splitIcon.length > 0) { + QuiltLoaderIcon icon = PluginIconImpl.deprecatedForFabric(splitIcon[0]); + + for (int i = 1; i < splitIcon.length && i < 3; i++) { + icon = icon.withDecoration(PluginIconImpl.deprecatedForFabric(splitIcon[i])); + } + + node.icon(icon); + } + + for (FabricStatusNode child : children) { + child.toQuilt(node); + } + + return node; + } + + public void moveTo(FabricStatusNode newParent) { + parent.children.remove(this); + this.parent = newParent; + newParent.children.add(this); + } + + public FabricTreeWarningLevel getMaximumWarningLevel() { + return warningLevel; + } + + public void setWarningLevel(FabricTreeWarningLevel level) { + if (this.warningLevel == level) { + return; + } + + if (warningLevel.isHigherThan(level)) { + // Just because I haven't written the back-fill revalidation for this + throw new Error("Why would you set the warning level multiple times?"); + } else { + if (parent != null && level.isHigherThan(parent.warningLevel)) { + parent.setWarningLevel(level); + } + + this.warningLevel = level; + expandByDefault |= level.isAtLeast(FabricTreeWarningLevel.WARN); + } + } + + public void setError() { + setWarningLevel(FabricTreeWarningLevel.ERROR); + } + + public void setWarning() { + setWarningLevel(FabricTreeWarningLevel.WARN); + } + + public void setInfo() { + setWarningLevel(FabricTreeWarningLevel.INFO); + } + + private FabricStatusNode addChild(String string) { + if (string.startsWith("\t")) { + if (children.size() == 0) { + FabricStatusNode rootChild = new FabricStatusNode(this, ""); + children.add(rootChild); + } + + FabricStatusNode lastChild = children.get(children.size() - 1); + lastChild.addChild(string.substring(1)); + lastChild.expandByDefault = true; + return lastChild; + } else { + FabricStatusNode child = new FabricStatusNode(this, cleanForNode(string)); + children.add(child); + return child; + } + } + + private String cleanForNode(String string) { + string = string.trim(); + + if (string.length() > 1) { + if (string.startsWith("-")) { + string = string.substring(1); + string = string.trim(); + } + } + + return string; + } + + public FabricStatusNode addMessage(String message, FabricTreeWarningLevel warningLevel) { + String[] lines = message.split("\n"); + + FabricStatusNode sub = new FabricStatusNode(this, lines[0]); + children.add(sub); + sub.setWarningLevel(warningLevel); + + for (int i = 1; i < lines.length; i++) { + sub.addChild(lines[i]); + } + + return sub; + } + + public FabricStatusNode addException(Throwable exception) { + return addException(this, Collections.newSetFromMap(new IdentityHashMap<>()), exception, UnaryOperator.identity(), new StackTraceElement[0]); + } + + public FabricStatusNode addCleanedException(Throwable exception) { + return addException(this, Collections.newSetFromMap(new IdentityHashMap<>()), exception, e -> { + // Remove some self-repeating exception traces from the tree + // (for example the RuntimeException that is is created unnecessarily by ForkJoinTask) + Throwable cause; + + while ((cause = e.getCause()) != null) { + if (e.getSuppressed().length > 0) { + break; + } + + String msg = e.getMessage(); + + if (msg == null) { + msg = e.getClass().getName(); + } + + if (!msg.equals(cause.getMessage()) && !msg.equals(cause.toString())) { + break; + } + + e = cause; + } + + return e; + }, new StackTraceElement[0]); + } + + private static FabricStatusNode addException(FabricStatusNode node, Set seen, Throwable exception, UnaryOperator filter, StackTraceElement[] parentTrace) { + if (!seen.add(exception)) { + return node; + } + + exception = filter.apply(exception); + FabricStatusNode sub = node.addException(exception, parentTrace); + StackTraceElement[] trace = exception.getStackTrace(); + + for (Throwable t : exception.getSuppressed()) { + FabricStatusNode suppressed = addException(sub, seen, t, filter, trace); + suppressed.name += " (suppressed)"; + suppressed.expandByDefault = false; + } + + if (exception.getCause() != null) { + addException(sub, seen, exception.getCause(), filter, trace); + } + + return sub; + } + + private FabricStatusNode addException(Throwable exception, StackTraceElement[] parentTrace) { + boolean showTrace = !(exception instanceof FormattedException) || exception.getCause() != null; + String msg; + + if (exception instanceof FormattedException) { + msg = Objects.toString(exception.getMessage()); + } else if (exception.getMessage() == null || exception.getMessage().isEmpty()) { + msg = exception.toString(); + } else { + msg = String.format("%s: %s", exception.getClass().getSimpleName(), exception.getMessage()); + } + + FabricStatusNode sub = addMessage(msg, FabricTreeWarningLevel.ERROR); + + if (!showTrace) return sub; + + StackTraceElement[] trace = exception.getStackTrace(); + int uniqueFrames = trace.length - 1; + + for (int i = parentTrace.length - 1; uniqueFrames >= 0 && i >= 0 && trace[uniqueFrames].equals(parentTrace[i]); i--) { + uniqueFrames--; + } + + StringJoiner frames = new StringJoiner("\n"); + int inheritedFrames = trace.length - 1 - uniqueFrames; + + for (int i = 0; i <= uniqueFrames; i++) { + frames.add("at " + trace[i]); + } + + if (inheritedFrames > 0) { + frames.add("... " + inheritedFrames + " more"); + } + + sub.addChild(frames.toString()).iconType = ICON_TYPE_JAVA_CLASS; + + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + sub.details = sw.toString(); + + return sub; + } + + /** If this node has one child then it merges the child node into this one. */ + public void mergeWithSingleChild(String join) { + if (children.size() != 1) { + return; + } + + FabricStatusNode child = children.remove(0); + name += join + child.name; + + for (FabricStatusNode cc : child.children) { + cc.parent = this; + children.add(cc); + } + + child.children.clear(); + } + + public void mergeSingleChildFilePath(String folderType) { + if (!iconType.equals(folderType)) { + return; + } + + while (children.size() == 1 && children.get(0).iconType.equals(folderType)) { + mergeWithSingleChild("/"); + } + + children.sort((a, b) -> a.name.compareTo(b.name)); + mergeChildFilePaths(folderType); + } + + public void mergeChildFilePaths(String folderType) { + for (FabricStatusNode node : children) { + node.mergeSingleChildFilePath(folderType); + } + } + + public FabricStatusNode getFileNode(String file, String folderType, String fileType) { + FabricStatusNode fileNode = this; + + pathIteration: for (String s : file.split("/")) { + if (s.isEmpty()) { + continue; + } + + for (FabricStatusNode c : fileNode.children) { + if (c.name.equals(s)) { + fileNode = c; + continue pathIteration; + } + } + + if (fileNode.iconType.equals(FabricStatusTree.ICON_TYPE_DEFAULT)) { + fileNode.iconType = folderType; + } + + fileNode = fileNode.addChild(s); + } + + fileNode.iconType = fileType; + return fileNode; + } + } +} diff --git a/src/main/java/org/quiltmc/loader/impl/gui/PluginIconImpl.java b/src/main/java/org/quiltmc/loader/impl/gui/PluginIconImpl.java index 5bd39be12..222c98bb8 100644 --- a/src/main/java/org/quiltmc/loader/impl/gui/PluginIconImpl.java +++ b/src/main/java/org/quiltmc/loader/impl/gui/PluginIconImpl.java @@ -98,6 +98,11 @@ String syncType() { return "icon"; } + @Deprecated + public static QuiltLoaderIcon deprecatedForFabric(String path) { + return new PluginIconImpl(path); + } + public static PluginIconImpl fromApi(QuiltLoaderIcon icon) { if (icon instanceof PluginIconImpl) { return (PluginIconImpl) icon; diff --git a/src/main/java/org/quiltmc/loader/impl/gui/QuiltStatusNode.java b/src/main/java/org/quiltmc/loader/impl/gui/QuiltStatusNode.java index fbd5f4efa..0d9ae8a57 100644 --- a/src/main/java/org/quiltmc/loader/impl/gui/QuiltStatusNode.java +++ b/src/main/java/org/quiltmc/loader/impl/gui/QuiltStatusNode.java @@ -24,8 +24,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.jetbrains.annotations.Nullable; import org.quiltmc.loader.api.LoaderValue;