--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -49,18 +_,32 @@
     private final GameProfile gameProfile;
     private final Queue<ConfigurationTask> configurationTasks = new ConcurrentLinkedQueue<>();
     @Nullable
-    private ConfigurationTask currentTask;
-    private ClientInformation clientInformation;
+    public ConfigurationTask currentTask;
+    public ClientInformation clientInformation;
     @Nullable
     private SynchronizeRegistriesTask synchronizeRegistriesTask;
     @Nullable
     private PrepareSpawnTask prepareSpawnTask;
+    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() {
@@ -69,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.name(), this.gameProfile.id(), details.reason().getString(), this.currentTask != null ? this.currentTask.type().id() : "null");
+        } else
+        // Paper end
         LOGGER.info("{} ({}) lost connection: {}", this.gameProfile.name(), this.gameProfile.id(), details.reason().getString());
         if (this.prepareSpawnTask != null) {
             this.prepareSpawnTask.close();
@@ -84,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();
@@ -100,11 +_,12 @@
         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.returnToWorld();
     }
 
     public void returnToWorld() {
-        this.prepareSpawnTask = new PrepareSpawnTask(this.server, new NameAndId(this.gameProfile));
+        this.prepareSpawnTask = new PrepareSpawnTask(this.server, this.gameProfile, this); // Paper - pass full GameProfile & listener for events
         this.configurationTasks.add(this.prepareSpawnTask);
         this.configurationTasks.add(new JoinWorldTask());
         this.startNextTask();
@@ -135,12 +_,14 @@
     @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()) {
+        this.connection.resourcePackStatus = org.bukkit.event.player.PlayerResourcePackStatusEvent.Status.values()[packet.action().ordinal()]; // Paper
+        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);
         }
     }
@@ -174,7 +_,7 @@
                 return;
             }
 
-            Component component = playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(this.gameProfile));
+            Component component = org.bukkit.craftbukkit.event.CraftEventFactory.handleLoginResult(playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(this.gameProfile)), this.paperConnection, this.connection, this.gameProfile, this.server, false); // Paper - Login event logic
             if (component != null) {
                 this.disconnect(component);
                 return;
@@ -225,7 +_,7 @@
         }
     }
 
-    private void finishCurrentTask(ConfigurationTask.Type taskType) {
+    public void finishCurrentTask(ConfigurationTask.Type taskType) {
         ConfigurationTask.Type type = this.currentTask != null ? this.currentTask.type() : null;
         if (!taskType.equals(type)) {
             throw new IllegalStateException("Unexpected request for task finish, current task: " + type + ", requested: " + taskType);
@@ -234,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
 }
