From 6e28e1c781a7336d2a343a448f01c63d3fac5f1f Mon Sep 17 00:00:00 2001 From: THCFree Date: Wed, 28 Jan 2026 02:04:41 +0100 Subject: [PATCH 1/6] Auto Updater module pending modals for user confirmation and forced restart --- .../module/modules/client/AutoUpdater.kt | 346 ++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt diff --git a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt new file mode 100644 index 000000000..04746cb44 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt @@ -0,0 +1,346 @@ +/* + * Copyright 2026 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.modules.client + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import net.fabricmc.loader.api.FabricLoader +import net.minecraft.SharedConstants +import org.slf4j.LoggerFactory +import java.net.URI +import java.nio.file.Path +import javax.xml.parsers.DocumentBuilderFactory +import kotlin.concurrent.thread + +object AutoUpdater : Module( + name = "AutoUpdater", + description = "Installs / uninstalls Lambda loader", + tag = ModuleTag.CLIENT, +) { + private val logger = LoggerFactory.getLogger("AutoUpdater") + + private val debug by setting("Debug", false, "Enable debug logging") + private val loaderBranch by setting("Loader Branch", Branch.STABLE, "Select loader update branch") + private val clientBranch by setting("Client Branch", Branch.STABLE, "Select client update branch") + + private enum class Branch { + STABLE, + SNAPSHOT + } + + init { + onEnable { + //TODO: Add modal to prompt user for confirmation before updating and restart after download and confirmation + thread(name = "Lambda-Client-Updater") { + try { + // Check if Lambda client mod is present + if (!isModContainerPresent("lambda")) { + logger.error("Lambda client mod not found!") + return@thread + } + + logger.info("Starting Lambda client update...") + + // Download the latest client JAR + val clientJar = downloadLatestClient() + if (clientJar == null) { + logger.error("Failed to download latest Lambda client") + return@thread + } + + // Get the current Lambda mod JAR path + val lambdaJarPath = getModJarPath("lambda") + if (debug) { + logger.info("Lambda JAR path: $lambdaJarPath") + } + + // Write the new JAR to replace the old one + val jarFile = lambdaJarPath.toFile() + jarFile.writeBytes(clientJar) + + logger.info("Successfully updated Lambda client! Restart required.") + } catch (e: Exception) { + logger.error("Error updating Lambda client", e) + } + } + } + + onDisable { + //TODO: Add modal to prompt user for confirmation before updating and restart after download and confirmation + thread(name = "Lambda-Loader-Updater") { + try { + // Check if Lambda loader mod is present + if (!isModContainerPresent("lambda-loader")) { + logger.error("Lambda loader mod not found!") + return@thread + } + + logger.info("Starting Lambda loader update...") + + // Download the latest loader JAR + val loaderJar = downloadLatestLoader() + if (loaderJar == null) { + logger.error("Failed to download latest Lambda loader") + return@thread + } + + // Get the current Lambda loader mod JAR path + val loaderJarPath = getModJarPath("lambda-loader") + if (debug) { + logger.info("Lambda loader JAR path: $loaderJarPath") + } + + // Write the new JAR to replace the old one + val jarFile = loaderJarPath.toFile() + jarFile.writeBytes(loaderJar) + + logger.info("Successfully updated Lambda loader! Restart required.") + } catch (e: Exception) { + logger.error("Error updating Lambda loader", e) + } + } + } + } + + // Maven URLs + private const val MAVEN_URL = "https://maven.lambda-client.org" + private const val MAVEN_THC_URL = "https://maven.lambda-client.org" + private const val LOADER_RELEASES_META = "$MAVEN_THC_URL/releases/com/lambda/loader/maven-metadata.xml" + private const val LOADER_SNAPSHOTS_META = "$MAVEN_THC_URL/snapshots/com/lambda/loader/maven-metadata.xml" + private const val CLIENT_RELEASES_META = "$MAVEN_URL/releases/com/lambda/lambda/maven-metadata.xml" + private const val CLIENT_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/lambda/maven-metadata.xml" + + /** + * Get the current Minecraft version + */ + private fun getMinecraftVersion(): String { + return SharedConstants.getGameVersion().name() + } + + private fun isModContainerPresent(modId: String): Boolean { + val fabricLoader = FabricLoader.getInstance() + return fabricLoader.getModContainer(modId).isPresent + } + + private fun getModJarPath(modId: String): Path { + val fabricLoader = FabricLoader.getInstance() + val modContainer = fabricLoader.getModContainer(modId) + return modContainer.get().origin.paths[0].toAbsolutePath() + } + + /** + * Parse latest version from maven-metadata.xml, optionally filtering by MC version + */ + private fun parseLatestVersion(xml: String, mcVersion: String? = null): String? { + return try { + val factory = DocumentBuilderFactory.newInstance() + val builder = factory.newDocumentBuilder() + val document = builder.parse(xml.byteInputStream()) + + val versionNodes = document.getElementsByTagName("version") + val versions = mutableListOf() + + for (i in 0 until versionNodes.length) { + versions.add(versionNodes.item(i).textContent) + } + + if (debug) { + if (mcVersion != null) { + logger.info("Target MC version: $mcVersion") + } + logger.info("Available versions: ${versions.joinToString(", ")}") + } + + val matchingVersions = if (mcVersion != null) { + versions.filter { version -> + val mcVersionInArtifact = version.substringAfter("+").substringBefore("-") + val normalizedArtifact = mcVersionInArtifact.replace(".", "") + val normalizedTarget = mcVersion.replace(".", "") + normalizedArtifact == normalizedTarget + } + } else { + versions + } + + if (matchingVersions.isEmpty()) { + if (debug) { + val versionMsg = mcVersion?.let { "for MC $it" } ?: "" + logger.warn("No versions found $versionMsg") + } + null + } else { + matchingVersions.last() + } + } catch (e: Exception) { + logger.error("Error parsing version", e) + null + } + } + + /** + * Get snapshot info (timestamp and build number) + */ + private data class SnapshotInfo( + val version: String, + val timestamp: String, + val buildNumber: String + ) + + private fun getSnapshotInfo(baseUrl: String, artifactPath: String, version: String): SnapshotInfo? { + return try { + val snapshotMetaUrl = URI("$baseUrl/$artifactPath/$version/maven-metadata.xml").toURL() + val xml = snapshotMetaUrl.readText() + + val factory = DocumentBuilderFactory.newInstance() + val builder = factory.newDocumentBuilder() + val document = builder.parse(xml.byteInputStream()) + + val timestamp = document.getElementsByTagName("timestamp").item(0).textContent + val buildNumber = document.getElementsByTagName("buildNumber").item(0).textContent + + SnapshotInfo(version, timestamp, buildNumber) + } catch (e: Exception) { + logger.error("Error getting snapshot info", e) + null + } + } + + /** + * Download the latest loader JAR from selected branch or fallback to snapshot + */ + fun downloadLatestLoader(): ByteArray? { + return try { + val branch = loaderBranch + val mcVersion = getMinecraftVersion() + + if (debug) { + logger.info("Downloading loader for MC $mcVersion from ${branch.name} branch") + } + + // Try selected branch first + var version: String? + var baseUrl: String? + + when (branch) { + Branch.STABLE -> { + val xml = URI(LOADER_RELEASES_META).toURL().readText() + version = parseLatestVersion(xml, null) + baseUrl = "$MAVEN_URL/releases" + } + Branch.SNAPSHOT -> { + val xml = URI(LOADER_SNAPSHOTS_META).toURL().readText() + version = parseLatestVersion(xml, null) + baseUrl = "$MAVEN_URL/snapshots" + } + } + + // Fallback to snapshot if stable not found + if (version == null && branch == Branch.STABLE) { + logger.warn("No stable loader found, falling back to snapshot") + val xml = URI(LOADER_SNAPSHOTS_META).toURL().readText() + version = parseLatestVersion(xml, null) + baseUrl = "$MAVEN_URL/snapshots" + } + + if (version == null) { + logger.error("No loader version found") + return null + } + + // Build download URL + val jarUrl = if (version.endsWith("-SNAPSHOT")) { + val snapshotInfo = getSnapshotInfo(baseUrl, "com/lambda/loader", version) ?: return null + val baseVersion = version.replace("-SNAPSHOT", "") + "$baseUrl/com/lambda/loader/$version/loader-$baseVersion-${snapshotInfo.timestamp}-${snapshotInfo.buildNumber}.jar" + } else { + "$baseUrl/com/lambda/loader/$version/loader-$version.jar" + } + + if (debug) { + logger.info("Downloading from: $jarUrl") + } + + URI(jarUrl).toURL().readBytes() + } catch (e: Exception) { + logger.error("Failed to download loader", e) + null + } + } + + /** + * Download the latest client JAR for current MC version from selected branch or fallback to snapshot + */ + fun downloadLatestClient(): ByteArray? { + return try { + val branch = clientBranch + val mcVersion = getMinecraftVersion() + + if (debug) { + logger.info("Downloading client for MC $mcVersion from ${branch.name} branch") + } + + // Try selected branch first + var version: String? + var baseUrl: String? + + when (branch) { + Branch.STABLE -> { + val xml = URI(CLIENT_RELEASES_META).toURL().readText() + version = parseLatestVersion(xml, mcVersion) + baseUrl = "$MAVEN_URL/releases" + } + Branch.SNAPSHOT -> { + val xml = URI(CLIENT_SNAPSHOTS_META).toURL().readText() + version = parseLatestVersion(xml, mcVersion) + baseUrl = "$MAVEN_URL/snapshots" + } + } + + // Fallback to snapshot if stable not found + if (version == null && branch == Branch.STABLE) { + logger.warn("No stable client found for MC $mcVersion, falling back to snapshot") + val xml = URI(CLIENT_SNAPSHOTS_META).toURL().readText() + version = parseLatestVersion(xml, mcVersion) + baseUrl = "$MAVEN_URL/snapshots" + } + + if (version == null) { + logger.error("No client version found for MC $mcVersion") + return null + } + + // Build download URL + val jarUrl = if (version.endsWith("-SNAPSHOT")) { + val snapshotInfo = getSnapshotInfo(baseUrl, "com/lambda/lambda", version) ?: return null + val baseVersion = version.replace("-SNAPSHOT", "") + "$baseUrl/com/lambda/lambda/$version/lambda-$baseVersion-${snapshotInfo.timestamp}-${snapshotInfo.buildNumber}.jar" + } else { + "$baseUrl/com/lambda/lambda/$version/lambda-$version.jar" + } + + if (debug) { + logger.info("Downloading from: $jarUrl") + } + + URI(jarUrl).toURL().readBytes() + } catch (e: Exception) { + logger.error("Failed to download client", e) + null + } + } +} \ No newline at end of file From 029ab82316fea2b99c4333b088b1d1e09a376643 Mon Sep 17 00:00:00 2001 From: THCFree Date: Wed, 28 Jan 2026 16:50:27 +0100 Subject: [PATCH 2/6] Added modal for user confirmation on loader install / uninstall --- .../module/modules/client/AutoUpdater.kt | 230 +++++++++++++----- 1 file changed, 168 insertions(+), 62 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt index 04746cb44..917f33ee3 100644 --- a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt +++ b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt @@ -17,8 +17,20 @@ package com.lambda.module.modules.client +import com.lambda.Lambda.mc +import com.lambda.event.events.GuiEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.gui.LambdaScreen +import com.lambda.gui.components.ClickGuiLayout +import com.lambda.gui.dsl.ImGuiBuilder.buildLayout +import com.lambda.gui.dsl.ImGuiBuilder.button +import com.lambda.gui.dsl.ImGuiBuilder.openPopup +import com.lambda.gui.dsl.ImGuiBuilder.popupModal +import com.lambda.gui.dsl.ImGuiBuilder.sameLine +import com.lambda.gui.dsl.ImGuiBuilder.text import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import imgui.flag.ImGuiWindowFlags import net.fabricmc.loader.api.FabricLoader import net.minecraft.SharedConstants import org.slf4j.LoggerFactory @@ -26,6 +38,7 @@ import java.net.URI import java.nio.file.Path import javax.xml.parsers.DocumentBuilderFactory import kotlin.concurrent.thread +import kotlin.system.exitProcess object AutoUpdater : Module( name = "AutoUpdater", @@ -38,6 +51,11 @@ object AutoUpdater : Module( private val loaderBranch by setting("Loader Branch", Branch.STABLE, "Select loader update branch") private val clientBranch by setting("Client Branch", Branch.STABLE, "Select client update branch") + var showInstallModal = false + private set + var showUninstallModal = false + private set + private enum class Branch { STABLE, SNAPSHOT @@ -45,83 +63,89 @@ object AutoUpdater : Module( init { onEnable { - //TODO: Add modal to prompt user for confirmation before updating and restart after download and confirmation - thread(name = "Lambda-Client-Updater") { - try { - // Check if Lambda client mod is present - if (!isModContainerPresent("lambda")) { - logger.error("Lambda client mod not found!") - return@thread - } - - logger.info("Starting Lambda client update...") + // Check if ClickGuiLayout is currently open + if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) { + // Open modal in GUI + showInstallModal = true + } else { + // Run update directly if GUI is not open + return@onEnable + } + } - // Download the latest client JAR - val clientJar = downloadLatestClient() - if (clientJar == null) { - logger.error("Failed to download latest Lambda client") - return@thread + // Listen for GUI events to render the install modal + listen { + if (showInstallModal) { + buildLayout { + // Open the modal popup + openPopup("Install Lambda Loader") + + // Render the modal + popupModal("Install Lambda Loader", ImGuiWindowFlags.AlwaysAutoResize) { + text("Do you want to install Lambda Client?") + text("") + text("This will close the client automatically once the install is finished.") + text("") + + button("Install", 120f, 0f) { + showInstallModal = false + performLoaderInstall() + } + + sameLine() + + button("Cancel", 120f, 0f) { + showInstallModal = false + } } + } + } - // Get the current Lambda mod JAR path - val lambdaJarPath = getModJarPath("lambda") - if (debug) { - logger.info("Lambda JAR path: $lambdaJarPath") + if (showUninstallModal) { + buildLayout { + // Open the modal popup + openPopup("Uninstall Lambda Loader") + + // Render the modal + popupModal("Uninstall Lambda Loader", ImGuiWindowFlags.AlwaysAutoResize) { + text("Do you want to uninstall Lambda Loader?") + text("") + text("This will close the client automatically once the uninstall is finished.") + text("") + + button("Uninstall", 120f, 0f) { + showUninstallModal = false + disable() + performLoaderUninstall() + } + + sameLine() + + button("Cancel", 120f, 0f) { + showUninstallModal = false + } } - - // Write the new JAR to replace the old one - val jarFile = lambdaJarPath.toFile() - jarFile.writeBytes(clientJar) - - logger.info("Successfully updated Lambda client! Restart required.") - } catch (e: Exception) { - logger.error("Error updating Lambda client", e) } } } onDisable { - //TODO: Add modal to prompt user for confirmation before updating and restart after download and confirmation - thread(name = "Lambda-Loader-Updater") { - try { - // Check if Lambda loader mod is present - if (!isModContainerPresent("lambda-loader")) { - logger.error("Lambda loader mod not found!") - return@thread - } - - logger.info("Starting Lambda loader update...") - - // Download the latest loader JAR - val loaderJar = downloadLatestLoader() - if (loaderJar == null) { - logger.error("Failed to download latest Lambda loader") - return@thread - } - - // Get the current Lambda loader mod JAR path - val loaderJarPath = getModJarPath("lambda-loader") - if (debug) { - logger.info("Lambda loader JAR path: $loaderJarPath") - } - - // Write the new JAR to replace the old one - val jarFile = loaderJarPath.toFile() - jarFile.writeBytes(loaderJar) - - logger.info("Successfully updated Lambda loader! Restart required.") - } catch (e: Exception) { - logger.error("Error updating Lambda loader", e) - } + // Check if ClickGuiLayout is currently open + if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) { + // Open modal in GUI - keep module enabled until user confirms + showUninstallModal = true + enable() + } else { + // Run uninstall directly if GUI is not open + performLoaderUninstall() } } } // Maven URLs private const val MAVEN_URL = "https://maven.lambda-client.org" - private const val MAVEN_THC_URL = "https://maven.lambda-client.org" - private const val LOADER_RELEASES_META = "$MAVEN_THC_URL/releases/com/lambda/loader/maven-metadata.xml" - private const val LOADER_SNAPSHOTS_META = "$MAVEN_THC_URL/snapshots/com/lambda/loader/maven-metadata.xml" + private const val LOADER_RELEASES_META = "$MAVEN_URL/releases/com/lambda/loader/maven-metadata.xml" + private const val LOADER_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/loader/maven-metadata.xml" private const val CLIENT_RELEASES_META = "$MAVEN_URL/releases/com/lambda/lambda/maven-metadata.xml" private const val CLIENT_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/lambda/maven-metadata.xml" @@ -282,6 +306,88 @@ object AutoUpdater : Module( } } + /** + * Performs the client install in a background thread + */ + private fun performLoaderInstall() { + thread(name = "Lambda-Client-Installer") { + try { + // Check if Lambda client mod is present + if (!isModContainerPresent("lambda")) { + logger.error("Lambda client mod not found!") + return@thread + } + + logger.info("Starting Lambda client install...") + + // Download the latest client JAR + val clientJar = downloadLatestClient() + if (clientJar == null) { + logger.error("Failed to download latest Lambda client") + return@thread + } + + // Get the current Lambda client mod JAR path + val clientJarPath = getModJarPath("lambda") + if (debug) { + logger.info("Lambda client JAR path: $clientJarPath") + } + + // Write the new JAR to replace the old one + val jarFile = clientJarPath.toFile() + jarFile.writeBytes(clientJar) + + logger.info("Successfully installed Lambda client! Restarting...") + + // Automatically restart the game + exitProcess(0) + } catch (e: Exception) { + logger.error("Error installing Lambda client", e) + } + } + } + + /** + * Performs the loader uninstall in a background thread + */ + private fun performLoaderUninstall() { + thread(name = "Lambda-Loader-Uninstaller") { + try { + // Check if Lambda loader mod is present + if (!isModContainerPresent("lambda-loader")) { + logger.error("Lambda loader mod not found!") + return@thread + } + + logger.info("Starting Lambda loader uninstall...") + + // Download the latest loader JAR + val loaderJar = downloadLatestLoader() + if (loaderJar == null) { + logger.error("Failed to download latest Lambda loader") + return@thread + } + + // Get the current Lambda loader mod JAR path + val loaderJarPath = getModJarPath("lambda-loader") + if (debug) { + logger.info("Lambda loader JAR path: $loaderJarPath") + } + + // Write the new JAR to replace the old one + val jarFile = loaderJarPath.toFile() + jarFile.writeBytes(loaderJar) + + logger.info("Successfully uninstalled Lambda loader! Restarting...") + + // Automatically restart the game + exitProcess(0) + } catch (e: Exception) { + logger.error("Error uninstalling Lambda loader", e) + } + } + } + /** * Download the latest client JAR for current MC version from selected branch or fallback to snapshot */ From dc225dca3b84297cc6b548140c67f1c9487deae5 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:46:18 +0000 Subject: [PATCH 3/6] cleanup --- .../module/modules/client/AutoUpdater.kt | 373 +++++++----------- 1 file changed, 140 insertions(+), 233 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt index 917f33ee3..2bc8fe41f 100644 --- a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt +++ b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt @@ -23,21 +23,17 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.gui.LambdaScreen import com.lambda.gui.components.ClickGuiLayout import com.lambda.gui.dsl.ImGuiBuilder.buildLayout -import com.lambda.gui.dsl.ImGuiBuilder.button -import com.lambda.gui.dsl.ImGuiBuilder.openPopup -import com.lambda.gui.dsl.ImGuiBuilder.popupModal -import com.lambda.gui.dsl.ImGuiBuilder.sameLine -import com.lambda.gui.dsl.ImGuiBuilder.text import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import imgui.flag.ImGuiWindowFlags +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import net.fabricmc.loader.api.FabricLoader import net.minecraft.SharedConstants import org.slf4j.LoggerFactory import java.net.URI import java.nio.file.Path import javax.xml.parsers.DocumentBuilderFactory -import kotlin.concurrent.thread import kotlin.system.exitProcess object AutoUpdater : Module( @@ -48,39 +44,35 @@ object AutoUpdater : Module( private val logger = LoggerFactory.getLogger("AutoUpdater") private val debug by setting("Debug", false, "Enable debug logging") - private val loaderBranch by setting("Loader Branch", Branch.STABLE, "Select loader update branch") - private val clientBranch by setting("Client Branch", Branch.STABLE, "Select client update branch") + private val loaderBranch by setting("Loader Branch", Branch.Stable, "Select loader update branch") + private val clientBranch by setting("Client Branch", Branch.Stable, "Select client update branch") var showInstallModal = false private set var showUninstallModal = false private set + private const val MAVEN_URL = "https://maven.lambda-client.org" + private const val LOADER_RELEASES_META = "$MAVEN_URL/releases/com/lambda/loader/maven-metadata.xml" + private const val LOADER_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/loader/maven-metadata.xml" + private const val CLIENT_RELEASES_META = "$MAVEN_URL/releases/com/lambda/lambda/maven-metadata.xml" + private const val CLIENT_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/lambda/maven-metadata.xml" + private enum class Branch { - STABLE, - SNAPSHOT + Stable, + Snapshot } init { onEnable { - // Check if ClickGuiLayout is currently open - if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) { - // Open modal in GUI + if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) showInstallModal = true - } else { - // Run update directly if GUI is not open - return@onEnable - } } - // Listen for GUI events to render the install modal listen { if (showInstallModal) { buildLayout { - // Open the modal popup openPopup("Install Lambda Loader") - - // Render the modal popupModal("Install Lambda Loader", ImGuiWindowFlags.AlwaysAutoResize) { text("Do you want to install Lambda Client?") text("") @@ -89,7 +81,7 @@ object AutoUpdater : Module( button("Install", 120f, 0f) { showInstallModal = false - performLoaderInstall() + installClient() } sameLine() @@ -103,10 +95,7 @@ object AutoUpdater : Module( if (showUninstallModal) { buildLayout { - // Open the modal popup openPopup("Uninstall Lambda Loader") - - // Render the modal popupModal("Uninstall Lambda Loader", ImGuiWindowFlags.AlwaysAutoResize) { text("Do you want to uninstall Lambda Loader?") text("") @@ -116,7 +105,7 @@ object AutoUpdater : Module( button("Uninstall", 120f, 0f) { showUninstallModal = false disable() - performLoaderUninstall() + installLoader() } sameLine() @@ -130,151 +119,89 @@ object AutoUpdater : Module( } onDisable { - // Check if ClickGuiLayout is currently open if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) { - // Open modal in GUI - keep module enabled until user confirms showUninstallModal = true enable() - } else { - // Run uninstall directly if GUI is not open - performLoaderUninstall() - } + } else installLoader() } } - // Maven URLs - private const val MAVEN_URL = "https://maven.lambda-client.org" - private const val LOADER_RELEASES_META = "$MAVEN_URL/releases/com/lambda/loader/maven-metadata.xml" - private const val LOADER_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/loader/maven-metadata.xml" - private const val CLIENT_RELEASES_META = "$MAVEN_URL/releases/com/lambda/lambda/maven-metadata.xml" - private const val CLIENT_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/lambda/maven-metadata.xml" - - /** - * Get the current Minecraft version - */ - private fun getMinecraftVersion(): String { - return SharedConstants.getGameVersion().name() - } - - private fun isModContainerPresent(modId: String): Boolean { - val fabricLoader = FabricLoader.getInstance() - return fabricLoader.getModContainer(modId).isPresent - } - - private fun getModJarPath(modId: String): Path { - val fabricLoader = FabricLoader.getInstance() - val modContainer = fabricLoader.getModContainer(modId) - return modContainer.get().origin.paths[0].toAbsolutePath() - } - - /** - * Parse latest version from maven-metadata.xml, optionally filtering by MC version - */ - private fun parseLatestVersion(xml: String, mcVersion: String? = null): String? { - return try { - val factory = DocumentBuilderFactory.newInstance() - val builder = factory.newDocumentBuilder() - val document = builder.parse(xml.byteInputStream()) + private fun installLoader() { + runBlocking(Dispatchers.IO) { + try { + logger.info("Starting Lambda loader uninstall...") - val versionNodes = document.getElementsByTagName("version") - val versions = mutableListOf() + val loaderJar = downloadLatestLoader() + if (loaderJar == null) { + logger.error("Failed to download latest Lambda loader") + return@runBlocking + } - for (i in 0 until versionNodes.length) { - versions.add(versionNodes.item(i).textContent) - } + val loaderJarPath = getModJarPath("lambda-loader") + if (debug) logger.info("Lambda loader JAR path: $loaderJarPath") - if (debug) { - if (mcVersion != null) { - logger.info("Target MC version: $mcVersion") - } - logger.info("Available versions: ${versions.joinToString(", ")}") - } + val jarFile = loaderJarPath.toFile() + jarFile.writeBytes(loaderJar) - val matchingVersions = if (mcVersion != null) { - versions.filter { version -> - val mcVersionInArtifact = version.substringAfter("+").substringBefore("-") - val normalizedArtifact = mcVersionInArtifact.replace(".", "") - val normalizedTarget = mcVersion.replace(".", "") - normalizedArtifact == normalizedTarget - } - } else { - versions - } + logger.info("Successfully uninstalled Lambda loader! Restarting...") - if (matchingVersions.isEmpty()) { - if (debug) { - val versionMsg = mcVersion?.let { "for MC $it" } ?: "" - logger.warn("No versions found $versionMsg") - } - null - } else { - matchingVersions.last() + mc.stop() + } catch (e: Exception) { + logger.error("Error uninstalling Lambda loader", e) } - } catch (e: Exception) { - logger.error("Error parsing version", e) - null } } - /** - * Get snapshot info (timestamp and build number) - */ - private data class SnapshotInfo( - val version: String, - val timestamp: String, - val buildNumber: String - ) + private fun installClient() { + runBlocking(Dispatchers.IO) { + try { + logger.info("Starting Lambda client install...") - private fun getSnapshotInfo(baseUrl: String, artifactPath: String, version: String): SnapshotInfo? { - return try { - val snapshotMetaUrl = URI("$baseUrl/$artifactPath/$version/maven-metadata.xml").toURL() - val xml = snapshotMetaUrl.readText() + val clientJar = downloadLatestClient() + if (clientJar == null) { + logger.error("Failed to download latest Lambda client") + return@runBlocking + } - val factory = DocumentBuilderFactory.newInstance() - val builder = factory.newDocumentBuilder() - val document = builder.parse(xml.byteInputStream()) + val clientJarPath = getModJarPath("lambda") + if (debug) logger.info("Lambda client JAR path: $clientJarPath") - val timestamp = document.getElementsByTagName("timestamp").item(0).textContent - val buildNumber = document.getElementsByTagName("buildNumber").item(0).textContent + val jarFile = clientJarPath.toFile() + jarFile.writeBytes(clientJar) - SnapshotInfo(version, timestamp, buildNumber) - } catch (e: Exception) { - logger.error("Error getting snapshot info", e) - null + logger.info("Successfully installed Lambda client! Restarting...") + + mc.stop() + } catch (e: Exception) { + logger.error("Error installing Lambda client", e) + } } } - /** - * Download the latest loader JAR from selected branch or fallback to snapshot - */ fun downloadLatestLoader(): ByteArray? { return try { val branch = loaderBranch val mcVersion = getMinecraftVersion() - if (debug) { - logger.info("Downloading loader for MC $mcVersion from ${branch.name} branch") - } + if (debug) logger.info("Downloading loader for MC $mcVersion from ${branch.name} branch") - // Try selected branch first var version: String? var baseUrl: String? - when (branch) { - Branch.STABLE -> { + when (branch) { + Branch.Stable -> { val xml = URI(LOADER_RELEASES_META).toURL().readText() version = parseLatestVersion(xml, null) baseUrl = "$MAVEN_URL/releases" } - Branch.SNAPSHOT -> { + Branch.Snapshot -> { val xml = URI(LOADER_SNAPSHOTS_META).toURL().readText() version = parseLatestVersion(xml, null) baseUrl = "$MAVEN_URL/snapshots" } } - // Fallback to snapshot if stable not found - if (version == null && branch == Branch.STABLE) { + if (version == null && branch == Branch.Stable) { logger.warn("No stable loader found, falling back to snapshot") val xml = URI(LOADER_SNAPSHOTS_META).toURL().readText() version = parseLatestVersion(xml, null) @@ -286,7 +213,6 @@ object AutoUpdater : Module( return null } - // Build download URL val jarUrl = if (version.endsWith("-SNAPSHOT")) { val snapshotInfo = getSnapshotInfo(baseUrl, "com/lambda/loader", version) ?: return null val baseVersion = version.replace("-SNAPSHOT", "") @@ -295,9 +221,7 @@ object AutoUpdater : Module( "$baseUrl/com/lambda/loader/$version/loader-$version.jar" } - if (debug) { - logger.info("Downloading from: $jarUrl") - } + if (debug) logger.info("Downloading from: $jarUrl") URI(jarUrl).toURL().readBytes() } catch (e: Exception) { @@ -306,119 +230,30 @@ object AutoUpdater : Module( } } - /** - * Performs the client install in a background thread - */ - private fun performLoaderInstall() { - thread(name = "Lambda-Client-Installer") { - try { - // Check if Lambda client mod is present - if (!isModContainerPresent("lambda")) { - logger.error("Lambda client mod not found!") - return@thread - } - - logger.info("Starting Lambda client install...") - - // Download the latest client JAR - val clientJar = downloadLatestClient() - if (clientJar == null) { - logger.error("Failed to download latest Lambda client") - return@thread - } - - // Get the current Lambda client mod JAR path - val clientJarPath = getModJarPath("lambda") - if (debug) { - logger.info("Lambda client JAR path: $clientJarPath") - } - - // Write the new JAR to replace the old one - val jarFile = clientJarPath.toFile() - jarFile.writeBytes(clientJar) - - logger.info("Successfully installed Lambda client! Restarting...") - - // Automatically restart the game - exitProcess(0) - } catch (e: Exception) { - logger.error("Error installing Lambda client", e) - } - } - } - - /** - * Performs the loader uninstall in a background thread - */ - private fun performLoaderUninstall() { - thread(name = "Lambda-Loader-Uninstaller") { - try { - // Check if Lambda loader mod is present - if (!isModContainerPresent("lambda-loader")) { - logger.error("Lambda loader mod not found!") - return@thread - } - - logger.info("Starting Lambda loader uninstall...") - - // Download the latest loader JAR - val loaderJar = downloadLatestLoader() - if (loaderJar == null) { - logger.error("Failed to download latest Lambda loader") - return@thread - } - - // Get the current Lambda loader mod JAR path - val loaderJarPath = getModJarPath("lambda-loader") - if (debug) { - logger.info("Lambda loader JAR path: $loaderJarPath") - } - - // Write the new JAR to replace the old one - val jarFile = loaderJarPath.toFile() - jarFile.writeBytes(loaderJar) - - logger.info("Successfully uninstalled Lambda loader! Restarting...") - - // Automatically restart the game - exitProcess(0) - } catch (e: Exception) { - logger.error("Error uninstalling Lambda loader", e) - } - } - } - - /** - * Download the latest client JAR for current MC version from selected branch or fallback to snapshot - */ fun downloadLatestClient(): ByteArray? { return try { val branch = clientBranch val mcVersion = getMinecraftVersion() - if (debug) { - logger.info("Downloading client for MC $mcVersion from ${branch.name} branch") - } + if (debug) logger.info("Downloading client for MC $mcVersion from ${branch.name} branch") - // Try selected branch first var version: String? var baseUrl: String? when (branch) { - Branch.STABLE -> { + Branch.Stable -> { val xml = URI(CLIENT_RELEASES_META).toURL().readText() version = parseLatestVersion(xml, mcVersion) baseUrl = "$MAVEN_URL/releases" } - Branch.SNAPSHOT -> { + Branch.Snapshot -> { val xml = URI(CLIENT_SNAPSHOTS_META).toURL().readText() version = parseLatestVersion(xml, mcVersion) baseUrl = "$MAVEN_URL/snapshots" } } - // Fallback to snapshot if stable not found - if (version == null && branch == Branch.STABLE) { + if (version == null && branch == Branch.Stable) { logger.warn("No stable client found for MC $mcVersion, falling back to snapshot") val xml = URI(CLIENT_SNAPSHOTS_META).toURL().readText() version = parseLatestVersion(xml, mcVersion) @@ -430,23 +265,95 @@ object AutoUpdater : Module( return null } - // Build download URL val jarUrl = if (version.endsWith("-SNAPSHOT")) { val snapshotInfo = getSnapshotInfo(baseUrl, "com/lambda/lambda", version) ?: return null val baseVersion = version.replace("-SNAPSHOT", "") "$baseUrl/com/lambda/lambda/$version/lambda-$baseVersion-${snapshotInfo.timestamp}-${snapshotInfo.buildNumber}.jar" - } else { - "$baseUrl/com/lambda/lambda/$version/lambda-$version.jar" + } else "$baseUrl/com/lambda/lambda/$version/lambda-$version.jar" + + if (debug) logger.info("Downloading from: $jarUrl") + + URI(jarUrl).toURL().readBytes() + } catch (e: Exception) { + logger.error("Failed to download client", e) + null + } + } + + private fun parseLatestVersion(xml: String, mcVersion: String? = null): String? { + return try { + val factory = DocumentBuilderFactory.newInstance() + val builder = factory.newDocumentBuilder() + val document = builder.parse(xml.byteInputStream()) + + val versionNodes = document.getElementsByTagName("version") + val versions = mutableListOf() + + for (i in 0 until versionNodes.length) { + versions.add(versionNodes.item(i).textContent) } if (debug) { - logger.info("Downloading from: $jarUrl") + if (mcVersion != null) { + logger.info("Target MC version: $mcVersion") + } + logger.info("Available versions: ${versions.joinToString(", ")}") } - URI(jarUrl).toURL().readBytes() + val matchingVersions = if (mcVersion != null) { + versions.filter { version -> + val mcVersionInArtifact = version.substringAfter("+").substringBefore("-") + val normalizedArtifact = mcVersionInArtifact.replace(".", "") + val normalizedTarget = mcVersion.replace(".", "") + normalizedArtifact == normalizedTarget + } + } else { + versions + } + + if (matchingVersions.isEmpty()) { + if (debug) { + val versionMsg = mcVersion?.let { "for MC $it" } ?: "" + logger.warn("No versions found $versionMsg") + } + null + } else matchingVersions.last() } catch (e: Exception) { - logger.error("Failed to download client", e) + logger.error("Error parsing version", e) null } } + + private fun getSnapshotInfo(baseUrl: String, artifactPath: String, version: String): SnapshotInfo? { + return try { + val snapshotMetaUrl = URI("$baseUrl/$artifactPath/$version/maven-metadata.xml").toURL() + val xml = snapshotMetaUrl.readText() + + val factory = DocumentBuilderFactory.newInstance() + val builder = factory.newDocumentBuilder() + val document = builder.parse(xml.byteInputStream()) + + val timestamp = document.getElementsByTagName("timestamp").item(0).textContent + val buildNumber = document.getElementsByTagName("buildNumber").item(0).textContent + + SnapshotInfo(version, timestamp, buildNumber) + } catch (e: Exception) { + logger.error("Error getting snapshot info", e) + null + } + } + + private fun getMinecraftVersion() = SharedConstants.getGameVersion().name() + + private fun getModJarPath(modId: String): Path { + val fabricLoader = FabricLoader.getInstance() + val modContainer = fabricLoader.getModContainer(modId) + return modContainer.get().origin.paths[0].toAbsolutePath() + } + + private data class SnapshotInfo( + val version: String, + val timestamp: String, + val buildNumber: String + ) } \ No newline at end of file From 3a8f7a79eeb0b57163268831cc31a983eeabe163 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:52:46 +0000 Subject: [PATCH 4/6] override the lambda jar as the other wont exist yet --- .../module/modules/client/AutoUpdater.kt | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt index 2bc8fe41f..0dfb861b6 100644 --- a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt +++ b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt @@ -25,6 +25,7 @@ import com.lambda.gui.components.ClickGuiLayout import com.lambda.gui.dsl.ImGuiBuilder.buildLayout import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import imgui.ImGui import imgui.flag.ImGuiWindowFlags import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -34,7 +35,6 @@ import org.slf4j.LoggerFactory import java.net.URI import java.nio.file.Path import javax.xml.parsers.DocumentBuilderFactory -import kotlin.system.exitProcess object AutoUpdater : Module( name = "AutoUpdater", @@ -45,7 +45,7 @@ object AutoUpdater : Module( private val debug by setting("Debug", false, "Enable debug logging") private val loaderBranch by setting("Loader Branch", Branch.Stable, "Select loader update branch") - private val clientBranch by setting("Client Branch", Branch.Stable, "Select client update branch") + private val clientBranch by setting("Client Branch", Branch.Snapshot, "Select client update branch") var showInstallModal = false private set @@ -69,19 +69,24 @@ object AutoUpdater : Module( showInstallModal = true } - listen { + onDisable { + if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) + showUninstallModal = true + } + + listen(alwaysListen = true) { if (showInstallModal) { buildLayout { - openPopup("Install Lambda Loader") - popupModal("Install Lambda Loader", ImGuiWindowFlags.AlwaysAutoResize) { + openPopup("install-lambda-loader") + popupModal("install-lambda-loader", ImGuiWindowFlags.AlwaysAutoResize) { text("Do you want to install Lambda Client?") - text("") + ImGui.spacing() text("This will close the client automatically once the install is finished.") - text("") + ImGui.spacing() button("Install", 120f, 0f) { showInstallModal = false - installClient() + installLoader() } sameLine() @@ -91,21 +96,21 @@ object AutoUpdater : Module( } } } + return@listen } if (showUninstallModal) { buildLayout { - openPopup("Uninstall Lambda Loader") - popupModal("Uninstall Lambda Loader", ImGuiWindowFlags.AlwaysAutoResize) { + openPopup("uninstall-lambda-loader") + popupModal("uninstall-lambda-loader", ImGuiWindowFlags.AlwaysAutoResize) { text("Do you want to uninstall Lambda Loader?") - text("") + ImGui.spacing() text("This will close the client automatically once the uninstall is finished.") - text("") + ImGui.spacing() button("Uninstall", 120f, 0f) { showUninstallModal = false - disable() - installLoader() + installClient() } sameLine() @@ -117,19 +122,12 @@ object AutoUpdater : Module( } } } - - onDisable { - if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) { - showUninstallModal = true - enable() - } else installLoader() - } } private fun installLoader() { runBlocking(Dispatchers.IO) { try { - logger.info("Starting Lambda loader uninstall...") + logger.info("Starting Lambda loader install...") val loaderJar = downloadLatestLoader() if (loaderJar == null) { @@ -137,17 +135,17 @@ object AutoUpdater : Module( return@runBlocking } - val loaderJarPath = getModJarPath("lambda-loader") - if (debug) logger.info("Lambda loader JAR path: $loaderJarPath") + val clientJarPath = getModJarPath("lambda") + if (debug) logger.info("Lambda client JAR path: $clientJarPath") - val jarFile = loaderJarPath.toFile() + val jarFile = clientJarPath.toFile() jarFile.writeBytes(loaderJar) - logger.info("Successfully uninstalled Lambda loader! Restarting...") + logger.info("Successfully installed Lambda loader! Restarting...") mc.stop() } catch (e: Exception) { - logger.error("Error uninstalling Lambda loader", e) + logger.error("Error installing Lambda loader", e) } } } @@ -163,10 +161,10 @@ object AutoUpdater : Module( return@runBlocking } - val clientJarPath = getModJarPath("lambda") - if (debug) logger.info("Lambda client JAR path: $clientJarPath") + val loaderJarPath = getModJarPath("lambda-loader") + if (debug) logger.info("Lambda loader JAR path: $loaderJarPath") - val jarFile = clientJarPath.toFile() + val jarFile = loaderJarPath.toFile() jarFile.writeBytes(clientJar) logger.info("Successfully installed Lambda client! Restarting...") @@ -289,7 +287,7 @@ object AutoUpdater : Module( val versionNodes = document.getElementsByTagName("version") val versions = mutableListOf() - for (i in 0 until versionNodes.length) { + (0 until versionNodes.length).forEach { i -> versions.add(versionNodes.item(i).textContent) } From db1971f58110a7d1f450d5734358fc7ed66f5f1a Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:49:46 +0000 Subject: [PATCH 5/6] fixes --- .../module/modules/client/AutoUpdater.kt | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt index 0dfb861b6..bf22888c2 100644 --- a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt +++ b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt @@ -25,10 +25,14 @@ import com.lambda.gui.components.ClickGuiLayout import com.lambda.gui.dsl.ImGuiBuilder.buildLayout import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.Communication.debug +import com.lambda.util.Communication.logError +import com.lambda.util.Communication.warn import imgui.ImGui import imgui.flag.ImGuiWindowFlags import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking +import net.caffeinemc.mods.sodium.client.SodiumClientMod.logger import net.fabricmc.loader.api.FabricLoader import net.minecraft.SharedConstants import org.slf4j.LoggerFactory @@ -41,20 +45,16 @@ object AutoUpdater : Module( description = "Installs / uninstalls Lambda loader", tag = ModuleTag.CLIENT, ) { - private val logger = LoggerFactory.getLogger("AutoUpdater") - private val debug by setting("Debug", false, "Enable debug logging") private val loaderBranch by setting("Loader Branch", Branch.Stable, "Select loader update branch") private val clientBranch by setting("Client Branch", Branch.Snapshot, "Select client update branch") - var showInstallModal = false - private set - var showUninstallModal = false - private set + private var showInstallModal = false + private var showUninstallModal = false private const val MAVEN_URL = "https://maven.lambda-client.org" - private const val LOADER_RELEASES_META = "$MAVEN_URL/releases/com/lambda/loader/maven-metadata.xml" - private const val LOADER_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/loader/maven-metadata.xml" + private const val LOADER_RELEASES_META = "$MAVEN_URL/releases/com/lambda/lambda-loader/maven-metadata.xml" + private const val LOADER_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/lambda-loader/maven-metadata.xml" private const val CLIENT_RELEASES_META = "$MAVEN_URL/releases/com/lambda/lambda/maven-metadata.xml" private const val CLIENT_SNAPSHOTS_META = "$MAVEN_URL/snapshots/com/lambda/lambda/maven-metadata.xml" @@ -127,25 +127,25 @@ object AutoUpdater : Module( private fun installLoader() { runBlocking(Dispatchers.IO) { try { - logger.info("Starting Lambda loader install...") + debug("Starting Lambda loader install...") val loaderJar = downloadLatestLoader() if (loaderJar == null) { - logger.error("Failed to download latest Lambda loader") + logError("Failed to download latest Lambda loader") return@runBlocking } val clientJarPath = getModJarPath("lambda") - if (debug) logger.info("Lambda client JAR path: $clientJarPath") + if (debug) debug("Lambda client JAR path: $clientJarPath") val jarFile = clientJarPath.toFile() jarFile.writeBytes(loaderJar) - logger.info("Successfully installed Lambda loader! Restarting...") + debug("Successfully installed Lambda loader! Restarting...") mc.stop() } catch (e: Exception) { - logger.error("Error installing Lambda loader", e) + logError("Error installing Lambda loader", e) } } } @@ -153,25 +153,25 @@ object AutoUpdater : Module( private fun installClient() { runBlocking(Dispatchers.IO) { try { - logger.info("Starting Lambda client install...") + debug("Starting Lambda client install...") val clientJar = downloadLatestClient() if (clientJar == null) { - logger.error("Failed to download latest Lambda client") + logError("Failed to download latest Lambda client") return@runBlocking } val loaderJarPath = getModJarPath("lambda-loader") - if (debug) logger.info("Lambda loader JAR path: $loaderJarPath") + if (debug) debug("Lambda loader JAR path: $loaderJarPath") val jarFile = loaderJarPath.toFile() jarFile.writeBytes(clientJar) - logger.info("Successfully installed Lambda client! Restarting...") + debug("Successfully installed Lambda client! Restarting...") mc.stop() } catch (e: Exception) { - logger.error("Error installing Lambda client", e) + logError("Error installing Lambda client", e) } } } @@ -181,7 +181,7 @@ object AutoUpdater : Module( val branch = loaderBranch val mcVersion = getMinecraftVersion() - if (debug) logger.info("Downloading loader for MC $mcVersion from ${branch.name} branch") + if (debug) debug("Downloading loader for MC $mcVersion from ${branch.name} branch") var version: String? var baseUrl: String? @@ -200,30 +200,30 @@ object AutoUpdater : Module( } if (version == null && branch == Branch.Stable) { - logger.warn("No stable loader found, falling back to snapshot") + warn("No stable loader found, falling back to snapshot") val xml = URI(LOADER_SNAPSHOTS_META).toURL().readText() version = parseLatestVersion(xml, null) baseUrl = "$MAVEN_URL/snapshots" } if (version == null) { - logger.error("No loader version found") + logError("No loader version found") return null } val jarUrl = if (version.endsWith("-SNAPSHOT")) { - val snapshotInfo = getSnapshotInfo(baseUrl, "com/lambda/loader", version) ?: return null + val snapshotInfo = getSnapshotInfo(baseUrl, "com/lambda/lambda-loader", version) ?: return null val baseVersion = version.replace("-SNAPSHOT", "") - "$baseUrl/com/lambda/loader/$version/loader-$baseVersion-${snapshotInfo.timestamp}-${snapshotInfo.buildNumber}.jar" + "$baseUrl/com/lambda/lambda-loader/$version/lambda-loader-$baseVersion-${snapshotInfo.timestamp}-${snapshotInfo.buildNumber}.jar" } else { - "$baseUrl/com/lambda/loader/$version/loader-$version.jar" + "$baseUrl/com/lambda/lambda-loader/$version/lambda-loader-$version.jar" } - if (debug) logger.info("Downloading from: $jarUrl") + if (debug) debug("Downloading from: $jarUrl") URI(jarUrl).toURL().readBytes() } catch (e: Exception) { - logger.error("Failed to download loader", e) + logError("Failed to download loader", e) null } } @@ -233,7 +233,7 @@ object AutoUpdater : Module( val branch = clientBranch val mcVersion = getMinecraftVersion() - if (debug) logger.info("Downloading client for MC $mcVersion from ${branch.name} branch") + if (debug) debug("Downloading client for MC $mcVersion from ${branch.name} branch") var version: String? var baseUrl: String? @@ -252,14 +252,14 @@ object AutoUpdater : Module( } if (version == null && branch == Branch.Stable) { - logger.warn("No stable client found for MC $mcVersion, falling back to snapshot") + warn("No stable client found for MC $mcVersion, falling back to snapshot") val xml = URI(CLIENT_SNAPSHOTS_META).toURL().readText() version = parseLatestVersion(xml, mcVersion) baseUrl = "$MAVEN_URL/snapshots" } if (version == null) { - logger.error("No client version found for MC $mcVersion") + logError("No client version found for MC $mcVersion") return null } @@ -269,11 +269,11 @@ object AutoUpdater : Module( "$baseUrl/com/lambda/lambda/$version/lambda-$baseVersion-${snapshotInfo.timestamp}-${snapshotInfo.buildNumber}.jar" } else "$baseUrl/com/lambda/lambda/$version/lambda-$version.jar" - if (debug) logger.info("Downloading from: $jarUrl") + if (debug) debug("Downloading from: $jarUrl") URI(jarUrl).toURL().readBytes() } catch (e: Exception) { - logger.error("Failed to download client", e) + logError("Failed to download client", e) null } } @@ -293,9 +293,9 @@ object AutoUpdater : Module( if (debug) { if (mcVersion != null) { - logger.info("Target MC version: $mcVersion") + debug("Target MC version: $mcVersion") } - logger.info("Available versions: ${versions.joinToString(", ")}") + debug("Available versions: ${versions.joinToString(", ")}") } val matchingVersions = if (mcVersion != null) { @@ -312,12 +312,12 @@ object AutoUpdater : Module( if (matchingVersions.isEmpty()) { if (debug) { val versionMsg = mcVersion?.let { "for MC $it" } ?: "" - logger.warn("No versions found $versionMsg") + warn("No versions found $versionMsg") } null } else matchingVersions.last() } catch (e: Exception) { - logger.error("Error parsing version", e) + logError("Error parsing version", e) null } } @@ -336,7 +336,7 @@ object AutoUpdater : Module( SnapshotInfo(version, timestamp, buildNumber) } catch (e: Exception) { - logger.error("Error getting snapshot info", e) + logError("Error getting snapshot info", e) null } } From 8961f756c14b5176d5dae7bbb2c12880ec194f85 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:06:26 +0000 Subject: [PATCH 6/6] parity with module enable and disable --- .../com/lambda/mixin/render/ScreenMixin.java | 17 +++- .../com/lambda/gui/components/QuickSearch.kt | 15 +-- .../lambda/gui/components/SettingsWidget.kt | 3 +- .../module/modules/client/AutoUpdater.kt | 92 ++++++++++--------- 4 files changed, 72 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/lambda/mixin/render/ScreenMixin.java b/src/main/java/com/lambda/mixin/render/ScreenMixin.java index 6ed4f2c53..083d0be56 100644 --- a/src/main/java/com/lambda/mixin/render/ScreenMixin.java +++ b/src/main/java/com/lambda/mixin/render/ScreenMixin.java @@ -18,6 +18,7 @@ package com.lambda.mixin.render; import com.lambda.gui.components.QuickSearch; +import com.lambda.module.modules.client.AutoUpdater; import com.lambda.module.modules.render.ContainerPreview; import com.lambda.module.modules.render.NoRender; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; @@ -37,9 +38,19 @@ public class ScreenMixin { @Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true) private void onKeyPressed(KeyInput input, CallbackInfoReturnable cir) { - if (input.key() == GLFW.GLFW_KEY_ESCAPE && QuickSearch.INSTANCE.isOpen()) { - QuickSearch.INSTANCE.close(); - cir.setReturnValue(true); + if (input.key() == GLFW.GLFW_KEY_ESCAPE) { + if (QuickSearch.INSTANCE.isOpen()) { + QuickSearch.INSTANCE.close(); + cir.setReturnValue(true); + } else if (AutoUpdater.getShowInstallModal()) { + AutoUpdater.INSTANCE.disable(); + AutoUpdater.setShowInstallModal(false); + cir.setReturnValue(true); + } else if (AutoUpdater.getShowUninstallModal()) { + AutoUpdater.INSTANCE.enable(); + AutoUpdater.setShowUninstallModal(false); + cir.setReturnValue(true); + } } } diff --git a/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt b/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt index a92fb7ca2..51fe17ae2 100644 --- a/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt +++ b/src/main/kotlin/com/lambda/gui/components/QuickSearch.kt @@ -31,6 +31,7 @@ import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.module.HudModule import com.lambda.module.Module import com.lambda.module.ModuleRegistry +import com.lambda.module.modules.client.AutoUpdater import com.lambda.util.KeyCode import com.lambda.util.StringUtils.capitalize import com.lambda.util.StringUtils.levenshteinDistance @@ -54,12 +55,13 @@ object QuickSearch { private const val DOUBLE_SHIFT_WINDOW_MS = 500L private const val MAX_RESULTS = 50 - private const val WINDOW_FLAGS = ImGuiWindowFlags.AlwaysAutoResize or - ImGuiWindowFlags.NoTitleBar or - ImGuiWindowFlags.NoMove or - ImGuiWindowFlags.NoResize or - ImGuiWindowFlags.NoScrollbar or - ImGuiWindowFlags.NoScrollWithMouse + const val WINDOW_FLAGS = + ImGuiWindowFlags.AlwaysAutoResize or + ImGuiWindowFlags.NoTitleBar or + ImGuiWindowFlags.NoMove or + ImGuiWindowFlags.NoResize or + ImGuiWindowFlags.NoScrollbar or + ImGuiWindowFlags.NoScrollWithMouse init { listenUnsafe { event -> @@ -303,6 +305,7 @@ object QuickSearch { } private fun handleKeyPress(event: ButtonEvent.Keyboard.Press) { + if (AutoUpdater.showInstallModal || AutoUpdater.showUninstallModal) return if ((!event.isPressed || event.isRepeated) || !(event.keyCode == KeyCode.LeftShift.code || event.keyCode == KeyCode.RightShift.code)) return diff --git a/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt b/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt index dabb904d7..15036936f 100644 --- a/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt +++ b/src/main/kotlin/com/lambda/gui/components/SettingsWidget.kt @@ -25,6 +25,7 @@ import com.lambda.config.UserAutomationConfig import com.lambda.config.configurations.UserAutomationConfigs import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.module.Module +import com.lambda.module.modules.client.AutoUpdater import com.lambda.util.NamedEnum import imgui.ImGui import imgui.flag.ImGuiPopupFlags @@ -36,7 +37,7 @@ object SettingsWidget { */ fun ImGuiBuilder.buildConfigSettingsContext(config: Configurable) { group { - if (config is Module) { + if (config is Module && config !== AutoUpdater) { with(config.keybindSetting) { buildLayout() } with(config.disableOnReleaseSetting) { buildLayout() } with(config.drawSetting) { buildLayout() } diff --git a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt index bf22888c2..10c518cc7 100644 --- a/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt +++ b/src/main/kotlin/com/lambda/module/modules/client/AutoUpdater.kt @@ -21,21 +21,18 @@ import com.lambda.Lambda.mc import com.lambda.event.events.GuiEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.gui.LambdaScreen -import com.lambda.gui.components.ClickGuiLayout -import com.lambda.gui.dsl.ImGuiBuilder.buildLayout +import com.lambda.gui.components.QuickSearch.WINDOW_FLAGS +import com.lambda.gui.dsl.ImGuiBuilder.popupModal import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.Communication.debug import com.lambda.util.Communication.logError import com.lambda.util.Communication.warn import imgui.ImGui -import imgui.flag.ImGuiWindowFlags import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import net.caffeinemc.mods.sodium.client.SodiumClientMod.logger import net.fabricmc.loader.api.FabricLoader import net.minecraft.SharedConstants -import org.slf4j.LoggerFactory import java.net.URI import java.nio.file.Path import javax.xml.parsers.DocumentBuilderFactory @@ -49,8 +46,8 @@ object AutoUpdater : Module( private val loaderBranch by setting("Loader Branch", Branch.Stable, "Select loader update branch") private val clientBranch by setting("Client Branch", Branch.Snapshot, "Select client update branch") - private var showInstallModal = false - private var showUninstallModal = false + @JvmStatic var showInstallModal = false + @JvmStatic var showUninstallModal = false private const val MAVEN_URL = "https://maven.lambda-client.org" private const val LOADER_RELEASES_META = "$MAVEN_URL/releases/com/lambda/lambda-loader/maven-metadata.xml" @@ -65,62 +62,65 @@ object AutoUpdater : Module( init { onEnable { - if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) + if (mc.currentScreen is LambdaScreen && !showUninstallModal) showInstallModal = true + showUninstallModal = false } onDisable { - if (mc.currentScreen is LambdaScreen && ClickGuiLayout.open) + if (mc.currentScreen is LambdaScreen && !showInstallModal) showUninstallModal = true + showInstallModal = false } listen(alwaysListen = true) { if (showInstallModal) { - buildLayout { - openPopup("install-lambda-loader") - popupModal("install-lambda-loader", ImGuiWindowFlags.AlwaysAutoResize) { - text("Do you want to install Lambda Client?") - ImGui.spacing() - text("This will close the client automatically once the install is finished.") - ImGui.spacing() - - button("Install", 120f, 0f) { - showInstallModal = false - installLoader() - } - - sameLine() - - button("Cancel", 120f, 0f) { - showInstallModal = false - } + ImGui.openPopup("Install Loader") + popupModal("Install Loader", WINDOW_FLAGS) { + text("Do you want to install Lambda Client?") + spacing() + text("This will close the client automatically once the install is finished.") + spacing() + + button("Install", 120f, 0f) { + installLoader() + showInstallModal = false + } + + sameLine() + + button("Cancel", 120f, 0f) { + disable() } } return@listen } + showInstallModal = false + if (showUninstallModal) { - buildLayout { - openPopup("uninstall-lambda-loader") - popupModal("uninstall-lambda-loader", ImGuiWindowFlags.AlwaysAutoResize) { - text("Do you want to uninstall Lambda Loader?") - ImGui.spacing() - text("This will close the client automatically once the uninstall is finished.") - ImGui.spacing() - - button("Uninstall", 120f, 0f) { - showUninstallModal = false - installClient() - } - - sameLine() - - button("Cancel", 120f, 0f) { - showUninstallModal = false - } + ImGui.openPopup("Uninstall Loader") + popupModal("Uninstall Loader", WINDOW_FLAGS) { + text("Do you want to uninstall Lambda Loader?") + spacing() + text("This will close the client automatically once the uninstall is finished.") + spacing() + + button("Uninstall", 120f, 0f) { + installClient() + showUninstallModal = false + } + + sameLine() + + button("Cancel", 120f, 0f) { + enable() } } + return@listen } + + showUninstallModal = false } } @@ -145,6 +145,7 @@ object AutoUpdater : Module( mc.stop() } catch (e: Exception) { + disable() logError("Error installing Lambda loader", e) } } @@ -171,6 +172,7 @@ object AutoUpdater : Module( mc.stop() } catch (e: Exception) { + enable() logError("Error installing Lambda client", e) } }