|
25 | 25 | import com.falsepattern.deploader.version.SemanticVersion; |
26 | 26 | import com.falsepattern.deploader.version.Version; |
27 | 27 | import com.google.gson.GsonBuilder; |
| 28 | +import com.google.gson.JsonElement; |
28 | 29 | import com.google.gson.JsonParser; |
29 | 30 | import io.netty.util.internal.ConcurrentSet; |
30 | 31 | import lombok.NonNull; |
|
41 | 42 |
|
42 | 43 | import cpw.mods.fml.common.MetadataCollection; |
43 | 44 | import cpw.mods.fml.common.ModMetadata; |
| 45 | +import cpw.mods.fml.fpdeploader.SystemExitBypassHelper; |
44 | 46 |
|
45 | 47 | import javax.swing.JFrame; |
46 | 48 | import javax.swing.JLabel; |
|
81 | 83 | import java.util.concurrent.atomic.AtomicBoolean; |
82 | 84 | import java.util.concurrent.atomic.AtomicLong; |
83 | 85 | import java.util.function.Consumer; |
| 86 | +import java.util.jar.JarFile; |
84 | 87 | import java.util.jar.JarInputStream; |
85 | 88 | import java.util.regex.Pattern; |
86 | 89 | import java.util.stream.Collectors; |
@@ -123,7 +126,7 @@ public class DependencyLoaderImpl { |
123 | 126 | metadataCollectionModListAccessor.setAccessible(true); |
124 | 127 | } |
125 | 128 |
|
126 | | - private static AtomicBoolean modDownloaded = new AtomicBoolean(false); |
| 129 | + private static AtomicBoolean needReboot = new AtomicBoolean(false); |
127 | 130 |
|
128 | 131 | private static void ensureExists(Path directory) { |
129 | 132 | if (!Files.exists(directory)) { |
@@ -464,17 +467,17 @@ public static void executeDependencyLoading() { |
464 | 467 | baseTasks.addAll(tasks); |
465 | 468 | tasks = executeArtifactLoading(baseTasks, false); |
466 | 469 | } |
467 | | - if (modDownloaded.get()) { |
| 470 | + if (needReboot.get()) { |
| 471 | + val msg = "One or more minecraft mods which have been loaded by the FP DepLoader require a game restart to fully install."; |
468 | 472 | if (!SystemUtils.IS_OS_MAC) { |
469 | 473 | try { |
470 | | - JOptionPane.showMessageDialog(null, "A minecraft mod has been downloaded by the FalsePatternLib dependency downloader.\nYou must restart the game to apply these changes.", "Mod Dependency Download notice", JOptionPane.WARNING_MESSAGE); |
| 474 | + JOptionPane.showMessageDialog(null, msg + "\nThe game will exit when you close this prompt.", "Mod Dependency Download notice", JOptionPane.WARNING_MESSAGE); |
471 | 475 | } catch (Exception ignored) {} |
472 | 476 | } |
473 | | - val msg = "A minecraft mod has been downloaded by the FalsePatternLib dependency downloader, and requires a game restart to get installed properly."; |
474 | 477 | for (int i = 0; i < 16; i++) { |
475 | 478 | LOG.warn(msg); |
476 | 479 | } |
477 | | - System.exit(0); |
| 480 | + SystemExitBypassHelper.exit(); |
478 | 481 | throw new ModDependencyDownloaded(msg); |
479 | 482 | } |
480 | 483 | } |
@@ -1113,7 +1116,7 @@ private void validateDownloadsAllowed() { |
1113 | 1116 | Files.delete(tmpFile); |
1114 | 1117 | } else { |
1115 | 1118 | if (isMod) { |
1116 | | - modDownloaded.set(true); |
| 1119 | + detectModReloadRequirement(tmpFile); |
1117 | 1120 | } |
1118 | 1121 | try { |
1119 | 1122 | Files.move(tmpFile, file, StandardCopyOption.ATOMIC_MOVE); |
@@ -1154,6 +1157,60 @@ private void validateDownloadsAllowed() { |
1154 | 1157 | } |
1155 | 1158 | } |
1156 | 1159 |
|
| 1160 | + private void detectModReloadRequirement(Path file) { |
| 1161 | + if (needReboot.get()) |
| 1162 | + return; |
| 1163 | + try (val jar = new JarFile(file.toFile())) { |
| 1164 | + if (LowLevelCallMultiplexer.rfbDetected) { |
| 1165 | + //Detect RFB plugins |
| 1166 | + val entries = jar.entries(); |
| 1167 | + while (entries.hasMoreElements()) { |
| 1168 | + val entry = entries.nextElement(); |
| 1169 | + if (entry.getName().contains("META-INF/rfb-plugin")) { |
| 1170 | + needReboot.set(true); |
| 1171 | + return; |
| 1172 | + } |
| 1173 | + } |
| 1174 | + return; |
| 1175 | + } |
| 1176 | + //Detect coremods |
| 1177 | + val manifest = jar.getManifest().getMainAttributes(); |
| 1178 | + val isCoreMod = manifest.getValue("TweakClass") != null || manifest.getValue("FMLCorePlugin") != null; |
| 1179 | + if (isCoreMod) { |
| 1180 | + needReboot.set(true); |
| 1181 | + return; |
| 1182 | + } |
| 1183 | + //Detect mixins |
| 1184 | + val entries = jar.entries(); |
| 1185 | + while (entries.hasMoreElements()) { |
| 1186 | + val entry = entries.nextElement(); |
| 1187 | + val name = entry.getName(); |
| 1188 | + if (name.contains("META-INF/rfb-plugin")) { |
| 1189 | + needReboot.set(true); |
| 1190 | + return; |
| 1191 | + } |
| 1192 | + if (!name.endsWith(".json")) { |
| 1193 | + continue; |
| 1194 | + } |
| 1195 | + val data = jar.getInputStream(entry); |
| 1196 | + val gson = new GsonBuilder().create(); |
| 1197 | + val element = gson.fromJson(new InputStreamReader(data), JsonElement.class); |
| 1198 | + if (!element.isJsonObject()) { |
| 1199 | + continue; |
| 1200 | + } |
| 1201 | + val obj = element.getAsJsonObject(); |
| 1202 | + if (obj.has("refmap") || obj.has("compatibilityLevel") || obj.has("target")) { |
| 1203 | + //Most likely contains mixins, better safe than sorry |
| 1204 | + needReboot.set(true); |
| 1205 | + return; |
| 1206 | + } |
| 1207 | + } |
| 1208 | + } catch (IOException e) { |
| 1209 | + //bail out safely |
| 1210 | + needReboot.set(true); |
| 1211 | + } |
| 1212 | + } |
| 1213 | + |
1157 | 1214 | private ChecksumStatus validateChecksum(String url) throws IOException { |
1158 | 1215 | for (val checksumType : CHECKSUM_TYPES) { |
1159 | 1216 | val checksumURL = url + "." + checksumType; |
|
0 commit comments