Skip to content

Commit 44763b3

Browse files
5.27.0 - commit.1
- Port most viable patches.
1 parent 5003989 commit 44763b3

70 files changed

Lines changed: 1672 additions & 233 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,4 @@ Temporary Items
182182
# Linux trash folder which might appear on any partition or disk
183183
.Trash-*
184184

185+
ModernFix-NeoForge/

gradle.properties

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ org.gradle.jvmargs=-Xmx2G
44

55
# Mod properties
66
mod_id=modernfix
7-
version=5.26.2-build.2
7+
version=5.27.0-build.1
88

99
# Minecraft/Fabric
10-
minecraft_version=26.1-pre-1
11-
loader_version=0.18.4
12-
loom_version=1.15-SNAPSHOT
10+
minecraft_version=26.1.2
11+
loader_version=0.18.6
12+
loom_version=1.16-SNAPSHOT
1313

1414
# Fabric API
15-
fabric_api_version=0.143.11+26.1
15+
fabric_api_version=0.145.4+26.1.2
1616

1717
# Dependencies
1818
mixinextras_version=0.4.1

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

src/main/java/org/embeddedt/modernfix/ModernFix.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
1414
import org.embeddedt.modernfix.resources.ReloadExecutor;
1515
import org.embeddedt.modernfix.util.ClassInfoManager;
16+
import org.spongepowered.asm.mixin.MixinEnvironment;
1617

1718
import java.lang.management.ManagementFactory;
1819

