From 5eba2466799196f906c72958c3fbaf0e43d11b17 Mon Sep 17 00:00:00 2001 From: Lilly Rose Berner Date: Mon, 22 Apr 2024 21:48:10 +0200 Subject: [PATCH] Add option to choose which update channel to receive notifications for (#704) * Allow choosing an update channel to receive notifications for * Pass user preferred update channel to UpdateChecker * Change method of getting the preferred update channel * Request preferred version types from Modrinth only * Switch update channel and quick configure button order * Rerun update checks when clicking done in the modmenu options screen - No longer promotes updates to beta or alpha versions by default --- .../com/terraformersmc/modmenu/ModMenu.java | 6 +++- .../modmenu/api/UpdateChannel.java | 19 +++++++++++ .../modmenu/api/UpdateChecker.java | 4 +-- .../modmenu/api/UpdateInfo.java | 5 ++- .../modmenu/config/ModMenuConfig.java | 2 ++ .../modmenu/gui/ModMenuOptionsScreen.java | 2 ++ .../modmenu/util/UpdateCheckerUtil.java | 33 +++++++++++++++++-- .../terraformersmc/modmenu/util/mod/Mod.java | 3 +- .../modmenu/util/mod/ModrinthUpdateInfo.java | 16 ++++++--- .../resources/assets/modmenu/lang/en_us.json | 6 +++- 10 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/terraformersmc/modmenu/api/UpdateChannel.java diff --git a/src/main/java/com/terraformersmc/modmenu/ModMenu.java b/src/main/java/com/terraformersmc/modmenu/ModMenu.java index 07ddcd3ab..770b7ab0e 100644 --- a/src/main/java/com/terraformersmc/modmenu/ModMenu.java +++ b/src/main/java/com/terraformersmc/modmenu/ModMenu.java @@ -99,7 +99,7 @@ public void onInitializeClient() { MODS.put(mod.getId(), mod); } - UpdateCheckerUtil.checkForUpdates(); + checkForUpdates(); Map dummyParents = new HashMap<>(); @@ -127,6 +127,10 @@ public static void clearModCountCache() { cachedDisplayedModCount = -1; } + public static void checkForUpdates() { + UpdateCheckerUtil.checkForUpdates(); + } + public static boolean areModUpdatesAvailable() { if (!ModMenuConfig.UPDATE_CHECKER.getValue()) { return false; diff --git a/src/main/java/com/terraformersmc/modmenu/api/UpdateChannel.java b/src/main/java/com/terraformersmc/modmenu/api/UpdateChannel.java new file mode 100644 index 000000000..4c51835c9 --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/api/UpdateChannel.java @@ -0,0 +1,19 @@ +package com.terraformersmc.modmenu.api; + +import com.terraformersmc.modmenu.config.ModMenuConfig; + +/** + * Supported update channels, in ascending order by stability. + */ +public enum UpdateChannel { + ALPHA, + BETA, + RELEASE; + + /** + * @return the user's preferred update channel. + */ + public static UpdateChannel getUserPreference() { + return ModMenuConfig.UPDATE_CHANNEL.getValue(); + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/api/UpdateChecker.java b/src/main/java/com/terraformersmc/modmenu/api/UpdateChecker.java index 1c2aa231e..de80fd93b 100644 --- a/src/main/java/com/terraformersmc/modmenu/api/UpdateChecker.java +++ b/src/main/java/com/terraformersmc/modmenu/api/UpdateChecker.java @@ -1,13 +1,13 @@ package com.terraformersmc.modmenu.api; public interface UpdateChecker { - /** * Gets called when ModMenu is checking for updates. * This is done in a separate thread, so this call can/should be blocking. * + *

Your update checker should aim to return an update on the same or a more stable channel than the user's preference which you can get via {@link UpdateChannel#getUserPreference()}.

+ * * @return The update info */ UpdateInfo checkForUpdates(); - } diff --git a/src/main/java/com/terraformersmc/modmenu/api/UpdateInfo.java b/src/main/java/com/terraformersmc/modmenu/api/UpdateInfo.java index fee9c07b7..b5f40a4f0 100644 --- a/src/main/java/com/terraformersmc/modmenu/api/UpdateInfo.java +++ b/src/main/java/com/terraformersmc/modmenu/api/UpdateInfo.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.Nullable; public interface UpdateInfo { - /** * @return If an update for the mod is available. */ @@ -23,4 +22,8 @@ default Text getUpdateMessage() { */ String getDownloadLink(); + /** + * @return The update channel this update is available for. + */ + UpdateChannel getUpdateChannel(); } diff --git a/src/main/java/com/terraformersmc/modmenu/config/ModMenuConfig.java b/src/main/java/com/terraformersmc/modmenu/config/ModMenuConfig.java index bdeba118b..038c419ea 100644 --- a/src/main/java/com/terraformersmc/modmenu/config/ModMenuConfig.java +++ b/src/main/java/com/terraformersmc/modmenu/config/ModMenuConfig.java @@ -1,6 +1,7 @@ package com.terraformersmc.modmenu.config; import com.google.gson.annotations.SerializedName; +import com.terraformersmc.modmenu.api.UpdateChannel; import com.terraformersmc.modmenu.config.option.BooleanConfigOption; import com.terraformersmc.modmenu.config.option.EnumConfigOption; import com.terraformersmc.modmenu.config.option.OptionConvertable; @@ -43,6 +44,7 @@ public class ModMenuConfig { public static final StringSetConfigOption DISABLE_UPDATE_CHECKER = new StringSetConfigOption("disable_update_checker", new HashSet<>()); public static final BooleanConfigOption UPDATE_CHECKER = new BooleanConfigOption("update_checker", true); public static final BooleanConfigOption BUTTON_UPDATE_BADGE = new BooleanConfigOption("button_update_badge", true); + public static final EnumConfigOption UPDATE_CHANNEL = new EnumConfigOption<>("update_channel", UpdateChannel.RELEASE); public static final BooleanConfigOption QUICK_CONFIGURE = new BooleanConfigOption("quick_configure", true); public static SimpleOption[] asOptions() { diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModMenuOptionsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModMenuOptionsScreen.java index 3e9c4a753..b0efeb3ba 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModMenuOptionsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModMenuOptionsScreen.java @@ -1,5 +1,6 @@ package com.terraformersmc.modmenu.gui; +import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.config.ModMenuConfigManager; import net.minecraft.client.MinecraftClient; @@ -29,6 +30,7 @@ protected void init() { this.addSelectableChild(this.list); this.addDrawableChild( ButtonWidget.builder(ScreenTexts.DONE, (button) -> { + ModMenu.checkForUpdates(); ModMenuConfigManager.save(); this.client.setScreen(this.previous); }).position(this.width / 2 - 100, this.height - 27) diff --git a/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java b/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java index 509c20600..ce4ef7ceb 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java +++ b/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java @@ -4,6 +4,7 @@ import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import com.terraformersmc.modmenu.ModMenu; +import com.terraformersmc.modmenu.api.UpdateChannel; import com.terraformersmc.modmenu.api.UpdateChecker; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.util.mod.Mod; @@ -85,7 +86,20 @@ public static void checkForModrinthUpdates() { .get().getMetadata().getVersion().getFriendlyString().split("\\+", 1); // Strip build metadata for privacy final var modMenuVersion = splitVersion.length > 1 ? splitVersion[1] : splitVersion[0]; final var userAgent = "%s/%s (%s/%s%s)".formatted(ModMenu.GITHUB_REF, modMenuVersion, mcVer, primaryLoader, environment); - String body = ModMenu.GSON_MINIFIED.toJson(new LatestVersionsFromHashesBody(modHashes.keySet(), loaders, mcVer)); + + List updateChannels; + UpdateChannel preferredChannel = UpdateChannel.getUserPreference(); + + if (preferredChannel == UpdateChannel.RELEASE) { + updateChannels = List.of(UpdateChannel.RELEASE); + } else if (preferredChannel == UpdateChannel.BETA) { + updateChannels = List.of(UpdateChannel.BETA, UpdateChannel.RELEASE); + } else { + updateChannels = List.of(UpdateChannel.ALPHA, UpdateChannel.BETA, UpdateChannel.RELEASE); + } + + String body = ModMenu.GSON_MINIFIED.toJson(new LatestVersionsFromHashesBody(modHashes.keySet(), loaders, mcVer, updateChannels)); + LOGGER.debug("User agent: " + userAgent); LOGGER.debug("Body: " + body); var latestVersionsRequest = HttpRequest.newBuilder() @@ -109,6 +123,7 @@ public static void checkForModrinthUpdates() { responseObject.asMap().forEach((lookupHash, versionJson) -> { var versionObj = versionJson.getAsJsonObject(); var projectId = versionObj.get("project_id").getAsString(); + var versionType = versionObj.get("version_type").getAsString(); var versionNumber = versionObj.get("version_number").getAsString(); var versionId = versionObj.get("id").getAsString(); var primaryFile = versionObj.get("files").getAsJsonArray().asList().stream() @@ -118,13 +133,14 @@ public static void checkForModrinthUpdates() { return; } + var updateChannel = UpdateCheckerUtil.getUpdateChannel(versionType); var versionHash = primaryFile.get().getAsJsonObject().get("hashes").getAsJsonObject().get("sha512").getAsString(); if (!Objects.equals(versionHash, lookupHash)) { // hashes different, there's an update. modHashes.get(lookupHash).forEach(mod -> { LOGGER.info("Update available for '{}@{}', (-> {})", mod.getId(), mod.getVersion(), versionNumber); - mod.setUpdateInfo(new ModrinthUpdateInfo(projectId, versionId, versionNumber)); + mod.setUpdateInfo(new ModrinthUpdateInfo(projectId, versionId, versionNumber, updateChannel)); }); } }); @@ -134,6 +150,14 @@ public static void checkForModrinthUpdates() { } } + private static UpdateChannel getUpdateChannel(String versionType) { + try { + return UpdateChannel.valueOf(versionType.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException | NullPointerException e) { + return UpdateChannel.RELEASE; + } + } + public static void triggerV2DeprecatedToast() { if (modrinthApiV2Deprecated && ModMenuConfig.UPDATE_CHECKER.getValue()) { MinecraftClient.getInstance().getToastManager().add(new SystemToast( @@ -150,11 +174,14 @@ public static class LatestVersionsFromHashesBody { public Collection loaders; @SerializedName("game_versions") public Collection gameVersions; + @SerializedName("version_types") + public Collection versionTypes; - public LatestVersionsFromHashesBody(Collection hashes, Collection loaders, String mcVersion) { + public LatestVersionsFromHashesBody(Collection hashes, Collection loaders, String mcVersion, Collection updateChannels) { this.hashes = hashes; this.loaders = loaders; this.gameVersions = Set.of(mcVersion); + this.versionTypes = updateChannels.stream().map(value -> value.toString().toLowerCase()).toList(); } } } diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java b/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java index 22c95d6a3..58e2ec4d2 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java @@ -114,7 +114,8 @@ default boolean hasUpdate() { if (updateInfo == null) { return false; } - return updateInfo.isUpdateAvailable(); + + return updateInfo.isUpdateAvailable() && updateInfo.getUpdateChannel().compareTo(ModMenuConfig.UPDATE_CHANNEL.getValue()) >= 0; } default @Nullable String getSha512Hash() throws IOException { diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/ModrinthUpdateInfo.java b/src/main/java/com/terraformersmc/modmenu/util/mod/ModrinthUpdateInfo.java index 9a17b5a46..f982211b0 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/mod/ModrinthUpdateInfo.java +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/ModrinthUpdateInfo.java @@ -1,17 +1,19 @@ package com.terraformersmc.modmenu.util.mod; +import com.terraformersmc.modmenu.api.UpdateChannel; import com.terraformersmc.modmenu.api.UpdateInfo; public class ModrinthUpdateInfo implements UpdateInfo { + protected final String projectId; + protected final String versionId; + protected final String versionNumber; + protected final UpdateChannel updateChannel; - protected String projectId; - protected String versionId; - protected String versionNumber; - - public ModrinthUpdateInfo(String projectId, String versionId, String versionNumber) { + public ModrinthUpdateInfo(String projectId, String versionId, String versionNumber, UpdateChannel updateChannel) { this.projectId = projectId; this.versionId = versionId; this.versionNumber = versionNumber; + this.updateChannel = updateChannel; } @Override @@ -36,4 +38,8 @@ public String getVersionNumber() { return versionNumber; } + @Override + public UpdateChannel getUpdateChannel() { + return this.updateChannel; + } } diff --git a/src/main/resources/assets/modmenu/lang/en_us.json b/src/main/resources/assets/modmenu/lang/en_us.json index 1c42d9fb8..0d6f80b03 100644 --- a/src/main/resources/assets/modmenu/lang/en_us.json +++ b/src/main/resources/assets/modmenu/lang/en_us.json @@ -164,5 +164,9 @@ "option.modmenu.button_update_badge.false": "Hidden", "option.modmenu.quick_configure": "Quick Configure", "option.modmenu.quick_configure.true": "Enabled", - "option.modmenu.quick_configure.false": "Disabled" + "option.modmenu.quick_configure.false": "Disabled", + "option.modmenu.update_channel": "Update Channel", + "option.modmenu.update_channel.alpha": "All", + "option.modmenu.update_channel.beta": "Release & Beta", + "option.modmenu.update_channel.release": "Release" }