From 4c60b452efda24794c0faa5f300fdd830edcd383 Mon Sep 17 00:00:00 2001 From: nextdaydelivery <79922345+nxtdaydelivery@users.noreply.github.com> Date: Sat, 3 Sep 2022 12:50:40 +0100 Subject: deprecator + Stack trace utilities (#121) * deprecator * check for more system packages because iterators n stuff * add LogScanner, new loader platform features, spam protection * oop~ * move to Preferences * diamond review * add back the exception * try-catch things and associate OneConfig mods with an ActiveMod * ok maybe not + api my beloved --- api/OneConfig.api | 13 +- .../cc/polyfrost/oneconfig/config/data/Mod.java | 7 +- .../oneconfig/internal/config/Preferences.java | 25 ++- .../oneconfig/internal/utils/Deprecator.java | 64 ++++++ .../oneconfig/platform/LoaderPlatform.java | 22 ++- .../cc/polyfrost/oneconfig/platform/Platform.java | 5 +- .../cc/polyfrost/oneconfig/utils/LogScanner.java | 219 +++++++++++++++++++++ .../cc/polyfrost/oneconfig/utils/TextUtils.java | 3 + versions/1.12.2-forge/api/1.12.2-forge.api | 3 + versions/1.16.2-fabric/api/1.16.2-fabric.api | 3 + versions/1.16.2-forge/api/1.16.2-forge.api | 3 + versions/1.8.9-forge/api/1.8.9-forge.api | 3 + .../platform/impl/LoaderPlatformImpl.java | 73 +++++-- .../oneconfig/platform/impl/PlatformImpl.java | 16 ++ 14 files changed, 426 insertions(+), 33 deletions(-) create mode 100644 src/main/java/cc/polyfrost/oneconfig/internal/utils/Deprecator.java create mode 100644 src/main/java/cc/polyfrost/oneconfig/utils/LogScanner.java diff --git a/api/OneConfig.api b/api/OneConfig.api index f99cbdc..aafb10b 100644 --- a/api/OneConfig.api +++ b/api/OneConfig.api @@ -1081,15 +1081,18 @@ public abstract interface class cc/polyfrost/oneconfig/platform/I18nPlatform { public abstract interface class cc/polyfrost/oneconfig/platform/LoaderPlatform { public abstract fun getActiveModContainer ()Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; + public abstract fun getLoadedMods ()Ljava/util/List; public abstract fun hasActiveModContainer ()Z public abstract fun isModLoaded (Ljava/lang/String;)Z + public abstract fun toActiveMod (Ljava/lang/Object;)Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; } public class cc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod { public final field id Ljava/lang/String; public final field name Ljava/lang/String; + public final field source Ljava/io/File; public final field version Ljava/lang/String; - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V } public abstract interface class cc/polyfrost/oneconfig/platform/MousePlatform { @@ -1136,6 +1139,7 @@ public abstract interface class cc/polyfrost/oneconfig/platform/Platform { public static fun getNanoVGPlatform ()Lcc/polyfrost/oneconfig/platform/NanoVGPlatform; public static fun getServerPlatform ()Lcc/polyfrost/oneconfig/platform/ServerPlatform; public abstract fun isCallingFromMinecraftThread ()Z + public abstract fun isDevelopmentEnvironment ()Z } public final class cc/polyfrost/oneconfig/platform/Platform$Loader : java/lang/Enum { @@ -1351,6 +1355,13 @@ public final class cc/polyfrost/oneconfig/utils/JsonUtils { public static fun parseString (Ljava/lang/String;Z)Lcom/google/gson/JsonElement; } +public class cc/polyfrost/oneconfig/utils/LogScanner { + public fun ()V + public static fun identifyCallerFromStacktrace (Ljava/lang/Throwable;)Ljava/util/Set; + public static fun identifyFromClass (Ljava/lang/String;)Ljava/util/Set; + public static fun identifyFromStacktrace (Ljava/lang/Throwable;)Ljava/util/Set; +} + public final class cc/polyfrost/oneconfig/utils/MathUtils { public fun ()V public static fun clamp (F)F diff --git a/src/main/java/cc/polyfrost/oneconfig/config/data/Mod.java b/src/main/java/cc/polyfrost/oneconfig/config/data/Mod.java index 61517ab..f77639f 100644 --- a/src/main/java/cc/polyfrost/oneconfig/config/data/Mod.java +++ b/src/main/java/cc/polyfrost/oneconfig/config/data/Mod.java @@ -29,15 +29,18 @@ package cc.polyfrost.oneconfig.config.data; import cc.polyfrost.oneconfig.config.Config; import cc.polyfrost.oneconfig.config.elements.OptionPage; import cc.polyfrost.oneconfig.config.migration.Migrator; -import cc.polyfrost.oneconfig.internal.config.OneConfigConfig; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class Mod implements Comparable { + @NotNull public final String name; public final ModType modType; + @Nullable public final String modIcon; + @Nullable public final Migrator migrator; + @NotNull public final OptionPage defaultPage; public Config config; @@ -47,7 +50,7 @@ public class Mod implements Comparable { * @param modIcon Path to icon of the mod (png or svg format) * @param migrator Migrator class to port the old config */ - public Mod(String name, ModType modType, @Nullable String modIcon, @Nullable Migrator migrator) { + public Mod(@NotNull String name, ModType modType, @Nullable String modIcon, @Nullable Migrator migrator) { this.name = name; this.modType = modType; this.modIcon = modIcon; diff --git a/src/main/java/cc/polyfrost/oneconfig/internal/config/Preferences.java b/src/main/java/cc/polyfrost/oneconfig/internal/config/Preferences.java index bc0e3a9..874abd2 100644 --- a/src/main/java/cc/polyfrost/oneconfig/internal/config/Preferences.java +++ b/src/main/java/cc/polyfrost/oneconfig/internal/config/Preferences.java @@ -35,10 +35,17 @@ import cc.polyfrost.oneconfig.platform.Platform; import cc.polyfrost.oneconfig.utils.TickDelay; public class Preferences extends InternalConfig { + + @Dropdown( + name = "Release Channel", + options = {"Releases", "Pre-Releases"} + ) + public static int updateChannel = 0; + @Switch( - name = "Enable Blur" + name = "Debug Mode" ) - public static boolean enableBlur = true; + public static boolean DEBUG = false; @KeyBind( name = "OneConfig Keybind", @@ -46,23 +53,21 @@ public class Preferences extends InternalConfig { ) public static OneKeyBind oneConfigKeyBind = new OneKeyBind(UKeyboard.KEY_RSHIFT); - @Dropdown( - name = "Release Channel", - options = {"Releases", "Pre-Releases"}, - size = 2 + @Switch( + name = "Enable Blur", + subcategory = "GUI Settings" ) - public static int updateChannel = 0; + public static boolean enableBlur = true; @Switch( name = "Use custom GUI scale", - subcategory = "GUI Scale", - size = 2 + subcategory = "GUI Settings" ) public static boolean enableCustomScale = false; @Slider( name = "Custom GUI scale", - subcategory = "GUI Scale", + subcategory = "GUI Settings", min = 0.5f, max = 5f ) diff --git a/src/main/java/cc/polyfrost/oneconfig/internal/utils/Deprecator.java b/src/main/java/cc/polyfrost/oneconfig/internal/utils/Deprecator.java new file mode 100644 index 0000000..e8d048c --- /dev/null +++ b/src/main/java/cc/polyfrost/oneconfig/internal/utils/Deprecator.java @@ -0,0 +1,64 @@ +/* + * This file is part of OneConfig. + * OneConfig - Next Generation Config Library for Minecraft: Java Edition + * Copyright (C) 2021, 2022 Polyfrost. + * + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * OneConfig is licensed under the terms of version 3 of the GNU Lesser + * General Public License as published by the Free Software Foundation, AND + * under the Additional Terms Applicable to OneConfig, as published by Polyfrost, + * either version 1.0 of the Additional Terms, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License. If not, see . You should + * have also received a copy of the Additional Terms Applicable + * to OneConfig, as published by Polyfrost. If not, see + * + */ + +package cc.polyfrost.oneconfig.internal.utils; + +import cc.polyfrost.oneconfig.utils.LogScanner; +import cc.polyfrost.oneconfig.utils.Notifications; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class used by OneConfig for deprecation related utilities. + */ +public final class Deprecator { + private static final List warned = new ArrayList<>(); + + /** + * mark a method as deprecated. When a method has this call, it will + * throw a new exception to grab the name (or package) of the mod that called said method.
+ * This will then send a notification detailing this to the user, and throw an UnsupportedOperationException to print a stack to the log. + */ + public static void markDeprecated() { + try { + throw new Exception("This method is deprecated"); + } catch (Exception e) { + String culprit = LogScanner.identifyCallerFromStacktrace(e).stream().map(activeMod -> activeMod.name).findFirst().orElse("Unknown"); + // sometimes it blames OneConfig as well so + if(culprit.equals("OneConfig")) return; + if (!warned.contains(culprit)) { + warned.add(culprit); + Notifications.INSTANCE.send("Deprecation Warning", "The mod '" + culprit + "' is using a deprecated method, and will no longer work in the future. Please report this to the mod author."); + try { + throw new UnsupportedOperationException("Method " + e.getStackTrace()[1].getClassName() + "." + e.getStackTrace()[1].getMethodName() + "() is deprecated; but is still being used by mod " + culprit + "!"); + } catch (UnsupportedOperationException e1) { + e1.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/cc/polyfrost/oneconfig/platform/LoaderPlatform.java b/src/main/java/cc/polyfrost/oneconfig/platform/LoaderPlatform.java index b271602..a9fc24e 100644 --- a/src/main/java/cc/polyfrost/oneconfig/platform/LoaderPlatform.java +++ b/src/main/java/cc/polyfrost/oneconfig/platform/LoaderPlatform.java @@ -26,20 +26,38 @@ package cc.polyfrost.oneconfig.platform; +import cc.polyfrost.oneconfig.libs.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + public interface LoaderPlatform { boolean isModLoaded(String id); + boolean hasActiveModContainer(); - ActiveMod getActiveModContainer(); + + @Nullable ActiveMod getActiveModContainer(); + + @Nullable ActiveMod toActiveMod(@Nullable Object in); + + /** + * Note: the list may contain null elements + */ + @NotNull + List getLoadedMods(); class ActiveMod { public final String name; public final String id; public final String version; + public final File source; - public ActiveMod(String name, String id, String version) { + public ActiveMod(String name, String id, String version, File source) { this.name = name; this.id = id; this.version = version; + this.source = source; } } } diff --git a/src/main/java/cc/polyfrost/oneconfig/platform/Platform.java b/src/main/java/cc/polyfrost/oneconfig/platform/Platform.java index 1298214..b4cbd28 100644 --- a/src/main/java/cc/polyfrost/oneconfig/platform/Platform.java +++ b/src/main/java/cc/polyfrost/oneconfig/platform/Platform.java @@ -30,7 +30,7 @@ import java.util.ServiceLoader; /** * Contains various platform-specific utilities for OneConfig. - * + *

* This is meant for internal usage, however other mods may use these (unless otherwise stated). */ public interface Platform { @@ -71,12 +71,15 @@ public interface Platform { int getMinecraftVersion(); + boolean isDevelopmentEnvironment(); + Loader getLoader(); class PlatformHolder { private PlatformHolder() { } + static PlatformHolder INSTANCE = new PlatformHolder(); Platform platform = ServiceLoader.load(Platform.class, Platform.class.getClassLoader()).iterator().next(); MousePlatform mousePlatform = ServiceLoader.load(MousePlatform.class, MousePlatform.class.getClassLoader()).iterator().next(); diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/LogScanner.java b/src/main/java/cc/polyfrost/oneconfig/utils/LogScanner.java new file mode 100644 index 0000000..5b448a1 --- /dev/null +++ b/src/main/java/cc/polyfrost/oneconfig/utils/LogScanner.java @@ -0,0 +1,219 @@ +/* + * This file is part of OneConfig. + * OneConfig - Next Generation Config Library for Minecraft: Java Edition + * Copyright (C) 2021, 2022 Polyfrost. + * + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * OneConfig is licensed under the terms of version 3 of the GNU Lesser + * General Public License as published by the Free Software Foundation, AND + * under the Additional Terms Applicable to OneConfig, as published by Polyfrost, + * either version 1.0 of the Additional Terms, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License. If not, see . You should + * have also received a copy of the Additional Terms Applicable + * to OneConfig, as published by Polyfrost. If not, see + * + */ + +/* COPYRIGHT NOTICE: MIT License + * Copyright (c) 2021 Fudge and NEC contributors + * + * 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 cc.polyfrost.oneconfig.utils; + +import cc.polyfrost.oneconfig.internal.config.Preferences; +import cc.polyfrost.oneconfig.platform.LoaderPlatform; +import cc.polyfrost.oneconfig.platform.Platform; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * Adapted from NotEnoughCrashes under the MIT License + */ +public class LogScanner { + static final Logger LOGGER = LogManager.getLogger("OneConfig Log Scanner"); + + /** + * Return a set of ActiveMods that have been blamed for the given stacktrace. + * This will be an empty set if no mods are blamed. + */ + @NotNull + public static Set identifyFromStacktrace(Throwable e) { + Set mods = new HashSet<>(); + // Include suppressed exceptions too + visitChildrenThrowables(e, throwable -> { + for (LoaderPlatform.ActiveMod newMod : identifyFromThrowable(throwable)) { + if (mods.stream().noneMatch(mod -> mod.id.equals(newMod.id))) { + mods.add(newMod); + } + } + }); + return mods; + } + + /** + * Attempt to get the caller from a given stacktrace. + * + * @return A singleton mod set that contains the caller, or an empty set if no caller is found. + */ + @NotNull + public static Set identifyCallerFromStacktrace(Throwable e) { + // first is this method name, second is the method it called, third is what called it + StackTraceElement target = null; + int i = 0; + for (StackTraceElement element : e.getStackTrace()) { + // ignore the first two + if (i > 2) { + // remove any that are native, or called from a system package + if (!element.isNativeMethod() && !element.getClassName().startsWith("sun.") && !element.getClassName().startsWith("java.") + && !element.getClassName().startsWith("javax.") && !element.getClassName().startsWith("jdk.") && !element.getClassName().startsWith("com.sun.")) { + target = element; + break; + } + } + i++; + } + if (target == null) { + return Collections.emptySet(); + } + Set classMods = identifyFromClass(target.getClassName()); + return classMods.isEmpty() ? Collections.emptySet() : classMods; + } + + private static void visitChildrenThrowables(Throwable e, Consumer visitor) { + visitor.accept(e); + for (Throwable child : e.getSuppressed()) visitChildrenThrowables(child, visitor); + } + + private static Set identifyFromThrowable(Throwable e) { + Set involvedClasses = new LinkedHashSet<>(); + while (e != null) { + for (StackTraceElement element : e.getStackTrace()) { + involvedClasses.add(element.getClassName()); + } + e = e.getCause(); + } + + Set mods = new LinkedHashSet<>(); + for (String className : involvedClasses) { + Set classMods = identifyFromClass(className); + mods.addAll(classMods); + } + return mods; + } + + private static void debug(Supplier message) { + if (Preferences.DEBUG) LOGGER.info(message.get()); + } + + // TODO: get a list of mixin transformers that affected the class and blame those too + + /** + * Return a set of ActiveMods that have been associated with the given class. + */ + @NotNull + public static Set identifyFromClass(String className) { + List modMap = Platform.getLoaderPlatform().getLoadedMods(); + // Skip identification for Mixin, one's mod copy of the library is shared with all other mods + if (className.startsWith("org.spongepowered.asm.mixin.")) { + debug(() -> "Ignoring class " + className + " for identification because it is a mixin class"); + return Collections.emptySet(); + } + + try { + // Get the URL of the class (don't initialize classes, though) + Class clazz = Class.forName(className, false, LogScanner.class.getClassLoader()); + CodeSource codeSource = clazz.getProtectionDomain().getCodeSource(); + if (codeSource == null) { + debug(() -> "Ignoring class " + className + " for identification because the code source could not be found"); + return Collections.emptySet(); // Some internal native sun classes + } + URL url = codeSource.getLocation(); + + if (url == null) { + LOGGER.warn("Failed to identify mod for " + className); + return Collections.emptySet(); + } + + // Transform JAR URL to a file URL + if (url.toURI().toString().startsWith("jar:")) { + url = new URL(url.toURI().toString().substring(4, url.toURI().toString().lastIndexOf("!"))); + } + if (url.toURI().toString().endsWith(".class") && Platform.getInstance().isDevelopmentEnvironment()) { + LOGGER.error("The mod you are currently developing caused this issue, or another class file. Returning 'this'."); + LOGGER.error("Class: " + className); + return Collections.singleton(new LoaderPlatform.ActiveMod("this", "this", "Unknown", null)); + } + Set mods = getModsAt(Paths.get(url.toURI()), modMap); + if (!mods.isEmpty()) { + //noinspection OptionalGetWithoutIsPresent + debug(() -> "Successfully placed blame of '" + className + "' on '" + + mods.stream().findFirst().get().name + "'"); + } + return mods; + } catch (URISyntaxException | ClassNotFoundException | NoClassDefFoundError | MalformedURLException e) { + debug(() -> "Ignoring class " + className + " for identification because an error occurred"); + return Collections.emptySet(); // we cannot do it + } + } + + @NotNull + private static Set getModsAt(Path path, List modMap) { + Set mods = modMap.stream().filter(m -> m.source.toPath().equals(path)).collect(Collectors.toSet()); + if (!mods.isEmpty()) return mods; + else if (Platform.getInstance().isDevelopmentEnvironment()) { + // For some reason, in dev, the mod being tested has the 'resources' folder as the origin instead of the 'classes' folder. + String resourcesPathString = path.toString().replace("\\", "/") + // Make it work with Architectury as well + .replace("common/build/classes/java/main", "fabric/build/resources/main") + .replace("common/build/classes/kotlin/main", "fabric/build/resources/main") + .replace("classes/java/main", "resources/main") + .replace("classes/kotlin/main", "resources/main"); + Path resourcesPath = Paths.get(resourcesPathString); + return modMap.stream().filter(m -> m.source.toPath().equals(resourcesPath)).collect(Collectors.toSet()); + } else { + debug(() -> "Mod at path '" + path.toAbsolutePath() + "' is at fault," + + " but it could not be found in the map of mod paths: " /*+ modMap*/); + return Collections.emptySet(); + } + } +} diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java b/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java index bc6e9c0..72b5527 100644 --- a/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java +++ b/src/main/java/cc/polyfrost/oneconfig/utils/TextUtils.java @@ -26,6 +26,7 @@ package cc.polyfrost.oneconfig.utils; +import cc.polyfrost.oneconfig.internal.utils.Deprecator; import cc.polyfrost.oneconfig.renderer.RenderManager; import cc.polyfrost.oneconfig.renderer.font.Font; @@ -47,7 +48,9 @@ public final class TextUtils { * @param font The font to use. * @return The array of lines. */ + @Deprecated public static ArrayList wrapText(long vg, String text, float maxWidth, float fontSize, Font font) { + Deprecator.markDeprecated(); ArrayList wrappedText = new ArrayList<>(); text += " "; int prevIndex = 0; diff --git a/versions/1.12.2-forge/api/1.12.2-forge.api b/versions/1.12.2-forge/api/1.12.2-forge.api index cba3f3b..8b824c6 100644 --- a/versions/1.12.2-forge/api/1.12.2-forge.api +++ b/versions/1.12.2-forge/api/1.12.2-forge.api @@ -49,8 +49,10 @@ public class cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl : cc/polyfros public class cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl : cc/polyfrost/oneconfig/platform/LoaderPlatform { public fun ()V public fun getActiveModContainer ()Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; + public fun getLoadedMods ()Ljava/util/List; public fun hasActiveModContainer ()Z public fun isModLoaded (Ljava/lang/String;)Z + public fun toActiveMod (Ljava/lang/Object;)Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; } public class cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl : cc/polyfrost/oneconfig/platform/MousePlatform { @@ -88,6 +90,7 @@ public class cc/polyfrost/oneconfig/platform/impl/PlatformImpl : cc/polyfrost/on public fun getLoader ()Lcc/polyfrost/oneconfig/platform/Platform$Loader; public fun getMinecraftVersion ()I public fun isCallingFromMinecraftThread ()Z + public fun isDevelopmentEnvironment ()Z } public class cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl : cc/polyfrost/oneconfig/platform/ServerPlatform { diff --git a/versions/1.16.2-fabric/api/1.16.2-fabric.api b/versions/1.16.2-fabric/api/1.16.2-fabric.api index 295d7c7..24c9dab 100644 --- a/versions/1.16.2-fabric/api/1.16.2-fabric.api +++ b/versions/1.16.2-fabric/api/1.16.2-fabric.api @@ -49,8 +49,10 @@ public class cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl : cc/polyfros public class cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl : cc/polyfrost/oneconfig/platform/LoaderPlatform { public fun ()V public fun getActiveModContainer ()Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; + public fun getLoadedMods ()Ljava/util/List; public fun hasActiveModContainer ()Z public fun isModLoaded (Ljava/lang/String;)Z + public fun toActiveMod (Ljava/lang/Object;)Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; } public class cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl : cc/polyfrost/oneconfig/platform/MousePlatform { @@ -88,6 +90,7 @@ public class cc/polyfrost/oneconfig/platform/impl/PlatformImpl : cc/polyfrost/on public fun getLoader ()Lcc/polyfrost/oneconfig/platform/Platform$Loader; public fun getMinecraftVersion ()I public fun isCallingFromMinecraftThread ()Z + public fun isDevelopmentEnvironment ()Z } public class cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl : cc/polyfrost/oneconfig/platform/ServerPlatform { diff --git a/versions/1.16.2-forge/api/1.16.2-forge.api b/versions/1.16.2-forge/api/1.16.2-forge.api index 0a584e1..3a94282 100644 --- a/versions/1.16.2-forge/api/1.16.2-forge.api +++ b/versions/1.16.2-forge/api/1.16.2-forge.api @@ -49,8 +49,10 @@ public class cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl : cc/polyfros public class cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl : cc/polyfrost/oneconfig/platform/LoaderPlatform { public fun ()V public fun getActiveModContainer ()Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; + public fun getLoadedMods ()Ljava/util/List; public fun hasActiveModContainer ()Z public fun isModLoaded (Ljava/lang/String;)Z + public fun toActiveMod (Ljava/lang/Object;)Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; } public class cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl : cc/polyfrost/oneconfig/platform/MousePlatform { @@ -88,6 +90,7 @@ public class cc/polyfrost/oneconfig/platform/impl/PlatformImpl : cc/polyfrost/on public fun getLoader ()Lcc/polyfrost/oneconfig/platform/Platform$Loader; public fun getMinecraftVersion ()I public fun isCallingFromMinecraftThread ()Z + public fun isDevelopmentEnvironment ()Z } public class cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl : cc/polyfrost/oneconfig/platform/ServerPlatform { diff --git a/versions/1.8.9-forge/api/1.8.9-forge.api b/versions/1.8.9-forge/api/1.8.9-forge.api index dddfb40..0e76dc7 100644 --- a/versions/1.8.9-forge/api/1.8.9-forge.api +++ b/versions/1.8.9-forge/api/1.8.9-forge.api @@ -49,8 +49,10 @@ public class cc/polyfrost/oneconfig/platform/impl/I18nPlatformImpl : cc/polyfros public class cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl : cc/polyfrost/oneconfig/platform/LoaderPlatform { public fun ()V public fun getActiveModContainer ()Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; + public fun getLoadedMods ()Ljava/util/List; public fun hasActiveModContainer ()Z public fun isModLoaded (Ljava/lang/String;)Z + public fun toActiveMod (Ljava/lang/Object;)Lcc/polyfrost/oneconfig/platform/LoaderPlatform$ActiveMod; } public class cc/polyfrost/oneconfig/platform/impl/MousePlatformImpl : cc/polyfrost/oneconfig/platform/MousePlatform { @@ -88,6 +90,7 @@ public class cc/polyfrost/oneconfig/platform/impl/PlatformImpl : cc/polyfrost/on public fun getLoader ()Lcc/polyfrost/oneconfig/platform/Platform$Loader; public fun getMinecraftVersion ()I public fun isCallingFromMinecraftThread ()Z + public fun isDevelopmentEnvironment ()Z } public class cc/polyfrost/oneconfig/platform/impl/ServerPlatformImpl : cc/polyfrost/oneconfig/platform/ServerPlatform { diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java index 68065fd..4c10997 100644 --- a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java +++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/LoaderPlatformImpl.java @@ -28,26 +28,33 @@ package cc.polyfrost.oneconfig.platform.impl; import cc.polyfrost.oneconfig.platform.LoaderPlatform; //#if MC>=11600 - //#if FORGE==1 - //$$ import net.minecraftforge.fml.ModList; - //#else - //$$ import net.fabricmc.loader.api.FabricLoader; - //#endif +//#if FORGE==1 +//$$ import net.minecraftforge.fml.ModList; +//#else +//$$ import net.fabricmc.loader.api.FabricLoader; +//$$ import net.fabricmc.loader.api.ModContainer; +//#endif //#endif //#if FORGE==1 import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; //#endif +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; public class LoaderPlatformImpl implements LoaderPlatform { @Override public boolean isModLoaded(String id) { //#if MC>=11600 - //#if FORGE==1 - //$$ return ModList.get().isLoaded(id); - //#else - //$$ return FabricLoader.getInstance().isModLoaded(id); - //#endif + //#if FORGE==1 + //$$ return ModList.get().isLoaded(id); + //#else + //$$ return FabricLoader.getInstance().isModLoaded(id); + //#endif //#else return Loader.isModLoaded(id); //#endif @@ -62,18 +69,50 @@ public class LoaderPlatformImpl implements LoaderPlatform { //#endif } + @Override + public @NotNull List getLoadedMods() { + try { + return + //#if FORGE==1 + //#if MC<=11202 + Loader.instance().getActiveModList().stream().map + //#else + //$$ ModList.get().applyForEachModContainer + //#endif + //#else + //$$ FabricLoader.getInstance().getAllMods().stream().map + //#endif + (this::toActiveMod).collect(Collectors.toList()); + } catch (Exception e) { + return Collections.emptyList(); + } + } + @Override public ActiveMod getActiveModContainer() { //#if FORGE==1 - ModContainer container = Loader.instance().activeModContainer(); - if (container == null) return null; - //#if MC==11202 - return new ActiveMod(container.getName(), container.getModId(), container.getVersion()); - //#else - //$$ return new ActiveMod(container.getModInfo().getDisplayName(), container.getModId(), container.getModInfo().getVersion().getQualifier()); - //#endif + return toActiveMod(Loader.instance().activeModContainer()); //#else //$$ return null; //#endif } + + @Override + public ActiveMod toActiveMod(@Nullable Object in) { + try { + ModContainer container = (ModContainer) in; + if (container == null) return null; + //#if FORGE==1 + //#if MC==11202 + return new ActiveMod(container.getName(), container.getModId(), container.getVersion(), container.getSource()); + //#else + //$$ return new ActiveMod(container.getModInfo().getDisplayName(), container.getModId(), container.getModInfo().getVersion().getQualifier(), ModList.get().getModFileById(container.getModId()).getFile().getFilePath().toFile()); + //#endif + //#else + //$$ return new ActiveMod(container.getMetadata().getName(), container.getMetadata().getId(), container.getMetadata().getVersion().getFriendlyString(), container.getRootPaths().get(0).toFile()); + //#endif + } catch (Exception e) { + return null; + } + } } diff --git a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java index 85bf795..e6e96f0 100644 --- a/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java +++ b/versions/src/main/java/cc/polyfrost/oneconfig/platform/impl/PlatformImpl.java @@ -64,6 +64,22 @@ public class PlatformImpl implements Platform { //#endif } + @Override + public boolean isDevelopmentEnvironment() { + //#if FORGE==1 && MC<=11202 + try { + Class.forName("net.minecraft.block.BlockDirt"); + return true; + } catch (Exception ignored) { + return false; + } + //#elseif FABRIC==1 + //$$ return net.fabricmc.loader.api.FabricLoader.getInstance().isDevelopmentEnvironment(); + //#else + //$$ return !net.minecraftforge.fml.loading.FMLLoader.isProduction(); + //#endif + } + @Override public Loader getLoader() { //#if FORGE==1 -- cgit