diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 650ab6f..a826c7f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,9 @@ on: required: false description: '' default: false + tag: + type: string + required: false jobs: build: @@ -40,6 +43,7 @@ jobs: - name: Build with Gradle run: ./gradlew remapJar --no-daemon env: + BUILD_TAG: ${{ inputs.tag }} BUILD_ID: ${{ github.run_number }} BUILD_RELEASE: ${{ inputs.release }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index acd5550..0afdf7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,10 +62,13 @@ jobs: uses: ./.github/workflows/build.yml with: release: true + tag: ${{ needs.generate_release_info.outputs.tag_name }} build_alpha: if: ${{ !((github.event_name == 'release' && !github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'true')) }} uses: ./.github/workflows/build.yml + with: + tag: ${{ needs.generate_release_info.outputs.tag_name }} release: needs: diff --git a/build.gradle b/build.gradle index bde07ea..dad1a23 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,14 @@ plugins { ext { getVersionSuffix = { String versionSuffix = "" + if (System.getenv("BUILD_TAG") != null && !System.getenv("BUILD_TAG").isEmpty() && System.getenv("BUILD_RELEASE") != "true") { + versionSplit = System.getenv("BUILD_TAG").split("\\+") + versionPart = (versionSplit.length >= 2) ? versionSplit[1] : "" + if ((versionPart || !versionPart.isEmpty()) && versionPart.startsWith("alpha") || versionPart.startsWith("beta")) { + versionSuffix += "+" + versionPart + } + return versionSuffix + } if (System.getenv("BUILD_RELEASE") != "true") { String buildNumber = System.getenv("BUILD_ID") versionSuffix += buildNumber != null ? ("+build." + buildNumber) : "+snapshot" diff --git a/common/src/main/java/io/github/skydynamic/quickbakcupmulti/ModContainer.java b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/ModContainer.java index 98c51f7..d3b28f0 100644 --- a/common/src/main/java/io/github/skydynamic/quickbakcupmulti/ModContainer.java +++ b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/ModContainer.java @@ -15,6 +15,8 @@ @Setter @Getter public class ModContainer { + private ModVersion modVersion; + private CommandDispatcher dispatcher; private Path configPath; private PermissionManager permissionManager; diff --git a/common/src/main/java/io/github/skydynamic/quickbakcupmulti/ModVersion.java b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/ModVersion.java new file mode 100644 index 0000000..e86c7a0 --- /dev/null +++ b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/ModVersion.java @@ -0,0 +1,150 @@ +package io.github.skydynamic.quickbakcupmulti; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ModVersion implements Comparable { + public int major; + public int minor; + public int patch; + public int buildNumber = 0; + + public boolean release = false; + public BuildType buildType = null; + + public ModVersion(int major, int minor, int patch, int buildNumber, boolean release, BuildType buildType) { + this.major = major; + this.minor = minor; + this.patch = patch; + this.buildNumber = buildNumber; + this.release = release; + this.buildType = buildType; + } + + public ModVersion(String version) { + this.parse(version); + } + + public void parse(String version) { + String regex = "^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:[-+](snapshot|build\\.(\\d+)|alpha(?:\\.(\\d+))?|beta(?:\\.(\\d+))?))?$"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(version); + + if (matcher.matches()) { + this.major = Integer.parseInt(matcher.group(1)); + this.minor = Integer.parseInt(matcher.group(2)); + this.patch = Integer.parseInt(matcher.group(3)); + + String suffix = matcher.group(4); + if (suffix != null) { + this.release = false; + if ("snapshot".equals(suffix)) { + this.buildNumber = -1; + this.buildType = BuildType.SNAPSHOT; + } else if (suffix.startsWith("build.")) { + this.buildNumber = Integer.parseInt(matcher.group(5)); + this.buildType = BuildType.DEV; + } else if (suffix.startsWith("alpha")) { + String alphaBuild = matcher.group(6); + if (alphaBuild != null) { + this.buildNumber = Integer.parseInt(alphaBuild); + } else { + this.buildNumber = 0; + } + this.buildType = BuildType.ALPHA; + } else if (suffix.startsWith("beta")) { + String betaBuild = matcher.group(7); + if (betaBuild != null) { + this.buildNumber = Integer.parseInt(betaBuild); + } else { + this.buildNumber = 0; + } + this.buildType = BuildType.BETA; + } + } else { + this.release = true; + } + } else { + throw new IllegalArgumentException("Invalid version format: " + version); + } + } + + + public boolean isNewerThan(ModVersion other) { + return this.compareTo(other) > 0; + } + + public enum BuildType { + ALPHA(0), + BETA(1), + DEV(2), + SNAPSHOT(3); + + public final int priority; + + BuildType(int priority) { + this.priority = priority; + } + + public int compare(ModVersion o) { + return Integer.compare(this.priority, o.buildType.priority); + } + } + + @Override + public int compareTo(ModVersion other) { + int majorCompare = Integer.compare(this.major, other.major); + if (majorCompare != 0) return majorCompare; + + int minorCompare = Integer.compare(this.minor, other.minor); + if (minorCompare != 0) return minorCompare; + + int patchCompare = Integer.compare(this.patch, other.patch); + if (patchCompare != 0) return patchCompare; + + if (this.release && !other.release) return 1; + if (!this.release && other.release) return -1; + + if (!this.release) { + int r = other.buildType.compare(this); + if (r == 0) { + if (this.buildNumber == -1 && other.buildNumber >= 0) return -1; + if (this.buildNumber >= 0 && other.buildNumber == -1) return 1; + return Integer.compare(this.buildNumber, other.buildNumber); + } + return r; + } + + return 0; + } + + @Override + public String toString() { + String baseVersion = this.major + "." + this.minor + "." + this.patch; + + if (this.release) { + return baseVersion; + } else { + switch (this.buildType) { + case SNAPSHOT: + return baseVersion + "+snapshot"; + case ALPHA: + if (this.buildNumber > 0) { + return baseVersion + "+alpha." + this.buildNumber; + } else { + return baseVersion + "+alpha"; + } + case BETA: + if (this.buildNumber > 0) { + return baseVersion + "+beta." + this.buildNumber; + } else { + return baseVersion + "+beta"; + } + case DEV: + return baseVersion + "+build." + this.buildNumber; + default: + return baseVersion; + } + } + } +} diff --git a/common/src/main/java/io/github/skydynamic/quickbakcupmulti/QuickbakcupmultiReforged.java b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/QuickbakcupmultiReforged.java index ca50261..aa0f976 100644 --- a/common/src/main/java/io/github/skydynamic/quickbakcupmulti/QuickbakcupmultiReforged.java +++ b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/QuickbakcupmultiReforged.java @@ -6,6 +6,7 @@ import io.github.skydynamic.quickbakcupmulti.config.ModConfig; import io.github.skydynamic.quickbakcupmulti.schedule.quartz.DisableQuartzInfoLogger; import io.github.skydynamic.quickbakcupmulti.translate.Translate; +import io.github.skydynamic.quickbakcupmulti.utils.UpdateChecker; import io.github.skydynamic.quickbakcupmulti.utils.permission.PermissionManager; import lombok.Getter; import lombok.Setter; @@ -39,6 +40,8 @@ public static void init(ModContainer container) { modConfig.save(); modContainer.setPermissionManager(new PermissionManager()); + new UpdateChecker().start(); + // Initialize Translate Translate.handleResourceReload(modConfig.getLang()); diff --git a/common/src/main/java/io/github/skydynamic/quickbakcupmulti/utils/UpdateChecker.java b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/utils/UpdateChecker.java new file mode 100644 index 0000000..7fbf943 --- /dev/null +++ b/common/src/main/java/io/github/skydynamic/quickbakcupmulti/utils/UpdateChecker.java @@ -0,0 +1,54 @@ +package io.github.skydynamic.quickbakcupmulti.utils; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.github.skydynamic.quickbakcupmulti.ModVersion; +import io.github.skydynamic.quickbakcupmulti.QuickbakcupmultiReforged; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +public class UpdateChecker extends Thread { + private static final HttpClient CLIENT = HttpClient.newHttpClient(); + private static final String RELEASE_API_URL = "https://api.github.com/repos/QuickBackupMultiMod-Dev/QuickBackupM-Reforged/releases"; + + public UpdateChecker() { + super("QuickBackupM-Reforged-Update-Checker"); + } + + @Override + public void run() { + ModVersion currentVersion = QuickbakcupmultiReforged.getModContainer().getModVersion(); + + try { + HttpResponse response = CLIENT.send( + HttpRequest.newBuilder().uri(new URI(RELEASE_API_URL)).build(), + HttpResponse.BodyHandlers.ofString() + ); + + JsonElement jsonElement = JsonParser.parseString(response.body()); + JsonObject release = null; + if (jsonElement.isJsonArray()) { + JsonArray releases = jsonElement.getAsJsonArray(); + release = releases.get(0).isJsonObject() ? releases.get(0).getAsJsonObject() : null; + } + + if (release != null) { + ModVersion releaseVersion = new ModVersion(release.get("tag_name").getAsString()); + System.out.println(releaseVersion); + if (releaseVersion.isNewerThan(currentVersion)) { + QuickbakcupmultiReforged.logger.info("New version available: {}", releaseVersion); + QuickbakcupmultiReforged.logger.info("Download link: {}", release.get("html_url").getAsString()); + } + } + } catch (IOException | InterruptedException | URISyntaxException e) { + QuickbakcupmultiReforged.logger.error("Failed to check update", e); + } + } +} diff --git a/fabric/src/main/java/io/github/skydynamic/quickbakcupmulti/fabric/QuickbakcupmultiReforgedFabric.java b/fabric/src/main/java/io/github/skydynamic/quickbakcupmulti/fabric/QuickbakcupmultiReforgedFabric.java index 50e4114..31fbbdc 100644 --- a/fabric/src/main/java/io/github/skydynamic/quickbakcupmulti/fabric/QuickbakcupmultiReforgedFabric.java +++ b/fabric/src/main/java/io/github/skydynamic/quickbakcupmulti/fabric/QuickbakcupmultiReforgedFabric.java @@ -1,5 +1,6 @@ package io.github.skydynamic.quickbakcupmulti.fabric; +import io.github.skydynamic.quickbakcupmulti.ModVersion; import io.github.skydynamic.quickbakcupmulti.fabric.events.FabricEvents; import io.github.skydynamic.quickbakcupmulti.ModContainer; import io.github.skydynamic.quickbakcupmulti.QuickbakcupmultiReforged; @@ -15,6 +16,9 @@ public final class QuickbakcupmultiReforgedFabric implements DedicatedServerModI public void onInitializeServer() { modContainer.setConfigPath(FabricLoader.getInstance().getConfigDir()); + FabricLoader.getInstance().getModContainer(QuickbakcupmultiReforged.MOD_ID).ifPresent(modContainer1 -> + modContainer.setModVersion(new ModVersion(modContainer1.getMetadata().getVersion().getFriendlyString()))); + QuickbakcupmultiReforged.init(modContainer); FabricEvents.register(); } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 67992f2..c75621e 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "quickbakcupmulti", + "id": "quickbakcupmulti_reforged", "version": "${version}", "name": "QuickBakcupMulti", "description": "A backup / restore mod, with multiple backup slots", diff --git a/neoforge/src/main/java/io/github/skydynamic/quickbakcupmulti/neoforge/QuickbakcupmultiReforgedNeoForge.java b/neoforge/src/main/java/io/github/skydynamic/quickbakcupmulti/neoforge/QuickbakcupmultiReforgedNeoForge.java index 0c9f243..0199f6e 100644 --- a/neoforge/src/main/java/io/github/skydynamic/quickbakcupmulti/neoforge/QuickbakcupmultiReforgedNeoForge.java +++ b/neoforge/src/main/java/io/github/skydynamic/quickbakcupmulti/neoforge/QuickbakcupmultiReforgedNeoForge.java @@ -1,11 +1,13 @@ package io.github.skydynamic.quickbakcupmulti.neoforge; import io.github.skydynamic.quickbakcupmulti.ModContainer; +import io.github.skydynamic.quickbakcupmulti.ModVersion; import io.github.skydynamic.quickbakcupmulti.QuickbakcupmultiReforged; import lombok.Getter; import lombok.Setter; import net.neoforged.api.distmarker.Dist; import net.neoforged.fml.common.Mod; +import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.FMLPaths; @Mod(value = QuickbakcupmultiReforged.MOD_ID, dist = Dist.DEDICATED_SERVER) @@ -18,6 +20,9 @@ public final class QuickbakcupmultiReforgedNeoForge { public QuickbakcupmultiReforgedNeoForge() { modContainer.setConfigPath(FMLPaths.CONFIGDIR.get()); + String version = FMLLoader.getLoadingModList().getModFileById(QuickbakcupmultiReforged.MOD_ID).getMods().getFirst().getVersion().toString(); + modContainer.setModVersion(new ModVersion(version)); + QuickbakcupmultiReforged.init(modContainer); } }