From fa47e923f0f5bbbcaba0ecb5b4e4036e9da451ae Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 12 Aug 2023 21:19:43 -0400 Subject: [PATCH 1/5] Better compatibility with mods that inject into initCache --- build.gradle | 1 + .../BlockStateBaseMixin.java | 27 +---- .../modernfix/core/ModernFixMixinPlugin.java | 109 ++++++++++++++++++ 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index 0df67a03..5d1c67c9 100644 --- a/build.gradle +++ b/build.gradle @@ -206,6 +206,7 @@ configure(subprojects.findAll {it.name == "forge" || it.name == "fabric"}) { client { vmArgs "-Xmx1G" vmArgs "-Xms1G" + property("mixin.debug.export", "true") } } } 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 db024b63..3974cdef 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 @@ -3,13 +3,10 @@ package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuil import net.minecraft.world.level.block.state.BlockBehaviour; import org.embeddedt.modernfix.duck.IBlockState; import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Dynamic; 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.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(BlockBehaviour.BlockStateBase.class) @@ -30,7 +27,7 @@ public abstract class BlockStateBaseMixin implements IBlockState { return cacheInvalid; } - private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) { + private void mfix$generateCache() { if(cacheInvalid) { // Ensure that only one block's cache is built at a time synchronized (BlockBehaviour.BlockStateBase.class) { @@ -49,7 +46,6 @@ public abstract class BlockStateBaseMixin implements IBlockState { } } - return this.cache; } @Redirect(method = "*", at = @At( @@ -59,24 +55,7 @@ public abstract class BlockStateBaseMixin implements IBlockState { ordinal = 0 )) private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) { - return generateCache(base); - } - - @Dynamic - @Inject(method = "getPathNodeType", at = @At("HEAD"), require = 0, remap = false) - private void generateCacheLithium(CallbackInfoReturnable cir) { - generateCache((BlockBehaviour.BlockStateBase)(Object)this); - } - - @Dynamic - @Inject(method = "getNeighborPathNodeType", at = @At("HEAD"), require = 0, remap = false) - private void generateCacheLithium2(CallbackInfoReturnable cir) { - generateCache((BlockBehaviour.BlockStateBase)(Object)this); - } - - @Dynamic - @Inject(method = "getAllFlags", at = @At("HEAD"), require = 0, remap = false) - private void generateCacheLithium3(CallbackInfoReturnable cir) { - generateCache((BlockBehaviour.BlockStateBase)(Object)this); + mfix$generateCache(); + return this.cache; } } diff --git a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 6c902036..c61492a3 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -1,14 +1,18 @@ package org.embeddedt.modernfix.core; +import com.google.common.collect.ImmutableSet; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig; import org.embeddedt.modernfix.core.config.Option; import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; import org.embeddedt.modernfix.world.ThreadDumper; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import org.spongepowered.asm.mixin.transformer.meta.MixinMerged; import java.io.File; import java.util.*; @@ -146,6 +150,111 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + if(mixinClassName.equals("org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds.BlockStateBaseMixin")) { + try { + applyBlockStateCacheScan(targetClass); + } catch(RuntimeException e) { + ModernFixMixinPlugin.instance.logger.error("Applying blockstate cache ASM patch failed", e); + } + } ModernFixPlatformHooks.INSTANCE.applyASMTransformers(mixinClassName, targetClass); } + + private void applyBlockStateCacheScan(ClassNode targetClass) { + Set initCacheMethodNames = ImmutableSet.of("m_60611_", "func_215692_c", "method_26200", "initCache"); + Set whitelistedInjections = ImmutableSet.of( + "getFluidState", "method_26227", "m_60819_", "func_204520_s" + ); + Map injectorMethodNames = new HashMap<>(); + Map injectorMixinSource = new HashMap<>(); + String descriptor = Type.getDescriptor(MixinMerged.class); + for(MethodNode m : targetClass.methods) { + if((m.access & Opcodes.ACC_STATIC) != 0) + continue; + Set seenNodes = new HashSet<>(); + if(m.invisibleAnnotations != null) { + for(AnnotationNode ann : m.invisibleAnnotations) { + if(ann.desc.equals(descriptor)) { + seenNodes.add(ann); + } + } + } + if(m.visibleAnnotations != null) { + for(AnnotationNode ann : m.visibleAnnotations) { + if(ann.desc.equals(descriptor)) { + seenNodes.add(ann); + } + } + } + if(seenNodes.size() > 0) { + injectorMethodNames.put(m.name, m); + for(AnnotationNode node : seenNodes) { + for(int i = 0; i < node.values.size(); i += 2) { + if(Objects.equals(node.values.get(i), "mixin")) { + injectorMixinSource.put(m.name, (String)node.values.get(i + 1)); + break; + } + } + } + } + } + Set cacheCalledInjectors = new HashSet<>(); + // Search for initCache in the class + for(MethodNode m : targetClass.methods) { + if((m.access & Opcodes.ACC_STATIC) != 0) + continue; + if(initCacheMethodNames.contains(m.name)) { + // This is it. Check for any injectors it calls + for(AbstractInsnNode n : m.instructions) { + if(n instanceof MethodInsnNode) { + MethodInsnNode invoke = (MethodInsnNode)n; + if(((MethodInsnNode)n).owner.equals(targetClass.name) && injectorMethodNames.containsKey(((MethodInsnNode)n).name)) { + cacheCalledInjectors.add(invoke.name); + } + } + } + break; + } + } + Set accessedFieldNames = new HashSet<>(); + // We now know all methods that have been injected into initCache. See what fields they write to + injectorMethodNames.forEach((name, method) -> { + if(cacheCalledInjectors.contains(name)) { + for(AbstractInsnNode n : method.instructions) { + if(n instanceof FieldInsnNode) { + FieldInsnNode fieldAcc = (FieldInsnNode)n; + if(fieldAcc.getOpcode() == Opcodes.PUTFIELD && fieldAcc.owner.equals(targetClass.name)) { + accessedFieldNames.add(fieldAcc.name); + } + } + } + } + }); + // Lastly, scan all injected methods and see if they retrieve from the field. If so, inject a generateCache + // call at the start. + injectorMethodNames.forEach((name, method) -> { + // skip whitelisted injectors, and injectors called by initCache itself (to prevent recursion) + if(whitelistedInjections.contains(name) || cacheCalledInjectors.contains(name)) + return; + boolean needInjection = false; + for(AbstractInsnNode n : method.instructions) { + if(n instanceof FieldInsnNode) { + FieldInsnNode fieldAcc = (FieldInsnNode)n; + if(fieldAcc.getOpcode() == Opcodes.GETFIELD && accessedFieldNames.contains(fieldAcc.name)) { + needInjection = true; + break; + } + } + } + if(needInjection) { + ModernFixMixinPlugin.instance.logger.info("Injecting BlockStateBase cache population hook into {} from {}", + name, injectorMixinSource.getOrDefault(name, "[unknown mixin]")); + // inject this.mfix$generateCache() at method head + InsnList injection = new InsnList(); + injection.add(new VarInsnNode(Opcodes.ALOAD, 0)); + injection.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, targetClass.name, "mfix$generateCache", "()V")); + method.instructions.insert(injection); + } + }); + } } \ No newline at end of file From 33e43f5b8fd23bd2428ed2d3f1250f26cccf1dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B6=E6=99=BA=E4=B9=83=E5=8F=8D=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E5=8F=8A?= <100760086+qznfbnj@users.noreply.github.com> Date: Mon, 14 Aug 2023 00:24:33 +0800 Subject: [PATCH 2/5] Update zh_cn.json (#211) --- common/src/main/resources/assets/modernfix/lang/zh_cn.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/resources/assets/modernfix/lang/zh_cn.json b/common/src/main/resources/assets/modernfix/lang/zh_cn.json index 846a902e..f0686d62 100644 --- a/common/src/main/resources/assets/modernfix/lang/zh_cn.json +++ b/common/src/main/resources/assets/modernfix/lang/zh_cn.json @@ -4,10 +4,11 @@ "modernfix.jei_load": "正在加载JEI,这可能会花费一段时间。", "modernfix.no_lazydfu": "未安装DFU载入优化。如果Minecraft需要从旧版本更新游戏数据,可能会出现极大的延迟。", "modernfix.no_ferritecore": "未安装铁氧体磁芯。内存占用将会非常高。", + "modernfix.connectedness_dynresoruces": "Connectedness模组(用于提供连接纹理)和现代化修复的动态资源(dynamic resources)功能不兼容。请删除Connectedness模组,或在现代化修复配置中禁用动态资源功能。", "modernfix.perf_mod_warning": "推荐安装这些模组,但你也可以在现代化修复的配置中禁用此警告。", "modernfix.config": "现代化修复Mixin配置", "modernfix.config.done_restart": "完成(生效需重启)", - "modernfix.message.reload_config": "在游戏外编辑完配置文件后,使用§b/mfrc§r命令使其生效。", + "modernfix.message.reload_config": "检测到模组配置文件的更改。为了避免加载尚未保存完毕的文件,重载过程必须通过使用§b/mfrc§r命令来触发。", "modernfix.option.on": "开启", "modernfix.option.off": "关闭", "modernfix.option.disabled": "已禁用", From d7b2f5b75b9e12f8351ef45d105e998c48402266 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 13 Aug 2023 13:39:24 -0400 Subject: [PATCH 3/5] Prevent mods from causing deadlocks in BlockState.getOffset If this method is called with a ServerLevel, we switch the BlockGetter for a safe wrapper that will only work on loaded chunks Related: https://github.com/N1nn1/twigs/issues/6 Related: https://github.com/N1nn1/etcetera/issues/28 --- .../modernfix/chunk/SafeBlockGetter.java | 68 +++++++++++++++++++ .../chunk_deadlock/BlockStateBaseMixin.java | 22 ++++++ .../chunk_deadlock/ServerLevelMixin.java | 18 +++++ .../modernfix/duck/ISafeBlockGetter.java | 7 ++ 4 files changed, 115 insertions(+) create mode 100644 common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/BlockStateBaseMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/ServerLevelMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/duck/ISafeBlockGetter.java diff --git a/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java b/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java new file mode 100644 index 00000000..3671e8fe --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java @@ -0,0 +1,68 @@ +package org.embeddedt.modernfix.chunk; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import org.jetbrains.annotations.Nullable; + +public class SafeBlockGetter implements BlockGetter { + private final ServerLevel wrapped; + private final Thread mainThread; + + public SafeBlockGetter(ServerLevel wrapped) { + this.wrapped = wrapped; + this.mainThread = Thread.currentThread(); + } + + public boolean shouldUse() { + return Thread.currentThread() != this.mainThread; + } + + @Nullable + private BlockGetter getChunkSafe(BlockPos pos) { + // can safely call getChunkForLighting off-thread + BlockGetter access = this.wrapped.getChunkSource().getChunkForLighting(pos.getX() >> 4, pos.getZ() >> 4); + if(!(access instanceof ChunkAccess)) + return null; + ChunkAccess chunk = (ChunkAccess)access; + if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL)) + return null; + return chunk; + } + + @Override + public int getMaxBuildHeight() { + return this.wrapped.getMaxBuildHeight(); + } + + @Override + public int getMaxLightLevel() { + return this.wrapped.getMaxLightLevel(); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + BlockGetter g = getChunkSafe(pos); + return g == null ? null : g.getBlockEntity(pos); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + BlockGetter g = getChunkSafe(pos); + return g == null ? Blocks.AIR.defaultBlockState() : g.getBlockState(pos); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + BlockGetter g = getChunkSafe(pos); + return g == null ? Fluids.EMPTY.defaultFluidState() : g.getFluidState(pos); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/BlockStateBaseMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/BlockStateBaseMixin.java new file mode 100644 index 00000000..464130c8 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/BlockStateBaseMixin.java @@ -0,0 +1,22 @@ +package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock; + +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.state.BlockBehaviour; +import org.embeddedt.modernfix.chunk.SafeBlockGetter; +import org.embeddedt.modernfix.duck.ISafeBlockGetter; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100) +public class BlockStateBaseMixin { + @ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1) + private BlockGetter useSafeGetter(BlockGetter g) { + if(g instanceof ISafeBlockGetter) { + SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter(); + if(replacement.shouldUse()) + return replacement; + } + return g; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/ServerLevelMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/ServerLevelMixin.java new file mode 100644 index 00000000..e7c3b137 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/ServerLevelMixin.java @@ -0,0 +1,18 @@ +package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock; + +import net.minecraft.server.level.ServerLevel; +import org.embeddedt.modernfix.chunk.SafeBlockGetter; +import org.embeddedt.modernfix.duck.ISafeBlockGetter; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(ServerLevel.class) +public class ServerLevelMixin implements ISafeBlockGetter { + @Unique + private final SafeBlockGetter mfix$safeBlockGetter = new SafeBlockGetter((ServerLevel)(Object)this); + + @Override + public SafeBlockGetter mfix$getSafeBlockGetter() { + return mfix$safeBlockGetter; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/duck/ISafeBlockGetter.java b/common/src/main/java/org/embeddedt/modernfix/duck/ISafeBlockGetter.java new file mode 100644 index 00000000..3fb462ef --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/duck/ISafeBlockGetter.java @@ -0,0 +1,7 @@ +package org.embeddedt.modernfix.duck; + +import org.embeddedt.modernfix.chunk.SafeBlockGetter; + +public interface ISafeBlockGetter { + SafeBlockGetter mfix$getSafeBlockGetter(); +} From 0aef731a79a85aa9ec00b1733b6199c3c38f47a0 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 13 Aug 2023 13:41:36 -0400 Subject: [PATCH 4/5] Update SafeBlockGetter for 1.18 world height --- .../org/embeddedt/modernfix/chunk/SafeBlockGetter.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java b/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java index 3671e8fe..3d4efe52 100644 --- a/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java +++ b/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java @@ -47,6 +47,16 @@ public class SafeBlockGetter implements BlockGetter { return this.wrapped.getMaxLightLevel(); } + @Override + public int getMinBuildHeight() { + return this.wrapped.getMinBuildHeight(); + } + + @Override + public int getHeight() { + return this.wrapped.getHeight(); + } + @Nullable @Override public BlockEntity getBlockEntity(BlockPos pos) { From d9184833ec430e8ae379f01c95cb53b50319bc83 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 13 Aug 2023 15:53:20 -0400 Subject: [PATCH 5/5] Introduce MixinExtras --- build.gradle | 1 + common/build.gradle | 1 + fabric/build.gradle | 1 + forge/build.gradle | 6 +++++- .../platform/forge/ModernFixPlatformHooksImpl.java | 2 ++ gradle.properties | 1 + 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5d1c67c9..6e8b099b 100644 --- a/build.gradle +++ b/build.gradle @@ -94,6 +94,7 @@ allprojects { maven { url 'https://maven.terraformersmc.com/releases' } + maven { url = "https://jitpack.io" } } } diff --git a/common/build.gradle b/common/build.gradle index c067d157..ec4d9687 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -10,6 +10,7 @@ dependencies { // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies // Do NOT use other classes from fabric loader modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}")) modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") { transitive = false diff --git a/fabric/build.gradle b/fabric/build.gradle index 42ff963d..e970eace 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -31,6 +31,7 @@ configurations { dependencies { modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}" + include(implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-fabric:${rootProject.mixinextras_version}"))) modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } modCompileOnly(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } diff --git a/forge/build.gradle b/forge/build.gradle index 3769452b..98886d77 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -45,6 +45,8 @@ repositories { dependencies { forge "net.minecraftforge:forge:${rootProject.forge_version}" + shadow(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}")) + runtimeOnly("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}") // Remove the next line if you don't want to depend on the API // modApi "me.shedaniel:architectury-forge:${rootProject.architectury_version}" @@ -93,8 +95,10 @@ shadowJar { exclude "fabric.mod.json" exclude "architectury.common.json" - configurations = [project.configurations.shadowCommon] + configurations = [project.configurations.shadowCommon, project.configurations.shadow] + relocate("com.llamalad7.mixinextras", "org.embeddedt.modernfix.forge.shadow.mixinextras") archiveClassifier.set("dev-shadow") + mergeServiceFiles() } remapJar { 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 6463a58c..b75ed47f 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 @@ -3,6 +3,7 @@ package org.embeddedt.modernfix.platform.forge; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.google.common.io.Resources; +import com.llamalad7.mixinextras.MixinExtrasBootstrap; import com.mojang.blaze3d.platform.NativeImage; import com.mojang.brigadier.CommandDispatcher; import cpw.mods.modlauncher.*; @@ -195,6 +196,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks { } NightConfigFixer.monitorFileWatcher(); + MixinExtrasBootstrap.init(); } private Method defineClassMethod = null; diff --git a/gradle.properties b/gradle.properties index 0a084c03..1cb73b92 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,6 +2,7 @@ org.gradle.jvmargs=-Xmx2G junit_version=5.10.0-M1 +mixinextras_version=0.2.0-beta.9 mod_id=modernfix minecraft_version=1.16.5