Skip to content

Commit fa47e92

Browse files
committed
Better compatibility with mods that inject into initCache
1 parent 1ad6ec4 commit fa47e92

File tree

3 files changed

+113
-24
lines changed

3 files changed

+113
-24
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ configure(subprojects.findAll {it.name == "forge" || it.name == "fabric"}) {
206206
client {
207207
vmArgs "-Xmx1G"
208208
vmArgs "-Xms1G"
209+
property("mixin.debug.export", "true")
209210
}
210211
}
211212
}

common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,10 @@
33
import net.minecraft.world.level.block.state.BlockBehaviour;
44
import org.embeddedt.modernfix.duck.IBlockState;
55
import org.objectweb.asm.Opcodes;
6-
import org.spongepowered.asm.mixin.Dynamic;
76
import org.spongepowered.asm.mixin.Mixin;
87
import org.spongepowered.asm.mixin.Shadow;
98
import org.spongepowered.asm.mixin.injection.At;
10-
import org.spongepowered.asm.mixin.injection.Inject;
119
import org.spongepowered.asm.mixin.injection.Redirect;
12-
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
1310

1411

1512
@Mixin(BlockBehaviour.BlockStateBase.class)
@@ -30,7 +27,7 @@ public boolean isCacheInvalid() {
3027
return cacheInvalid;
3128
}
3229

33-
private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) {
30+
private void mfix$generateCache() {
3431
if(cacheInvalid) {
3532
// Ensure that only one block's cache is built at a time
3633
synchronized (BlockBehaviour.BlockStateBase.class) {
@@ -49,7 +46,6 @@ private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockSt
4946

5047
}
5148
}
52-
return this.cache;
5349
}
5450

5551
@Redirect(method = "*", at = @At(
@@ -59,24 +55,7 @@ private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockSt
5955
ordinal = 0
6056
))
6157
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
62-
return generateCache(base);
63-
}
64-
65-
@Dynamic
66-
@Inject(method = "getPathNodeType", at = @At("HEAD"), require = 0, remap = false)
67-
private void generateCacheLithium(CallbackInfoReturnable<?> cir) {
68-
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
69-
}
70-
71-
@Dynamic
72-
@Inject(method = "getNeighborPathNodeType", at = @At("HEAD"), require = 0, remap = false)
73-
private void generateCacheLithium2(CallbackInfoReturnable<?> cir) {
74-
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
75-
}
76-
77-
@Dynamic
78-
@Inject(method = "getAllFlags", at = @At("HEAD"), require = 0, remap = false)
79-
private void generateCacheLithium3(CallbackInfoReturnable<?> cir) {
80-
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
58+
mfix$generateCache();
59+
return this.cache;
8160
}
8261
}

common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package org.embeddedt.modernfix.core;
22

3+
import com.google.common.collect.ImmutableSet;
34
import org.apache.logging.log4j.LogManager;
45
import org.apache.logging.log4j.Logger;
56
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
67
import org.embeddedt.modernfix.core.config.Option;
78
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
89
import org.embeddedt.modernfix.world.ThreadDumper;
10+
import org.objectweb.asm.Opcodes;
11+
import org.objectweb.asm.Type;
912
import org.objectweb.asm.tree.*;
1013
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
1114
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
15+
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
1216

