diff --git a/common/src/main/java/org/embeddedt/modernfix/ModernFix.java b/common/src/main/java/org/embeddedt/modernfix/ModernFix.java index b571b718..26ed63dd 100644 --- a/common/src/main/java/org/embeddedt/modernfix/ModernFix.java +++ b/common/src/main/java/org/embeddedt/modernfix/ModernFix.java @@ -11,6 +11,7 @@ import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; import org.embeddedt.modernfix.resources.ReloadExecutor; import org.embeddedt.modernfix.util.ClassInfoManager; +import org.embeddedt.modernfix.world.IntegratedWatchdog; import java.lang.management.ManagementFactory; import java.util.concurrent.ExecutorService; @@ -46,6 +47,19 @@ public class ModernFix { public ModernFix() { INSTANCE = this; ModernFixPlatformHooks.onServerCommandRegister(ModernFixCommands::register); + if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spam_thread_dump.ThreadDumper")) { + Thread t = new Thread() { + public void run() { + while(true) { + LOGGER.error("------ DEBUG THREAD DUMP (occurs every 60 seconds) ------"); + LOGGER.error(IntegratedWatchdog.obtainThreadDump()); + try { Thread.sleep(60000); } catch(InterruptedException e) {} + } + } + }; + t.setDaemon(true); + t.start(); + } } public void onServerStarted() { diff --git a/common/src/main/java/org/embeddedt/modernfix/api/entrypoint/ModernFixClientIntegration.java b/common/src/main/java/org/embeddedt/modernfix/api/entrypoint/ModernFixClientIntegration.java index f5757766..aa3b78bc 100644 --- a/common/src/main/java/org/embeddedt/modernfix/api/entrypoint/ModernFixClientIntegration.java +++ b/common/src/main/java/org/embeddedt/modernfix/api/entrypoint/ModernFixClientIntegration.java @@ -34,6 +34,19 @@ public interface ModernFixClientIntegration { return originalModel; } + /** + * Called to allow mods to observe the use of an unbaked model at bake time and either make changes to it or wrap it with their + * own instance. + * @param location the ResourceLocation of the model (this may be a ModelResourceLocation) + * @param originalModel the original model + * @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect + * with dynamic resources on + * @return the model which should actually be loaded for this resource location + */ + default UnbakedModel onUnbakedModelPreBake(ResourceLocation location, UnbakedModel originalModel, ModelBakery bakery) { + return originalModel; + } + /** * Called to allow mods to observe the loading of a baked model and either make changes to it or wrap it with their * own instance. diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java index 3e87a8c0..75de5fed 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java @@ -6,27 +6,24 @@ import org.embeddedt.modernfix.util.CanonizingStringMap; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Map; @Mixin(CompoundTag.class) public class CompoundTagMixin { - @Shadow @Final @Mutable + @Shadow @Final private Map tags; /** - * Ensure that the backing map is always a CanonizingStringMap. + * Ensure that the default backing map is a CanonizingStringMap. */ - @Redirect(method = "(Ljava/util/Map;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/nbt/CompoundTag;tags:Ljava/util/Map;", ordinal = 0)) - private void replaceTagMap(CompoundTag tag, Map incomingMap) { - if(incomingMap instanceof CanonizingStringMap) - this.tags = incomingMap; - else { - this.tags = new CanonizingStringMap<>(); - this.tags.putAll(incomingMap); - } + @ModifyArg(method = "()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;(Ljava/util/Map;)V"), index = 0) + private static Map useCanonizingStringMap(Map incoming) { + CanonizingStringMap newMap = new CanonizingStringMap<>(); + newMap.putAll(incoming); + return newMap; } /** diff --git a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index f52536b8..8ecd0453 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -143,6 +143,7 @@ public class ModernFixEarlyConfig { .put("mixin.perf.dynamic_entity_renderers", false) .put("mixin.feature.integrated_server_watchdog", true) .put("mixin.perf.faster_item_rendering", false) + .put("mixin.feature.spam_thread_dump", false) .put("mixin.devenv", isDevEnv) .put("mixin.perf.remove_spawn_chunks", isDevEnv) .build(); diff --git a/common/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java b/common/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java index 32a5707a..ae7c987e 100644 --- a/common/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java +++ b/common/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java @@ -25,6 +25,27 @@ public class IntegratedWatchdog extends Thread { this.setName("ModernFix integrated server watchdog"); } + public static String obtainThreadDump() { + ThreadMXBean threadmxbean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] athreadinfo = threadmxbean.dumpAllThreads(true, true); + StringBuilder sb = new StringBuilder(); + sb.append("Thread Dump:\n"); + for(ThreadInfo threadinfo : athreadinfo) { + sb.append(threadinfo); + StackTraceElement[] elements = threadinfo.getStackTrace(); + if(elements.length > 8) { + sb.append("extended trace:\n"); + for(int i = 8; i < elements.length; i++) { + sb.append("\tat "); + sb.append(elements[i]); + sb.append('\n'); + } + } + sb.append('\n'); + } + return sb.toString(); + } + public void run() { while(true) { MinecraftServer server = this.server.get(); @@ -35,24 +56,7 @@ public class IntegratedWatchdog extends Thread { long delta = curTime - nextTick; if(delta > MAX_TICK_DELTA) { LOGGER.error("A single server tick has taken {}, more than {} milliseconds", delta, MAX_TICK_DELTA); - ThreadMXBean threadmxbean = ManagementFactory.getThreadMXBean(); - ThreadInfo[] athreadinfo = threadmxbean.dumpAllThreads(true, true); - StringBuilder sb = new StringBuilder(); - sb.append("Thread Dump:\n"); - for(ThreadInfo threadinfo : athreadinfo) { - sb.append(threadinfo); - StackTraceElement[] elements = threadinfo.getStackTrace(); - if(elements.length > 8) { - sb.append("extended trace:\n"); - for(int i = 8; i < elements.length; i++) { - sb.append("\tat "); - sb.append(elements[i]); - sb.append('\n'); - } - } - sb.append('\n'); - } - LOGGER.error(sb.toString()); + LOGGER.error(obtainThreadDump()); nextTick = 0; curTime = 0; } diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java index a141a754..64b0a284 100644 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -455,6 +455,17 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { if(iunbakedmodel == missingModel && debugDynamicModelLoading) LOGGER.warn("Model {} not present", arg); + + if(iunbakedmodel != missingModel) { + for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) { + try { + iunbakedmodel = integration.onUnbakedModelPreBake(arg, iunbakedmodel, (ModelBakery)(Object)this); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Exception firing model pre-bake event for {}", arg, e); + } + } + } + BakedModel ibakedmodel = null; if (iunbakedmodel instanceof BlockModel) { BlockModel blockmodel = (BlockModel)iunbakedmodel; diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java index b0eeecfe..d3687a03 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -322,6 +322,17 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { iunbakedmodel.getMaterials(this::getModel, new HashSet<>()); if(iunbakedmodel == missingModel && debugDynamicModelLoading) LOGGER.warn("Model {} not present", arg); + + if(iunbakedmodel != missingModel) { + for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) { + try { + iunbakedmodel = integration.onUnbakedModelPreBake(arg, iunbakedmodel, (ModelBakery)(Object)this); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Exception encountered firing bake event for {}", arg, e); + } + } + } + BakedModel ibakedmodel = null; if (iunbakedmodel instanceof BlockModel) { BlockModel blockmodel = (BlockModel)iunbakedmodel;