--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -34,24 +34,33 @@
 import net.minecraft.server.network.config.SynchronizeRegistriesTask;
 import net.minecraft.server.packs.repository.KnownPack;
 import net.minecraft.server.players.PlayerList;
+import net.minecraft.util.thread.BlockableEventLoop;
 import net.minecraft.world.flag.FeatureFlags;
 import org.slf4j.Logger;
 
+// CraftBukkit start
+import org.bukkit.craftbukkit.CraftServerLinks;
+import org.bukkit.event.player.PlayerLinksSendEvent;
+// CraftBukkit end
+
 public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketListenerImpl implements ServerConfigurationPacketListener, TickablePacketListener {
+
     private static final Logger LOGGER = LogUtils.getLogger();
     private static final Component DISCONNECT_REASON_INVALID_DATA = Component.translatable("multiplayer.disconnect.invalid_player_data");
     private final GameProfile gameProfile;
-    private final Queue<ConfigurationTask> configurationTasks = new ConcurrentLinkedQueue<>();
+    private final Queue<ConfigurationTask> configurationTasks = new ConcurrentLinkedQueue();
     @Nullable
     private ConfigurationTask currentTask;
     private ClientInformation clientInformation;
     @Nullable
     private SynchronizeRegistriesTask synchronizeRegistriesTask;
 
-    public ServerConfigurationPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie clientData) {
-        super(server, connection, clientData);
-        this.gameProfile = clientData.gameProfile();
-        this.clientInformation = clientData.clientInformation();
+    // CraftBukkit start
+    public ServerConfigurationPacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, CommonListenerCookie commonlistenercookie, ServerPlayer player) {
+        super(minecraftserver, networkmanager, commonlistenercookie, player);
+        // CraftBukkit end
+        this.gameProfile = commonlistenercookie.gameProfile();
+        this.clientInformation = commonlistenercookie.clientInformation();
     }
 
     @Override
@@ -61,7 +70,11 @@
 
     @Override
     public void onDisconnect(DisconnectionDetails info) {
-        LOGGER.info("{} lost connection: {}", this.gameProfile, info.reason().getString());
+        // Paper start - Debugging
+        if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) {
+            ServerConfigurationPacketListenerImpl.LOGGER.info("{} lost connection: {}, while in configuration phase {}", this.gameProfile, info.reason().getString(), currentTask != null ? currentTask.type().id() : "null");
+        } else // Paper end
+        ServerConfigurationPacketListenerImpl.LOGGER.info("{} lost connection: {}", this.gameProfile, info.reason().getString());
         super.onDisconnect(info);
     }
 
