diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/MinecraftMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/MinecraftMixin.java index 08ac0530..53c3b846 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/MinecraftMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/measure_time/MinecraftMixin.java @@ -20,7 +20,7 @@ public class MinecraftMixin { @Inject(method = "tick", at = @At("HEAD")) private void onClientTick(CallbackInfo ci) { - if(this.overlay == null) { + if(this.overlay == null && ModernFixClient.INSTANCE != null) { ModernFixClient.INSTANCE.onGameLaunchFinish(); } } diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/blast_search_trees/MinecraftMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/blast_search_trees/MinecraftMixin.java index d4421e56..3cef3b8c 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/blast_search_trees/MinecraftMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/blast_search_trees/MinecraftMixin.java @@ -33,7 +33,6 @@ public abstract class MinecraftMixin { if(provider == null) return; ModernFix.LOGGER.info("Replacing search trees with '{}' provider", provider.getName()); - mfix$runItemFillingQuirk(); SearchRegistry.TreeBuilderSupplier nameSupplier = list -> provider.getSearchTree(false); SearchRegistry.TreeBuilderSupplier tagSupplier = list -> provider.getSearchTree(true); this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, nameSupplier); @@ -50,8 +49,4 @@ public abstract class MinecraftMixin { GLFW.glfwSetErrorCallback(oldCb); ci.cancel(); } - - private void mfix$runItemFillingQuirk() { - // currently unimplemented on 1.19.4+ - } } diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/BlockEntityTypeMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/BlockEntityTypeMixin.java new file mode 100644 index 00000000..23180408 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/BlockEntityTypeMixin.java @@ -0,0 +1,19 @@ +package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.types.Type; +import net.minecraft.world.level.block.entity.BlockEntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/** + * Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here. + */ +@Mixin(BlockEntityType.class) +public class BlockEntityTypeMixin { + @Redirect(method = "register", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;")) + private static Type skipSchemaCheck(DSL.TypeReference ref, String s) { + return null; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/EntityTypeBuilderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/EntityTypeBuilderMixin.java new file mode 100644 index 00000000..737778f7 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/EntityTypeBuilderMixin.java @@ -0,0 +1,19 @@ +package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.types.Type; +import net.minecraft.world.entity.EntityType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/** + * Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here. + */ +@Mixin(EntityType.Builder.class) +public class EntityTypeBuilderMixin { + @Redirect(method = "build", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;")) + private Type skipSchemaCheck(DSL.TypeReference ref, String s) { + return null; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/SharedConstantsMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/SharedConstantsMixin.java deleted file mode 100644 index ece6aaf5..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_dfu/SharedConstantsMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu; - -import net.minecraft.SharedConstants; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(SharedConstants.class) -public class SharedConstantsMixin { - @Inject(method = "", at = @At("RETURN")) - private static void skipSchemaCheck(CallbackInfo ci) { - SharedConstants.CHECK_DATA_FIXER_SCHEMA = false; - } -} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java index 84acec8a..3d3b3acc 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java @@ -1,7 +1,14 @@ package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds; +import com.google.common.collect.ImmutableMap; +import com.mojang.serialization.MapCodec; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateHolder; +import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; import org.embeddedt.modernfix.duck.IBlockState; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Dynamic; @@ -14,13 +21,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(BlockBehaviour.BlockStateBase.class) -public abstract class BlockStateBaseMixin implements IBlockState { +public abstract class BlockStateBaseMixin extends StateHolder implements IBlockState { + protected BlockStateBaseMixin(Block object, ImmutableMap, Comparable> immutableMap, MapCodec mapCodec) { + super(object, immutableMap, mapCodec); + } + + private static final FluidState MFIX$VANILLA_DEFAULT_FLUID = Fluids.EMPTY.defaultFluidState(); + @Shadow public abstract void initCache(); @Shadow private BlockBehaviour.BlockStateBase.Cache cache; @Shadow private FluidState fluidState; @Shadow private boolean isRandomlyTicking; + @Shadow protected abstract BlockState asState(); + private volatile boolean cacheInvalid = false; private static boolean buildingCache = false; @Override @@ -72,7 +87,11 @@ public abstract class BlockStateBaseMixin implements IBlockState { ordinal = 0 ), require = 0) private FluidState genCacheBeforeGettingFluid(BlockBehaviour.BlockStateBase base) { - generateCache(base); + // don't generate the full cache here as mods will iterate for the fluid state a lot + // assume blockstates will not change their contained fluidstate at runtime more than once + // this is how Lithium's implementation used to work, so it should be fine + if(this.cacheInvalid && this.fluidState == MFIX$VANILLA_DEFAULT_FLUID) + this.fluidState = this.owner.getFluidState(this.asState()); return this.fluidState; } @@ -83,7 +102,8 @@ public abstract class BlockStateBaseMixin implements IBlockState { ordinal = 0 )) private boolean genCacheBeforeGettingTicking(BlockBehaviour.BlockStateBase base) { - generateCache(base); + if(this.cacheInvalid) + return this.owner.isRandomlyTicking(this.asState()); return this.isRandomlyTicking; } 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 498092f5..bb97dc39 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 @@ -173,6 +173,7 @@ public class ModernFixEarlyConfig { .put("mixin.devenv", isDevEnv) .put("mixin.perf.remove_spawn_chunks", isDevEnv) .putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true) + .putConditionally(() -> !isFabric, "mixin.bugfix.forge_at_inject_error", true) .putConditionally(() -> isFabric, "mixin.perf.clear_fabric_mapping_tables", false) .build(); diff --git a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java b/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java index 1603f0f3..06b6a697 100644 --- a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java +++ b/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java @@ -9,9 +9,12 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BlockModelRotation; import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.Direction; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import org.apache.commons.lang3.tuple.Triple; import org.embeddedt.modernfix.duck.IExtendedModelBakery; @@ -106,6 +109,27 @@ public class DynamicBakedModelProvider implements Map blockOpt = BuiltInRegistries.BLOCK.getOptional(registryKey); + if(blockOpt.isPresent()) { + return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), mrl).size() > 0; + } + } catch(RuntimeException ignored) { + // can occur if the MRL is not valid for that blockstate, ignore + } + } + if(location.getNamespace().equals("minecraft") && location.getPath().equals("builtin/missing")) + return true; + return false; + } @Override public BakedModel get(Object o) { @@ -120,11 +144,11 @@ public class DynamicBakedModelProvider implements Map copyTopLevelModelList(Map map) { + return new ArrayList<>(map.values()); + } + private BiFunction textureGetter; @Inject(method = "bakeModels", at = @At("HEAD")) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java b/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java new file mode 100644 index 00000000..f86b9623 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/classloading/ATInjector.java @@ -0,0 +1,36 @@ +package org.embeddedt.modernfix.forge.classloading; + +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModValidator; +import net.minecraftforge.fml.util.ObfuscationReflectionHelper; +import org.apache.commons.lang3.tuple.Pair; +import org.embeddedt.modernfix.core.ModernFixMixinPlugin; +import org.embeddedt.modernfix.util.CommonModUtil; + +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +public class ATInjector { + public static void injectModATs() { + CommonModUtil.runWithoutCrash(() -> { + ModValidator validator = ObfuscationReflectionHelper.getPrivateValue(FMLLoader.class, null, "modValidator"); + List modFiles = ObfuscationReflectionHelper.getPrivateValue(ModValidator.class, validator, "candidateMods"); + List> list = modFiles.stream() + .filter(file -> file.getAccessTransformer().isPresent()) + .map(file -> Pair.of(file, file.getAccessTransformer().get())) + .collect(Collectors.toList()); + if(list.size() > 0) { + ModernFixMixinPlugin.instance.logger.warn("Applying ATs from {} mods despite being in errored state, this might cause a crash!", list.size()); + for(var pair : list) { + try { + FMLLoader.addAccessTransformer(pair.getRight(), pair.getLeft()); + } catch(RuntimeException e) { + ModernFixMixinPlugin.instance.logger.error("Exception occured applying AT from {}", pair.getLeft().getFileName(), e); + } + } + } + }, "applying mod ATs in errored state"); + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java index b08e75f1..96db11ec 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java @@ -8,6 +8,9 @@ import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; public class ConfigFixer { @@ -35,6 +38,7 @@ public class ConfigFixer { private static class LockingConfigHandler implements Consumer { private final Consumer actualHandler; private final String modId; + private final Lock lock = new ReentrantLock(); LockingConfigHandler(String id, Consumer actualHandler) { this.modId = id; @@ -43,14 +47,17 @@ public class ConfigFixer { @Override public void accept(IConfigEvent modConfigEvent) { - Object cfgObj = NightConfigFixer.toWriteSyncConfig(modConfigEvent.getConfig().getConfigData()); - if(cfgObj != null) { - // don't synchronize on 'this' as it produces a deadlock when used alongside NightConfigFixer - synchronized (cfgObj) { - this.actualHandler.accept(modConfigEvent); - } - } else { - this.actualHandler.accept(modConfigEvent); + try { + if(lock.tryLock(2, TimeUnit.SECONDS)) { + try { + this.actualHandler.accept(modConfigEvent); + } finally { + lock.unlock(); + } + } else + ModernFix.LOGGER.error("Failed to post config event for {}, someone else is holding the lock", modId); + } catch(InterruptedException e) { + Thread.currentThread().interrupt(); } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java index b0879cdd..f412dae2 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java @@ -1,29 +1,29 @@ package org.embeddedt.modernfix.forge.config; -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.electronwill.nightconfig.core.file.FileConfig; import com.electronwill.nightconfig.core.file.FileWatcher; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.util.ObfuscationReflectionHelper; +import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.util.CommonModUtil; import java.lang.reflect.Field; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.function.Function; -/** - * Relatively simple patch to wait for config saving to finish, made complex by Night Config classes being package-private, - * and Forge not allowing mixins into libraries. - */ public class NightConfigFixer { + public static final LinkedHashSet configsToReload = new LinkedHashSet<>(); public static void monitorFileWatcher() { + if(!ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.fix_config_crashes.NightConfigFixerMixin")) + return; CommonModUtil.runWithoutCrash(() -> { FileWatcher watcher = FileWatcher.defaultInstance(); Field field = FileWatcher.class.getDeclaredField("watchedFiles"); @@ -35,34 +35,26 @@ public class NightConfigFixer { }, "replacing Night Config watchedFiles map"); } - private static final Class WATCHED_FILE = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile")); - private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper.findField(WATCHED_FILE, "changeHandler"); - - public static final Class WRITE_SYNC_CONFIG = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.WriteSyncFileConfig")); - private static final Class AUTOSAVE_CONFIG = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.AutosaveCommentedFileConfig")); - private static final Field AUTOSAVE_FILECONFIG = ObfuscationReflectionHelper.findField(AUTOSAVE_CONFIG, "fileConfig"); - - private static final Field CURRENTLY_WRITING = ObfuscationReflectionHelper.findField(WRITE_SYNC_CONFIG, "currentlyWriting"); - - private static final Map, Field> CONFIG_WATCHER_TO_CONFIG_FIELD = Collections.synchronizedMap(new HashMap<>()); - - private static Field getCurrentlyWritingFieldOnWatcher(Object watcher) { - return CONFIG_WATCHER_TO_CONFIG_FIELD.computeIfAbsent(watcher.getClass(), clz -> { - while(clz != null && clz != Object.class) { - for(Field f : clz.getDeclaredFields()) { - if(CommentedFileConfig.class.isAssignableFrom(f.getType())) { - f.setAccessible(true); - ModernFixMixinPlugin.instance.logger.debug("Found CommentedFileConfig: field '{}' on {}", f.getName(), clz.getName()); - return f; - } - } - clz = clz.getSuperclass(); + public static void runReloads() { + List runnablesToRun; + synchronized (configsToReload) { + runnablesToRun = new ArrayList<>(configsToReload); + configsToReload.clear(); + } + for(Runnable r : runnablesToRun) { + try { + r.run(); + } catch(RuntimeException e) { + e.printStackTrace(); } - return null; - }); + } + ModernFix.LOGGER.info("Processed {} config reloads", runnablesToRun.size()); } static class MonitoringMap extends ConcurrentHashMap { + private static final Class WATCHED_FILE = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile")); + private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper.findField(WATCHED_FILE, "changeHandler"); + public MonitoringMap(ConcurrentHashMap oldMap) { super(oldMap); } @@ -82,24 +74,16 @@ public class NightConfigFixer { } } - private static final Set> UNKNOWN_FILE_CONFIG_CLASSES = Collections.synchronizedSet(new ReferenceOpenHashSet<>()); + private static long lastConfigTrigger = System.nanoTime(); - public static Object toWriteSyncConfig(Object config) { - if(config == null) - return null; - try { - if(WRITE_SYNC_CONFIG.isAssignableFrom(config.getClass())) { - return config; - } else if(AUTOSAVE_CONFIG.isAssignableFrom(config.getClass())) { - FileConfig fc = (FileConfig)AUTOSAVE_FILECONFIG.get(config); - return toWriteSyncConfig(fc); - } else { - if (UNKNOWN_FILE_CONFIG_CLASSES.add(config.getClass())) - ModernFixMixinPlugin.instance.logger.warn("Unexpected FileConfig class: {}", config.getClass().getName()); - return null; - } - } catch(ReflectiveOperationException e) { - return null; + private static void triggerConfigMessage() { + if((System.nanoTime() - lastConfigTrigger) >= TimeUnit.SECONDS.toNanos(5)) { + lastConfigTrigger = System.nanoTime(); + Minecraft.getInstance().execute(() -> { + if(Minecraft.getInstance().level != null) { + Minecraft.getInstance().gui.getChat().addMessage(Component.translatable("modernfix.message.reload_config")); + } + }); } } @@ -110,47 +94,17 @@ public class NightConfigFixer { this.configTracker = r; } - private void protectFromSaving(FileConfig config, Runnable runnable) throws ReflectiveOperationException { - Object writeSyncConfig = toWriteSyncConfig(config); - if(writeSyncConfig != null) { - // keep trying to write, releasing the config lock each time in case something else needs to lock it - // for any reason - while(true) { - // acquiring synchronized block here should in theory prevent any other concurrent loads/saves, based - // off WriteSyncFileConfig implementation - synchronized (writeSyncConfig) { - if(CURRENTLY_WRITING.getBoolean(writeSyncConfig)) { - ModernFixMixinPlugin.instance.logger.fatal("Config being written during load!!!"); - try { Thread.sleep(500); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } - continue; - } - // at this point, currentlyWriting is false, and we acquired synchronized lock, should be good to - // go - runnable.run(); - break; - } - } - } else { - runnable.run(); - } - } - /** - * This entrypoint runs when the file watcher has detected a change on the config file. Before passing - * this through to Forge, use reflection hacks to confirm the config system is not still writing to the file. - * If it is, spin until writing finishes. Immediately returning might result in the event never being observed. + * Add the config runnable to the list to be processed by the main thread. */ @Override public void run() { - try { - Field theField = getCurrentlyWritingFieldOnWatcher(this.configTracker); - if(theField != null) { - CommentedFileConfig cfg = (CommentedFileConfig)theField.get(this.configTracker); - // will synchronize and check saving flag - protectFromSaving(cfg, configTracker); - } - } catch(ReflectiveOperationException e) { - e.printStackTrace(); + synchronized(configsToReload) { + if(FMLLoader.getDist().isClient()) + triggerConfigMessage(); + if(configsToReload.size() == 0) + ModernFixMixinPlugin.instance.logger.info("Please use /{} to reload any changed mod config files", FMLLoader.getDist().isDedicatedServer() ? "mfsrc" : "mfrc"); + configsToReload.add(configTracker); } } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/IModelBakerImpl.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/IModelBakerImpl.java new file mode 100644 index 00000000..b2cced35 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/IModelBakerImpl.java @@ -0,0 +1,5 @@ +package org.embeddedt.modernfix.forge.dynresources; + +public interface IModelBakerImpl { + void mfix$ignoreCache(); +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java index ffe4cc87..f89d1c20 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/dynresources/ModelBakeEventHelper.java @@ -6,6 +6,7 @@ import com.google.common.collect.Sets; import com.google.common.graph.GraphBuilder; import com.google.common.graph.MutableGraph; import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; @@ -14,6 +15,7 @@ import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.registries.ForgeRegistries; +import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; import org.jetbrains.annotations.Nullable; @@ -69,12 +71,23 @@ public class ModelBakeEventHelper { if(modIdsToInclude.stream().noneMatch(INCOMPATIBLE_MODS::contains)) return this.modelRegistry; Set ourModelLocations = Sets.filter(this.topLevelModelLocations, loc -> modIdsToInclude.contains(loc.getNamespace())); + BakedModel missingModel = modelRegistry.get(ModelBakery.MISSING_MODEL_LOCATION); return new ForwardingMap() { @Override protected Map delegate() { return modelRegistry; } + @Override + public BakedModel get(@Nullable Object key) { + BakedModel model = super.get(key); + if(model == null && key != null && modIdsToInclude.contains(((ResourceLocation)key).getNamespace())) { + ModernFix.LOGGER.warn("Model {} is missing, but was requested in model bake event. Returning missing model", key); + return missingModel; + } + return model; + } + @Override public Set keySet() { return ourModelLocations; diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java index 879a8202..af34719a 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java @@ -1,12 +1,15 @@ package org.embeddedt.modernfix.forge.init; import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.DebugScreenOverlay; +import net.minecraft.commands.CommandSourceStack; import net.minecraftforge.client.ConfigScreenHandler; import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; import net.minecraftforge.client.event.RecipesUpdatedEvent; +import net.minecraftforge.client.event.RegisterClientCommandsEvent; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.client.gui.overlay.ForgeGui; import net.minecraftforge.client.settings.KeyConflictContext; @@ -20,6 +23,7 @@ import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import org.embeddedt.modernfix.ModernFixClient; +import org.embeddedt.modernfix.forge.config.NightConfigFixer; import org.embeddedt.modernfix.screen.ModernFixConfigScreen; import java.util.ArrayList; @@ -51,6 +55,15 @@ public class ModernFixClientForge { } } + @SubscribeEvent(priority = EventPriority.LOW) + public void onClientChat(RegisterClientCommandsEvent event) { + event.getDispatcher().register(LiteralArgumentBuilder.literal("mfrc") + .executes(context -> { + NightConfigFixer.runReloads(); + return 1; + })); + } + private static final List brandingList = new ArrayList<>(); @SubscribeEvent(priority = EventPriority.HIGHEST) diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java index ceaa06ec..d732d0d9 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java @@ -1,11 +1,14 @@ package org.embeddedt.modernfix.forge.init; import com.google.common.collect.ImmutableList; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.OnDatapackSyncEvent; +import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; import net.minecraftforge.event.server.ServerStoppedEvent; import net.minecraftforge.eventbus.api.EventPriority; @@ -28,6 +31,7 @@ import org.embeddedt.modernfix.forge.ModernFixConfig; import org.embeddedt.modernfix.forge.classloading.ClassLoadHack; import org.embeddedt.modernfix.forge.classloading.ModFileScanDataDeduplicator; import org.embeddedt.modernfix.forge.config.ConfigFixer; +import org.embeddedt.modernfix.forge.config.NightConfigFixer; import org.embeddedt.modernfix.forge.packet.PacketHandler; import org.embeddedt.modernfix.forge.registry.ObjectHolderClearer; @@ -36,6 +40,7 @@ import java.util.List; @Mod(ModernFix.MODID) public class ModernFixForge { private static ModernFix commonMod; + public static boolean launchDone = false; public ModernFixForge() { commonMod = new ModernFix(); @@ -52,6 +57,18 @@ public class ModernFixForge { ConfigFixer.replaceConfigHandlers(); } + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { + for(String name : new String[] { "mfsrc"}) { + event.getDispatcher().register(LiteralArgumentBuilder.literal(name) + .requires(source -> source.hasPermission(3)) + .executes(context -> { + NightConfigFixer.runReloads(); + return 1; + })); + } + } + @SubscribeEvent public void onDatapackSync(OnDatapackSyncEvent event) { if(event.getPlayer() != null) { 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 ba9ee2e9..9ca72ed5 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 @@ -9,6 +9,7 @@ import org.embeddedt.modernfix.ModernFixClient; import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration; import org.embeddedt.modernfix.duck.IExtendedModelBakery; import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; +import org.embeddedt.modernfix.forge.dynresources.IModelBakerImpl; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -19,16 +20,23 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.function.Function; @Mixin(ModelBakery.ModelBakerImpl.class) -public abstract class ModelBakerImplMixin { +public abstract class ModelBakerImplMixin implements IModelBakerImpl { private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading"); @Shadow @Final private ModelBakery field_40571; @Shadow public abstract UnbakedModel getModel(ResourceLocation arg); + private boolean mfix$ignoreCache = false; + + @Override + public void mfix$ignoreCache() { + mfix$ignoreCache = true; + } + @Inject(method = "bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;Ljava/util/function/Function;)Lnet/minecraft/client/resources/model/BakedModel;", at = @At("HEAD"), cancellable = true, remap = false) public void getOrLoadBakedModelDynamic(ResourceLocation arg, ModelState arg2, Function textureGetter, CallbackInfoReturnable cir) { ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(arg, arg2.getRotation(), arg2.isUvLocked()); - BakedModel existing = this.field_40571.bakedCache.get(key); + BakedModel existing = mfix$ignoreCache ? null : this.field_40571.bakedCache.get(key); if (existing != null) { cir.setReturnValue(existing); } else { 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 fe2d51d9..adf3c186 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 @@ -146,6 +146,14 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { return ImmutableList.of(); } + /** + * Make a copy of the top-level model list to avoid CME if more models get loaded here. + */ + @Redirect(method = "", at = @At(value = "INVOKE", target = "Ljava/util/Map;values()Ljava/util/Collection;", ordinal = 0)) + private Collection copyTopLevelModelList(Map map) { + return new ArrayList<>(map.values()); + } + private BiFunction textureGetter; @Inject(method = "bakeModels", at = @At("HEAD")) @@ -266,7 +274,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { public BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state) { ModelBakery self = (ModelBakery) (Object) this; ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation); - return theBaker.bake(modelLocation, state); + return theBaker.bake(modelLocation, state, theBaker.getModelTextureGetter()); } @Override diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java index 76c909b7..34e740a5 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java @@ -1,20 +1,28 @@ package org.embeddedt.modernfix.forge.mixin.perf.dynamic_resources.ctm; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import com.mojang.datafixers.util.Pair; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.*; import net.minecraft.resources.ResourceLocation; import org.embeddedt.modernfix.ModernFixClient; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.annotation.RequiresMod; import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration; -import org.embeddedt.modernfix.api.helpers.ModelHelpers; +import org.embeddedt.modernfix.forge.dynresources.IModelBakerImpl; +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 team.chisel.ctm.CTM; +import team.chisel.ctm.api.model.IModelCTM; +import team.chisel.ctm.client.mixin.ModelBakerImplAccessor; import team.chisel.ctm.client.model.AbstractCTMBakedModel; +import team.chisel.ctm.client.model.ModelCTM; import team.chisel.ctm.client.texture.IMetadataSectionCTM; import team.chisel.ctm.client.util.ResourceUtil; import team.chisel.ctm.client.util.TextureMetadataHandler; @@ -22,20 +30,23 @@ import team.chisel.ctm.client.util.TextureMetadataHandler; import javax.annotation.Nonnull; import java.io.IOException; import java.util.*; +import java.util.function.Function; @Mixin(TextureMetadataHandler.class) @RequiresMod("ctm") @ClientOnlyMixin public abstract class TextureMetadataHandlerMixin implements ModernFixClientIntegration { - @Shadow @Nonnull protected abstract BakedModel wrap(ResourceLocation loc, UnbakedModel model, BakedModel object, ModelBaker loader) throws IOException; + @Shadow @Nonnull protected abstract BakedModel wrap(UnbakedModel model, BakedModel object) throws IOException; + + @Shadow @Final public static Multimap TEXTURES_SCRAPED; @Inject(method = "", at = @At("RETURN")) private void subscribeDynamic(CallbackInfo ci) { ModernFixClient.CLIENT_INTEGRATIONS.add(this); } - @Inject(method = "onModelBake", at = @At("HEAD"), cancellable = true, remap = false) + @Inject(method = { "onModelBake(Lnet/minecraftforge/client/event/ModelEvent$ModifyBakingResult;)V", "onModelBake(Lnet/minecraftforge/client/event/ModelEvent$BakingCompleted;)V" }, at = @At("HEAD"), cancellable = true, remap = false) private void noIteration(CallbackInfo ci) { ci.cancel(); } @@ -59,8 +70,7 @@ public abstract class TextureMetadataHandlerMixin implements ModernFixClientInte continue; } - // TODO port - Collection textures = Collections.emptyList(); // model.getMaterials(event.getModelLoader()::getModel, errors); + Collection textures = Sets.newHashSet(TEXTURES_SCRAPED.get(dep)); Collection newDependencies = model.getDependencies(); for (Material tex : textures) { IMetadataSectionCTM meta = null; @@ -82,7 +92,8 @@ public abstract class TextureMetadataHandlerMixin implements ModernFixClientInte } if (shouldWrap) { try { - baked = wrap(rl, rootModel, baked, ModelHelpers.adaptBakery(bakery)); + baked = wrap(rootModel, baked); + handleInit(rl, baked, bakery); dependencies.clear(); } catch (IOException e) { CTM.logger.error("Could not wrap model " + rl + ". Aborting...", e); @@ -91,4 +102,23 @@ public abstract class TextureMetadataHandlerMixin implements ModernFixClientInte } return baked; } + + private void handleInit(ResourceLocation key, BakedModel wrappedModel, ModelBakery bakery) { + if(wrappedModel instanceof AbstractCTMBakedModel baked) { + IModelCTM var10 = baked.getModel(); + if (var10 instanceof ModelCTM ctmModel) { + if (!ctmModel.isInitialized()) { + Function spriteGetter = (m) -> { + return Minecraft.getInstance().getModelManager().getAtlas(m.atlasLocation()).getSprite(m.texture()); + }; + ModelBakery.ModelBakerImpl baker = ModelBakerImplAccessor.createImpl(bakery, ($, m) -> { + return spriteGetter.apply(m); + }, key); + // bypass bakedCache so that dependent models get re-baked and thus retrieve their sprites again + ((IModelBakerImpl)baker).mfix$ignoreCache(); + ctmModel.bake(baker, spriteGetter, BlockModelRotation.X0_Y0, key); + } + } + } + } } 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 c744f03f..f03444c1 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 @@ -22,8 +22,10 @@ import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.server.ServerLifecycleHooks; import org.embeddedt.modernfix.api.constants.IntegrationConstants; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; +import org.embeddedt.modernfix.forge.classloading.ATInjector; import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList; import org.embeddedt.modernfix.forge.config.NightConfigFixer; +import org.embeddedt.modernfix.forge.init.ModernFixForge; import org.embeddedt.modernfix.forge.packet.PacketHandler; import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; import org.embeddedt.modernfix.spark.SparkLaunchProfiler; @@ -92,6 +94,9 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks { } public void injectPlatformSpecificHacks() { + if(!isEarlyLoadingNormally() && ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.forge_at_inject_error.ATInjector")) { + ATInjector.injectModATs(); + } FastAccessTransformerList.attemptReplace(); /* https://github.com/FabricMC/Mixin/pull/99 */ @@ -164,6 +169,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks { if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnForge")) { CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler"); } + ModernFixForge.launchDone = true; } public String getPlatformName() { diff --git a/gradle.properties b/gradle.properties index 361dd306..5baa61b3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ forge_version=1.20.1-47.0.14 refined_storage_version=4392788 jei_version=13.1.0.2 rei_version=11.0.597 -ctm_version=1.19.2-1.1.7+11 +ctm_version=1.20.1-1.1.8+4 kubejs_version=1902.6.0-build.142 rhino_version=1902.2.2-build.268 supported_minecraft_versions=23w31a