From fbb10a577e96ee7b0ea207b29a12efd3efa11779 Mon Sep 17 00:00:00 2001 From: DancingSnow <60736156+DancingSnow0517@users.noreply.github.com> Date: Sun, 10 Nov 2024 11:33:07 +0800 Subject: [PATCH] Add update checker to check version with GitHub latest release (#64) * add update checker to check version with GitHub latest release * better exception logger * make UpdateChecker extends Thread, cache latestVersion * show update msg when player join world * fix mod version --- .../quickbackupmulti/QuickBackupMulti.java | 10 +++ .../config/ConfigStorage.java | 22 +++---- .../config/QbmTempConfig.java | 5 ++ .../config/QuickBackupMultiConfig.java | 6 ++ .../client/ClientPlayNetworkHandlerMixin.java | 45 ++++++++++++++ .../mixin/client/MinecraftClientMixin.java | 9 +-- .../quickbackupmulti/utils/Messenger.java | 2 - .../quickbackupmulti/utils/UpdateChecker.java | 61 +++++++++++++++++++ .../assets/quickbackupmulti/lang/en_us.yml | 4 ++ .../assets/quickbackupmulti/lang/zh_cn.yml | 4 ++ .../assets/quickbackupmulti/lang/zh_tw.yml | 4 ++ .../resources/quickbackupmulti.mixins.json | 1 + 12 files changed, 156 insertions(+), 17 deletions(-) create mode 100644 src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/ClientPlayNetworkHandlerMixin.java create mode 100644 src/main/java/io/github/skydynamic/quickbackupmulti/utils/UpdateChecker.java diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/QuickBackupMulti.java b/src/main/java/io/github/skydynamic/quickbackupmulti/QuickBackupMulti.java index fdf1d49..c841b99 100644 --- a/src/main/java/io/github/skydynamic/quickbackupmulti/QuickBackupMulti.java +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/QuickBackupMulti.java @@ -9,6 +9,7 @@ import io.github.skydynamic.quickbackupmulti.config.ConfigStorage; import io.github.skydynamic.quickbackupmulti.command.QuickBackupMultiCommand; import io.github.skydynamic.quickbackupmulti.utils.QbmManager; +import io.github.skydynamic.quickbackupmulti.utils.UpdateChecker; import net.fabricmc.api.EnvType; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; @@ -47,7 +48,11 @@ public class QuickBackupMulti implements ModInitializer { //#else //$$ public static final Logger LOGGER = LogManager.getLogger(QuickBackupMulti.class); //#endif + + public static final UpdateChecker updateChecker = new UpdateChecker(); + public static final String modName = "QuickBackupMulti"; + public static final String modId = "quickbackupmulti"; EnvType env = FabricLoader.getInstance().getEnvironmentType(); @@ -56,6 +61,9 @@ public class QuickBackupMulti implements ModInitializer { @Override public void onInitialize() { + FabricLoader.getInstance().getModContainer(modId).ifPresent(modContainer -> + Config.TEMP_CONFIG.setModVersion(modContainer.getMetadata().getVersion().getFriendlyString())); + //#if MC>=12005 //$$ Packets.registerPacketCodec(); //#endif @@ -104,6 +112,8 @@ public void onInitialize() { } Config.TEMP_CONFIG.server = null; }); + + if (Config.INSTANCE.getCheckUpdata()) updateChecker.start(); } public void initDataBase() { diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/config/ConfigStorage.java b/src/main/java/io/github/skydynamic/quickbackupmulti/config/ConfigStorage.java index aeec6bb..dddd0ab 100644 --- a/src/main/java/io/github/skydynamic/quickbackupmulti/config/ConfigStorage.java +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/config/ConfigStorage.java @@ -7,6 +7,7 @@ public class ConfigStorage implements IConfig { @Ignore public static final ConfigStorage DEFAULT = new ConfigStorage( + true, new ArrayList<>(), new ArrayList<>(), "zh_cn", @@ -21,8 +22,9 @@ public class ConfigStorage implements IConfig { "QuickBackupMulti" ); - private ArrayList ignoredFiles; - private ArrayList ignoredFolders; + private final boolean checkUpdate; + private final ArrayList ignoredFiles; + private final ArrayList ignoredFolders; private String lang; private boolean scheduleBackup; private String scheduleCron; @@ -34,9 +36,10 @@ public class ConfigStorage implements IConfig { private boolean useInternalDataBase; private String mongoDBUri; - private String storagePath; + private final String storagePath; public ConfigStorage( + boolean checkUpdate, ArrayList IgnoredFiles, ArrayList ignoredFolders, String lang, @@ -49,6 +52,7 @@ public ConfigStorage( boolean useInternalDataBase, String mongoDBUri, String storagePath) { + this.checkUpdate = checkUpdate; this.ignoredFiles = IgnoredFiles; this.ignoredFolders = ignoredFolders; this.lang = lang; @@ -63,22 +67,18 @@ public ConfigStorage( this.storagePath = storagePath; } - public ArrayList getIgnoredFiles() { - return ignoredFiles; + public boolean isCheckUpedate() { + return checkUpdate; } - public void setIgnoredFiles(ArrayList ignoredFiles) { - this.ignoredFiles = ignoredFiles; + public ArrayList getIgnoredFiles() { + return ignoredFiles; } public ArrayList getIgnoredFolders() { return ignoredFolders; } - public void setIgnoredFolders(ArrayList ignoredFolders) { - this.ignoredFolders = ignoredFolders; - } - public String getLang() { return lang; } diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/config/QbmTempConfig.java b/src/main/java/io/github/skydynamic/quickbackupmulti/config/QbmTempConfig.java index 201e3c7..9c0e8db 100644 --- a/src/main/java/io/github/skydynamic/quickbackupmulti/config/QbmTempConfig.java +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/config/QbmTempConfig.java @@ -14,6 +14,7 @@ public class QbmTempConfig { public String worldName; public @Nullable Scheduler scheduler; public long latestScheduleExecuteTime; + public String modVersion; public void setIsBackupValue(Boolean value) { this.isBackup = value; @@ -42,4 +43,8 @@ public void setScheduler(@NotNull Scheduler scheduler) { public void setLatestScheduleExecuteTime(long time) { this.latestScheduleExecuteTime = time; } + + public void setModVersion(String modVersion) { + this.modVersion = modVersion; + } } diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/config/QuickBackupMultiConfig.java b/src/main/java/io/github/skydynamic/quickbackupmulti/config/QuickBackupMultiConfig.java index fb1e9bf..f447b02 100644 --- a/src/main/java/io/github/skydynamic/quickbackupmulti/config/QuickBackupMultiConfig.java +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/config/QuickBackupMultiConfig.java @@ -95,6 +95,12 @@ public void setConfigStorage(ConfigStorage configStorage) { } } + public boolean getCheckUpdata() { + synchronized (lock) { + return configStorage.isCheckUpedate(); + } + } + public List getIgnoredFiles() { synchronized (lock) { List list = new ArrayList<>(configStorage.getIgnoredFiles()); diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/ClientPlayNetworkHandlerMixin.java b/src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/ClientPlayNetworkHandlerMixin.java new file mode 100644 index 0000000..9021157 --- /dev/null +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/ClientPlayNetworkHandlerMixin.java @@ -0,0 +1,45 @@ +package io.github.skydynamic.quickbackupmulti.mixin.client; + +import io.github.skydynamic.quickbackupmulti.QuickBackupMulti; +import io.github.skydynamic.quickbackupmulti.utils.Messenger; +import io.github.skydynamic.quickbackupmulti.utils.UpdateChecker; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.MutableText; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import static io.github.skydynamic.quickbackupmulti.i18n.Translate.tr; + +@Mixin(ClientPlayNetworkHandler.class) +public class ClientPlayNetworkHandlerMixin { + @Inject( + method = "onGameJoin", + at = @At("TAIL") + ) + private void showUpdateMsg(GameJoinS2CPacket packet, CallbackInfo ci) { + if (MinecraftClient.getInstance().player == null) { + return; + } + UpdateChecker checker = QuickBackupMulti.updateChecker; + if (checker.needUpdate) { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + MutableText updateText = Messenger.literal( + tr("quickbackupmulti.check_update.on_player_join", checker.latestVersion) + ); + updateText.styled(style -> style + .withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, checker.latestVersionHtmUrl)) + .withHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, Messenger.literal(tr("quickbackupmulti.check_update.click_tooltip")) + )) + ); + player.sendMessage(updateText, false); + } + } +} diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/MinecraftClientMixin.java b/src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/MinecraftClientMixin.java index 0c8cb9a..513b658 100644 --- a/src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/MinecraftClientMixin.java +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/mixin/client/MinecraftClientMixin.java @@ -1,7 +1,6 @@ package io.github.skydynamic.quickbackupmulti.mixin.client; import io.github.skydynamic.quickbackupmulti.config.Config; -import io.github.skydynamic.quickbackupmulti.i18n.Translate; import net.minecraft.client.MinecraftClient; import net.minecraft.client.toast.SystemToast; import net.minecraft.client.toast.ToastManager; @@ -13,16 +12,18 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import static io.github.skydynamic.quickbackupmulti.i18n.Translate.tr; + @Mixin(MinecraftClient.class) -public class MinecraftClientMixin { +public abstract class MinecraftClientMixin { @Shadow @Final public ToastManager toastManager; @Inject(method = "setScreen", at = @At("RETURN")) private void inj(CallbackInfo ci) { if (Config.TEMP_CONFIG.isBackup) { - Text title = Text.of(Translate.tr("quickbackupmulti.toast.start_title")); - Text content = Text.of(Translate.tr("quickbackupmulti.toast.start_content")); + Text title = Text.of(tr("quickbackupmulti.toast.start_title")); + Text content = Text.of(tr("quickbackupmulti.toast.start_content")); //#if MC>=11800 SystemToast.show(this.toastManager, SystemToast.Type.PERIODIC_NOTIFICATION, title, content); //#else diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/utils/Messenger.java b/src/main/java/io/github/skydynamic/quickbackupmulti/utils/Messenger.java index 2ce1d4d..b06fb24 100644 --- a/src/main/java/io/github/skydynamic/quickbackupmulti/utils/Messenger.java +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/utils/Messenger.java @@ -8,7 +8,6 @@ //#endif public class Messenger { - public static void sendMessage(ServerCommandSource commandSource, Text text) { //#if MC>=11900 commandSource.sendMessage(text); @@ -24,5 +23,4 @@ public static MutableText literal(String string) { //$$ return new LiteralText(string); //#endif } - } diff --git a/src/main/java/io/github/skydynamic/quickbackupmulti/utils/UpdateChecker.java b/src/main/java/io/github/skydynamic/quickbackupmulti/utils/UpdateChecker.java new file mode 100644 index 0000000..648b926 --- /dev/null +++ b/src/main/java/io/github/skydynamic/quickbackupmulti/utils/UpdateChecker.java @@ -0,0 +1,61 @@ +package io.github.skydynamic.quickbackupmulti.utils; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.github.skydynamic.quickbackupmulti.QuickBackupMulti; +import io.github.skydynamic.quickbackupmulti.config.Config; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; + +public class UpdateChecker extends Thread { + + private static final HttpClient CLIENT = HttpClients.createDefault(); + private static final String RELEASE_API_URL = "https://api.github.com/repos/QuickBackupMultiMod-Dev/QuickBackupM-Fabric/releases/latest"; + + public String latestVersion; + public String latestVersionHtmUrl; + public boolean needUpdate = false; + + public UpdateChecker() { + super("QuickBackupM-Fabric-Update-Checker"); + } + + @Override + public void run() { + try { + if (Config.TEMP_CONFIG.modVersion == null) { + QuickBackupMulti.LOGGER.warn("Current mod version is not found."); + return; + } + HttpResponse httpResponse = CLIENT.execute(new HttpGet(RELEASE_API_URL)); + String body = EntityUtils.toString(httpResponse.getEntity()); + + // Get Meta data + JsonObject jsonObject = JsonParser.parseString(body).getAsJsonObject(); + latestVersion = jsonObject.get("tag_name").getAsString(); + latestVersionHtmUrl = jsonObject.get("html_url").getAsString(); + + String tag = latestVersion + .replaceAll("\\+.*", "") + .replaceFirst("^v", ""); + String currentVersion = Config.TEMP_CONFIG.modVersion + .replaceAll("\\+.*", "") + .replaceFirst("^v", ""); + + if (tag.compareTo(currentVersion) > 0) { + needUpdate = true; + QuickBackupMulti.LOGGER.info( + "{} has new version {}. You can see: {}", QuickBackupMulti.modName, latestVersion, latestVersionHtmUrl + ); + } + + } catch (IOException e) { + QuickBackupMulti.LOGGER.error("Check update failed", e); + } + } +} diff --git a/src/main/resources/assets/quickbackupmulti/lang/en_us.yml b/src/main/resources/assets/quickbackupmulti/lang/en_us.yml index 5ac3f2b..8c14dd5 100644 --- a/src/main/resources/assets/quickbackupmulti/lang/en_us.yml +++ b/src/main/resources/assets/quickbackupmulti/lang/en_us.yml @@ -2,6 +2,10 @@ quickbackupmulti: empty_comment: §7Empty§r list_empty: "§7Do not have any backups now" + check_update: + on_player_join: "§bQuickBackupMulti §rhas new release: §6%s" + click_tooltip: "Click to open release page" + config_page: save_button: "Save Config" close_button: "Close Screen" diff --git a/src/main/resources/assets/quickbackupmulti/lang/zh_cn.yml b/src/main/resources/assets/quickbackupmulti/lang/zh_cn.yml index e20057e..bf49d2c 100644 --- a/src/main/resources/assets/quickbackupmulti/lang/zh_cn.yml +++ b/src/main/resources/assets/quickbackupmulti/lang/zh_cn.yml @@ -2,6 +2,10 @@ quickbackupmulti: empty_comment: §7空§r list_empty: "§7当前没有任何备份" + check_update: + on_player_join: "检测到 §bQuickBackupMulti §r有新版本: §6%s" + click_tooltip: "点击这里打开新版本发布页面" + config_page: save_button: "保存" close_button: "关闭" diff --git a/src/main/resources/assets/quickbackupmulti/lang/zh_tw.yml b/src/main/resources/assets/quickbackupmulti/lang/zh_tw.yml index e306ab0..0d77985 100644 --- a/src/main/resources/assets/quickbackupmulti/lang/zh_tw.yml +++ b/src/main/resources/assets/quickbackupmulti/lang/zh_tw.yml @@ -2,6 +2,10 @@ quickbackupmulti: empty_comment: §7空§r list_empty: "§7目前沒有任何備份" + check_update: + on_player_join: "§bQuickBackupMulti §rhas new release: §6%s" + click_tooltip: "Click to open release page" + config_page: save_button: "儲存" close_button: "關閉" diff --git a/src/main/resources/quickbackupmulti.mixins.json b/src/main/resources/quickbackupmulti.mixins.json index e197ed1..62bd105 100644 --- a/src/main/resources/quickbackupmulti.mixins.json +++ b/src/main/resources/quickbackupmulti.mixins.json @@ -11,6 +11,7 @@ "server.MinecraftServerMixin" ], "client": [ + "client.ClientPlayNetworkHandlerMixin", "client.MinecraftClientMixin", "client.MinecraftServerMixin", "client.TitleScreenMixin",