Skip to content

Commit f05d339

Browse files
committed
Merge remote-tracking branch 'origin/1.18' into 1.19.2
2 parents c5f73c5 + f225e6c commit f05d339

File tree

7 files changed

+117
-101
lines changed

7 files changed

+117
-101
lines changed

common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ public DefaultSettingMapBuilder put(String key, Boolean value) {
172172
.put("mixin.devenv", isDevEnv)
173173
.put("mixin.perf.remove_spawn_chunks", isDevEnv)
174174
.putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true)
175+
.putConditionally(() -> !isFabric, "mixin.bugfix.forge_at_inject_error", true)
175176
.putConditionally(() -> isFabric, "mixin.perf.clear_fabric_mapping_tables", false)
176177
.build();
177178

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.embeddedt.modernfix.forge.classloading;
2+
3+
import net.minecraftforge.fml.loading.FMLLoader;
4+
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
5+
import net.minecraftforge.fml.loading.moddiscovery.ModValidator;
6+
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
7+
import org.apache.commons.lang3.tuple.Pair;
8+
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
9+
import org.embeddedt.modernfix.util.CommonModUtil;
10+
11+
import java.nio.file.Path;
12+
import java.util.List;
13+
import java.util.stream.Collectors;
14+
15+
public class ATInjector {
16+
public static void injectModATs() {
17+
CommonModUtil.runWithoutCrash(() -> {
18+
ModValidator validator = ObfuscationReflectionHelper.getPrivateValue(FMLLoader.class, null, "modValidator");
19+
List<ModFile> modFiles = ObfuscationReflectionHelper.getPrivateValue(ModValidator.class, validator, "candidateMods");
20+
List<Pair<ModFile, Path>> list = modFiles.stream()
21+
.filter(file -> file.getAccessTransformer().isPresent())
22+
.map(file -> Pair.of(file, file.getAccessTransformer().get()))
23+
.collect(Collectors.toList());
24+
if(list.size() > 0) {
25+
ModernFixMixinPlugin.instance.logger.warn("Applying ATs from {} mods despite being in errored state, this might cause a crash!", list.size());
26+
for(var pair : list) {
27+
try {
28+
FMLLoader.addAccessTransformer(pair.getRight(), pair.getLeft());
29+
} catch(RuntimeException e) {
30+
ModernFixMixinPlugin.instance.logger.error("Exception occured applying AT from {}", pair.getLeft().getFileName(), e);
31+
}
32+
}
33+
}
34+
}, "applying mod ATs in errored state");
35+
}
36+
}

forge/src/main/java/org/embeddedt/modernfix/forge/config/ConfigFixer.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
99

1010
import java.util.Optional;
11+
import java.util.concurrent.TimeUnit;
12+
import java.util.concurrent.locks.Lock;
13+
import java.util.concurrent.locks.ReentrantLock;
1114
import java.util.function.Consumer;
1215

