diff --git a/src/main/java/org/quiltmc/loader/impl/discovery/ClasspathModCandidateFinder.java b/src/main/java/org/quiltmc/loader/impl/discovery/ClasspathModCandidateFinder.java index 5722381c5..db212c547 100644 --- a/src/main/java/org/quiltmc/loader/impl/discovery/ClasspathModCandidateFinder.java +++ b/src/main/java/org/quiltmc/loader/impl/discovery/ClasspathModCandidateFinder.java @@ -56,6 +56,7 @@ public static void findCandidatesStatic(ModAdder out) { if (QuiltLauncherBase.getLauncher().isDevelopment()) { Map> pathGroups = getPathGroups(); try { + findCandidates("quilt.mod.json5", pathGroups, out); findCandidates("quilt.mod.json", pathGroups, out); findCandidates("fabric.mod.json", pathGroups, out); } catch (IOException e) { @@ -84,21 +85,27 @@ private static void findCandidates(String metadataName, Map> pa Path path = LoaderUtil.normalizeExistingPath(UrlUtil.getCodeSource(url, metadataName)); List paths = pathGroups.get(path); - if (paths == null) { - if (!url.getProtocol().equals("jar")) { - if (!pathGroups.isEmpty()) { - // path groups are enabled, warn for misconfiguration - Log.error(LogCategory.DISCOVERY,"Mod at path " + url + " lacks a class path group! Make sure the loom 'mods' block " + - "is configured correctly!"); + // If this is specifically reading for quilt.mod.json files, we check against QMJ5s as well + // This is so we can ignore QMJs from mods that already have a QMJ5 + if (metadataName.equals("quilt.mod.json") && Files.isRegularFile(path.resolve("quilt.mod.json5"))) { + Log.debug(LogCategory.DISCOVERY, "Ignoring quilt.mod.json from mod at path %s that already has a quilt.mod.json5", path); + } else { + if (paths == null) { + if (!url.getProtocol().equals("jar")) { + if (!pathGroups.isEmpty()) { + // path groups are enabled, warn for misconfiguration + Log.error(LogCategory.DISCOVERY,"Mod at path " + url + " lacks a class path group! Make sure the loom 'mods' block " + + "is configured correctly!"); + } + + Log.error(LogCategory.DISCOVERY, "Mod at path %s is not included in the loom 'mods' block! " + + "This will be an error in a future version of Loader!", path); } - Log.error(LogCategory.DISCOVERY, "Mod at path %s is not included in the loom 'mods' block! " + - "This will be an error in a future version of Loader!", path); - } - - out.addMod(Collections.singletonList(path)); - } else { - out.addMod(paths); + out.addMod(Collections.singletonList(path)); + } else { + out.addMod(paths); + } } } catch (UrlConversionException e) { Log.debug(LogCategory.DISCOVERY, "Error determining location for %s from %s", metadataName, url, e); diff --git a/src/main/java/org/quiltmc/loader/impl/metadata/qmj/ModMetadataReader.java b/src/main/java/org/quiltmc/loader/impl/metadata/qmj/ModMetadataReader.java index d57856faf..dab9a3f3c 100644 --- a/src/main/java/org/quiltmc/loader/impl/metadata/qmj/ModMetadataReader.java +++ b/src/main/java/org/quiltmc/loader/impl/metadata/qmj/ModMetadataReader.java @@ -71,7 +71,12 @@ public static InternalModMetadata read(InputStream json) throws IOException, Par public static InternalModMetadata read(InputStream json, Path path, QuiltPluginManager manager, PluginGuiTreeNode warningNode) throws IOException, ParseException { JsonLoaderValue value; - try (JsonReader reader = JsonReader.json(new InputStreamReader(json, StandardCharsets.UTF_8))) { + try (JsonReader reader = JsonReader.json5(new InputStreamReader(json, StandardCharsets.UTF_8))) { + // Only use the reader as a JSON5 one if we're dealing with a JSON5 file + if (!path.toString().endsWith(".json5")) { + reader.setStrictJson(); + } + // Root must be an object if (reader.peek() != JsonToken.BEGIN_OBJECT) { throw new ParseException(reader, "A quilt.mod.json must have an object at the root"); diff --git a/src/main/java/org/quiltmc/loader/impl/plugin/quilt/StandardQuiltPlugin.java b/src/main/java/org/quiltmc/loader/impl/plugin/quilt/StandardQuiltPlugin.java index 0d02fd283..555ac6f1e 100644 --- a/src/main/java/org/quiltmc/loader/impl/plugin/quilt/StandardQuiltPlugin.java +++ b/src/main/java/org/quiltmc/loader/impl/plugin/quilt/StandardQuiltPlugin.java @@ -280,12 +280,86 @@ private ModLoadOption[] scan0(Path root, QuiltLoaderIcon fileIcon, ModLocation l PluginGuiTreeNode guiNode) throws IOException { Path qmj = root.resolve("quilt.mod.json"); - if (!FasterFiles.isRegularFile(qmj)) { + Path qmj5 = root.resolve("quilt.mod.json5"); + Path usedQmj = qmj; + + if (FasterFiles.isRegularFile(qmj5)) { + if (QuiltLoader.isDevelopmentEnvironment()) { + if (Boolean.parseBoolean(System.getProperty(SystemProperties.ENABLE_QUILT_MOD_JSON5_IN_DEV_ENV))) { + if (FasterFiles.exists(qmj)) { + QuiltLoaderText title = QuiltLoaderText.translate("gui.text.qmj_qmj5_coexistence.title"); + QuiltDisplayedError error = context().reportError(title); + String describedPath = context().manager().describePath(usedQmj); + error.appendReportText( + "A coexistence of 'quilt.mod.json5' and 'quilt.mod.json' has been detected at " + describedPath, + "These metadata files cannot coexist with each other due to their conflicting purposes!" + ); + error.appendDescription( + QuiltLoaderText.translate("gui.text.qmj_qmj5_coexistence.desc.0"), + QuiltLoaderText.translate("gui.text.qmj_qmj5_coexistence.desc.1"), + QuiltLoaderText.translate("gui.text.qmj_qmj5_coexistence.desc.2", describedPath) + ); + context().manager().getRealContainingFile(root).ifPresent(real -> + error.addFileViewButton(real) + .icon(QuiltLoaderGui.iconJarFile().withDecoration(QuiltLoaderGui.iconQuilt())) + ); + + guiNode.addChild(QuiltLoaderText.translate("gui.text.qmj_qmj5_coexistence")); + return null; + } else { + usedQmj = qmj5; + } + } else { + QuiltLoaderText title = QuiltLoaderText.translate("gui.text.qmj5_in_dev_env_not_enabled.title"); + QuiltDisplayedError error = context().reportError(title); + String describedPath = context().manager().describePath(usedQmj); + error.appendReportText( + "Attempted to read a 'quilt.mod.json5' file at " + describedPath + " without the support being enabled!", + "If regenerating your run configurations doesn't fix this issue, then your build system doesn't support 'quilt.mod.json5' files!" + ); + error.appendDescription( + QuiltLoaderText.translate("gui.text.qmj5_in_dev_env_not_enabled.desc.0"), + QuiltLoaderText.translate("gui.text.qmj5_in_dev_env_not_enabled.desc.1"), + QuiltLoaderText.translate("gui.text.qmj5_in_dev_env_not_enabled.desc.2"), + QuiltLoaderText.translate("gui.text.qmj5_in_dev_env_not_enabled.desc.3", describedPath) + ); + context().manager().getRealContainingFile(root).ifPresent(real -> + error.addFileViewButton(real) + .icon(QuiltLoaderGui.iconJarFile().withDecoration(QuiltLoaderGui.iconQuilt())) + ); + + guiNode.addChild(QuiltLoaderText.translate("gui.text.qmj5_in_dev_env_not_enabled")); + return null; + } + } else { + QuiltLoaderText title = QuiltLoaderText.translate("gui.text.qmj5_on_production.title"); + QuiltDisplayedError error = context().reportError(title); + String describedPath = context().manager().describePath(usedQmj); + error.appendReportText( + "An attempt to read a 'quilt.mod.json5' file was detected: " + describedPath, + "'quilt.mod.json5' files can't be used outside of a development environment without converting to a 'quilt.mod.json' file!" + ); + error.appendDescription( + QuiltLoaderText.translate("gui.text.qmj5_on_production.desc.0"), + QuiltLoaderText.translate("gui.text.qmj5_on_production.desc.1"), + QuiltLoaderText.translate("gui.text.qmj5_on_production.desc.2", describedPath) + ); + context().manager().getRealContainingFile(root).ifPresent(real -> + error.addFileViewButton(real) + .icon(QuiltLoaderGui.iconJarFile().withDecoration(QuiltLoaderGui.iconQuilt())) + ); + + guiNode.addChild(QuiltLoaderText.translate("gui.text.qmj5_on_production")); + return null; + } + } + + if (!FasterFiles.isRegularFile(usedQmj)) { return null; } try { - InternalModMetadata meta = ModMetadataReader.read(qmj, context().manager(), guiNode); + InternalModMetadata meta = ModMetadataReader.read(usedQmj, context().manager(), guiNode); Path from = root; if (isZip) { @@ -330,7 +404,7 @@ private ModLoadOption[] scan0(Path root, QuiltLoaderIcon fileIcon, ModLocation l "gui.text.invalid_metadata.title", "quilt.mod.json", parse.getMessage() ); QuiltDisplayedError error = context().reportError(title); - String describedPath = context().manager().describePath(qmj); + String describedPath = context().manager().describePath(usedQmj); error.appendReportText("Invalid 'quilt.mod.json' metadata file:" + describedPath); error.appendDescription(QuiltLoaderText.translate("gui.text.invalid_metadata.desc.0", describedPath)); error.appendThrowable(parse); diff --git a/src/main/java/org/quiltmc/loader/impl/util/SystemProperties.java b/src/main/java/org/quiltmc/loader/impl/util/SystemProperties.java index d22b61acf..c199807c9 100644 --- a/src/main/java/org/quiltmc/loader/impl/util/SystemProperties.java +++ b/src/main/java/org/quiltmc/loader/impl/util/SystemProperties.java @@ -93,7 +93,7 @@ private SystemProperties() {} public static final String LOG_CACHE_KEY_CHANGES = "loader.transform_cache.log_changed_keys"; // enable useTempFile in ZipFileSystem, reduces memory usage when writing transform cache at the cost of speed public static final String USE_ZIPFS_TEMP_FILE = "loader.zipfs.use_temp_file"; - public static final String DISABLE_BEACON = "loader.disable_beacon"; + public static final String ENABLE_QUILT_MOD_JSON5_IN_DEV_ENV = "loader.enable_quilt_mod_json5_in_dev_env"; public static final String DEBUG_DUMP_FILESYSTEM_CONTENTS = "loader.debug.filesystem.dump_contents"; public static final String ALWAYS_DEFER_FILESYSTEM_OPERATIONS = "loader.workaround.defer_all_filesystem_operations"; public static final String DISABLE_QUILT_CLASS_PATH_CUSTOM_TABLE = "loader.quilt_class_path.disable_custom_table"; diff --git a/src/main/resources/lang/quilt_loader.properties b/src/main/resources/lang/quilt_loader.properties index 4e7bb94a4..e94829c9e 100644 --- a/src/main/resources/lang/quilt_loader.properties +++ b/src/main/resources/lang/quilt_loader.properties @@ -86,6 +86,22 @@ gui.error.zerobytezip.desc.0=Try deleting the file and downloading it again. gui.text.invalid_metadata=Invalid Metadata: %s gui.text.invalid_metadata.title=Unable to read %s: %s gui.text.invalid_metadata.desc.0=From %s +gui.text.qmj5_on_production=Unconverted 'quilt.mod.json5' found +gui.text.qmj5_on_production.title=Attempted to read an unconverted 'quilt.mod.json5' file +gui.text.qmj5_on_production.desc.0=An attempt to read a 'quilt.mod.json5' file was detected! +gui.text.qmj5_on_production.desc.1='quilt.mod.json5' files can't be used outside a development environment without converting to a 'quilt.mod.json' file! +gui.text.qmj5_on_production.desc.2=From %s +gui.text.qmj_qmj5_coexistence=Coexistence of conflicting Quilt metadata found +gui.text.qmj_qmj5_coexistence.title=Detected the coexistence of conflicting metadata! +gui.text.qmj_qmj5_coexistence.desc.0=A coexistence of 'quilt.mod.json5' and 'quilt.mod.json' has been detected. +gui.text.qmj_qmj5_coexistence.desc.1=These metadata files cannot coexist with each other due to their conflicting purposes! +gui.text.qmj_qmj5_coexistence.desc.2=From %s +gui.text.qmj5_in_dev_env_not_enabled=Found a 'quilt.mod.json5' file without support enabled! +gui.text.qmj5_in_dev_env_not_enabled.title=Found a 'quilt.mod.json5' file without support enabled! +gui.text.qmj5_in_dev_env_not_enabled.desc.0=The Loader support for 'quilt.mod.json5' hasn't been enabled! +gui.text.qmj5_in_dev_env_not_enabled.desc.1=If regenerating your run configurations doesn't fix this issue, +gui.text.qmj5_in_dev_env_not_enabled.desc.2=then your build system doesn't support 'quilt.mod.json5' files! +gui.text.qmj5_in_dev_env_not_enabled.desc.3=From %s error.unhandled_solver=Complex solver error; see the crash report for more information error.unhandled_solver.desc=Please try updating quilt-loader to see if an update can describe this.\nOr report this to the quilt-loader project so this can be fixed. error.unhandled_solver.desc.rule_n= \n \nRule %s (%s):