--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -47,12 +_,26 @@
     public ClientInformation clientInformation;
     @Nullable
     private SynchronizeRegistriesTask synchronizeRegistriesTask;
+    public io.papermc.paper.connection.PaperPlayerConfigurationConnection paperConnection; // Paper
 
     public ServerConfigurationPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
         super(server, connection, cookie);
         this.gameProfile = cookie.gameProfile();
         this.clientInformation = cookie.clientInformation();
-    }
+        this.paperConnection = new io.papermc.paper.connection.PaperPlayerConfigurationConnection(this); // Paper
+    }
+
+    // Paper start - configuration phase API
+    @Override
+    public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
+        return this.paperConnection;
+    }
+
+    @Override
+    public net.kyori.adventure.audience.Audience getAudience() {
+        return this.paperConnection.getAudience();
+    }
+    // Paper end - configuration phase API
 
     @Override
     protected GameProfile playerProfile() {
@@ -61,6 +_,11 @@
 
     @Override
     public void onDisconnect(DisconnectionDetails details) {
+        // Paper start - Debugging
+        if (this.server.isDebugging()) {
+            ServerConfigurationPacketListenerImpl.LOGGER.info("{} lost connection: {}, while in configuration phase {}", this.gameProfile, details.reason().getString(), this.currentTask != null ? this.currentTask.type().id() : "null");
+        } else
+        // Paper end
         LOGGER.info("{} lost connection: {}", this.gameProfile, details.reason().getString());
         super.onDisconnect(details);
     }
@@ -71,10 +_,15 @@
     }
 
     public void startConfiguration() {
+        new io.papermc.paper.event.connection.configuration.PlayerConnectionInitialConfigureEvent(this.paperConnection).callEvent(); // Paper
         this.send(new ClientboundCustomPayloadPacket(new BrandPayload(this.server.getServerModName())));
         ServerLinks serverLinks = this.server.serverLinks();
         if (!serverLinks.isEmpty()) {
-            this.send(new ClientboundServerLinksPacket(serverLinks.untrust()));
+            // Paper start
+            org.bukkit.craftbukkit.CraftServerLinks links = new org.bukkit.craftbukkit.CraftServerLinks(serverLinks);
+            new org.bukkit.event.player.PlayerLinksSendEvent(this.paperConnection, links).callEvent();
+            this.send(new ClientboundServerLinksPacket(links.getServerLinks().untrust()));
+            // Paper end
         }
 
         LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = this.server.registries();
@@ -87,6 +_,7 @@
         this.synchronizeRegistriesTask = new SynchronizeRegistriesTask(list, layeredRegistryAccess);
         this.configurationTasks.add(this.synchronizeRegistriesTask);
         this.addOptionalTasks();
+        this.configurationTasks.add(new io.papermc.paper.connection.PaperConfigurationTask(this)); // Paper
         this.configurationTasks.add(new JoinWorldTask());
         this.startNextTask();
     }
@@ -105,12 +_,13 @@
     @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
     public void handleResourcePackResponse(ServerboundResourcePackPacket packet) {
         super.handleResourcePackResponse(packet);
-        if (packet.action().isTerminal()) {
+        if (packet.action().isTerminal() && packet.id().equals(this.server.getServerResourcePack().map(MinecraftServer.ServerResourcePackInfo::id).orElse(null))) { // Paper - Ignore resource pack requests that are not vanilla
             this.finishCurrentTask(ServerResourcePackConfigurationTask.TYPE);
         }
     }
@@ -139,16 +_,30 @@
                 return;
             }
 
-            Component component = playerList.canPlayerLogin(this.connection.getRemoteAddress(), this.gameProfile);
+            Component component = org.bukkit.craftbukkit.event.CraftEventFactory.handleLoginResult(playerList.canPlayerLogin(this.connection.getRemoteAddress(), this.gameProfile), this.paperConnection, this.connection, this.gameProfile, this.server, false); // Paper - Login event logic
             if (component != null) {
                 this.disconnect(component);
                 return;
             }
 
-            ServerPlayer serverPlayer = new ServerPlayer(this.server, this.server.overworld(), this.gameProfile, this.clientInformation);
+            // Paper start
+            ServerPlayer serverPlayer;
+            if (this.connection.savedPlayerForLoginEventLegacy != null) {
+                serverPlayer = this.connection.savedPlayerForLoginEventLegacy;
+                // WE have to do this because the player isnt updated properly
+                serverPlayer.updateOptionsNoEvents(this.clientInformation);
+            } else {
+                serverPlayer = new ServerPlayer(this.server, this.server.overworld(), this.gameProfile, this.clientInformation);
+            }
+            // Paper end
             playerList.placeNewPlayer(this.connection, serverPlayer, this.createCookie(this.clientInformation));
         } catch (Exception var5) {
             LOGGER.error("Couldn't place player in world", (Throwable)var5);
+            // Paper start - Debugging
+            if (this.server.isDebugging()) {
+                var5.printStackTrace();
+            }
+            // Paper end - Debugging
             this.connection.send(new ClientboundDisconnectPacket(DISCONNECT_REASON_INVALID_DATA));
             this.connection.disconnect(DISCONNECT_REASON_INVALID_DATA);
         }
@@ -180,4 +_,29 @@
             this.startNextTask();
         }
     }
+
+    // Paper start
+    @Override
+    public void disconnectAsync(final net.minecraft.network.DisconnectionDetails disconnectionInfo) {
+        if (this.cserver.isPrimaryThread()) {
+            this.disconnect(disconnectionInfo);
+            return;
+        }
+
+        this.connection.setReadOnly();
+        this.server.scheduleOnMain(() -> {
+            this.disconnect(disconnectionInfo); // Currently you cannot cancel disconnect during the config stage
+        });
+    }
+
+    @Override
+    public void handleCustomPayload(net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket packet) {
+        super.handleCustomPayload(packet);
+    }
+
+    @Override
+    public io.papermc.paper.connection.PaperPlayerConfigurationConnection paperConnection() {
+        return paperConnection;
+    }
+    // Paper end
 }
