diff --git a/common/build.gradle b/common/build.gradle index e0e3b085..d38450e6 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -23,6 +23,9 @@ dependencies { modCompileOnly("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") { transitive = false } + + modCompileOnly("curse.maven:diagonal-fences-458048:${diagonal_fences_version}") + // compile against the JEI API but do not include it at runtime modCompileOnly("mezz.jei:jei-${minecraft_version}-common:${jei_version}") modCompileOnly("mezz.jei:jei-${minecraft_version}-gui:${jei_version}") 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/dynamic_resources/diagonalfences/MultipartAppenderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/diagonalfences/MultipartAppenderMixin.java new file mode 100644 index 00000000..5be40715 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/diagonalfences/MultipartAppenderMixin.java @@ -0,0 +1,48 @@ +package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources.diagonalfences; + +import fuzs.diagonalfences.api.world.level.block.DiagonalBlock; +import fuzs.diagonalfences.client.model.MultipartAppender; +import net.minecraft.client.renderer.block.model.multipart.MultiPart; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FenceBlock; +import org.embeddedt.modernfix.ModernFixClient; +import org.embeddedt.modernfix.annotation.RequiresMod; +import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration; +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; + +@Mixin(MultipartAppender.class) +@RequiresMod("diagonalfences") +public abstract class MultipartAppenderMixin { + @Shadow(remap = false) + public static void appendDiagonalSelectors(ModelBakery modelBakery, MultiPart multiPart) { + throw new AssertionError(); + } + + private static boolean handlerInjected = false; + + @Inject(method = "onPrepareModelBaking", at = @At("RETURN")) + private static void setupHelper(CallbackInfo ci) { + if(handlerInjected) + return; + handlerInjected = true; + ModernFixClient.CLIENT_INTEGRATIONS.add(new ModernFixClientIntegration() { + @Override + public UnbakedModel onUnbakedModelLoad(ResourceLocation location, UnbakedModel originalModel, ModelBakery bakery) { + if(originalModel instanceof MultiPart multipart) { + Block block = multipart.definition.getOwner(); + if(block instanceof FenceBlock && block instanceof DiagonalBlock diagonalBlock && diagonalBlock.hasProperties()) { + appendDiagonalSelectors(bakery, multipart); + } + } + return originalModel; + } + }); + } +} 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 3f0c5a4b..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(); @@ -185,7 +186,7 @@ public class ModernFixEarlyConfig { disableIfModPresent("mixin.launch.class_search_cache", "optifine"); disableIfModPresent("mixin.perf.faster_texture_stitching", "optifine"); disableIfModPresent("mixin.perf.datapack_reload_exceptions", "cyanide"); - disableIfModPresent("mixin.perf.faster_texture_loading", "stitch", "optifine"); + disableIfModPresent("mixin.perf.faster_texture_loading", "stitch", "optifine", "changed"); } private void disableIfModPresent(String configName, String... ids) { diff --git a/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java b/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java index 25cf6267..6bf1ec8f 100644 --- a/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java +++ b/common/src/main/java/org/embeddedt/modernfix/resources/PackResourcesCacheEngine.java @@ -8,6 +8,7 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; import org.embeddedt.modernfix.util.PackTypeHelper; import java.io.IOException; @@ -163,6 +164,8 @@ public class PackResourcesCacheEngine { } public static void invalidate() { + if(!ModernFixPlatformHooks.isDevEnv()) + return; synchronized (cachingPacks) { cachingPacks.keySet().forEach(pack -> { if(pack != null) diff --git a/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java b/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java index e237733a..8b3e41c9 100644 --- a/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java +++ b/common/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java @@ -98,7 +98,7 @@ public class JEIBackedSearchTree extends DummySearchTree { @Override public boolean canUse() { - return ModernFixPlatformHooks.modPresent("jei") && getIngredientListUncached != null && filterField != null; + return ModernFixPlatformHooks.modPresent("jei") && !ModernFixPlatformHooks.modPresent("emi") && getIngredientListUncached != null && filterField != null; } @Override diff --git a/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java b/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java index 6bf07666..08debc7c 100644 --- a/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java +++ b/common/src/main/java/org/embeddedt/modernfix/structure/CachingStructureManager.java @@ -63,7 +63,8 @@ public class CachingStructureManager { currentTag.putInt("DataVersion", 500); } int currentDataVersion = currentTag.getInt("DataVersion"); - if(currentDataVersion < SharedConstants.getCurrentVersion().getDataVersion().getVersion()) { + int requiredMinimumDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); + if(currentDataVersion < requiredMinimumDataVersion) { /* Needs upgrade, try looking up from cache */ MessageDigest hasher = digestThreadLocal.get(); hasher.reset(); @@ -71,13 +72,13 @@ public class CachingStructureManager { CompoundTag cachedUpgraded = getCachedUpgraded(location, truncateHash(hash)); if(cachedUpgraded == null) cachedUpgraded = getCachedUpgraded(location, hash); /* pick up old cache */ - if(cachedUpgraded != null && cachedUpgraded.getInt("DataVersion") == SharedConstants.getCurrentVersion().getDataVersion().getVersion()) { + if(cachedUpgraded != null && cachedUpgraded.getInt("DataVersion") == requiredMinimumDataVersion) { ModernFix.LOGGER.debug("Using cached upgraded version of {}", location); currentTag = cachedUpgraded; } else { synchronized (laggyStructureMods) { if(laggyStructureMods.add(location.getNamespace())) { - ModernFix.LOGGER.warn("Mod {} is shipping outdated structure files, which can cause worldgen lag; please report this to them.", location.getNamespace()); + ModernFix.LOGGER.warn("The namespace {} contains an outdated structure file, which can cause worldgen lag. Please view debug.log for the full filename, determine which mod provides the structure, and report to the mod/datapack author, including the debug log.", location.getNamespace()); } } ModernFix.LOGGER.debug("Structure {} is being run through DFU (hash {}), this will cause launch time delays", location, hash); 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/common/src/main/resources/modernfix.accesswidener b/common/src/main/resources/modernfix.accesswidener index 01657b7f..e422ca5e 100644 --- a/common/src/main/resources/modernfix.accesswidener +++ b/common/src/main/resources/modernfix.accesswidener @@ -36,3 +36,4 @@ accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/s accessible class net/minecraft/server/MinecraftServer$ReloadableResources accessible method net/minecraft/client/gui/screens/Screen addRenderableWidget (Lnet/minecraft/client/gui/components/events/GuiEventListener;)Lnet/minecraft/client/gui/components/events/GuiEventListener; accessible field net/minecraft/client/KeyMapping ALL Ljava/util/Map; +accessible field net/minecraft/client/renderer/block/model/multipart/MultiPart definition Lnet/minecraft/world/level/block/state/StateDefinition; diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java index e184b4e2..22f450e6 100644 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java @@ -95,6 +95,17 @@ public abstract class ModelBakerImplMixin { this.field_40571.topLevelModels.clear(); } else cir.setReturnValue(this.field_40571.getModel(arg)); + UnbakedModel toReplace = cir.getReturnValue(); + if(!wasMissingModel) { + for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) { + try { + toReplace = integration.onUnbakedModelPreBake(arg, toReplace, (ModelBakery)(Object)this); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Exception firing model pre-bake event for {}", arg, e); + } + } + } + cir.setReturnValue(toReplace); if(cir.getReturnValue() == extendedBakery.mfix$getUnbakedMissingModel()) { if(arg != ModelBakery.MISSING_MODEL_LOCATION && debugDynamicModelLoading) ModernFix.LOGGER.warn("Model {} not present", arg); diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/safety/DynamicTextureMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/safety/DynamicTextureMixin.java new file mode 100644 index 00000000..5cc7f1ac --- /dev/null +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/safety/DynamicTextureMixin.java @@ -0,0 +1,30 @@ +package org.embeddedt.modernfix.fabric.mixin.safety; + +import com.mojang.blaze3d.platform.NativeImage; +import net.minecraft.client.renderer.texture.DynamicTexture; +import org.embeddedt.modernfix.ModernFix; +import org.jetbrains.annotations.Nullable; +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; + +@Mixin(DynamicTexture.class) +public class DynamicTextureMixin { + @Shadow @Nullable private NativeImage pixels; + + private Exception closeTrace; + + @Inject(method = "method_22793", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/texture/DynamicTexture;pixels:Lcom/mojang/blaze3d/platform/NativeImage;", ordinal = 0)) + private void checkNullPixels(CallbackInfo ci) { + if(pixels == null) { + ModernFix.LOGGER.error("Attempted to upload null texture! This is not allowed, closed here", closeTrace); + } + } + + @Inject(method = "close", at = @At("HEAD")) + private void storeCloseTrace(CallbackInfo ci) { + closeTrace = new Exception(); + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakerImplMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakerImplMixin.java index a1e21bd9..7402eb9b 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakerImplMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakerImplMixin.java @@ -48,6 +48,15 @@ public abstract class ModelBakerImplMixin { ibakedmodel = ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(textureGetter, blockmodel).bake((ModelBaker)this, blockmodel, textureGetter, arg2, arg, false); } } + if(iunbakedmodel != extendedBakery.mfix$getUnbakedMissingModel()) { + 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); + } + } + } if(ibakedmodel == null) { if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) { // use a shared baked missing model diff --git a/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java b/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java index fab24381..183cb7d5 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java +++ b/forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java @@ -17,12 +17,11 @@ import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.FMLPaths; import net.minecraftforge.fml.loading.LoadingModList; -import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.server.ServerLifecycleHooks; -import net.minecraftforge.fml.loading.moddiscovery.ModInfo; -import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.api.constants.IntegrationConstants; +import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList; import org.embeddedt.modernfix.forge.packet.PacketHandler; import org.embeddedt.modernfix.util.DummyList; @@ -59,7 +58,7 @@ public class ModernFixPlatformHooksImpl { } public static boolean isDevEnv() { - return !FMLLoader.isProduction() && FMLLoader.getLoadingModList().getModFileById("modernfix").getFile().getProvider() instanceof ExplodedDirectoryLocator; + return !FMLLoader.isProduction(); } public static MinecraftServer getCurrentServer() { diff --git a/gradle.properties b/gradle.properties index 6c8c5965..1b7c7558 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,4 @@ fabric_api_version=0.80.0+1.19.4 continuity_version=3.0.0-beta.2+1.19.3 modmenu_version=6.2.2 +diagonal_fences_version=4545943