Implement a very primitive datapack function profiler
This commit is contained in:
parent
98af2ec35a
commit
155faefe81
|
|
@ -2,9 +2,78 @@ package org.embeddedt.modernfix.command;
|
|||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.duck.IProfilingServerFunctionManager;
|
||||
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class ModernFixCommands {
|
||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
dispatcher.register(literal("modernfix")
|
||||
.then(literal("upgradeStructures")
|
||||
.requires(source -> source.hasPermission(3))
|
||||
.executes(context -> {
|
||||
ServerLevel level = context.getSource().getLevel();
|
||||
if(level == null) {
|
||||
context.getSource().sendFailure(Component.literal("Couldn't find server level"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResourceManager manager = level.getServer().resources.resourceManager();
|
||||
Map<ResourceLocation, Resource> structures = manager.listResources("structures", p -> p.getPath().endsWith(".nbt"));
|
||||
int upgradedNum = 0;
|
||||
Pattern pathPattern = Pattern.compile("^structures/(.*)\\.nbt$");
|
||||
for(Map.Entry<ResourceLocation, Resource> entry : structures.entrySet()) {
|
||||
upgradedNum++;
|
||||
ResourceLocation found = entry.getKey();
|
||||
Matcher matcher = pathPattern.matcher(found.getPath());
|
||||
if(!matcher.matches())
|
||||
continue;
|
||||
ResourceLocation structureLocation = new ResourceLocation(found.getNamespace(), matcher.group(1));
|
||||
try(InputStream resource = entry.getValue().open()) {
|
||||
CachingStructureManager.readStructureTag(structureLocation, level.getServer().getFixerUpper(), resource);
|
||||
Component msg = Component.literal("checked " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")");
|
||||
context.getSource().sendSuccess(() -> msg, false);
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Couldn't upgrade structure " + found, e);
|
||||
context.getSource().sendFailure(Component.literal("error reading " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")"));
|
||||
}
|
||||
}
|
||||
|
||||
context.getSource().sendSuccess(() -> Component.literal("All structures upgraded"), false);
|
||||
|
||||
return 1;
|
||||
}))
|
||||
.then(literal("mcfunctions").requires(source -> source.hasPermission(3))
|
||||
.executes(context -> {
|
||||
ServerLevel level = context.getSource().getLevel();
|
||||
if(level == null) {
|
||||
context.getSource().sendFailure(Component.literal("Couldn't find server level"));
|
||||
return 0;
|
||||
}
|
||||
if (level.getServer().getFunctions() instanceof IProfilingServerFunctionManager profiler) {
|
||||
context.getSource().sendSuccess(() -> Component.literal("mcfunction runtime breakdown:"), false);
|
||||
for(String line : profiler.mfix$getProfilingResults().split("\n")) {
|
||||
context.getSource().sendSuccess(() -> Component.literal(line), false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
context.getSource().sendFailure(Component.literal("ModernFix mcfunction profiling is not enabled on this server."));
|
||||
return 0;
|
||||
}
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.mcfunction_profiling;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.llamalad7.mixinextras.sugar.Share;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.commands.CommandFunction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.ServerFunctionManager;
|
||||
import org.embeddedt.modernfix.duck.IProfilingServerFunctionManager;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ServerFunctionManager.class)
|
||||
public class ServerFunctionManagerMixin implements IProfilingServerFunctionManager {
|
||||
@Shadow @Final private static ResourceLocation TICK_FUNCTION_TAG;
|
||||
|
||||
private final Map<ResourceLocation, Stopwatch> mfix$functionWatches = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
@Inject(method = "executeTagFunctions", at = @At("HEAD"))
|
||||
private void resetWatches(Collection<CommandFunction> functionObjects, ResourceLocation identifier, CallbackInfo ci) {
|
||||
mfix$functionWatches.values().forEach(Stopwatch::reset);
|
||||
}
|
||||
|
||||
@Inject(method = "executeTagFunctions", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ServerFunctionManager;execute(Lnet/minecraft/commands/CommandFunction;Lnet/minecraft/commands/CommandSourceStack;)I"))
|
||||
private void startWatch(Collection<CommandFunction> functionObjects, ResourceLocation identifier, CallbackInfo ci, @Local(ordinal = 0) CommandFunction function, @Share("stopwatch") LocalRef<Stopwatch> watchRef) {
|
||||
watchRef.set(null);
|
||||
if (identifier == TICK_FUNCTION_TAG) {
|
||||
var watch = mfix$functionWatches.computeIfAbsent(function.getId(), i -> Stopwatch.createUnstarted());
|
||||
watch.start();
|
||||
watchRef.set(watch);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "executeTagFunctions", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ServerFunctionManager;execute(Lnet/minecraft/commands/CommandFunction;Lnet/minecraft/commands/CommandSourceStack;)I", shift = At.Shift.AFTER))
|
||||
private void stopWatch(Collection<CommandFunction> functionObjects, ResourceLocation identifier, CallbackInfo ci, @Share("stopwatch") LocalRef<Stopwatch> watchRef) {
|
||||
var watch = watchRef.get();
|
||||
if (watch != null && watch.isRunning()) {
|
||||
watch.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "executeTagFunctions", at = @At("RETURN"))
|
||||
private void pruneUnusedWatches(Collection<CommandFunction> functionObjects, ResourceLocation identifier, CallbackInfo ci) {
|
||||
mfix$functionWatches.values().removeIf(watch -> watch.elapsed().isZero());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mfix$getProfilingResults() {
|
||||
var list = new ArrayList<>(mfix$functionWatches.entrySet());
|
||||
list.sort(Comparator.<Map.Entry<ResourceLocation, Stopwatch>, Duration>comparing(e -> e.getValue().elapsed()).reversed());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (var entry : list) {
|
||||
sb.append(entry.getKey().toString());
|
||||
sb.append(" - ");
|
||||
sb.append(entry.getValue().toString());
|
||||
sb.append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
public interface IProfilingServerFunctionManager {
|
||||
String mfix$getProfilingResults();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user