1317
import java.io.File;
1418
import java.util.*;
@@ -146,6 +150,111 @@ public void preApply(String targetClassName, ClassNode targetClass, String mixin
146150

147151
@Override
148152
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
153+
if(mixinClassName.equals("org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds.BlockStateBaseMixin")) {
154+
try {
155+
applyBlockStateCacheScan(targetClass);
156+
} catch(RuntimeException e) {
157+
ModernFixMixinPlugin.instance.logger.error("Applying blockstate cache ASM patch failed", e);
158+
}
159+
}
149160
ModernFixPlatformHooks.INSTANCE.applyASMTransformers(mixinClassName, targetClass);
150161
}
162+
163+
private void applyBlockStateCacheScan(ClassNode targetClass) {
164+
Set<String> initCacheMethodNames = ImmutableSet.of("m_60611_", "func_215692_c", "method_26200", "initCache");
165+
Set<String> whitelistedInjections = ImmutableSet.of(
166+
"getFluidState", "method_26227", "m_60819_", "func_204520_s"
167+
);
168+
Map<String, MethodNode> injectorMethodNames = new HashMap<>();
169+
Map<String, String> injectorMixinSource = new HashMap<>();
170+
String descriptor = Type.getDescriptor(MixinMerged.class);
171+
for(MethodNode m : targetClass.methods) {
172+
if((m.access & Opcodes.ACC_STATIC) != 0)
173+
continue;
174+
Set<AnnotationNode> seenNodes = new HashSet<>();
175+
if(m.invisibleAnnotations != null) {
176+
for(AnnotationNode ann : m.invisibleAnnotations) {
177+
if(ann.desc.equals(descriptor)) {
178+
seenNodes.add(ann);
179+
}
180+
}
181+
}
182+
if(m.visibleAnnotations != null) {
183+
for(AnnotationNode ann : m.visibleAnnotations) {
184+
if(ann.desc.equals(descriptor)) {
185+
seenNodes.add(ann);
186+
}
187+
}
188+
}
189+
if(seenNodes.size() > 0) {
190+
injectorMethodNames.put(m.name, m);
191+
for(AnnotationNode node : seenNodes) {
192+
for(int i = 0; i < node.values.size(); i += 2) {
193+
if(Objects.equals(node.values.get(i), "mixin")) {
194+
injectorMixinSource.put(m.name, (String)node.values.get(i + 1));
195+
break;
196+
}
197+
}
198+
}
199+
}
200+
}
201+
Set<String> cacheCalledInjectors = new HashSet<>();
202+
// Search for initCache in the class
203+
for(MethodNode m : targetClass.methods) {
204+
if((m.access & Opcodes.ACC_STATIC) != 0)
205+
continue;
206+
if(initCacheMethodNames.contains(m.name)) {
207+
// This is it. Check for any injectors it calls
208+
for(AbstractInsnNode n : m.instructions) {
209+
if(n instanceof MethodInsnNode) {
210+
MethodInsnNode invoke = (MethodInsnNode)n;
211+
if(((MethodInsnNode)n).owner.equals(targetClass.name) && injectorMethodNames.containsKey(((MethodInsnNode)n).name)) {
212+
cacheCalledInjectors.add(invoke.name);
213+
}
214+
}
215+
}
216+
break;
217+
}
218+
}
219+
Set<String> accessedFieldNames = new HashSet<>();
220+
// We now know all methods that have been injected into initCache. See what fields they write to
221+
injectorMethodNames.forEach((name, method) -> {
222+
if(cacheCalledInjectors.contains(name)) {
223+
for(AbstractInsnNode n : method.instructions) {
224+
if(n instanceof FieldInsnNode) {
225+
FieldInsnNode fieldAcc = (FieldInsnNode)n;
226+
if(fieldAcc.getOpcode() == Opcodes.PUTFIELD && fieldAcc.owner.equals(targetClass.name)) {
227+
accessedFieldNames.add(fieldAcc.name);
228+
}
229+
}
230+
}
231+
}
232+
});
233+
// Lastly, scan all injected methods and see if they retrieve from the field. If so, inject a generateCache
234+
// call at the start.
235+
injectorMethodNames.forEach((name, method) -> {
236+
// skip whitelisted injectors, and injectors called by initCache itself (to prevent recursion)
237+
if(whitelistedInjections.contains(name) || cacheCalledInjectors.contains(name))
238+
return;
239+
boolean needInjection = false;
240+
for(AbstractInsnNode n : method.instructions) {
241+
if(n instanceof FieldInsnNode) {
242+
FieldInsnNode fieldAcc = (FieldInsnNode)n;
243+
if(fieldAcc.getOpcode() == Opcodes.GETFIELD && accessedFieldNames.contains(fieldAcc.name)) {
244+
needInjection = true;
245+
break;
246+
}
247+
}
248+
}
249+
if(needInjection) {
250+
ModernFixMixinPlugin.instance.logger.info("Injecting BlockStateBase cache population hook into {} from {}",
251+
name, injectorMixinSource.getOrDefault(name, "[unknown mixin]"));
252+
// inject this.mfix$generateCache() at method head
253+
InsnList injection = new InsnList();
254+
injection.add(new VarInsnNode(Opcodes.ALOAD, 0));
255+
injection.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, targetClass.name, "mfix$generateCache", "()V"));
256+
method.instructions.insert(injection);
257+
}
258+
});
259+
}
151260
}

0 commit comments

Comments
 (0)