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",