diff --git a/annotations/src/main/java/mindustry/annotations/BaseProcessor.java b/annotations/src/main/java/mindustry/annotations/BaseProcessor.java index 05f9aae5f6..d9a4ac5998 100644 --- a/annotations/src/main/java/mindustry/annotations/BaseProcessor.java +++ b/annotations/src/main/java/mindustry/annotations/BaseProcessor.java @@ -211,10 +211,7 @@ public abstract class BaseProcessor extends AbstractProcessor{ if(round++ >= rounds) return false; //only process 1 round if(rootDirectory == null){ try{ - String path = Fi.get(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no") - .toUri().toURL().toString().substring(OS.isWindows ? 6 : "file:".length())) - .parent().parent().parent().parent().parent().parent().parent().toString().replace("%20", " "); - rootDirectory = Fi.get(path).parent(); + rootDirectory = new Fi(new File(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no").toUri())).parent().parent().parent().parent().parent().parent().parent().parent(); }catch(IOException e){ throw new RuntimeException(e); } diff --git a/core/src/mindustry/mod/ModListing.java b/core/src/mindustry/mod/ModListing.java index 68d26ce95f..8c16c84f01 100644 --- a/core/src/mindustry/mod/ModListing.java +++ b/core/src/mindustry/mod/ModListing.java @@ -1,9 +1,15 @@ package mindustry.mod; +import arc.struct.*; +import arc.util.*; +import mindustry.ui.dialogs.*; + /** Mod listing as a data class. */ public class ModListing{ public String repo, name, internalName, author, lastUpdated, description, minGameVersion, version, iconHash = ""; public boolean hasScripts, hasJava, iosCompatible, legacyCompatible, hasIcon; + /** game build -> release ID + mod version */ + public @Nullable ArrayMap releases; public String[] contentTypes = {}; public int stars; @@ -22,4 +28,22 @@ public class ModListing{ ", stars=" + stars + '}'; } + + public @Nullable ModRelease getMatchingRelease(){ + if(releases == null) return null; + for(int i = 0; i < releases.size; i ++){ + String key = releases.keys[i]; + if(ModsDialog.matchesGameVersion(ModsDialog.parseVersion(key))){ + return releases.values[i]; + } + } + return null; + } + + public static class ModRelease{ + /** Github release ID */ + public String id = ""; + /** Actual mod version string in release's mod.json */ + public String version = ""; + } } diff --git a/core/src/mindustry/ui/dialogs/ModsDialog.java b/core/src/mindustry/ui/dialogs/ModsDialog.java index 9446c191de..37af75cadc 100644 --- a/core/src/mindustry/ui/dialogs/ModsDialog.java +++ b/core/src/mindustry/ui/dialogs/ModsDialog.java @@ -17,6 +17,7 @@ import arc.util.Http.*; import arc.util.io.*; import arc.util.serialization.*; import arc.util.serialization.Jval.*; +import mindustry.core.*; import mindustry.ctype.*; import mindustry.game.EventType.*; import mindustry.gen.*; @@ -26,10 +27,13 @@ import mindustry.mod.Mods.*; import mindustry.ui.*; import java.util.*; +import java.util.regex.*; import static mindustry.Vars.*; public class ModsDialog extends BaseDialog{ + private static final Pattern modVersionPattern = Pattern.compile("\\[[vb](\\d+)(?:\\.(\\d+))?\\]"); + public ModBrowserDialog browser; protected float modImportProgress; @@ -686,4 +690,51 @@ public class ModsDialog extends BaseDialog{ }, this::importFail); } } + + /** @return {major, minor} parsed from a leading/trailing "[v{major}]" or "[v{major}.{minor}]" tag, or null if none is found at either end (or the numbers are unparseable). */ + public static @Nullable int[] parseVersionTag(String str){ + if(str == null) return null; + + Matcher m = modVersionPattern.matcher(str); + while(m.find()){ + if(m.start() == 0 || m.end() == str.length()){ + int major = Strings.parseInt(m.group(1)); + if(major == Integer.MIN_VALUE) return null; + int minor = m.group(2) != null ? Strings.parseInt(m.group(2)) : 0; + if(minor == Integer.MIN_VALUE) return null; + return new int[]{major, minor}; + } + } + return null; + } + + public static @Nullable int[] parseVersion(String str){ + if(str == null || str.isEmpty()) return null; + + int dot = str.indexOf('.'); + String majorStr = dot == -1 ? str : str.substring(0, dot); + String minorStr = dot == -1 ? null : str.substring(dot + 1); + + if(majorStr.isEmpty() || (dot != -1 && (minorStr.isEmpty() || str.indexOf('.', dot + 1) != -1))) return null; + + int major = Strings.parseInt(majorStr); + if(major == Integer.MIN_VALUE) return null; + int minor = minorStr != null ? Strings.parseInt(minorStr) : 0; + if(minor == Integer.MIN_VALUE) return null; + + return new int[]{major, minor}; + } + + /** @return whether the specified release name and its tags match the current game build (e.g. "Bingus Mod [v160]" should only match build 160) */ + public static boolean matchesGameVersion(String releaseTitle){ + return matchesGameVersion(parseVersionTag(releaseTitle)); + } + + public static boolean matchesGameVersion(@Nullable int[] tag){ + //version not specified, matches anything. + if(tag == null) return true; + //must match exactly; custom builds (-1) do not match. + //for revisions, either match if the revision is unspecified (v160 -> matches ALL 160.x builds), OR, if it is specified, match the exact version (the mod author must have had a good reason for it) + return Version.build == tag[0] && (tag[1] == 0 || Version.revision == tag[1]); + } }