diff options
| author | Linnea Gräf <nea@nea.moe> | 2024-04-12 14:32:41 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-12 14:32:41 +0200 |
| commit | afd3f0f861ebe7f8957eb6abc6e19f92c7b5896a (patch) | |
| tree | 0e7990ed1e7083dd4da5f90a842d0122919f8c7c /src/main/kotlin | |
| parent | a4a1c15a0b0de01e862c0f11882b45fee2c0cca7 (diff) | |
| download | NotEnoughUpdates-afd3f0f861ebe7f8957eb6abc6e19f92c7b5896a.tar.gz NotEnoughUpdates-afd3f0f861ebe7f8957eb6abc6e19f92c7b5896a.tar.bz2 NotEnoughUpdates-afd3f0f861ebe7f8957eb6abc6e19f92c7b5896a.zip | |
Add in-game updater (#1050)
Co-authored-by: IRONM00N <64110067+IRONM00N@users.noreply.github.com>
Diffstat (limited to 'src/main/kotlin')
5 files changed, 263 insertions, 11 deletions
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt index e8c12442..53e62045 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt @@ -43,6 +43,7 @@ import net.minecraft.util.EnumParticleTypes import net.minecraftforge.common.MinecraftForge import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.util.function.Predicate +import kotlin.io.path.absolutePathString import kotlin.math.floor @NEUAutoSubscribe diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt index 40919a8e..0e6fb1ff 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt @@ -32,7 +32,9 @@ import io.github.moulberry.notenoughupdates.core.config.GuiOptionEditorBlocked import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent import io.github.moulberry.notenoughupdates.miscfeatures.EnforcedConfigValues import io.github.moulberry.notenoughupdates.miscfeatures.IQTest +import io.github.moulberry.notenoughupdates.miscfeatures.updater.ConfigVersionGuiOption import io.github.moulberry.notenoughupdates.options.NEUConfig +import io.github.moulberry.notenoughupdates.options.customtypes.ConfigVersionDisplay import io.github.moulberry.notenoughupdates.util.brigadier.* import net.minecraft.client.gui.GuiScreen import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -122,6 +124,9 @@ object SettingsCommand { fun createConfigElement(search: String): MoulConfigEditor<NEUConfig> { val processor = BlockingMoulConfigProcessor() BuiltinMoulConfigGuis.addProcessors(processor) + processor.registerConfigEditor(ConfigVersionDisplay::class.java) { option, annotation -> + ConfigVersionGuiOption(option) + } ConfigProcessorDriver.processConfig( NotEnoughUpdates.INSTANCE.config.javaClass, NotEnoughUpdates.INSTANCE.config, diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt index 422871b4..70a72342 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt @@ -117,17 +117,6 @@ class MiscCommands { } }.withHelp("Look up someones pronouns using their minecraft username") } - event.command("neuupdate", "neuupdates", "enoughupdates") { - thenLiteralExecute("check") { - NotEnoughUpdates.INSTANCE.autoUpdater.displayUpdateMessageIfOutOfDate() - }.withHelp("Check for updates") - thenLiteralExecute("scheduledownload") { - NotEnoughUpdates.INSTANCE.autoUpdater.scheduleDownload() - }.withHelp("Queue a new version of NEU to be downloaded") - thenLiteralExecute("updatemodes") { - reply("§bTo ensure we do not accidentally corrupt your mod folder, we can only offer support for auto-updates on system with certain capabilities for file deletions (specifically unix systems). You can still manually update your files") - }.withHelp("Display an explanation why you cannot auto update") - } event.command("neudynamiclights", "neudli", "neudynlights", "neudynamicitems") { thenExecute { NotEnoughUpdates.INSTANCE.openGui = DynamicLightItemsEditor() diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt new file mode 100644 index 00000000..b9ce28b2 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2024 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.miscfeatures.updater + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent +import io.github.moulberry.notenoughupdates.util.MinecraftExecutor +import io.github.moulberry.notenoughupdates.util.brigadier.thenExecute +import moe.nea.libautoupdate.CurrentVersion +import moe.nea.libautoupdate.PotentialUpdate +import moe.nea.libautoupdate.UpdateContext +import moe.nea.libautoupdate.UpdateTarget +import net.minecraft.client.Minecraft +import net.minecraft.event.ClickEvent +import net.minecraft.util.ChatComponentText +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import org.apache.logging.log4j.LogManager +import java.util.concurrent.CompletableFuture + +@NEUAutoSubscribe +object AutoUpdater { + val updateContext = UpdateContext( + SigningGithubSource("NotEnoughUpdates", "NotEnoughUpdates"), + UpdateTarget.deleteAndSaveInTheSameFolder(AutoUpdater::class.java), + CurrentVersion.ofTag(NotEnoughUpdates.VERSION.substringBefore("+")), + "notenoughupdates" + ) + val logger = LogManager.getLogger("NEUUpdater") + private var activePromise: CompletableFuture<*>? = null + set(value) { + field?.cancel(true) + field = value + } + + + var updateState: UpdateState = UpdateState.NONE + private set + + var potentialUpdate: PotentialUpdate? = null + + enum class UpdateState { + AVAILABLE, + QUEUED, + DOWNLOADED, + NONE + } + + fun reset() { + updateState = UpdateState.NONE + activePromise = null + potentialUpdate = null + logger.info("Reset update state") + } + + fun checkUpdate() { + if (updateState != UpdateState.NONE) { + logger.error("Trying to perform update check while another update is already in progress") + return + } + logger.info("Starting update check") + val updateStream = config.updateStream.get() + activePromise = updateContext.checkUpdate(updateStream.stream) + .thenAcceptAsync({ + logger.info("Update check completed") + if (updateState != UpdateState.NONE) { + logger.warn("This appears to be the second update check. Ignoring this one") + return@thenAcceptAsync + } + potentialUpdate = it + if (it.isUpdateAvailable) { + updateState = UpdateState.AVAILABLE + Minecraft.getMinecraft().thePlayer?.addChatMessage(ChatComponentText("§e[NEU] §aNEU found a new update: ${it.update.versionName}. Click here to automatically install this update.").apply { + this.chatStyle = this.chatStyle.setChatClickEvent( + ClickEvent( + ClickEvent.Action.RUN_COMMAND, + "/neuinternalupdatenow" + ) + ) + }) + } + }, MinecraftExecutor.OnThread) + } + + fun queueUpdate() { + if (updateState != UpdateState.AVAILABLE) { + logger.error("Trying to enqueue an update while another one is already downloaded or none is present") + } + updateState = UpdateState.QUEUED + activePromise = CompletableFuture.supplyAsync { + logger.info("Update download started") + potentialUpdate!!.prepareUpdate() + }.thenAcceptAsync({ + logger.info("Update download completed, setting exit hook") + updateState = UpdateState.DOWNLOADED + potentialUpdate!!.executeUpdate() + }, MinecraftExecutor.OnThread) + } + + init { + updateContext.cleanup() + config.updateStream.whenChanged { _, _ -> + reset() + } + } + + fun getCurrentVersion(): String { + return NotEnoughUpdates.VERSION + } + + + val config get() = NotEnoughUpdates.INSTANCE.config.about + + @SubscribeEvent + fun onPlayerAvailableOnce(event: TickEvent.ClientTickEvent) { + val p = Minecraft.getMinecraft().thePlayer ?: return + MinecraftForge.EVENT_BUS.unregister(this) + if (config.autoUpdates) + checkUpdate() + } + + + @SubscribeEvent + fun testCommand(event: RegisterBrigadierCommandEvent) { + event.command("neuinternalupdatenow") { + thenExecute { + queueUpdate() + } + } + } + + fun getNextVersion(): String? { + return potentialUpdate?.update?.versionName + } + + +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt new file mode 100644 index 00000000..3a920aec --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.miscfeatures.updater + +import io.github.moulberry.moulconfig.gui.GuiOptionEditor +import io.github.moulberry.moulconfig.processor.ProcessedOption +import io.github.moulberry.notenoughupdates.core.util.render.TextRenderUtils +import io.github.moulberry.notenoughupdates.itemeditor.GuiElementButton +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.EnumChatFormatting.* + +import org.lwjgl.input.Mouse + +class ConfigVersionGuiOption(option: ProcessedOption) : GuiOptionEditor(option) { + val button = GuiElementButton("", -1) { } + override fun render(x: Int, y: Int, width: Int) { + val fr = Minecraft.getMinecraft().fontRendererObj + GlStateManager.pushMatrix() + GlStateManager.translate(x.toFloat() + 10, y.toFloat(), 1F) + val width = width - 20 + val nextVersion = AutoUpdater.getNextVersion() + + button.text = when (AutoUpdater.updateState) { + AutoUpdater.UpdateState.AVAILABLE -> "Download update" + AutoUpdater.UpdateState.QUEUED -> "Downloading..." + AutoUpdater.UpdateState.DOWNLOADED -> "Downloaded" + AutoUpdater.UpdateState.NONE -> if (nextVersion == null) "Check for Updates" else "Up to date" + } + button.render(getButtonPosition(width), 10) + + if (AutoUpdater.updateState == AutoUpdater.UpdateState.DOWNLOADED) { + TextRenderUtils.drawStringCentered( + "${GREEN}The update will be installed after your next restart.", + fr, + width / 2F, + 40F, + true, + -1 + ) + } + + val widthRemaining = width - button.width - 10 + + GlStateManager.scale(2F, 2F, 1F) + TextRenderUtils.drawStringCenteredScaledMaxWidth( + "${if (AutoUpdater.updateState == AutoUpdater.UpdateState.NONE) GREEN else RED}${AutoUpdater.getCurrentVersion()}" + + if (nextVersion != null) "➜ ${GREEN}${nextVersion}" else "", + widthRemaining / 4F, + 10F, + true, + widthRemaining / 2, + -1 + ) + + GlStateManager.popMatrix() + } + + fun getButtonPosition(width: Int) = width - button.width + override fun getHeight(): Int { + return 55 + } + + override fun mouseInput(x: Int, y: Int, width: Int, mouseX: Int, mouseY: Int): Boolean { + val width = width - 20 + if (Mouse.getEventButtonState()) { + if ((mouseX - getButtonPosition(width) - x) in (0..button.width) && (mouseY - 10 - y) in (0..button.height)) { + when (AutoUpdater.updateState) { + AutoUpdater.UpdateState.AVAILABLE -> AutoUpdater.queueUpdate() + AutoUpdater.UpdateState.QUEUED -> {} + AutoUpdater.UpdateState.DOWNLOADED -> {} + AutoUpdater.UpdateState.NONE -> AutoUpdater.checkUpdate() + } + return true + } + } + return false + } + + override fun keyboardInput(): Boolean { + return false + } + +} |
