Skip to content

Commit 351e4d3

Browse files
committed
Merge remote-tracking branch 'origin/1.19.2' into 1.19.4
2 parents d13cfd5 + 5079ce6 commit 351e4d3

File tree

14 files changed

+324
-25
lines changed

14 files changed

+324
-25
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ allprojects {
105105
maven {
106106
url 'https://maven.terraformersmc.com/releases'
107107
}
108+
maven { url = "https://jitpack.io" }
108109
}
109110
}
110111

@@ -217,6 +218,7 @@ configure(subprojects.findAll {it.name == "forge" || it.name == "fabric"}) {
217218
client {
218219
vmArgs "-Xmx1G"
219220
vmArgs "-Xms1G"
221+
property("mixin.debug.export", "true")
220222
}
221223
}
222224
}

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+
}
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.world_screen_skipped;
2+
3+
import net.minecraft.client.Minecraft;
4+
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
5+
import net.minecraft.client.gui.screens.worldselection.WorldSelectionList;
6+
import org.spongepowered.asm.mixin.Final;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.Shadow;
9+
import org.spongepowered.asm.mixin.injection.At;
10+
import org.spongepowered.asm.mixin.injection.Inject;
11+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
12+
13+
@Mixin(WorldSelectionList.WorldListEntry.class)
14+
public class WorldSelectionListMixin {
15+
@Shadow @Final private Minecraft minecraft;
16+
17+
@Inject(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/worldselection/WorldSelectionList$WorldListEntry;doDeleteWorld()V", ordinal = 0, shift = At.Shift.AFTER), cancellable = true)
18+
private void preventClosingCreateScreenAfterDelete(CallbackInfo ci) {
19+
if(minecraft.screen instanceof CreateWorldScreen)
20+
ci.cancel();
21+
}
22+
}

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
@@ -11,13 +11,10 @@
1111
import net.minecraft.world.level.material.Fluids;
1212
import org.embeddedt.modernfix.duck.IBlockState;
1313
import org.objectweb.asm.Opcodes;
14-
import org.spongepowered.asm.mixin.Dynamic;
1514
import org.spongepowered.asm.mixin.Mixin;
1615
import org.spongepowered.asm.mixin.Shadow;
1716
import org.spongepowered.asm.mixin.injection.At;
18-
import org.spongepowered.asm.mixin.injection.Inject;
1917
import org.spongepowered.asm.mixin.injection.Redirect;
20-
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
2118

2219

2320
@Mixin(BlockBehaviour.BlockStateBase.class)
@@ -48,7 +45,7 @@ public boolean isCacheInvalid() {
4845
return cacheInvalid;
4946
}
5047

51-
private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) {
48+
private void mfix$generateCache() {
5249
if(cacheInvalid) {
5350
// Ensure that only one block's cache is built at a time
5451
synchronized (BlockBehaviour.BlockStateBase.class) {
@@ -67,7 +64,6 @@ private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockSt
6764

6865
}
6966
}
70-
return this.cache;
7167
}
7268

7369
@Redirect(method = "*", at = @At(
@@ -77,7 +73,8 @@ private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockSt
7773
ordinal = 0
7874
))
7975
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
80-
return generateCache(base);
76+
mfix$generateCache();
77+
return this.cache;
8178
}
8279

8380
@Redirect(method = "*", at = @At(
@@ -106,22 +103,4 @@ private boolean genCacheBeforeGettingTicking(BlockBehaviour.BlockStateBase base)
106103
return this.owner.isRandomlyTicking(this.asState());
107104
return this.isRandomlyTicking;
108105
}
109-
110-
@Dynamic
111-
@Inject(method = "getPathNodeType", at = @At("HEAD"), require = 0, remap = false)
112-
private void generateCacheLithium(CallbackInfoReturnable<?> cir) {
113-
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
114-
}
115-
116-
@Dynamic
117-
@Inject(method = "getNeighborPathNodeType", at = @At("HEAD"), require = 0, remap = false)
118-
private void generateCacheLithium2(CallbackInfoReturnable<?> cir) {
119-
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
120-
}
121-
122-
@Dynamic
123-
@Inject(method = "getAllFlags", at = @At("HEAD"), require = 0, remap = false)
124-
private void generateCacheLithium3(CallbackInfoReturnable<?> cir) {
125-
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
126-
}
127106
}

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": "已禁用",

0 commit comments

Comments
 (0)