@@ -72,15 +85,25 @@
 
     public void startConfiguration() {
         this.send(new ClientboundCustomPayloadPacket(new BrandPayload(this.server.getServerModName())));
-        ServerLinks serverLinks = this.server.serverLinks();
-        if (!serverLinks.isEmpty()) {
-            this.send(new ClientboundServerLinksPacket(serverLinks.untrust()));
+        ServerLinks serverlinks = this.server.serverLinks();
+        // CraftBukkit start
+        CraftServerLinks wrapper = new CraftServerLinks(serverlinks);
+        PlayerLinksSendEvent event = new PlayerLinksSendEvent(this.player.getBukkitEntity(), wrapper);
+        this.player.getBukkitEntity().getServer().getPluginManager().callEvent(event);
+        serverlinks = wrapper.getServerLinks();
+        // CraftBukkit end
+
+        if (!serverlinks.isEmpty()) {
+            this.send(new ClientboundServerLinksPacket(serverlinks.untrust()));
         }
 
-        LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = this.server.registries();
-        List<KnownPack> list = this.server.getResourceManager().listPacks().flatMap(pack -> pack.location().knownPackInfo().stream()).toList();
+        LayeredRegistryAccess<RegistryLayer> layeredregistryaccess = this.server.registries();
+        List<KnownPack> list = this.server.getResourceManager().listPacks().flatMap((iresourcepack) -> {
+            return iresourcepack.location().knownPackInfo().stream();
+        }).toList();
+
         this.send(new ClientboundUpdateEnabledFeaturesPacket(FeatureFlags.REGISTRY.toNames(this.server.getWorldData().enabledFeatures())));
-        this.synchronizeRegistriesTask = new SynchronizeRegistriesTask(list, layeredRegistryAccess);
+        this.synchronizeRegistriesTask = new SynchronizeRegistriesTask(list, layeredregistryaccess);
         this.configurationTasks.add(this.synchronizeRegistriesTask);
         this.addOptionalTasks();
         this.configurationTasks.add(new JoinWorldTask());
@@ -93,12 +116,15 @@
     }
 
     private void addOptionalTasks() {
-        this.server.getServerResourcePack().ifPresent(properties -> this.configurationTasks.add(new ServerResourcePackConfigurationTask(properties)));
+        this.server.getServerResourcePack().ifPresent((minecraftserver_serverresourcepackinfo) -> {
+            this.configurationTasks.add(new ServerResourcePackConfigurationTask(minecraftserver_serverresourcepackinfo));
+        });
     }
 
     @Override
     public void handleClientInformation(ServerboundClientInformationPacket packet) {
         this.clientInformation = packet.information();
+        this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper
     }
 
     @Override
@@ -107,11 +133,12 @@
         if (packet.action().isTerminal()) {
             this.finishCurrentTask(ServerResourcePackConfigurationTask.TYPE);
         }
+
     }
 
     @Override
     public void handleSelectKnownPacks(ServerboundSelectKnownPacks packet) {
-        PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
+        PacketUtils.ensureRunningOnSameThread(packet, this, (BlockableEventLoop) this.server);
         if (this.synchronizeRegistriesTask == null) {
             throw new IllegalStateException("Unexpected response from client: received pack selection, but no negotiation ongoing");
         } else {
@@ -122,30 +149,39 @@
 
     @Override
     public void handleConfigurationFinished(ServerboundFinishConfigurationPacket packet) {
-        PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
+        PacketUtils.ensureRunningOnSameThread(packet, this, (BlockableEventLoop) this.server);
         this.finishCurrentTask(JoinWorldTask.TYPE);
         this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess())));
 
         try {
-            PlayerList playerList = this.server.getPlayerList();
-            if (playerList.getPlayer(this.gameProfile.getId()) != null) {
+            PlayerList playerlist = this.server.getPlayerList();
+
+            if (playerlist.getPlayer(this.gameProfile.getId()) != null) {
                 this.disconnect(PlayerList.DUPLICATE_LOGIN_DISCONNECT_MESSAGE);
                 return;
             }
 
-            Component component = playerList.canPlayerLogin(this.connection.getRemoteAddress(), this.gameProfile);
-            if (component != null) {
-                this.disconnect(component);
+            Component ichatbasecomponent = null; // CraftBukkit - login checks already completed
+
+            if (ichatbasecomponent != null) {
+                this.disconnect(ichatbasecomponent);
                 return;
             }
 
-            ServerPlayer serverPlayer = playerList.getPlayerForLogin(this.gameProfile, this.clientInformation);
-            playerList.placeNewPlayer(this.connection, serverPlayer, this.createCookie(this.clientInformation));
-        } catch (Exception var5) {
-            LOGGER.error("Couldn't place player in world", (Throwable)var5);
-            this.connection.send(new ClientboundDisconnectPacket(DISCONNECT_REASON_INVALID_DATA));
-            this.connection.disconnect(DISCONNECT_REASON_INVALID_DATA);
+            ServerPlayer entityplayer = playerlist.getPlayerForLogin(this.gameProfile, this.clientInformation, this.player); // CraftBukkit
+
+            playerlist.placeNewPlayer(this.connection, entityplayer, this.createCookie(this.clientInformation));
+        } catch (Exception exception) {
+            ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
+            // Paper start - Debugging
+            if (MinecraftServer.getServer().isDebugging()) {
+                exception.printStackTrace();
+            }
+            // Paper end - Debugging
+            this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
+            this.connection.disconnect(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA);
         }
+
     }
 
     @Override
@@ -157,18 +193,23 @@
         if (this.currentTask != null) {
             throw new IllegalStateException("Task " + this.currentTask.type().id() + " has not finished yet");
         } else if (this.isAcceptingMessages()) {
-            ConfigurationTask configurationTask = this.configurationTasks.poll();
-            if (configurationTask != null) {
-                this.currentTask = configurationTask;
-                configurationTask.start(this::send);
+            ConfigurationTask configurationtask = (ConfigurationTask) this.configurationTasks.poll();
+
+            if (configurationtask != null) {
+                this.currentTask = configurationtask;
+                configurationtask.start(this::send);
             }
+
         }
     }
 
     private void finishCurrentTask(ConfigurationTask.Type key) {
-        ConfigurationTask.Type type = this.currentTask != null ? this.currentTask.type() : null;
-        if (!key.equals(type)) {
-            throw new IllegalStateException("Unexpected request for task finish, current task: " + type + ", requested: " + key);
+        ConfigurationTask.Type configurationtask_a1 = this.currentTask != null ? this.currentTask.type() : null;
+
+        if (!key.equals(configurationtask_a1)) {
+            String s = String.valueOf(configurationtask_a1);
+
+            throw new IllegalStateException("Unexpected request for task finish, current task: " + s + ", requested: " + String.valueOf(key));
         } else {
             this.currentTask = null;
             this.startNextTask();
