Add option to profile launch using Spark
This commit is contained in:
parent
b611830b7e
commit
60525ad594
|
|
@ -71,6 +71,7 @@ public class ModernFix {
|
|||
if(ModernFixPlatformHooks.isDedicatedServer()) {
|
||||
float gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||
ModernFix.LOGGER.warn("Dedicated server took " + gameStartTime + " seconds to load");
|
||||
ModernFixPlatformHooks.onLaunchComplete();
|
||||
}
|
||||
ClassInfoManager.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ public class ModernFixClient {
|
|||
} else if (openingScreen instanceof TitleScreen && gameStartTimeSeconds < 0) {
|
||||
gameStartTimeSeconds = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
||||
ModernFixPlatformHooks.onLaunchComplete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ public class ModernFixEarlyConfig {
|
|||
.put("mixin.perf.faster_item_rendering", false)
|
||||
.put("mixin.feature.spam_thread_dump", false)
|
||||
.put("mixin.feature.snapshot_easter_egg", true)
|
||||
.put("mixin.feature.spark_profile_launch", false)
|
||||
.put("mixin.perf.blast_search_trees", shouldReplaceSearchTrees)
|
||||
.put("mixin.devenv", isDevEnv)
|
||||
.put("mixin.perf.remove_spawn_chunks", isDevEnv)
|
||||
|
|
|
|||
|
|
@ -91,4 +91,9 @@ public class ModernFixPlatformHooks {
|
|||
public static Multimap<String, String> getCustomModOptions() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static void onLaunchComplete() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
|
||||
public class CommonModUtil {
|
||||
/**
|
||||
* Avoid using this, it's bad practice but cleanest way of suppressing errors for nonessential mod-dependent
|
||||
* functionality.
|
||||
*/
|
||||
public static void runWithoutCrash(Runnable r, String errorMsg) {
|
||||
try {
|
||||
r.run();
|
||||
} catch(Throwable e) {
|
||||
ModernFixMixinPlugin.instance.logger.error(errorMsg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ dependencies {
|
|||
modIncludeImplementation(fabricApi.module("fabric-models-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modImplementation(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
|
||||
modImplementation "curse.maven:spark-361579:${rootProject.spark_fabric_version}"
|
||||
modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}") { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
// Remove the next line if you don't want to depend on the API
|
||||
// modApi "me.shedaniel:architectury-fabric:${rootProject.architectury_version}"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.fabric.spark.SparkLaunchProfiler;
|
||||
import org.embeddedt.modernfix.util.CommonModUtil;
|
||||
|
||||
public class ModernFixPreLaunchFabric implements PreLaunchEntrypoint {
|
||||
@Override
|
||||
public void onPreLaunch() {
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.start("launch"), "Failed to start profiler");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package org.embeddedt.modernfix.fabric.spark;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
|
||||
import me.lucko.spark.common.SparkPlatform;
|
||||
import me.lucko.spark.common.SparkPlugin;
|
||||
import me.lucko.spark.common.command.sender.CommandSender;
|
||||
import me.lucko.spark.common.platform.AbstractPlatformInfo;
|
||||
import me.lucko.spark.common.platform.PlatformInfo;
|
||||
import me.lucko.spark.common.sampler.Sampler;
|
||||
import me.lucko.spark.common.sampler.ThreadDumper;
|
||||
import me.lucko.spark.common.sampler.ThreadGrouper;
|
||||
import me.lucko.spark.common.sampler.ThreadNodeOrder;
|
||||
import me.lucko.spark.common.sampler.async.AsyncProfilerAccess;
|
||||
import me.lucko.spark.common.sampler.async.AsyncSampler;
|
||||
import me.lucko.spark.common.sampler.java.JavaSampler;
|
||||
import me.lucko.spark.common.sampler.node.MergeMode;
|
||||
import me.lucko.spark.common.util.MethodDisambiguator;
|
||||
import me.lucko.spark.lib.adventure.text.Component;
|
||||
import me.lucko.spark.lib.okhttp3.MediaType;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/* Inspired by CensoredASM */
|
||||
public class SparkLaunchProfiler {
|
||||
private static PlatformInfo platformInfo = new ModernFixPlatformInfo();
|
||||
private static CommandSender commandSender = new ModernFixCommandSender();
|
||||
private static Map<String, Sampler> ongoingSamplers = new Object2ReferenceOpenHashMap<>();
|
||||
private static MediaType mediaType = MediaType.parse("application/x-spark-sampler");
|
||||
private static ExecutorService executor = Executors.newSingleThreadScheduledExecutor((new ThreadFactoryBuilder()).setNameFormat("spark-modernfix-async-worker").build());
|
||||
private static final SparkPlatform platform = new SparkPlatform(new ModernFixSparkPlugin());
|
||||
|
||||
private static final boolean USE_JAVA_SAMPLER_FOR_LAUNCH = true; //Boolean.getBoolean("modernfix.profileLaunchWithJavaSampler");
|
||||
|
||||
public static void start(String key) {
|
||||
if (!ongoingSamplers.containsKey(key)) {
|
||||
Sampler sampler;
|
||||
try {
|
||||
if(USE_JAVA_SAMPLER_FOR_LAUNCH) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
AsyncProfilerAccess.INSTANCE.getProfiler();
|
||||
sampler = new AsyncSampler(4000, ThreadDumper.ALL, ThreadGrouper.BY_NAME);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
sampler = new JavaSampler(4000, ThreadDumper.ALL, ThreadGrouper.BY_NAME, -1, true, true);
|
||||
}
|
||||
ongoingSamplers.put(key, sampler);
|
||||
ModernFixMixinPlugin.instance.logger.warn("Profiler has started for stage [{}]...", key);
|
||||
sampler.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void stop(String key) {
|
||||
Sampler sampler = ongoingSamplers.remove(key);
|
||||
if (sampler != null) {
|
||||
sampler.stop();
|
||||
output(key, sampler);
|
||||
}
|
||||
}
|
||||
|
||||
private static void output(String key, Sampler sampler) {
|
||||
executor.execute(() -> {
|
||||
ModernFixMixinPlugin.instance.logger.warn("Stage [{}] profiler has stopped! Uploading results...", key);
|
||||
byte[] output = sampler.formCompressedDataPayload(new Sampler.ExportProps(platformInfo, commandSender, ThreadNodeOrder.BY_TIME, "Stage: " + key, MergeMode.separateParentCalls(new MethodDisambiguator()), platform.getClassSourceLookup()));
|
||||
try {
|
||||
String urlKey = SparkPlatform.BYTEBIN_CLIENT.postContent(output, mediaType).key();
|
||||
String url = "https://spark.lucko.me/" + urlKey;
|
||||
ModernFixMixinPlugin.instance.logger.warn("Profiler results for Stage [{}]: {}", key, url);
|
||||
} catch (Exception e) {
|
||||
ModernFixMixinPlugin.instance.logger.fatal("An error occurred whilst uploading the results.", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class ModernFixPlatformInfo extends AbstractPlatformInfo {
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT ? Type.CLIENT : Type.SERVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ModernFix";
|
||||
}
|
||||
|
||||
private static String versionOfModContainer(Optional<ModContainer> containerOpt) {
|
||||
return containerOpt.map(container -> container.getMetadata().getVersion().toString()).orElse("unknown");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return versionOfModContainer(FabricLoader.getInstance().getModContainer("modernfix"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMinecraftVersion() {
|
||||
return versionOfModContainer(FabricLoader.getInstance().getModContainer("minecraft"));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModernFixCommandSender implements CommandSender {
|
||||
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
private final String name;
|
||||
|
||||
public ModernFixCommandSender() {
|
||||
this.name = "ModernFix";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component component) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class ModernFixSparkPlugin implements SparkPlugin {
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPluginDirectory() {
|
||||
return FabricLoader.getInstance().getGameDir().resolve("spark-modernfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "spark-modernfix";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends CommandSender> getCommandSenders() {
|
||||
return Stream.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeAsync(Runnable runnable) {
|
||||
executor.execute(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformInfo getPlatformInfo() {
|
||||
return platformInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,9 @@ import net.minecraft.server.packs.resources.Resource;
|
|||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.embeddedt.modernfix.ModernFixFabric;
|
||||
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.fabric.spark.SparkLaunchProfiler;
|
||||
import org.embeddedt.modernfix.util.CommonModUtil;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
|
@ -109,4 +112,10 @@ public class ModernFixPlatformHooksImpl {
|
|||
}
|
||||
return modOptions;
|
||||
}
|
||||
|
||||
public static void onLaunchComplete() {
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@
|
|||
"client": [
|
||||
"org.embeddedt.modernfix.ModernFixClientFabric"
|
||||
],
|
||||
"preLaunch": [
|
||||
"org.embeddedt.modernfix.ModernFixPreLaunchFabric"
|
||||
],
|
||||
"modmenu": [ "org.embeddedt.modernfix.fabric.modmenu.ModernFixModMenuApiImpl" ]
|
||||
},
|
||||
"mixins": [
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ dependencies {
|
|||
modCompileOnly files("deps/starlight-1.2.jar")
|
||||
modCompileOnly("appeng:appliedenergistics2:8.4.7")
|
||||
modCompileOnly("vazkii.patchouli:Patchouli:1.16.4-53.3")
|
||||
modImplementation "curse.maven:spark-361579:${rootProject.spark_forge_version}"
|
||||
|
||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
||||
shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
package org.embeddedt.modernfix.forge.spark;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
|
||||
import me.lucko.spark.common.SparkPlatform;
|
||||
import me.lucko.spark.common.SparkPlugin;
|
||||
import me.lucko.spark.common.command.sender.CommandSender;
|
||||
import me.lucko.spark.common.platform.PlatformInfo;
|
||||
import me.lucko.spark.common.sampler.Sampler;
|
||||
import me.lucko.spark.common.sampler.ThreadDumper;
|
||||
import me.lucko.spark.common.sampler.ThreadGrouper;
|
||||
import me.lucko.spark.common.sampler.ThreadNodeOrder;
|
||||
import me.lucko.spark.common.sampler.async.AsyncProfilerAccess;
|
||||
import me.lucko.spark.common.sampler.async.AsyncSampler;
|
||||
import me.lucko.spark.common.sampler.java.JavaSampler;
|
||||
import me.lucko.spark.common.sampler.node.MergeMode;
|
||||
import me.lucko.spark.common.util.MethodDisambiguator;
|
||||
import me.lucko.spark.lib.adventure.text.Component;
|
||||
import me.lucko.spark.lib.okhttp3.MediaType;
|
||||
import me.lucko.spark.proto.SparkSamplerProtos;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import net.minecraftforge.fml.loading.LoadingModList;
|
||||
import net.minecraftforge.versions.mcp.MCPVersion;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/* Inspired by CensoredASM */
|
||||
public class SparkLaunchProfiler {
|
||||
private static PlatformInfo platformInfo = new ModernFixPlatformInfo();
|
||||
private static CommandSender commandSender = new ModernFixCommandSender();
|
||||
private static Map<String, Sampler> ongoingSamplers = new Object2ReferenceOpenHashMap<>();
|
||||
private static MediaType mediaType = MediaType.parse("application/x-spark-sampler");
|
||||
private static ExecutorService executor = Executors.newSingleThreadScheduledExecutor((new ThreadFactoryBuilder()).setNameFormat("spark-modernfix-async-worker").build());
|
||||
private static final SparkPlatform platform = new SparkPlatform(new ModernFixSparkPlugin());
|
||||
|
||||
private static final boolean USE_JAVA_SAMPLER_FOR_LAUNCH = true; //Boolean.getBoolean("modernfix.profileLaunchWithJavaSampler");
|
||||
|
||||
public static void start(String key) {
|
||||
if (!ongoingSamplers.containsKey(key)) {
|
||||
Sampler sampler;
|
||||
try {
|
||||
if(USE_JAVA_SAMPLER_FOR_LAUNCH) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
AsyncProfilerAccess.INSTANCE.getProfiler();
|
||||
sampler = new AsyncSampler(4000, ThreadDumper.ALL, ThreadGrouper.BY_NAME, -1);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
sampler = new JavaSampler(4000, ThreadDumper.ALL, ThreadGrouper.BY_NAME, -1, true, true);
|
||||
}
|
||||
ongoingSamplers.put(key, sampler);
|
||||
ModernFixMixinPlugin.instance.logger.warn("Profiler has started for stage [{}]...", key);
|
||||
sampler.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void stop(String key) {
|
||||
Sampler sampler = ongoingSamplers.remove(key);
|
||||
if (sampler != null) {
|
||||
sampler.stop();
|
||||
output(key, sampler);
|
||||
}
|
||||
}
|
||||
|
||||
private static void output(String key, Sampler sampler) {
|
||||
executor.execute(() -> {
|
||||
ModernFixMixinPlugin.instance.logger.warn("Stage [{}] profiler has stopped! Uploading results...", key);
|
||||
SparkSamplerProtos.SamplerData output = sampler.toProto(platform, commandSender, ThreadNodeOrder.BY_TIME, "Stage: " + key, MergeMode.separateParentCalls(new MethodDisambiguator()), platform.createClassSourceLookup());
|
||||
try {
|
||||
String urlKey = platform.getBytebinClient().postContent(output, mediaType).key();
|
||||
String url = "https://spark.lucko.me/" + urlKey;
|
||||
ModernFixMixinPlugin.instance.logger.warn("Profiler results for Stage [{}]: {}", key, url);
|
||||
} catch (Exception e) {
|
||||
ModernFixMixinPlugin.instance.logger.fatal("An error occurred whilst uploading the results.", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class ModernFixPlatformInfo implements PlatformInfo {
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return FMLLoader.getDist().isClient() ? Type.CLIENT : Type.SERVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ModernFix";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return LoadingModList.get().getModFileById("modernfix").getMods().get(0).getVersion().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMinecraftVersion() {
|
||||
return MCPVersion.getMCVersion();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ModernFixCommandSender implements CommandSender {
|
||||
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
private final String name;
|
||||
|
||||
public ModernFixCommandSender() {
|
||||
this.name = "ModernFix";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component component) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class ModernFixSparkPlugin implements SparkPlugin {
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPluginDirectory() {
|
||||
return FMLPaths.GAMEDIR.get().resolve("spark-modernfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "spark-modernfix";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends CommandSender> getCommandSenders() {
|
||||
return Stream.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeAsync(Runnable runnable) {
|
||||
executor.execute(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String s) {
|
||||
ModernFixMixinPlugin.instance.logger.warn(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformInfo getPlatformInfo() {
|
||||
return platformInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,8 @@ import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
|||
import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList;
|
||||
import org.embeddedt.modernfix.forge.classloading.ModernFixResourceFinder;
|
||||
import org.embeddedt.modernfix.forge.packet.PacketHandler;
|
||||
import org.embeddedt.modernfix.forge.spark.SparkLaunchProfiler;
|
||||
import org.embeddedt.modernfix.util.CommonModUtil;
|
||||
import org.embeddedt.modernfix.util.DummyList;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
|
@ -174,6 +176,10 @@ public class ModernFixPlatformHooksImpl {
|
|||
} catch(RuntimeException | ReflectiveOperationException e) {
|
||||
ModernFixMixinPlugin.instance.logger.error("Failed to patch mixin memory leak", e);
|
||||
}
|
||||
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnForge")) {
|
||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.start("launch"), "Failed to start profiler");
|
||||
}
|
||||
}
|
||||
|
||||
private Method defineClassMethod = null;
|
||||
|
|
@ -245,4 +251,10 @@ public class ModernFixPlatformHooksImpl {
|
|||
}
|
||||
return modOptions;
|
||||
}
|
||||
|
||||
public static void onLaunchComplete() {
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnForge")) {
|
||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,7 @@ supported_minecraft_versions=1.16.4,1.16.5
|
|||
fabric_loader_version=0.14.18
|
||||
fabric_api_version=0.42.0+1.16
|
||||
|
||||
modmenu_version=1.16.23
|
||||
modmenu_version=1.16.23
|
||||
|
||||
spark_forge_version=3767277
|
||||
spark_fabric_version=3337642
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user