207 lines
10 KiB
Java
207 lines
10 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.mojang.logging.LogUtils
|
|
* io.netty.channel.ChannelFuture
|
|
* org.jspecify.annotations.Nullable
|
|
* org.slf4j.Logger
|
|
*/
|
|
package net.minecraft.client.gui.screens;
|
|
|
|
import com.mojang.logging.LogUtils;
|
|
import io.netty.channel.ChannelFuture;
|
|
import java.net.InetSocketAddress;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import net.minecraft.DefaultUncaughtExceptionHandler;
|
|
import net.minecraft.client.GameNarrator;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.GuiGraphics;
|
|
import net.minecraft.client.gui.components.Button;
|
|
import net.minecraft.client.gui.screens.DisconnectedScreen;
|
|
import net.minecraft.client.gui.screens.Screen;
|
|
import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl;
|
|
import net.minecraft.client.multiplayer.LevelLoadTracker;
|
|
import net.minecraft.client.multiplayer.ServerData;
|
|
import net.minecraft.client.multiplayer.TransferState;
|
|
import net.minecraft.client.multiplayer.chat.report.ReportEnvironment;
|
|
import net.minecraft.client.multiplayer.resolver.ResolvedServerAddress;
|
|
import net.minecraft.client.multiplayer.resolver.ServerAddress;
|
|
import net.minecraft.client.multiplayer.resolver.ServerNameResolver;
|
|
import net.minecraft.client.quickplay.QuickPlay;
|
|
import net.minecraft.client.quickplay.QuickPlayLog;
|
|
import net.minecraft.client.resources.server.ServerPackManager;
|
|
import net.minecraft.network.Connection;
|
|
import net.minecraft.network.chat.CommonComponents;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.protocol.PacketFlow;
|
|
import net.minecraft.network.protocol.login.LoginProtocols;
|
|
import net.minecraft.network.protocol.login.ServerboundHelloPacket;
|
|
import net.minecraft.server.network.EventLoopGroupHolder;
|
|
import net.minecraft.util.Util;
|
|
import org.jspecify.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class ConnectScreen
|
|
extends Screen {
|
|
private static final AtomicInteger UNIQUE_THREAD_ID = new AtomicInteger(0);
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final long NARRATION_DELAY_MS = 2000L;
|
|
public static final Component ABORT_CONNECTION = Component.translatable("connect.aborted");
|
|
public static final Component UNKNOWN_HOST_MESSAGE = Component.translatable("disconnect.genericReason", Component.translatable("disconnect.unknownHost"));
|
|
private volatile @Nullable Connection connection;
|
|
private @Nullable ChannelFuture channelFuture;
|
|
private volatile boolean aborted;
|
|
private final Screen parent;
|
|
private Component status = Component.translatable("connect.connecting");
|
|
private long lastNarration = -1L;
|
|
private final Component connectFailedTitle;
|
|
|
|
private ConnectScreen(Screen parent, Component connectFailedTitle) {
|
|
super(GameNarrator.NO_TITLE);
|
|
this.parent = parent;
|
|
this.connectFailedTitle = connectFailedTitle;
|
|
}
|
|
|
|
public static void startConnecting(Screen parent, Minecraft minecraft, ServerAddress hostAndPort, ServerData data, boolean isQuickPlay, @Nullable TransferState transferState) {
|
|
if (minecraft.screen instanceof ConnectScreen) {
|
|
LOGGER.error("Attempt to connect while already connecting");
|
|
return;
|
|
}
|
|
Component connectFailedTitle = transferState != null ? CommonComponents.TRANSFER_CONNECT_FAILED : (isQuickPlay ? QuickPlay.ERROR_TITLE : CommonComponents.CONNECT_FAILED);
|
|
ConnectScreen screen = new ConnectScreen(parent, connectFailedTitle);
|
|
if (transferState != null) {
|
|
screen.updateStatus(Component.translatable("connect.transferring"));
|
|
}
|
|
minecraft.disconnectWithProgressScreen();
|
|
minecraft.prepareForMultiplayer();
|
|
minecraft.updateReportEnvironment(ReportEnvironment.thirdParty(data.ip));
|
|
minecraft.quickPlayLog().setWorldData(QuickPlayLog.Type.MULTIPLAYER, data.ip, data.name);
|
|
minecraft.setScreen(screen);
|
|
screen.connect(minecraft, hostAndPort, data, transferState);
|
|
}
|
|
|
|
private void connect(final Minecraft minecraft, final ServerAddress hostAndPort, final ServerData server, final @Nullable TransferState transferState) {
|
|
LOGGER.info("Connecting to {}, {}", (Object)hostAndPort.getHost(), (Object)hostAndPort.getPort());
|
|
Thread thread = new Thread("Server Connector #" + UNIQUE_THREAD_ID.incrementAndGet()){
|
|
|
|
/*
|
|
* WARNING - Removed try catching itself - possible behaviour change.
|
|
*/
|
|
@Override
|
|
public void run() {
|
|
InetSocketAddress address = null;
|
|
try {
|
|
Connection pendingConnection;
|
|
if (ConnectScreen.this.aborted) {
|
|
return;
|
|
}
|
|
Optional<InetSocketAddress> resolvedAddress = ServerNameResolver.DEFAULT.resolveAddress(hostAndPort).map(ResolvedServerAddress::asInetSocketAddress);
|
|
if (ConnectScreen.this.aborted) {
|
|
return;
|
|
}
|
|
if (resolvedAddress.isEmpty()) {
|
|
minecraft.execute(() -> minecraft.setScreen(new DisconnectedScreen(ConnectScreen.this.parent, ConnectScreen.this.connectFailedTitle, UNKNOWN_HOST_MESSAGE)));
|
|
return;
|
|
}
|
|
address = resolvedAddress.get();
|
|
ConnectScreen connectScreen = ConnectScreen.this;
|
|
synchronized (connectScreen) {
|
|
if (ConnectScreen.this.aborted) {
|
|
return;
|
|
}
|
|
pendingConnection = new Connection(PacketFlow.CLIENTBOUND);
|
|
pendingConnection.setBandwidthLogger(minecraft.getDebugOverlay().getBandwidthLogger());
|
|
ConnectScreen.this.channelFuture = Connection.connect(address, EventLoopGroupHolder.remote(minecraft.options.useNativeTransport()), pendingConnection);
|
|
}
|
|
ConnectScreen.this.channelFuture.syncUninterruptibly();
|
|
connectScreen = ConnectScreen.this;
|
|
synchronized (connectScreen) {
|
|
if (ConnectScreen.this.aborted) {
|
|
pendingConnection.disconnect(ABORT_CONNECTION);
|
|
return;
|
|
}
|
|
ConnectScreen.this.connection = pendingConnection;
|
|
minecraft.getDownloadedPackSource().configureForServerControl(pendingConnection, 1.convertPackStatus(server.getResourcePackStatus()));
|
|
}
|
|
ConnectScreen.this.connection.initiateServerboundPlayConnection(address.getHostName(), address.getPort(), LoginProtocols.SERVERBOUND, LoginProtocols.CLIENTBOUND, new ClientHandshakePacketListenerImpl(ConnectScreen.this.connection, minecraft, server, ConnectScreen.this.parent, false, null, ConnectScreen.this::updateStatus, new LevelLoadTracker(), transferState), transferState != null);
|
|
ConnectScreen.this.connection.send(new ServerboundHelloPacket(minecraft.getUser().getName(), minecraft.getUser().getProfileId()));
|
|
}
|
|
catch (Exception exception) {
|
|
Exception originalCause;
|
|
if (ConnectScreen.this.aborted) {
|
|
return;
|
|
}
|
|
Throwable throwable = exception.getCause();
|
|
Exception cause = throwable instanceof Exception ? (originalCause = (Exception)throwable) : exception;
|
|
LOGGER.error("Couldn't connect to server", (Throwable)exception);
|
|
String message = address == null ? cause.getMessage() : cause.getMessage().replaceAll(address.getHostName() + ":" + address.getPort(), "").replaceAll(address.toString(), "");
|
|
minecraft.execute(() -> minecraft.setScreen(new DisconnectedScreen(ConnectScreen.this.parent, ConnectScreen.this.connectFailedTitle, (Component)Component.translatable("disconnect.genericReason", message))));
|
|
}
|
|
}
|
|
|
|
private static ServerPackManager.PackPromptStatus convertPackStatus(ServerData.ServerPackStatus resourcePackStatus) {
|
|
return switch (resourcePackStatus) {
|
|
default -> throw new MatchException(null, null);
|
|
case ServerData.ServerPackStatus.ENABLED -> ServerPackManager.PackPromptStatus.ALLOWED;
|
|
case ServerData.ServerPackStatus.DISABLED -> ServerPackManager.PackPromptStatus.DECLINED;
|
|
case ServerData.ServerPackStatus.PROMPT -> ServerPackManager.PackPromptStatus.PENDING;
|
|
};
|
|
}
|
|
};
|
|
thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER));
|
|
thread.start();
|
|
}
|
|
|
|
private void updateStatus(Component status) {
|
|
this.status = status;
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (this.connection != null) {
|
|
if (this.connection.isConnected()) {
|
|
this.connection.tick();
|
|
} else {
|
|
this.connection.handleDisconnection();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldCloseOnEsc() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected void init() {
|
|
this.addRenderableWidget(Button.builder(CommonComponents.GUI_CANCEL, button -> {
|
|
ConnectScreen connectScreen = this;
|
|
synchronized (connectScreen) {
|
|
this.aborted = true;
|
|
if (this.channelFuture != null) {
|
|
this.channelFuture.cancel(true);
|
|
this.channelFuture = null;
|
|
}
|
|
if (this.connection != null) {
|
|
this.connection.disconnect(ABORT_CONNECTION);
|
|
}
|
|
}
|
|
this.minecraft.setScreen(this.parent);
|
|
}).bounds(this.width / 2 - 100, this.height / 4 + 120 + 12, 200, 20).build());
|
|
}
|
|
|
|
@Override
|
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float a) {
|
|
super.render(graphics, mouseX, mouseY, a);
|
|
long current = Util.getMillis();
|
|
if (current - this.lastNarration > 2000L) {
|
|
this.lastNarration = current;
|
|
this.minecraft.getNarrator().saySystemNow(Component.translatable("narrator.joining"));
|
|
}
|
|
graphics.drawCenteredString(this.font, this.status, this.width / 2, this.height / 2 - 50, -1);
|
|
}
|
|
}
|
|
|