Skip to content

Commit 53cc458

Browse files
committed
Merge remote-tracking branch 'origin/1.18' into 1.19.2
2 parents 97b6672 + 3492f9a commit 53cc458

File tree

12 files changed

+246
-25
lines changed

12 files changed

+246
-25
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ allprojects {
9494
maven {
9595
url 'https://maven.terraformersmc.com/releases'
9696
}
97+
maven { url = "https://jitpack.io" }
9798
}
9899
}
99100

@@ -206,6 +207,7 @@ configure(subprojects.findAll {it.name == "forge" || it.name == "fabric"}) {
206207
client {
207208
vmArgs "-Xmx1G"
208209
vmArgs "-Xms1G"
210+
property("mixin.debug.export", "true")
209211
}
210212
}
211213
}

common/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
1111
// Do NOT use other classes from fabric loader
1212
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
13+
implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
1314

1415
modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") {
1516
transitive = false
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package org.embeddedt.modernfix.chunk;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.server.level.ServerLevel;
5+
import net.minecraft.world.level.BlockGetter;
6+
import net.minecraft.world.level.block.Blocks;
7+
import net.minecraft.world.level.block.entity.BlockEntity;
8+
import net.minecraft.world.level.block.state.BlockState;
9+
import net.minecraft.world.level.chunk.ChunkAccess;
10+
import net.minecraft.world.level.chunk.ChunkStatus;
11+
import net.minecraft.world.level.material.FluidState;
12+
import net.minecraft.world.level.material.Fluids;
13+
import org.jetbrains.annotations.Nullable;
14+
15+
public class SafeBlockGetter implements BlockGetter {
16+
private final ServerLevel wrapped;
17+
private final Thread mainThread;
18+
19+
public SafeBlockGetter(ServerLevel wrapped) {
20+
this.wrapped = wrapped;
21+
this.mainThread = Thread.currentThread();
22+
}
23+
24+
public boolean shouldUse() {
25+
return Thread.currentThread() != this.mainThread;
26+
}
27+
28+
@Nullable
29+
private BlockGetter getChunkSafe(BlockPos pos) {
30+
// can safely call getChunkForLighting off-thread
31+
BlockGetter access = this.wrapped.getChunkSource().getChunkForLighting(pos.getX() >> 4, pos.getZ() >> 4);
32+
if(!(access instanceof ChunkAccess))
33+
return null;
34+
ChunkAccess chunk = (ChunkAccess)access;
35+
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
36+
return null;
37+
return chunk;
38+
}
39+
40+
@Override
41+
public int getMaxBuildHeight() {
42+
return this.wrapped.getMaxBuildHeight();
43+
}
44+
45+
@Override
46+
public int getMaxLightLevel() {
47+
return this.wrapped.getMaxLightLevel();
48+
}
49+
50+
@Override
51+
public int getMinBuildHeight() {
52+
return this.wrapped.getMinBuildHeight();
53+
}
54+
55+
@Override
56+
public int getHeight() {
57+
return this.wrapped.getHeight();
58+
}
59+
60+
@Nullable
61+
@Override
62+
public BlockEntity getBlockEntity(BlockPos pos) {
63+
BlockGetter g = getChunkSafe(pos);
64+
return g == null ? null : g.getBlockEntity(pos);
65+
}
66+
67+
@Override
68+
public BlockState getBlockState(BlockPos pos) {
69+
BlockGetter g = getChunkSafe(pos);
70+
return g == null ? Blocks.AIR.defaultBlockState() : g.getBlockState(pos);
71+
}
72+
73+
@Override
74+
public FluidState getFluidState(BlockPos pos) {
75+
BlockGetter g = getChunkSafe(pos);
76+
return g == null ? Fluids.EMPTY.defaultFluidState() : g.getFluidState(pos);
77+
}
78+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
2+
3+
import net.minecraft.world.level.BlockGetter;
4+
import net.minecraft.world.level.block.state.BlockBehaviour;
5+
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
6+
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.injection.At;
9+
import org.spongepowered.asm.mixin.injection.ModifyVariable;
10+
11+
@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100)
12+
public class BlockStateBaseMixin {
13+
@ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1)
14+
private BlockGetter useSafeGetter(BlockGetter g) {
15+
if(g instanceof ISafeBlockGetter) {
16+
SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter();
17+
if(replacement.shouldUse())
18+
return replacement;
19+
}
20+
return g;
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
2+
3+
import net.minecraft.server.level.ServerLevel;
4+
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
5+
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.Unique;
8+
9+
@Mixin(ServerLevel.class)
10+
public class ServerLevelMixin implements ISafeBlockGetter {
11+
@Unique
12+
private final SafeBlockGetter mfix$safeBlockGetter = new SafeBlockGetter((ServerLevel)(Object)this);
13+
14+
@Override
15+
public SafeBlockGetter mfix$getSafeBlockGetter() {
16+
return mfix$safeBlockGetter;
17+
}
18+
}

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
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.embeddedt.modernfix.duck;
2+
3+
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
4+
5+
public interface ISafeBlockGetter {
6+
SafeBlockGetter mfix$getSafeBlockGetter();
7+
}

common/src/main/resources/assets/modernfix/lang/zh_cn.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
"modernfix.jei_load": "正在加载JEI,这可能会花费一段时间。",
55
"modernfix.no_lazydfu": "未安装DFU载入优化。如果Minecraft需要从旧版本更新游戏数据,可能会出现极大的延迟。",
66
"modernfix.no_ferritecore": "未安装铁氧体磁芯。内存占用将会非常高。",
7+
"modernfix.connectedness_dynresoruces": "Connectedness模组(用于提供连接纹理)和现代化修复的动态资源(dynamic resources)功能不兼容。请删除Connectedness模组,或在现代化修复配置中禁用动态资源功能。",
78
"modernfix.perf_mod_warning": "推荐安装这些模组,但你也可以在现代化修复的配置中禁用此警告。",
89
"modernfix.config": "现代化修复Mixin配置",
910
"modernfix.config.done_restart": "完成(生效需重启)",
10-
"modernfix.message.reload_config": "在游戏外编辑完配置文件后,使用§b/mfrc§r命令使其生效",
11+
"modernfix.message.reload_config": "检测到模组配置文件的更改。为了避免加载尚未保存完毕的文件,重载过程必须通过使用§b/mfrc§r命令来触发",
1112
"modernfix.option.on": "开启",
1213
"modernfix.option.off": "关闭",
1314
"modernfix.option.disabled": "已禁用",

fabric/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ configurations {
3131
dependencies {
3232
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
3333
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
34+
include(implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-fabric:${rootProject.mixinextras_version}")))
3435

3536
modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
3637
modCompileOnly(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }

0 commit comments

Comments
 (0)