Skip to content

Commit 13820f7

Browse files
committed
Merge remote-tracking branch 'origin/1.20' into 1.21.1
2 parents c669878 + 4bdddf1 commit 13820f7

File tree

10 files changed

+422
-130
lines changed

10 files changed

+422
-130
lines changed

common/src/main/java/org/embeddedt/modernfix/command/ModernFixCommands.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,34 @@
22

33
import com.mojang.brigadier.CommandDispatcher;
44
import net.minecraft.commands.CommandSourceStack;
5+
import net.minecraft.network.chat.Component;
6+
import net.minecraft.server.level.ServerLevel;
7+
import org.embeddedt.modernfix.duck.IProfilingServerFunctionManager;
8+
9+
import static net.minecraft.commands.Commands.literal;
510

611
public class ModernFixCommands {
712
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
13+
dispatcher.register(literal("modernfix")
14+
.then(literal("mcfunctions").requires(source -> source.hasPermission(3))
15+
.executes(context -> {
16+
ServerLevel level = context.getSource().getLevel();
17+
if(level == null) {
18+
context.getSource().sendFailure(Component.literal("Couldn't find server level"));
19+
return 0;
20+
}
21+
if (level.getServer().getFunctions() instanceof IProfilingServerFunctionManager profiler) {
22+
context.getSource().sendSuccess(() -> Component.literal("mcfunction runtime breakdown:"), false);
23+
for(String line : profiler.mfix$getProfilingResults().split("\n")) {
24+
context.getSource().sendSuccess(() -> Component.literal(line), false);
25+
}
826

27+
return 1;
28+
} else {
29+
context.getSource().sendFailure(Component.literal("ModernFix mcfunction profiling is not enabled on this server."));
30+
return 0;
31+
}
32+
}))
33+
);
934
}
1035
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.embeddedt.modernfix.common.mixin.feature.mcfunction_profiling;
2+
3+
import com.google.common.base.Stopwatch;
4+
import com.llamalad7.mixinextras.sugar.Local;
5+
import com.llamalad7.mixinextras.sugar.Share;
6+
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
7+
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
8+
import net.minecraft.commands.CommandSourceStack;
9+
import net.minecraft.commands.functions.CommandFunction;
10+
import net.minecraft.resources.ResourceLocation;
11+
import net.minecraft.server.ServerFunctionManager;
12+
import org.embeddedt.modernfix.duck.IProfilingServerFunctionManager;
13+
import org.spongepowered.asm.mixin.Final;
14+
import org.spongepowered.asm.mixin.Mixin;
15+
import org.spongepowered.asm.mixin.Shadow;
16+
import org.spongepowered.asm.mixin.injection.At;
17+
import org.spongepowered.asm.mixin.injection.Inject;
18+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
19+
20+
import java.time.Duration;
21+
import java.util.ArrayList;
22+
import java.util.Collection;
23+
import java.util.Comparator;
24+
import java.util.Map;
25+
26+
@Mixin(ServerFunctionManager.class)
27+
public class ServerFunctionManagerMixin implements IProfilingServerFunctionManager {
28+
@Shadow @Final private static ResourceLocation TICK_FUNCTION_TAG;
29+
30+
private final Map<ResourceLocation, Stopwatch> mfix$functionWatches = new Object2ObjectOpenHashMap<>();
31+
32+
@Inject(method = "executeTagFunctions", at = @At("HEAD"))
33+
private void resetWatches(Collection<CommandFunction<CommandSourceStack>> functionObjects, ResourceLocation identifier, CallbackInfo ci) {
34+
mfix$functionWatches.values().forEach(Stopwatch::reset);
35+
}
36+
37+
@Inject(method = "executeTagFunctions", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ServerFunctionManager;execute(Lnet/minecraft/commands/functions/CommandFunction;Lnet/minecraft/commands/CommandSourceStack;)V"))
38+
private void startWatch(Collection<CommandFunction<CommandSourceStack>> functionObjects, ResourceLocation identifier, CallbackInfo ci, @Local(ordinal = 0) CommandFunction<CommandSourceStack> function, @Share("stopwatch") LocalRef<Stopwatch> watchRef) {
39+
watchRef.set(null);
40+
if (identifier == TICK_FUNCTION_TAG) {
41+
var watch = mfix$functionWatches.computeIfAbsent(function.id(), i -> Stopwatch.createUnstarted());
42+
watch.start();
43+
watchRef.set(watch);
44+
}
45+
}
46+
47+
@Inject(method = "executeTagFunctions", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ServerFunctionManager;execute(Lnet/minecraft/commands/functions/CommandFunction;Lnet/minecraft/commands/CommandSourceStack;)V", shift = At.Shift.AFTER))
48+
private void stopWatch(Collection<CommandFunction<CommandSourceStack>> functionObjects, ResourceLocation identifier, CallbackInfo ci, @Share("stopwatch") LocalRef<Stopwatch> watchRef) {
49+
var watch = watchRef.get();
50+
if (watch != null && watch.isRunning()) {
51+
watch.stop();
52+
}
53+
}
54+
55+
@Inject(method = "executeTagFunctions", at = @At("RETURN"))
56+
private void pruneUnusedWatches(Collection<CommandFunction<CommandSourceStack>> functionObjects, ResourceLocation identifier, CallbackInfo ci) {
57+
mfix$functionWatches.values().removeIf(watch -> watch.elapsed().isZero());
58+
}
59+
60+
@Override
61+
public String mfix$getProfilingResults() {
62+
var list = new ArrayList<>(mfix$functionWatches.entrySet());
63+
list.sort(Comparator.<Map.Entry<ResourceLocation, Stopwatch>, Duration>comparing(e -> e.getValue().elapsed()).reversed());
64+
StringBuilder sb = new StringBuilder();
65+
for (var entry : list) {
66+
sb.append(entry.getKey().toString());
67+
sb.append(" - ");
68+
sb.append(entry.getValue().toString());
69+
sb.append('\n');
70+
}
71+
return sb.toString();
72+
}
73+
}

common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/ProfiledReloadInstanceMixin.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,38 @@
88
import org.spongepowered.asm.mixin.injection.ModifyVariable;
99

1010
import java.util.ArrayList;
11+
import java.util.Comparator;
1112
import java.util.List;
1213

1314
@Mixin(ProfiledReloadInstance.class)
1415
public class ProfiledReloadInstanceMixin {
16+
/**
17+
* @author embeddedt
18+
* @reason Decorate reload listeners with their class name as well as the simple name
19+
*/
1520
@ModifyVariable(method = "<init>", at = @At("HEAD"), argsOnly = true, ordinal = 0)
1621
private static List<PreparableReloadListener> getWrappedListeners(List<PreparableReloadListener> listeners) {
1722
List<PreparableReloadListener> newList = new ArrayList<>(listeners.size());
1823
for(PreparableReloadListener listener : listeners) {
19-
newList.add(new NamedPreparableResourceListener(listener));
24+
// No need to wrap listeners that are already wrapped/provided by a mod loader
25+
String className = listener.getClass().getName();
26+
if (className.startsWith("net.minecraftforge.") || className.startsWith("net.neoforged.") || className.startsWith("net.fabricmc.")) {
27+
newList.add(listener);
28+
} else {
29+
newList.add(new NamedPreparableResourceListener(listener));
30+
}
2031
}
2132
return newList;
2233
}
34+
35+
/**
36+
* @author embeddedt
37+
* @reason Place most expensive reload listeners first
38+
*/
39+
@ModifyVariable(method = "finish", ordinal = 0, argsOnly = true, at = @At("HEAD"))
40+
private List<ProfiledReloadInstance.State> sortStates(List<ProfiledReloadInstance.State> datapoints) {
41+
datapoints = new ArrayList<>(datapoints);
42+
datapoints.sort(Comparator.<ProfiledReloadInstance.State>comparingLong(s -> s.preparationNanos.get() + s.reloadNanos.get()).reversed());
43+
return datapoints;
44+
}
2345
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.resourcepacks;
2+
3+
import net.minecraft.server.packs.PackLocationInfo;
4+
import net.minecraft.server.packs.PackResources;
5+
import net.minecraft.server.packs.PackType;
6+
import net.minecraft.server.packs.PathPackResources;
7+
import org.embeddedt.modernfix.resources.ICachingResourcePack;
8+
import org.embeddedt.modernfix.resources.PackResourcesCacheEngine;
9+
import org.embeddedt.modernfix.util.PackTypeHelper;
10+
import org.spongepowered.asm.mixin.Final;
11+
import org.spongepowered.asm.mixin.Mixin;
12+
import org.spongepowered.asm.mixin.Shadow;
13+
import org.spongepowered.asm.mixin.injection.At;
14+
import org.spongepowered.asm.mixin.injection.Inject;
15+
import org.spongepowered.asm.mixin.injection.Redirect;
16+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
17+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
18+
19+
import java.nio.file.Files;
20+
import java.nio.file.LinkOption;
21+
import java.nio.file.Path;
22+
import java.util.Objects;
23+
import java.util.Set;
24+
25+
@Mixin(value = PathPackResources.class, priority = 1100)
26+
public abstract class PathPackResourcesMixin implements ICachingResourcePack {
27+
28+
@Shadow @Final private Path root;
29+
private PackResourcesCacheEngine cacheEngine;
30+
31+
@Inject(method = "<init>", at = @At("TAIL"))
32+
private void cacheResources(PackLocationInfo location, Path root, CallbackInfo ci) {
33+
invalidateCache();
34+
}
35+
36+
private PackResourcesCacheEngine generateResourceCache() {
37+
synchronized (this) {
38+
PackResourcesCacheEngine engine = this.cacheEngine;
39+
if(engine != null)
40+
return engine;
41+
this.cacheEngine = engine = new PackResourcesCacheEngine((type) -> this.root.resolve(type.getDirectory()));
42+
return engine;
43+
}
44+
}
45+
46+
@Override
47+
public void invalidateCache() {
48+
this.cacheEngine = null;
49+
}
50+
51+
@Inject(method = "getNamespaces", at = @At("HEAD"), cancellable = true)
52+
private void useCacheForNamespaces(PackType type, CallbackInfoReturnable<Set<String>> cir) {
53+
PackResourcesCacheEngine engine = cacheEngine;
54+
if(engine != null) {
55+
Set<String> namespaces = engine.getNamespaces(type);
56+
if(namespaces != null)
57+
cir.setReturnValue(namespaces);
58+
}
59+
}
60+
61+
@Redirect(method = "getRootResource", at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;exists(Ljava/nio/file/Path;[Ljava/nio/file/LinkOption;)Z"))
62+
private boolean useCacheForExistence(Path path, LinkOption[] options, String[] originalPaths) {
63+
// the cache only stores things with a namespace and pack type
64+
if(originalPaths.length < 3 || (!Objects.equals(originalPaths[0], "assets") && !Objects.equals(originalPaths[0], "data")))
65+
return Files.exists(path, options);
66+
else
67+
return this.generateResourceCache().hasResource(originalPaths);
68+
}
69+
70+
/**
71+
* @author embeddedt
72+
* @reason Use cached listing of mod resources
73+
*/
74+
@Inject(method = "listResources", at = @At("HEAD"), cancellable = true)
75+
private void fastGetResources(PackType type, String namespace, String path, PackResources.ResourceOutput resourceOutput, CallbackInfo ci)
76+
{
77+
if(!PackTypeHelper.isVanillaPackType(type))
78+
return;
79+
ci.cancel();
80+
this.generateResourceCache().collectResources(type, namespace, path.split("/"), Integer.MAX_VALUE, resourceOutput);
81+
}
82+
}

common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/resourcepacks/ReloadableResourceManagerMixin.java

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.embeddedt.modernfix.duck;
2+
3+
public interface IProfilingServerFunctionManager {
4+
String mfix$getProfilingResults();
5+
}

0 commit comments

Comments
 (0)