diff --git a/minecraft/src/test/resources/frog.mod.toml b/minecraft/src/test/resources/frog.mod.toml index ce51f13..e36ca45 100644 --- a/minecraft/src/test/resources/frog.mod.toml +++ b/minecraft/src/test/resources/frog.mod.toml @@ -2,14 +2,23 @@ format_version = "1.0.0" [frog.mod] -id = "example_mod_test" -name = "Example Mod Test Mod" -version = "0.0.1" license = "CC0-1.0" credits = [ { name = "You", roles = ["author", "other_role"] } ] +[frog.dependencies] +depends = [ + { id = "awesome-mod", versions = "0.0.0", name = "Awesome Mod" }, + { id = "other-awesome-mod", versions = "0.0.0", name = "Other Awesome Mod", link = "https://google.com" }, + { id = "other-awesome-mod2", versions = "0.0.0", name = "Other Awesome Mod" }, + { id = "other-awesome-mod3", versions = "0.0.0", name = "Other Awesome Mod" }, + { id = "other-awesome-mod4", versions = "0.0.0", name = "Other Awesome Mod" }, + { id = "other-awesome-mod5", versions = "0.0.0", name = "Other Awesome Mod" }, + { id = "other-awesome-mod6", versions = "0.0.0", name = "Other Awesome Mod" }, + { id = "other-awesome-mod7", versions = "0.0.0", name = "Other Awesome Mod" } +] + [frog.extensions] prelaunch="dev.frogmc.example.ExampleTestMod" diff --git a/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java b/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java index c945911..540e226 100644 --- a/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java +++ b/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java @@ -7,6 +7,7 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import com.electronwill.nightconfig.core.CommentedConfig; @@ -16,7 +17,10 @@ import com.electronwill.nightconfig.toml.TomlParser; import dev.frogmc.frogloader.api.mod.ModDependencies; import dev.frogmc.frogloader.api.mod.ModExtensions; import dev.frogmc.frogloader.api.mod.ModProperties; +import dev.frogmc.frogloader.api.mod.SemVer; +import dev.frogmc.frogloader.impl.SemVerParseException; import lombok.AllArgsConstructor; +import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +36,7 @@ public class ModPropertiesReader { CommentedConfig props = PARSER.parse(fs.getPath(PROPERTIES_FILE_NAME), FileNotFoundAction.THROW_ERROR); return Optional.of(readProperties(props, Collections.singleton(mod))); - } catch (IOException e) { + } catch (IOException | InvalidModPropertiesException e) { LOGGER.warn("Failed to read mod properties: ", e); } @@ -60,7 +64,7 @@ public class ModPropertiesReader { return Optional.empty(); } - private static ModProperties readProperties(UnmodifiableConfig config, Collection sources) { + private static ModProperties readProperties(UnmodifiableConfig config, Collection sources) throws InvalidModPropertiesException { String manifestVer = config.get("frog.format_version"); try { @@ -73,53 +77,91 @@ public class ModPropertiesReader { @AllArgsConstructor private enum FormatVersion { V1_0_0("1.0.0", (config, sources) -> { + List badProperties = new ArrayList<>(); + String id = config.get("frog.mod.id"); String name = config.get("frog.mod.name"); String icon = config.get("frog.mod.icon"); String version = config.get("frog.mod.version"); - String license = config.get("frog.mod.license"); + String license = Objects.requireNonNullElse(config.get("frog.mod.license"), ""); - if (license == null) { - license = ""; + if (id == null || id.isEmpty()) + badProperties.add("frog.mod.id"); + + if (name == null || name.isEmpty()) + badProperties.add("frog.mod.name"); + + SemVer semVer = null; + + if (version == null || version.isEmpty()) + badProperties.add("frog.mod.version"); + else { + try { + semVer = SemVerImpl.parse(version); + } catch (SemVerParseException e) { + badProperties.add("frog.mod.version"); + } } List creditsList = config.get("frog.mod.credits"); Map> credits = new HashMap<>(); if (creditsList != null) { - creditsList.forEach(c -> credits.put(c.get("name"), c.get("roles"))); + int i = 0; + + for (UnmodifiableConfig entry : creditsList) { + String entryName = entry.get("name"); + Collection entryRoles = entry.get("roles"); + + if (entryName == null || entryName.isEmpty()) + badProperties.add("frog.mod.credits." + i + ".name"); + + if (entryRoles == null || entryRoles.isEmpty()) + badProperties.add("frog.mod.credits." + i + ".name"); + + credits.put(entryName, entryRoles); + + ++i; + } } - Collection depends = new HashSet<>(); - List dependsConfig = config.get("frog.dependencies.depends"); - if (dependsConfig != null) { - dependsConfig.forEach(entry -> depends.add(new ModDependencies.Entry(entry.get("id"), entry.get("versions"), entry.get("link"), entry.get("name")))); - } + Function> parseDependencies = key -> { + Collection result = new HashSet<>(); - Collection breaks = new HashSet<>(); - List breaksConfig = config.get("frog.dependencies.breaks"); - if (breaksConfig != null) { - breaksConfig.forEach(entry -> breaks.add(new ModDependencies.Entry(entry.get("id"), entry.get("versions"), entry.get("link"), entry.get("name")))); - } + List dependenciesConfig = config.get("frog.dependencies." + key); + if (dependenciesConfig != null) { + int i = 0; - Collection suggests = new HashSet<>(); - List suggestsConfig = config.get("frog.dependencies.suggests"); - if (suggestsConfig != null) { - suggestsConfig.forEach(entry -> suggests.add(new ModDependencies.Entry(entry.get("id"), entry.get("versions"), entry.get("link"), entry.get("name")))); - } + for (UnmodifiableConfig entry : dependenciesConfig) { + String entryId = entry.get("id"); + String entryVersions = entry.get("versions"); - Collection provides = new HashSet<>(); - List providesConfig = config.get("frog.dependencies.provides"); - if (providesConfig != null) { - providesConfig.forEach(entry -> provides.add(new ModDependencies.Entry(entry.get("id"), entry.get("version"), entry.get("link"), entry.get("name")))); - } + if (entryId == null || entryId.isEmpty()) + badProperties.add("frog.dependencies." + key + '.' + i + ".id"); + + result.add(new ModDependencies.Entry(entryId, entryVersions, entry.get("link"), entry.get("name"))); + + ++i; + } + } + + return result; + }; + + + var depends = parseDependencies.apply("depends"); + var breaks = parseDependencies.apply("breaks"); + var suggests = parseDependencies.apply("suggests"); + var provides = parseDependencies.apply("provides"); + + if (!badProperties.isEmpty()) + throw new InvalidModPropertiesException(id, sources, badProperties); UnmodifiableConfig extensionsConfig = config.get("frog.extensions"); Map extensions = new HashMap<>(); - if (extensionsConfig != null) { + if (extensionsConfig != null) extensionsConfig.entrySet().forEach(entry -> extensions.put(entry.getKey(), entry.getValue())); - } - return new ModPropertiesImpl(id, name, icon, SemVerImpl.parse(version), license, Collections.unmodifiableMap(credits), + return new ModPropertiesImpl(id, name, icon, semVer, license, Collections.unmodifiableMap(credits), new ModDependencies(depends, breaks, suggests, provides), ModExtensions.of(extensions), sources); }); @@ -133,8 +175,26 @@ public class ModPropertiesReader { @FunctionalInterface public interface Parser { - ModProperties parse(UnmodifiableConfig config, Collection sources) throws IOException; + ModProperties parse(UnmodifiableConfig config, Collection sources) throws IOException, InvalidModPropertiesException; } } + + @Getter + public static class InvalidModPropertiesException extends Exception { + + private final Collection invalid; + + public InvalidModPropertiesException(String id, Collection sources, Collection invalid) { + super( + "Invalid properties for %s (%s) - invalid or missing values for: %s".formatted( + Objects.requireNonNullElse(id, ""), + sources.stream().map(Path::toString).collect(Collectors.joining(", ")), + String.join(", ", invalid) + ) + ); + this.invalid = invalid; + } + + } }