@@ -35,7 +36,7 @@ public class ModernFix {
3536

3637
static {
3738
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.dedicated_reload_executor.ReloadExecutor")) {
38-
resourceReloadService = ReloadExecutor.createCustomResourceReloadExecutor();
39+
resourceReloadService = new TracingExecutor(ReloadExecutor.createCustomResourceReloadExecutor());
3940
} else {
4041
resourceReloadService = Util.backgroundExecutor();
4142
}
@@ -45,6 +46,15 @@ public static TracingExecutor resourceReloadExecutor() {
4546
return resourceReloadService;
4647
}
4748

49+
public static void runAuditIfRequested() {
50+
boolean auditAndExit = Boolean.getBoolean("modernfix.auditAndExit");
51+
if (auditAndExit || Boolean.getBoolean("modernfix.auditMixinsAtStart")) {
52+
MixinEnvironment.getCurrentEnvironment().audit();
53+
if (auditAndExit) {
54+
System.exit(0);
55+
}
56+
}
57+
}
4858

4959
public ModernFix() {
5060
INSTANCE = this;

src/main/java/org/embeddedt/modernfix/ModernFixClient.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package org.embeddedt.modernfix;
22

33
import net.minecraft.client.Minecraft;
4-
import net.minecraft.network.syncher.EntityDataAccessor;
54
import net.minecraft.server.MinecraftServer;
65
import net.minecraft.util.MemoryReserve;
7-
import net.minecraft.world.entity.Entity;
86
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
97
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
108
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
@@ -98,25 +96,6 @@ public void onRenderTickEnd() {
9896
}
9997
}
10098

101-
/**
102-
* Check if the IDs match and remap them if not.
103-
* @return true if ID remap was needed
104-
*/
105-
private static boolean compareAndSwitchIds(Class<? extends Entity> eClass, String fieldName, EntityDataAccessor<?> accessor, int newId) {
106-
if(accessor.id != newId) {
107-
ModernFix.LOGGER.warn("Corrected ID mismatch on {} field {}. Client had {} but server wants {}.",
108-
eClass,
109-
fieldName,
110-
accessor.id,
111-
newId);
112-
accessor.id = newId;
113-
return true;
114-
} else {
115-
ModernFix.LOGGER.debug("{} {} ID fine: {}", eClass, fieldName, newId);
116-
return false;
117-
}
118-
}
119-
12099
public void onServerStarted(MinecraftServer server) {
121100
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
122101
return;

src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import org.embeddedt.modernfix.duck.IBlockState;
77

88
public class BlockStateCacheHandler {
9-
public static void rebuildParallel(boolean force) {
9+
public static void invalidateCache() {
1010
synchronized (BlockBehaviour.BlockStateBase.class) {
1111
for (BlockState blockState : Block.BLOCK_STATE_REGISTRY) {
1212
((IBlockState)blockState).clearCache();

src/main/java/org/embeddedt/modernfix/command/ModernFixCommands.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,17 @@
22

33
import com.mojang.brigadier.CommandDispatcher;
44
import net.minecraft.commands.CommandSourceStack;
5-
import net.minecraft.commands.Commands;
65
import net.minecraft.network.chat.Component;
76
import net.minecraft.server.level.ServerLevel;
7+
import net.minecraft.server.permissions.Permissions;
88
import org.embeddedt.modernfix.duck.IProfilingServerFunctionManager;
99

10-
1110
import static net.minecraft.commands.Commands.literal;
1211

1312
public class ModernFixCommands {
14-
15-
1613
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
1714
dispatcher.register(literal("modernfix")
18-
.requires(Commands.hasPermission(Commands.LEVEL_ADMINS))
19-
.then(literal("mcfunctions").requires(Commands.hasPermission(Commands.LEVEL_ADMINS))
15+
.then(literal("mcfunctions").requires(source -> source.permissions().hasPermission(Permissions.COMMANDS_ADMIN))
2016
.executes(context -> {
2117
ServerLevel level = context.getSource().getLevel();
2218
if(level == null) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
2+
3+
import com.llamalad7.mixinextras.sugar.Local;
4+
import net.minecraft.CrashReport;
5+
import net.minecraft.ReportedException;
6+
import net.minecraft.server.MinecraftServer;
7+
import net.minecraft.world.level.chunk.ChunkAccess;
8+
import net.minecraft.world.level.chunk.LevelChunk;
9+
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
10+
import net.minecraft.world.level.chunk.status.WorldGenContext;
11+
import org.spongepowered.asm.mixin.Mixin;
12+
import org.spongepowered.asm.mixin.Unique;
13+
import org.spongepowered.asm.mixin.injection.At;
14+
import org.spongepowered.asm.mixin.injection.Inject;
15+
import org.spongepowered.asm.mixin.injection.Redirect;
16+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
17+
18+
import java.util.concurrent.CompletableFuture;
19+
import java.util.concurrent.Executor;
20+
import java.util.function.Supplier;
21+
22+
@Mixin(ChunkStatusTasks.class)
23+
public abstract class ChunkMapLoadMixin {
24+
@Unique
25+
private static final ThreadLocal<CompletableFuture<ChunkAccess>> MFIX_SURROGATE_FUTURE = new ThreadLocal<>();
26+
27+
/**
28+
* @author embeddedt
29+
* @reason This redirect makes several changes to how full chunk promotion works. First of all, promotion runs
30+
* directly in the context of the main thread executor, rather than going through the priority sorter.
31+
* This change allows attempts to load other chunks from within the promotion lambda to succeed (important
32+
* for bad EntityJoinLevelEvent implementations to not deadlock the game). Second, it slightly alters the
33+
* semantics of protoChunkToFullChunk so that the FULL chunk future will be completed before postload
34+
* callbacks finish running. This change allows attempts to load the _same_ chunk in the promotion lambda to
35+
* succeed, as otherwise the future would block waiting for itself to complete.
36+
*
37+
* <p>This is a cleaner version of a similar trick used in ModernFix versions for 1.16, which deferred specifically
38+
* entity addition to happen outside the futures.
39+
*/
40+
@Redirect(method = "full", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;supplyAsync(Ljava/util/function/Supplier;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", ordinal = 0))
41+
private static CompletableFuture<ChunkAccess> createSurrogateFuture(Supplier<ChunkAccess> supplier, Executor executor,
42+
@Local(ordinal = 0, argsOnly = true) WorldGenContext worldGenContext) {
43+
var surrogate = new CompletableFuture<ChunkAccess>();
44+
// Unlike vanilla, we execute the promotion lambda in mainThreadExecutor, rather than within the context
45+
// of the task sorter. Doing this avoids deadlocking the sorter if a blocking chunk load is attempted
46+
// during chunk promotion. We still initially compose the future through the sorter's executor to stop promotion
47+
// from running earlier than it would in vanilla.
48+
var mainThreadExecutor = ((ServerChunkCacheAccessor) worldGenContext.level().getChunkSource()).mfix$getMainThreadProcessor();
49+
CompletableFuture.runAsync(() -> {}, executor).thenApplyAsync($ -> {
50+
// running on thread that executes lambda body
51+
MFIX_SURROGATE_FUTURE.set(surrogate);
52+
try {
53+
return supplier.get();
54+
} finally {
55+
MFIX_SURROGATE_FUTURE.remove();
56+
}
57+
}, mainThreadExecutor).whenComplete((either, throwable) -> {
58+
if (throwable != null) {
59+
if (!surrogate.isDone()) {
60+
surrogate.completeExceptionally(throwable);
61+
} else {
62+
// The chunk has already become visible at FULL status, so we
63+
// track the exception ourselves and manually rethrow it at the right point
64+
// to trigger a server crash
65+
var exc = new ReportedException(CrashReport.forThrowable(throwable, "Exception during promotion of chunk to FULL status"));
66+
mainThreadExecutor.schedule(() -> {
67+
throw exc;
68+
});
69+
}
70+
} else {
71+
surrogate.complete(either);
72+
}
73+
});
74+
// Return the surrogate
75+
return surrogate;
76+
}
77+
78+
/**
79+
* @author embeddedt
80+
* @reason Complete the surrogate future as soon as basic promotion is done, and before we start loading entities
81+
* & block entities. This allows EntityJoinLevelEvent to read the current chunk.
82+
*/
83+
@Inject(method = "lambda$full$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;runPostLoad()V"))
84+
private static void completeSurrogateFuture(CallbackInfoReturnable<ChunkAccess> cir, @Local(ordinal = 0) LevelChunk levelChunk) {
85+
var future = MFIX_SURROGATE_FUTURE.get();
86+
if (future != null) {
87+
future.complete(levelChunk);
88+
}
89+
}
90+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
2+
3+
import net.minecraft.server.level.ServerChunkCache;
4+
import org.spongepowered.asm.mixin.Mixin;
5+
import org.spongepowered.asm.mixin.gen.Accessor;
6+
7+
@Mixin(ServerChunkCache.class)
8+
public interface ServerChunkCacheAccessor {
9+
@Accessor("mainThreadProcessor")
10+
ServerChunkCache.MainThreadExecutor mfix$getMainThreadProcessor();
11+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.embeddedt.modernfix.common.mixin.bugfix.cofh_core_crash;
2+
3+
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
4+
import org.embeddedt.modernfix.annotation.RequiresMod;
5+
import org.spongepowered.asm.mixin.*;
6+
import org.spongepowered.asm.mixin.injection.At;
7+
import org.spongepowered.asm.mixin.injection.Coerce;
8+
import org.spongepowered.asm.mixin.injection.Redirect;
9+
10+
import java.lang.invoke.MethodHandle;
11+
import java.lang.invoke.MethodHandles;
12+
import java.lang.reflect.Method;
13+
14+
/**
15+
* Fix getOrCreateFlag accessing the FLAGS map without synchronization by wrapping all calls to it
16+
* in a synchronized block.
17+
*/
18+
@Pseudo
19+
@Mixin(targets = { "cofh/lib/util/flags/FlagManager" }, remap = false)
20+
@RequiresMod("cofh_core")
21+
public class FlagManagerMixin {
22+
@Shadow @Final
23+
private static Object2ObjectOpenHashMap<String, ?> FLAGS;
24+
25+
@Unique
26+
private static final MethodHandle mfix$getOrCreateFlag;
27+
28+
static {
29+
// use this reflection dance to avoid depending on whether it's implemented via BooleanSupplier or Supplier<Boolean>
30+
try {
31+
Method m = MethodHandles.lookup().lookupClass().getDeclaredMethod("getOrCreateFlag", String.class);
32+
m.setAccessible(true);
33+
mfix$getOrCreateFlag = MethodHandles.lookup().unreflect(m);
34+
} catch(ReflectiveOperationException e) {
35+
throw new AssertionError(e);
36+
}
37+
}
38+
39+
@Redirect(method = "*", at = @At(value = "INVOKE", target = "getOrCreateFlag"), require = 0)
40+
@Coerce
41+
private Object getFlag(@Coerce Object flagHandler, String flag) {
42+
if(flagHandler != this)
43+
throw new AssertionError("Redirect targeted bad getOrCreateFlag invocation");
44+
synchronized (FLAGS) {
45+
try {
46+
return mfix$getOrCreateFlag.invoke((Object)this, flag);
47+
} catch(Throwable e) {
48+
if(e instanceof RuntimeException)
49+
throw (RuntimeException)e;
50+
else
51+
throw new RuntimeException(e);
52+
}
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)