aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-04-12 14:32:41 +0200
committerGitHub <noreply@github.com>2024-04-12 14:32:41 +0200
commitafd3f0f861ebe7f8957eb6abc6e19f92c7b5896a (patch)
tree0e7990ed1e7083dd4da5f90a842d0122919f8c7c /src/main/kotlin
parenta4a1c15a0b0de01e862c0f11882b45fee2c0cca7 (diff)
downloadNotEnoughUpdates-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')
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt1
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt5
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt11
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt156
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt101
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
+ }
+
+}