1316
public class ConfigFixer {
@@ -35,6 +38,7 @@ public static void replaceConfigHandlers() {
3538
private static class LockingConfigHandler implements Consumer<IConfigEvent> {
3639
private final Consumer<IConfigEvent> actualHandler;
3740
private final String modId;
41+
private final Lock lock = new ReentrantLock();
3842

3943
LockingConfigHandler(String id, Consumer<IConfigEvent> actualHandler) {
4044
this.modId = id;
@@ -43,14 +47,17 @@ private static class LockingConfigHandler implements Consumer<IConfigEvent> {
4347

4448
@Override
4549
public void accept(IConfigEvent modConfigEvent) {
46-
Object cfgObj = NightConfigFixer.toWriteSyncConfig(modConfigEvent.getConfig().getConfigData());
47-
if(cfgObj != null) {
48-
// don't synchronize on 'this' as it produces a deadlock when used alongside NightConfigFixer
49-
synchronized (cfgObj) {
50-
this.actualHandler.accept(modConfigEvent);
51-
}
52-
} else {
53-
this.actualHandler.accept(modConfigEvent);
50+
try {
51+
if(lock.tryLock(2, TimeUnit.SECONDS)) {
52+
try {
53+
this.actualHandler.accept(modConfigEvent);
54+
} finally {
55+
lock.unlock();
56+
}
57+
} else
58+
ModernFix.LOGGER.error("Failed to post config event for {}, someone else is holding the lock", modId);
59+
} catch(InterruptedException e) {
60+
Thread.currentThread().interrupt();
5461
}
5562
}
5663

forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java

Lines changed: 29 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
package org.embeddedt.modernfix.forge.config;
22

3-
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
4-
import com.electronwill.nightconfig.core.file.FileConfig;
53
import com.electronwill.nightconfig.core.file.FileWatcher;
64
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
7-
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
5+
import net.minecraftforge.fml.loading.FMLLoader;
86
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
7+
import org.embeddedt.modernfix.ModernFix;
98
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
109
import org.embeddedt.modernfix.util.CommonModUtil;
1110

1211
import java.lang.reflect.Field;
1312
import java.nio.file.Path;
14-
import java.util.Collections;
15-
import java.util.HashMap;
16-
import java.util.Map;
17-
import java.util.Set;
13+
import java.util.ArrayList;
14+
import java.util.LinkedHashSet;
15+
import java.util.List;
1816
import java.util.concurrent.ConcurrentHashMap;
1917
import java.util.function.Function;
2018

21-
/**
22-
* Relatively simple patch to wait for config saving to finish, made complex by Night Config classes being package-private,
23-
* and Forge not allowing mixins into libraries.
24-
*/
2519
public class NightConfigFixer {
20+
public static final LinkedHashSet<Runnable> configsToReload = new LinkedHashSet<>();
2621
public static void monitorFileWatcher() {
22+
if(!ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.fix_config_crashes.NightConfigFixerMixin"))
23+
return;
2724
CommonModUtil.runWithoutCrash(() -> {
2825
FileWatcher watcher = FileWatcher.defaultInstance();
2926
Field field = FileWatcher.class.getDeclaredField("watchedFiles");
@@ -35,33 +32,25 @@ public static void monitorFileWatcher() {
3532
}, "replacing Night Config watchedFiles map");
3633
}
3734

38-
private static final Class<?> WATCHED_FILE = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile"));
39-
private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper.findField(WATCHED_FILE, "changeHandler");
40-
41-
public static final Class<?> WRITE_SYNC_CONFIG = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.WriteSyncFileConfig"));
42-
private static final Class<?> AUTOSAVE_CONFIG = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.AutosaveCommentedFileConfig"));
43-
private static final Field AUTOSAVE_FILECONFIG = ObfuscationReflectionHelper.findField(AUTOSAVE_CONFIG, "fileConfig");
44-
45-
private static final Field CURRENTLY_WRITING = ObfuscationReflectionHelper.findField(WRITE_SYNC_CONFIG, "currentlyWriting");
46-
47-
private static final Map<Class<?>, Field> CONFIG_WATCHER_TO_CONFIG_FIELD = Collections.synchronizedMap(new HashMap<>());
48-
49-
private static Field getCurrentlyWritingFieldOnWatcher(Object watcher) {
50-
return CONFIG_WATCHER_TO_CONFIG_FIELD.computeIfAbsent(watcher.getClass(), clz -> {
51-
while(clz != null && clz != Object.class) {
52-
for(Field f : clz.getDeclaredFields()) {
53-
if(CommentedFileConfig.class.isAssignableFrom(f.getType())) {
54-
f.setAccessible(true);
55-
ModernFixMixinPlugin.instance.logger.debug("Found CommentedFileConfig: field '{}' on {}", f.getName(), clz.getName());
56-
return f;
57-
}
58-
}
59-
clz = clz.getSuperclass();
35+
public static void runReloads() {
36+
List<Runnable> runnablesToRun;
37+
synchronized (configsToReload) {
38+
runnablesToRun = new ArrayList<>(configsToReload);
39+
configsToReload.clear();
40+
}
41+
for(Runnable r : runnablesToRun) {
42+
try {
43+
r.run();
44+
} catch(RuntimeException e) {
45+
e.printStackTrace();
6046
}
61-
return null;
62-
});
47+
}
48+
ModernFix.LOGGER.info("Processed {} config reloads", runnablesToRun.size());
6349
}
6450

51+
private static final Class<?> WATCHED_FILE = LamdbaExceptionUtils.uncheck(() -> Class.forName("com.electronwill.nightconfig.core.file.FileWatcher$WatchedFile"));
52+
private static final Field CHANGE_HANDLER = ObfuscationReflectionHelper.findField(WATCHED_FILE, "changeHandler");
53+
6554
static class MonitoringMap extends ConcurrentHashMap<Path, Object> {
6655
public MonitoringMap(ConcurrentHashMap<Path, ?> oldMap) {
6756
super(oldMap);
@@ -82,75 +71,22 @@ public Object computeIfAbsent(Path key, Function<? super Path, ?> mappingFunctio
8271
}
8372
}
8473

85-
private static final Set<Class<?>> UNKNOWN_FILE_CONFIG_CLASSES = Collections.synchronizedSet(new ReferenceOpenHashSet<>());
86-
87-
public static Object toWriteSyncConfig(Object config) {
88-
if(config == null)
89-
return null;
90-
try {
91-
if(WRITE_SYNC_CONFIG.isAssignableFrom(config.getClass())) {
92-
return config;
93-
} else if(AUTOSAVE_CONFIG.isAssignableFrom(config.getClass())) {
94-
FileConfig fc = (FileConfig)AUTOSAVE_FILECONFIG.get(config);
95-
return toWriteSyncConfig(fc);
96-
} else {
97-
if (UNKNOWN_FILE_CONFIG_CLASSES.add(config.getClass()))
98-
ModernFixMixinPlugin.instance.logger.warn("Unexpected FileConfig class: {}", config.getClass().getName());
99-
return null;
100-
}
101-
} catch(ReflectiveOperationException e) {
102-
return null;
103-
}
104-
}
105-
10674
static class MonitoringConfigTracker implements Runnable {
10775
private final Runnable configTracker;
10876

10977
MonitoringConfigTracker(Runnable r) {
11078
this.configTracker = r;
11179
}
11280

113-
private void protectFromSaving(FileConfig config, Runnable runnable) throws ReflectiveOperationException {
114-
Object writeSyncConfig = toWriteSyncConfig(config);
115-
if(writeSyncConfig != null) {
116-
// keep trying to write, releasing the config lock each time in case something else needs to lock it
117-
// for any reason
118-
while(true) {
119-
// acquiring synchronized block here should in theory prevent any other concurrent loads/saves, based
120-
// off WriteSyncFileConfig implementation
121-
synchronized (writeSyncConfig) {
122-
if(CURRENTLY_WRITING.getBoolean(writeSyncConfig)) {
123-
ModernFixMixinPlugin.instance.logger.fatal("Config being written during load!!!");
124-
try { Thread.sleep(500); } catch(InterruptedException e) { Thread.currentThread().interrupt(); }
125-
continue;
126-
}
127-
// at this point, currentlyWriting is false, and we acquired synchronized lock, should be good to
128-
// go
129-
runnable.run();
130-
break;
131-
}
132-
}
133-
} else {
134-
runnable.run();
135-
}
136-
}
137-
13881
/**
139-
* This entrypoint runs when the file watcher has detected a change on the config file. Before passing
140-
* this through to Forge, use reflection hacks to confirm the config system is not still writing to the file.
141-
* If it is, spin until writing finishes. Immediately returning might result in the event never being observed.
82+
* Add the config runnable to the list to be processed by the main thread.
14283
*/
14384
@Override
14485
public void run() {
145-
try {
146-
Field theField = getCurrentlyWritingFieldOnWatcher(this.configTracker);
147-
if(theField != null) {
148-
CommentedFileConfig cfg = (CommentedFileConfig)theField.get(this.configTracker);
149-
// will synchronize and check saving flag
150-
protectFromSaving(cfg, configTracker);
151-
}
152-
} catch(ReflectiveOperationException e) {
153-
e.printStackTrace();
86+
synchronized(configsToReload) {
87+
if(configsToReload.size() == 0)
88+
ModernFixMixinPlugin.instance.logger.info("Please use /{} to reload any changed mod config files", FMLLoader.getDist().isDedicatedServer() ? "mfsrc" : "mfrc");
89+
configsToReload.add(configTracker);
15490
}
15591
}
15692
}

forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import net.minecraft.client.Minecraft;
66
import net.minecraft.client.gui.components.DebugScreenOverlay;
77
import net.minecraftforge.client.ConfigScreenHandler;
8+
import net.minecraftforge.client.event.ClientChatEvent;
89
import net.minecraftforge.client.event.CustomizeGuiOverlayEvent;
910
import net.minecraftforge.client.event.RecipesUpdatedEvent;
1011
import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
@@ -20,6 +21,7 @@
2021
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
2122
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
2223
import org.embeddedt.modernfix.ModernFixClient;
24+
import org.embeddedt.modernfix.forge.config.NightConfigFixer;
2325
import org.embeddedt.modernfix.screen.ModernFixConfigScreen;
2426

2527
import java.util.ArrayList;
@@ -51,6 +53,16 @@ public void onConfigKey(TickEvent.ClientTickEvent event) {
5153
}
5254
}
5355

56+
@SubscribeEvent(priority = EventPriority.LOW)
57+
public void onClientChat(ClientChatEvent event) {
58+
if(event.getMessage() != null && event.getMessage().trim().equals("/mfrc")) {
59+
NightConfigFixer.runReloads();
60+
event.setCanceled(true);
61+
// add it to chat history
62+
Minecraft.getInstance().gui.getChat().addRecentChat(event.getMessage());
63+
}
64+
}
65+
5466
private static final List<String> brandingList = new ArrayList<>();
5567

5668
@SubscribeEvent(priority = EventPriority.HIGHEST)

forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixForge.java

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

33
import com.google.common.collect.ImmutableList;
4+
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
5+
import net.minecraft.commands.CommandSourceStack;
46
import net.minecraft.resources.ResourceLocation;
57
import net.minecraft.world.item.Item;
68
import net.minecraftforge.api.distmarker.Dist;
79
import net.minecraftforge.common.MinecraftForge;
810
import net.minecraftforge.event.OnDatapackSyncEvent;
11+
import net.minecraftforge.event.RegisterCommandsEvent;
912
import net.minecraftforge.event.server.ServerStartedEvent;
1013
import net.minecraftforge.event.server.ServerStoppedEvent;
1114
import net.minecraftforge.eventbus.api.EventPriority;
@@ -29,6 +32,7 @@
2932
import org.embeddedt.modernfix.forge.classloading.ClassLoadHack;
3033
import org.embeddedt.modernfix.forge.classloading.ModFileScanDataDeduplicator;
3134
import org.embeddedt.modernfix.forge.config.ConfigFixer;
35+
import org.embeddedt.modernfix.forge.config.NightConfigFixer;
3236
import org.embeddedt.modernfix.forge.packet.PacketHandler;
3337
import org.embeddedt.modernfix.forge.registry.ObjectHolderClearer;
3438

@@ -37,6 +41,7 @@
3741
@Mod(ModernFix.MODID)
3842
public class ModernFixForge {
3943
private static ModernFix commonMod;
44+
public static boolean launchDone = false;
4045

4146
public ModernFixForge() {
4247
commonMod = new ModernFix();
@@ -53,6 +58,19 @@ public ModernFixForge() {
5358
ConfigFixer.replaceConfigHandlers();
5459
}
5560

61+
@SubscribeEvent
62+
public void onCommandRegister(RegisterCommandsEvent event) {
63+
// Register separate commands since redirecting doesn't work without arguments
64+
for(String name : new String[] { "mfrc", "mfsrc"}) {
65+
event.getDispatcher().register(LiteralArgumentBuilder.<CommandSourceStack>literal(name)
66+
.requires(source -> source.hasPermission(3))
67+
.executes(context -> {
68+
NightConfigFixer.runReloads();
69+
return 1;
70+
}));
71+
}
72+
}
73+
5674
@SubscribeEvent
5775
public void onDatapackSync(OnDatapackSyncEvent event) {
5876
if(event.getPlayer() != null) {

forge/src/main/java/org/embeddedt/modernfix/platform/forge/ModernFixPlatformHooksImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
import net.minecraftforge.server.ServerLifecycleHooks;
2525
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
2626
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
27+
import org.embeddedt.modernfix.forge.classloading.ATInjector;
2728
import org.embeddedt.modernfix.forge.classloading.FastAccessTransformerList;
2829
import org.embeddedt.modernfix.forge.config.NightConfigFixer;
30+
import org.embeddedt.modernfix.forge.init.ModernFixForge;
2931
import org.embeddedt.modernfix.forge.packet.PacketHandler;
3032
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
3133
import org.embeddedt.modernfix.spark.SparkLaunchProfiler;
@@ -139,6 +141,9 @@ public void sendPacket(ServerPlayer player, Object packet) {
139141
}
140142

141143
public void injectPlatformSpecificHacks() {
144+
if(!isEarlyLoadingNormally() && ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.forge_at_inject_error.ATInjector")) {
145+
ATInjector.injectModATs();
146+
}
142147
FastAccessTransformerList.attemptReplace();
143148

144149
/* https://github.com/FabricMC/Mixin/pull/99 */
@@ -194,6 +199,7 @@ public void onLaunchComplete() {
194199
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnForge")) {
195200
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler");
196201
}
202+
ModernFixForge.launchDone = true;
197203
}
198204

199205
public String getPlatformName() {

0 commit comments

Comments
 (0)