From 0ba86ae7201fceaaff82ecfc69fd1522fd93186b Mon Sep 17 00:00:00 2001 From: Tom Martin Date: Sat, 28 Jun 2025 02:23:16 +0100 Subject: [PATCH 1/2] Add @Unique to inline annotations --- .../opencubicchunks/gradle/FixAnnotationsFormatterStep.java | 2 +- .../core/common/server/network/MixinPlayerChunkSender.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/java/io/github/opencubicchunks/gradle/FixAnnotationsFormatterStep.java b/buildSrc/src/main/java/io/github/opencubicchunks/gradle/FixAnnotationsFormatterStep.java index 49a94565..d35080bb 100644 --- a/buildSrc/src/main/java/io/github/opencubicchunks/gradle/FixAnnotationsFormatterStep.java +++ b/buildSrc/src/main/java/io/github/opencubicchunks/gradle/FixAnnotationsFormatterStep.java @@ -12,7 +12,7 @@ public class FixAnnotationsFormatterStep implements FormatterStep { } private static final String[] ANNOTATIONS = { "@Shadow", "@Final", "@Mutable", "@Public", "@Override", "@Nullable", "@NotNull", "@Invoker", - "@Accessor", "@Dynamic" }; + "@Accessor", "@Dynamic", "@Unique" }; @Override public @Nullable String format(String s, @NotNull File file) { var outputString = s; diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/network/MixinPlayerChunkSender.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/network/MixinPlayerChunkSender.java index 2d0f6a7b..93dc13c7 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/network/MixinPlayerChunkSender.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/server/network/MixinPlayerChunkSender.java @@ -112,8 +112,7 @@ private void cc_onSendNextChunks(ServerPlayer player, CallbackInfo ci) { } } - @Unique - private static void cc_sendCube(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelCube cube) { + @Unique private static void cc_sendCube(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelCube cube) { PacketDistributor.sendToPlayer(packetListener.player, new CCClientboundLevelCubeWithLightPacket(cube)); // ChunkPos chunkpos = chunk.getPos(); @@ -125,8 +124,7 @@ private static void cc_sendCube(ServerGamePacketListenerImpl packetListener, Ser // net.neoforged.neoforge.event.EventHooks.fireChunkSent(packetListener.player, chunk, level); } - @Unique - private static void cc_sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { + @Unique private static void cc_sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { PacketDistributor.sendToPlayer(packetListener.player, new CCClientboundLevelChunkPacket(chunk.getPos())); // ChunkPos chunkpos = chunk.getPos(); From 302bfaabf3db4f18e5c69134f65e9b44350797cd Mon Sep 17 00:00:00 2001 From: Tom Martin Date: Sat, 28 Jun 2025 02:07:18 +0100 Subject: [PATCH 2/2] Add per-tick async profiler modes --- build.gradle | 28 +++--- .../cubicchunks/mixin/DebugMixinConfig.java | 32 +++++++ .../debug/common/MixinMinecraftServer.java | 86 +++++++++++++++++++ .../mixin/debug/common/package-info.java | 7 ++ .../resources/META-INF/neoforge.mods.toml | 5 +- 5 files changed, 140 insertions(+), 18 deletions(-) create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/mixin/DebugMixinConfig.java create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java create mode 100644 src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/package-info.java diff --git a/build.gradle b/build.gradle index bba38f72..3d12712e 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,13 @@ mixinGen { it.injectorsDefaultRequire = 1 } + config(sourceSets.main, "debug") { + it.required = false + it.conformVisibility = true + it.injectorsDefaultRequire = 0 + it.configurationPlugin = "io.github.opencubicchunks.cubicchunks.mixin.DebugMixinConfig" + } + config(sourceSets.forge, "forge") { it.required = true it.conformVisibility = true @@ -129,13 +136,6 @@ mixinGen { injectorsDefaultRequire = 1 } - config("debug") { - required = false - conformVisibility = true - injectorsDefaultRequire = 0 - configurationPlugin = "io.github.opencubicchunks.cubicchunks.mixin.DebugMixinConfig" - } - config("test") { required = true conformVisibility = true @@ -173,16 +173,8 @@ neoForge { "-Dmixin.debug.export=true", "-Dmixin.checks.interfaces=true", "-Dcubicchunks.debug=false", - "-Dcubicchunks.debug.loadorder=false", - "-Dcubicchunks.debug.window=false", - "-Dcubicchunks.debug.statusrenderer=false", - "-Dcubicchunks.debug.heightmaprenderer=false", - "-Dcubicchunks.debug.heightmaprenderer.server=false", - "-Dcubicchunks.debug.heightmaprenderer.render_lightmap=false", - "-Dcubicchunks.debug.heightmaprenderer.radius=2", - "-Dcubicchunks.debug.heightmapverification=false", - "-Dcubicchunks.debug.heightmapverification.frequency=1", - "-Dcubicchunks.debug.biomes=false", + "-Dcubicchunks.debug.profiler=false", + "-Dcubicchunks.debug.profiler.interval_ns=1", "-ea" ] // applies to all the run configs below @@ -273,6 +265,8 @@ dependencies { libraries("io.github.opencubicchunks:regionlib:0.63.0-SNAPSHOT") libraries("org.spongepowered:noise:2.0.0-SNAPSHOT") + libraries('tools.profiler:async-profiler:4.0') + testImplementation("org.assertj:assertj-core:3.25.1") testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0") testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.0") diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/DebugMixinConfig.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/DebugMixinConfig.java new file mode 100644 index 00000000..780c212c --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/DebugMixinConfig.java @@ -0,0 +1,32 @@ +package io.github.opencubicchunks.cubicchunks.mixin; + +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +public class DebugMixinConfig implements IMixinConfigPlugin { + private static final boolean DEBUG_MIXINS_ENABLED = System.getProperty("cubicchunks.debug", "false").equals("true"); + + @Override public void onLoad(String mixinPackage) {} + + @Override public String getRefMapperConfig() { + return null; + } + + @Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return DEBUG_MIXINS_ENABLED; + } + + @Override public void acceptTargets(Set myTargets, Set otherTargets) {} + + @Override public List getMixins() { + return null; + } + + @Override public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} + + @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java new file mode 100644 index 00000000..d5ec21cb --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java @@ -0,0 +1,86 @@ +package io.github.opencubicchunks.cubicchunks.mixin.debug.common; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.function.BooleanSupplier; + +import io.github.opencubicchunks.cubicchunks.CubicChunks; +import net.minecraft.server.MinecraftServer; +import one.profiler.AsyncProfiler; +import org.apache.commons.io.FileUtils; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MinecraftServer.class) +public class MixinMinecraftServer { + @Unique private static final boolean perf_doProfiling = System.getProperty("cubicchunks.debug.profiler", "false").equals("true"); + + @Unique private static final Path perf_basePath = Path.of(System.getProperty("cubicchunks.debug.profiler.path", "profiler")); + @Unique private static final int perf_interval = Integer.parseInt(System.getProperty("cubicchunks.debug.profiler.interval_ns", "1")); + + @Unique private static final AsyncProfiler perf_PROFILER = AsyncProfiler.getInstance(); + @Unique private static boolean perf_started = false; + @Unique private static int perf_idx = 0; + + @Unique private static long perf_tickStartTime = 0; + + static { + try { + FileUtils.deleteDirectory(perf_basePath.toFile()); + } catch (IOException e) { + CubicChunks.LOGGER.error("[tick-profiler] Failed to clear existing output directory {}", perf_basePath, e); + } + } + + @Inject(method = "tickServer", at = @At("HEAD")) + private void onTick(BooleanSupplier hasTimeLeft, CallbackInfo ci) { + if (!perf_doProfiling) { + return; + } + + perf_stopProfiler(); + + // Start a new profiler + Path path = perf_basePath.resolve(perf_idx++ + ".jfr").toAbsolutePath(); + try { + Files.createDirectories(path.getParent()); + perf_PROFILER.execute("start,jfr,interval=" + perf_interval + ",event=cpu,file=" + path.toAbsolutePath()); + } catch (IOException e) { + CubicChunks.LOGGER.error("[tick-profiler] Failed to start async-profiler", e); + } + perf_started = true; + perf_tickStartTime = System.nanoTime(); + } + + @Inject(method = "stopServer", at = @At("HEAD")) + private void onStop(CallbackInfo ci) { + perf_stopProfiler(); + } + + @Unique private static void perf_stopProfiler() { + if (perf_started) { + // Stop the currently running profiler + try { + perf_PROFILER.execute("stop"); + } catch (IOException e) { + CubicChunks.LOGGER.error("[tick-profiler] Failed to stop async-profiler", e); + } + perf_started = false; + + // Rename the just stopped profiler output file to include its duration for easy sorting + int previousIndex = perf_idx - 1; + Duration duration = Duration.ofNanos(System.nanoTime() - perf_tickStartTime); + try { + Files.move(perf_basePath.resolve(previousIndex + ".jfr"), perf_basePath.resolve(duration.toMillis() + "_" + previousIndex + ".jfr")); + } catch (IOException e) { + CubicChunks.LOGGER.error("[tick-profiler] Failed to rename jfr file to include tick duration", e); + } + } + } + +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/package-info.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/package-info.java new file mode 100644 index 00000000..45e36b8c --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package io.github.opencubicchunks.cubicchunks.mixin.debug.common; + +import javax.annotation.ParametersAreNonnullByDefault; + +import io.github.opencubicchunks.cc_core.annotation.MethodsReturnNonnullByDefault; diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 04866125..8df642ca 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -76,4 +76,7 @@ description='''${mod_description}''' config="${mod_id}.mixins.core.json" [[mixins]] -config="${mod_id}.mixins.access.json" \ No newline at end of file +config="${mod_id}.mixins.access.json" + +[[mixins]] +config="${mod_id}.mixins.debug.json"