diff --git a/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java b/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java
index 24954cb22..39cd5d969 100644
--- a/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java
+++ b/src/main/java/com/lambda/mixin/input/KeyBindingMixin.java
@@ -19,7 +19,6 @@
import com.lambda.module.modules.movement.Speed;
import com.lambda.module.modules.movement.Sprint;
-import com.lambda.module.modules.movement.TargetStrafe;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import net.minecraft.client.option.KeyBinding;
import org.spongepowered.asm.mixin.Mixin;
diff --git a/src/main/java/com/lambda/mixin/render/ConnectScreenMixin.java b/src/main/java/com/lambda/mixin/render/ConnectScreenMixin.java
index 96c877fa1..f862cc48e 100644
--- a/src/main/java/com/lambda/mixin/render/ConnectScreenMixin.java
+++ b/src/main/java/com/lambda/mixin/render/ConnectScreenMixin.java
@@ -17,8 +17,8 @@
package com.lambda.mixin.render;
-import com.lambda.module.modules.combat.autodisconnect.AutoDisconnect;
-import com.lambda.module.modules.combat.autodisconnect.MultiplayerReconnectTarget;
+import com.lambda.module.modules.network.AutoReconnect;
+import com.lambda.module.modules.network.MultiplayerReconnectTarget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.multiplayer.ConnectScreen;
import net.minecraft.client.network.CookieStorage;
@@ -31,9 +31,8 @@
@Mixin(value = ConnectScreen.class)
public class ConnectScreenMixin {
-
@Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;Lnet/minecraft/client/network/CookieStorage;)V", at= @At("HEAD"))
private void connectHead(MinecraftClient client, ServerAddress address, ServerInfo info, CookieStorage cookieStorage, CallbackInfo ci) {
- AutoDisconnect.INSTANCE.setLastReconnectTarget(new MultiplayerReconnectTarget(address, info, cookieStorage));
+ AutoReconnect.setLastReconnectTarget(new MultiplayerReconnectTarget(address, info, cookieStorage));
}
}
diff --git a/src/main/java/com/lambda/mixin/render/DisconnectedScreenMixin.java b/src/main/java/com/lambda/mixin/render/DisconnectedScreenMixin.java
new file mode 100644
index 000000000..4b9d7daa2
--- /dev/null
+++ b/src/main/java/com/lambda/mixin/render/DisconnectedScreenMixin.java
@@ -0,0 +1,111 @@
+/*
+ * 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.mixin.render;
+
+import com.lambda.module.modules.network.AutoReconnect;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.DisconnectedScreen;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.gui.widget.DirectionalLayoutWidget;
+import net.minecraft.text.Text;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.Locale;
+
+@Mixin(DisconnectedScreen.class)
+public abstract class DisconnectedScreenMixin extends Screen {
+ @Shadow @Final private DirectionalLayoutWidget grid;
+ @Shadow @Final private Screen parent;
+
+ @Unique
+ private ButtonWidget lambda$reconnectButton;
+
+ @Unique
+ private long lambda$autoReconnectTime = -1L;
+
+ @Unique
+ private boolean lambda$reconnected = false;
+
+ protected DisconnectedScreenMixin(Text title) {
+ super(title);
+ }
+
+ @Inject(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;isMultiplayerEnabled()Z"))
+ private void addReconnectButton(CallbackInfo ci) {
+ lambda$reconnected = false;
+
+ boolean hasTarget = AutoReconnect.getLastReconnectTarget() != null;
+ if (AutoReconnect.canAutoReconnect()) {
+ lambda$autoReconnectTime = System.currentTimeMillis() + (long) (AutoReconnect.INSTANCE.getDelay() * 1000.0);
+ } else {
+ lambda$autoReconnectTime = -1L;
+ }
+
+ lambda$reconnectButton = ButtonWidget.builder(lambda$getButtonText(), button -> {
+ lambda$reconnected = true;
+ AutoReconnect.reconnect(this.parent);
+ }).width(200).build();
+
+ lambda$reconnectButton.active = hasTarget;
+ this.grid.add(lambda$reconnectButton);
+ }
+
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float deltaTicks) {
+ super.render(context, mouseX, mouseY, deltaTicks);
+
+ if (lambda$reconnectButton == null || lambda$reconnected) return;
+
+ if (AutoReconnect.canAutoReconnect() && lambda$autoReconnectTime > 0) {
+ long remainingMillis = lambda$autoReconnectTime - System.currentTimeMillis();
+ if (remainingMillis <= 0) {
+ lambda$reconnected = true;
+ AutoReconnect.reconnect(this.parent);
+ } else {
+ double remainingSeconds = remainingMillis / 1000.0;
+ lambda$reconnectButton.setMessage(Text.literal(String.format(Locale.ROOT, "Reconnect (%.1fs)", remainingSeconds)));
+ }
+ } else {
+ lambda$reconnectButton.setMessage(Text.literal("Reconnect"));
+ }
+ }
+
+ @Override
+ public void removed() {
+ super.removed();
+ lambda$autoReconnectTime = -1L;
+ lambda$reconnected = true;
+ }
+
+ @Unique
+ private Text lambda$getButtonText() {
+ if (AutoReconnect.canAutoReconnect() && lambda$autoReconnectTime > 0) {
+ long remainingMillis = Math.max(0, lambda$autoReconnectTime - System.currentTimeMillis());
+ double remainingSeconds = remainingMillis / 1000.0;
+ return Text.literal(String.format(Locale.ROOT, "Reconnect (%.1fs)", remainingSeconds));
+ }
+ return Text.literal("Reconnect");
+ }
+}
diff --git a/src/main/java/com/lambda/mixin/world/IntegratedServerLoaderMixin.java b/src/main/java/com/lambda/mixin/world/IntegratedServerLoaderMixin.java
index 1137c3601..92274f7c6 100644
--- a/src/main/java/com/lambda/mixin/world/IntegratedServerLoaderMixin.java
+++ b/src/main/java/com/lambda/mixin/world/IntegratedServerLoaderMixin.java
@@ -17,8 +17,8 @@
package com.lambda.mixin.world;
-import com.lambda.module.modules.combat.autodisconnect.AutoDisconnect;
-import com.lambda.module.modules.combat.autodisconnect.SingleplayerReconnectTarget;
+import com.lambda.module.modules.network.AutoReconnect;
+import com.lambda.module.modules.network.SingleplayerReconnectTarget;
import net.minecraft.server.integrated.IntegratedServerLoader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -29,7 +29,6 @@
public class IntegratedServerLoaderMixin {
@Inject(method = "start(Ljava/lang/String;Ljava/lang/Runnable;)V", at = @At("HEAD"))
private void onStart(String name, Runnable onCancel, CallbackInfo ci) {
- AutoDisconnect.INSTANCE.setLastReconnectTarget(new SingleplayerReconnectTarget(name));
+ AutoReconnect.setLastReconnectTarget(new SingleplayerReconnectTarget(name));
}
-
}
diff --git a/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt
index ed2c94041..00629b99f 100644
--- a/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt
+++ b/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt
@@ -36,7 +36,6 @@ import com.lambda.event.Muteable
import com.lambda.event.events.ButtonEvent
import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
import com.lambda.gui.dsl.ImGuiBuilder
-import com.lambda.imgui.ImGui.isMouseClicked
import com.lambda.imgui.flag.ImGuiCol
import com.lambda.imgui.flag.ImGuiHoveredFlags
import com.lambda.imgui.flag.ImGuiMouseButton
diff --git a/src/main/kotlin/com/lambda/gui/MenuBar.kt b/src/main/kotlin/com/lambda/gui/MenuBar.kt
index 2b205382b..2dc94faec 100644
--- a/src/main/kotlin/com/lambda/gui/MenuBar.kt
+++ b/src/main/kotlin/com/lambda/gui/MenuBar.kt
@@ -36,7 +36,6 @@ import com.lambda.gui.components.QuickSearch
import com.lambda.gui.components.SettingsWidget.buildConfigSettingsContext
import com.lambda.gui.dsl.ImGuiBuilder
import com.lambda.imgui.ImGui
-import com.lambda.imgui.ImGui.closeCurrentPopup
import com.lambda.imgui.flag.ImGuiCol
import com.lambda.imgui.flag.ImGuiStyleVar
import com.lambda.imgui.flag.ImGuiWindowFlags
diff --git a/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt b/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt
index 449b8bc40..5ba9f8b16 100644
--- a/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt
+++ b/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt
@@ -20,6 +20,8 @@
package com.lambda.gui.dsl
import com.lambda.gui.components.ClickGuiLayout
+import com.lambda.gui.dsl.ImGuiBuilder.onItemClick
+import com.lambda.gui.dsl.ImGuiBuilder.onItemHover
import com.lambda.gui.dsl.ImGuiBuilder.openPopup
import com.lambda.gui.dsl.ImGuiBuilder.text
import com.lambda.imgui.ImDrawList
@@ -108,10 +110,8 @@ import com.lambda.imgui.ImGui.isItemEdited
import com.lambda.imgui.ImGui.isItemFocused
import com.lambda.imgui.ImGui.isItemHovered
import com.lambda.imgui.ImGui.isItemToggledOpen
-import com.lambda.imgui.ImGui.isMouseClicked
import com.lambda.imgui.ImGui.isWindowAppearing
import com.lambda.imgui.ImGui.isWindowCollapsed
-import com.lambda.imgui.ImGui.isWindowHovered
import com.lambda.imgui.ImGui.plotHistogram
import com.lambda.imgui.ImGui.plotLines
import com.lambda.imgui.ImGui.popFont
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt
index a9872acce..bc54e958f 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt
@@ -23,8 +23,8 @@ import com.lambda.interaction.handlers.ContainerHandler
import com.lambda.interaction.material.container.ExternalContainer
import com.lambda.interaction.material.container.MaterialContainer
import com.lambda.task.Task
-import com.lambda.task.wrappers.TaskSupplier
import com.lambda.task.tasks.OpenContainerTask
+import com.lambda.task.wrappers.TaskSupplier
import com.lambda.task.wrappers.thenAction
import com.lambda.util.extension.containerSlots
import com.lambda.util.text.buildText
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt
index 1b0c9182a..cad16b9f5 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt
@@ -24,10 +24,10 @@ import com.lambda.interaction.handlers.ContainerHandler.findSlotsWithMaterial
import com.lambda.interaction.material.StackSelection.Companion.select
import com.lambda.interaction.material.container.ExternalContainer
import com.lambda.interaction.material.container.MaterialContainer
-import com.lambda.task.wrappers.TaskSupplier
import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock
import com.lambda.task.tasks.OpenContainerTask
import com.lambda.task.tasks.PlaceContainerTask
+import com.lambda.task.wrappers.TaskSupplier
import com.lambda.task.wrappers.thenAction
import com.lambda.task.wrappers.thenOrNull
import com.lambda.util.extension.containerSlots
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt
index d5505e801..6bc95a8ad 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt
@@ -22,10 +22,10 @@ import com.lambda.context.SafeContext
import com.lambda.interaction.handlers.ContainerHandler
import com.lambda.interaction.material.container.ExternalContainer
import com.lambda.interaction.material.container.MaterialContainer
-import com.lambda.task.wrappers.TaskSupplier
import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock
import com.lambda.task.tasks.OpenContainerTask
import com.lambda.task.tasks.PlaceContainerTask
+import com.lambda.task.wrappers.TaskSupplier
import com.lambda.task.wrappers.thenAction
import com.lambda.task.wrappers.thenOrNull
import com.lambda.threading.runSafe
diff --git a/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnect.kt b/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnect.kt
index 20f684903..75f9e5f19 100644
--- a/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnect.kt
+++ b/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnect.kt
@@ -28,6 +28,7 @@ import com.lambda.event.listener.SafeListener.Companion.listen
import com.lambda.gui.components.ClickGuiLayout
import com.lambda.interaction.handlers.FriendHandler
import com.lambda.module.Module
+import com.lambda.module.modules.network.AutoReconnect
import com.lambda.module.tag.ModuleTag
import com.lambda.sound.SoundHandler.playSound
import com.lambda.util.CommunicationUtils
@@ -56,9 +57,6 @@ import com.lambda.util.text.literal
import com.lambda.util.text.text
import com.lambda.util.world.WorldUtils.isLoaded
import com.lambda.util.world.fastEntitySearch
-import net.minecraft.client.network.CookieStorage
-import net.minecraft.client.network.ServerAddress
-import net.minecraft.client.network.ServerInfo
import net.minecraft.client.texture.NativeImageBackedTexture
import net.minecraft.client.util.ScreenshotRecorder
import net.minecraft.component.DataComponentTypes
@@ -194,6 +192,7 @@ object AutoDisconnect : Module(
@Tab(TRIGGERS_TAB) @Group(DAMAGE_TRIGGER_GROUP) private val arrow by setting("Arrow", false, "Disconnect from the server when you take arrow damage.") { damage }
@Tab(TRIGGERS_TAB) @Group(DAMAGE_TRIGGER_GROUP) private val trident by setting("Trident", false, "Disconnect from the server when you take trident damage.") { damage }
+ @Tab(GENERAL_TAB) private val disableAutoReconnect by setting("Disable ${AutoReconnect.name}", false, "Disables ${AutoReconnect.name} when disconnecting")
@Tab(GENERAL_TAB) @Group(PACKET_DISCONNECT_GROUP) private val invalidHotbarDisconnect by setting("Select Invalid Hotbar Slot", false, "Sends an invalid hotbar selection to force the server to kick the player")
@Tab(GENERAL_TAB) @Group(PACKET_DISCONNECT_GROUP) private val attackSelfDisconnect by setting("Attack Self", false, "Sends an attack self packet to force the server to kick the player")
@Tab(GENERAL_TAB) @Group(PACKET_DISCONNECT_GROUP) private val impossibleTimestampChatDisconnect by setting("Send Impossible Chat Timestamp", false, "Sends a chat message with an impossible timestamp to force the server to kick the player")
@@ -205,7 +204,6 @@ object AutoDisconnect : Module(
private var disconnectDetails: DisconnectDetails? = null
private var disconnectInProgress: Boolean = false
private val joinTimer = TickTimer()
- var lastReconnectTarget: ReconnectTarget? = null
init {
listen {
@@ -303,6 +301,7 @@ object AutoDisconnect : Module(
private fun SafeContext.requestDisconnect(reasonText: Text, disarmedTrigger: Reason? = null): Boolean {
if (player.gameMode != GameMode.SURVIVAL && player.gameMode != GameMode.ADVENTURE || disconnectInProgress) return false
disconnectInProgress = true
+ if (AutoReconnect.isEnabled && disableAutoReconnect) AutoReconnect.disable()
ScreenshotRecorder.takeScreenshot(Lambda.mc.framebuffer, 1) { image ->
val imageIdentifier = Identifier.of("lambda", "auto_disconnect_screenshot")
val texture = NativeImageBackedTexture({ "auto-disconnect-screenshot" }, image)
@@ -913,16 +912,4 @@ data class DisconnectDetails(
val reason: Text,
val sections: List,
val hideDetails: Boolean
-)
-
-sealed interface ReconnectTarget
-
-data class MultiplayerReconnectTarget(
- val address: ServerAddress,
- val info: ServerInfo,
- val cookieStorage: CookieStorage?
-) : ReconnectTarget
-
-data class SingleplayerReconnectTarget(
- val levelName: String
-) : ReconnectTarget
\ No newline at end of file
+)
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnectScreen.kt b/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnectScreen.kt
index ace4e2e8e..98bd40308 100644
--- a/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnectScreen.kt
+++ b/src/main/kotlin/com/lambda/module/modules/combat/autodisconnect/AutoDisconnectScreen.kt
@@ -18,6 +18,9 @@
package com.lambda.module.modules.combat.autodisconnect
import com.lambda.gui.OverlayBackgroundScreen
+import com.lambda.module.modules.network.AutoReconnect
+import com.lambda.module.modules.network.MultiplayerReconnectTarget
+import com.lambda.module.modules.network.SingleplayerReconnectTarget
import com.lambda.util.render.CursorOverrideProvider
import net.minecraft.client.gl.RenderPipelines
import net.minecraft.client.gui.Click
@@ -127,7 +130,7 @@ class AutoDisconnectScreen(
private fun reconnect() {
releaseTexture()
- when (val target = AutoDisconnect.lastReconnectTarget) {
+ when (val target = AutoReconnect.lastReconnectTarget) {
is MultiplayerReconnectTarget -> {
ConnectScreen.connect(parent, client, target.address, target.info, false, target.cookieStorage)
}
@@ -145,7 +148,7 @@ class AutoDisconnectScreen(
private fun openWorldListScreen() {
releaseTexture()
client?.setScreen(
- when (AutoDisconnect.lastReconnectTarget) {
+ when (AutoReconnect.lastReconnectTarget) {
is SingleplayerReconnectTarget -> SelectWorldScreen(parent)
else -> MultiplayerScreen(parent)
}
@@ -249,7 +252,7 @@ class AutoDisconnectScreen(
}
private fun updateReconnectButton() {
- reconnectButton.active = AutoDisconnect.lastReconnectTarget != null
+ reconnectButton.active = AutoReconnect.lastReconnectTarget != null
}
private fun detailToggleText() =
@@ -257,7 +260,7 @@ class AutoDisconnectScreen(
private fun listButtonText() =
Text.literal(
- when (AutoDisconnect.lastReconnectTarget) {
+ when (AutoReconnect.lastReconnectTarget) {
is SingleplayerReconnectTarget -> "World List"
else -> "Server List"
}
diff --git a/src/main/kotlin/com/lambda/module/modules/combat/crystalaura/Opportunity.kt b/src/main/kotlin/com/lambda/module/modules/combat/crystalaura/Opportunity.kt
index a8827f757..e51b411da 100644
--- a/src/main/kotlin/com/lambda/module/modules/combat/crystalaura/Opportunity.kt
+++ b/src/main/kotlin/com/lambda/module/modules/combat/crystalaura/Opportunity.kt
@@ -28,10 +28,8 @@ import com.lambda.interaction.material.StackSelection
import com.lambda.interaction.material.container.containers.HotbarContainer
import com.lambda.interaction.material.container.containers.OffHandContainer
import com.lambda.module.modules.combat.crystalaura.CrystalAura.ActionType
-import com.lambda.module.modules.combat.crystalaura.CrystalAura.PredictionMode
import com.lambda.module.modules.combat.crystalaura.CrystalAura.crystalPosition
import com.lambda.module.modules.combat.crystalaura.CrystalAura.explodeDelay
-import com.lambda.module.modules.combat.crystalaura.CrystalAuraExploding.explodeInternal
import com.lambda.module.modules.combat.crystalaura.CrystalAura.explodeTimer
import com.lambda.module.modules.combat.crystalaura.CrystalAura.lastEntityId
import com.lambda.module.modules.combat.crystalaura.CrystalAura.lastHit
@@ -48,7 +46,7 @@ import com.lambda.module.modules.combat.crystalaura.CrystalAura.rotate
import com.lambda.module.modules.combat.crystalaura.CrystalAura.safeToPlaceInstantly
import com.lambda.module.modules.combat.crystalaura.CrystalAura.swap
import com.lambda.module.modules.combat.crystalaura.CrystalAura.swapHand
-import com.lambda.module.modules.combat.crystalaura.CrystalAura.waitingForCrystal
+import com.lambda.module.modules.combat.crystalaura.CrystalAuraExploding.explodeInternal
import com.lambda.module.modules.combat.crystalaura.CrystalAuraPlacing.placeInternal
import com.lambda.threading.runSafe
import com.lambda.threading.runSafeAutomated
diff --git a/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt b/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt
index cb334170d..4436fabd8 100644
--- a/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt
+++ b/src/main/kotlin/com/lambda/module/modules/movement/TargetStrafe.kt
@@ -17,8 +17,6 @@
package com.lambda.module.modules.movement
-import com.lambda.config.forEachSetting
-import com.lambda.config.hideAllExcept
import com.lambda.config.blocks.WorldLineSettings
import com.lambda.config.forEachSetting
import com.lambda.config.hideAllExcept
@@ -39,6 +37,7 @@ import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
+@Suppress("unused")
object TargetStrafe : Module(
name = "TargetStrafe",
description = "Strafes around a target in a circle. The default settings work great for old NCP.",
diff --git a/src/main/kotlin/com/lambda/module/modules/network/AutoReconnect.kt b/src/main/kotlin/com/lambda/module/modules/network/AutoReconnect.kt
new file mode 100644
index 000000000..c4e208f43
--- /dev/null
+++ b/src/main/kotlin/com/lambda/module/modules/network/AutoReconnect.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.network
+
+import com.lambda.Lambda.mc
+import com.lambda.module.Module
+import com.lambda.module.tag.ModuleTag
+import net.minecraft.client.gui.screen.Screen
+import net.minecraft.client.gui.screen.multiplayer.ConnectScreen
+import net.minecraft.client.gui.screen.world.SelectWorldScreen
+import net.minecraft.client.network.CookieStorage
+import net.minecraft.client.network.ServerAddress
+import net.minecraft.client.network.ServerInfo
+
+@Suppress("unused")
+object AutoReconnect : Module(
+ name = "AutoReconnect",
+ description = "Automatically reconnects to the last server or world after disconnecting",
+ tag = ModuleTag.NETWORK,
+) {
+ val delay by setting("Delay", 5.0, 0.5..60.0, 0.5, unit = "s", description = "Time in seconds before automatically reconnecting.")
+ val singleplayer by setting("Singleplayer", false, description = "Whether to auto-reconnect to singleplayer worlds.")
+
+ @JvmStatic
+ var lastReconnectTarget: ReconnectTarget? = null
+
+ @JvmStatic
+ fun canAutoReconnect(): Boolean {
+ if (!isEnabled) return false
+ val target = lastReconnectTarget ?: return false
+ return when (target) {
+ is MultiplayerReconnectTarget -> true
+ is SingleplayerReconnectTarget -> singleplayer
+ }
+ }
+
+ @JvmStatic
+ fun reconnect(parent: Screen) {
+ when (val target = lastReconnectTarget) {
+ is MultiplayerReconnectTarget -> {
+ ConnectScreen.connect(parent, mc, target.address, target.info, false, target.cookieStorage)
+ }
+
+ is SingleplayerReconnectTarget -> {
+ mc.createIntegratedServerLoader().start(target.levelName) {
+ mc.setScreen(SelectWorldScreen(parent))
+ }
+ }
+
+ null -> return
+ }
+ }
+}
+
+sealed interface ReconnectTarget
+
+data class MultiplayerReconnectTarget(
+ val address: ServerAddress,
+ val info: ServerInfo,
+ val cookieStorage: CookieStorage?
+) : ReconnectTarget
+
+data class SingleplayerReconnectTarget(
+ val levelName: String
+) : ReconnectTarget
\ No newline at end of file
diff --git a/src/main/resources/lambda.mixins.json b/src/main/resources/lambda.mixins.json
index 78929fc21..91a5111b4 100644
--- a/src/main/resources/lambda.mixins.json
+++ b/src/main/resources/lambda.mixins.json
@@ -46,6 +46,7 @@
"render.ChunkBorderDebugRendererMixin",
"render.ChunkOcclusionDataBuilderMixin",
"render.ConnectScreenMixin",
+ "render.DisconnectedScreenMixin",
"render.DrawContextMixin",
"render.ElytraFeatureRendererMixin",
"render.EntityRendererMixin",