From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 19 Apr 2020 04:28:29 -0400 Subject: [PATCH] Load Chunks for Login Asynchronously diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java index 55aae33d3b296ff8e02b1c36c55678979d48ac3d..c1d9c365edc8b6d2c5b94926faaa00180ca3885b 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -180,6 +180,7 @@ public class ServerPlayer extends Player { private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32; private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; public ServerGamePacketListenerImpl connection; + public net.minecraft.network.Connection networkManager; // Paper public final MinecraftServer server; public final ServerPlayerGameMode gameMode; private final PlayerAdvancements advancements; @@ -253,6 +254,7 @@ public class ServerPlayer extends Player { public boolean joining = true; public boolean sentListPacket = false; public boolean supressTrackerForLogin = false; // Paper + public boolean didPlayerJoinEvent = false; // Paper public Integer clientViewDistance; public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent // CraftBukkit end diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java index be677d437d17b74c6188ce1bd5fc6fdc228fd92f..78fbb4c3e52e900956ae0811aaf934c81ee5ea48 100644 --- a/src/main/java/net/minecraft/server/level/TicketType.java +++ b/src/main/java/net/minecraft/server/level/TicketType.java @@ -23,6 +23,7 @@ public class TicketType { public static final TicketType FORCED = TicketType.create("forced", Comparator.comparingLong(ChunkPos::toLong)); public static final TicketType LIGHT = TicketType.create("light", Comparator.comparingLong(ChunkPos::toLong)); public static final TicketType PORTAL = TicketType.create("portal", Vec3i::compareTo, 300); + public static final TicketType LOGIN = create("login", Long::compareTo, 100); // Paper public static final TicketType POST_TELEPORT = TicketType.create("post_teleport", Integer::compareTo, 5); public static final TicketType UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1); public static final TicketType PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java index 912e98d589f7469c6ea000d28f87e3fbe1ef3aab..766a5ce5d18aed80e37075f7cfc3b6cb9d33e63a 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -238,6 +238,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser private static final int NO_BLOCK_UPDATES_TO_ACK = -1; public final Connection connection; private final MinecraftServer server; + public Runnable playerJoinReady; // Paper public ServerPlayer player; private int tickCount; private int ackBlockChangesUpTo = -1; @@ -315,6 +316,15 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser // CraftBukkit end public void tick() { + // Paper start - login async + Runnable playerJoinReady = this.playerJoinReady; + if (playerJoinReady != null) { + this.playerJoinReady = null; + playerJoinReady.run(); + } + // Don't tick if not valid (dead), otherwise we load chunks below + if (this.player.valid) { + // Paper end if (this.ackBlockChangesUpTo > -1) { this.send(new ClientboundBlockChangedAckPacket(this.ackBlockChangesUpTo)); this.ackBlockChangesUpTo = -1; @@ -361,7 +371,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser this.lastVehicle = null; this.clientVehicleIsFloating = false; this.aboveGroundVehicleTickCount = 0; - } + }} // Paper - end if (valid) this.server.getProfiler().push("keepAlive"); // Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java index b070624c7ee85b6692f1f44ded6c78139925b669..22a2380388ed9fd6d28edbfbbb5ed3f646217525 100644 --- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java @@ -93,7 +93,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener } // Paper end } else if (this.state == ServerLoginPacketListenerImpl.State.DELAY_ACCEPT) { - ServerPlayer entityplayer = this.server.getPlayerList().getPlayer(this.gameProfile.getId()); + ServerPlayer entityplayer = this.server.getPlayerList().getActivePlayer(this.gameProfile.getId()); // Paper if (entityplayer == null) { this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT; @@ -192,7 +192,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener } this.connection.send(new ClientboundGameProfilePacket(this.gameProfile)); - ServerPlayer entityplayer = this.server.getPlayerList().getPlayer(this.gameProfile.getId()); + ServerPlayer entityplayer = this.server.getPlayerList().getActivePlayer(this.gameProfile.getId()); // Paper try { ServerPlayer entityplayer1 = this.server.getPlayerList().getPlayerForLogin(this.gameProfile, s); // CraftBukkit - add player reference diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java index ac0bae0f7ce766a67cf2dc2c5c35f244e5a08837..4b9e030016bef762c01ace5181ade7d1480b8702 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -138,6 +138,7 @@ public abstract class PlayerList { private final IpBanList ipBans; private final ServerOpList ops; private final UserWhiteList whitelist; + private final Map pendingPlayers = Maps.newHashMap(); // Paper // CraftBukkit start // private final Map stats; // private final Map advancements; @@ -177,6 +178,11 @@ public abstract class PlayerList { } public void placeNewPlayer(Connection connection, ServerPlayer player) { + ServerPlayer prev = pendingPlayers.put(player.getUUID(), player);// Paper + if (prev != null) { + disconnectPendingPlayer(prev); + } + player.networkManager = connection; // Paper player.loginTime = System.currentTimeMillis(); // Paper GameProfile gameprofile = player.getGameProfile(); GameProfileCache usercache = this.server.getProfileCache(); @@ -190,7 +196,7 @@ public abstract class PlayerList { if (nbttagcompound != null && nbttagcompound.contains("bukkit")) { CompoundTag bukkit = nbttagcompound.getCompound("bukkit"); s = bukkit.contains("lastKnownName", 8) ? bukkit.getString("lastKnownName") : s; - } + }String lastKnownName = s; // Paper // CraftBukkit end if (nbttagcompound != null) { @@ -217,11 +223,15 @@ public abstract class PlayerList { if (nbttagcompound == null) player.fudgeSpawnLocation(worldserver1); // Paper - only move to spawn on first login, otherwise, stay where you are.... player.setLevel(worldserver1); - String s1 = "local"; + // Paper start - make s1 final + final String s1; if (connection.getRemoteAddress() != null) { s1 = connection.getRemoteAddress().toString(); + } else { + s1 = "local"; } + // Paper end // Spigot start - spawn location event Player spawnPlayer = player.getBukkitEntity(); @@ -263,6 +273,52 @@ public abstract class PlayerList { player.getRecipeBook().sendInitialRecipeBook(player); this.updateEntireScoreboard(worldserver1.getScoreboard(), player); this.server.invalidateStatus(); + // Paper start - async load spawn in chunk + ServerLevel finalWorldserver = worldserver1; + int chunkX = loc.getBlockX() >> 4; + int chunkZ = loc.getBlockZ() >> 4; + final net.minecraft.world.level.ChunkPos pos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ); + net.minecraft.server.level.ChunkMap playerChunkMap = worldserver1.getChunkSource().chunkMap; + net.minecraft.server.level.DistanceManager distanceManager = playerChunkMap.distanceManager; + distanceManager.addTicket(net.minecraft.server.level.TicketType.LOGIN, pos, 31, pos.toLong()); + worldserver1.getChunkSource().runDistanceManagerUpdates(); + worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> { + net.minecraft.server.level.ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pos.toLong()); + if (updatingChunk != null) { + return updatingChunk.getEntityTickingChunkFuture(); + } else { + return java.util.concurrent.CompletableFuture.completedFuture(chunk); + } + }).thenAccept(chunk -> { + playerconnection.playerJoinReady = () -> { + postChunkLoadJoin( + player, finalWorldserver, connection, playerconnection, + nbttagcompound, s1, lastKnownName + ); + }; + }); + } + + public ServerPlayer getActivePlayer(UUID uuid) { + ServerPlayer player = this.playersByUUID.get(uuid); + return player != null ? player : pendingPlayers.get(uuid); + } + + void disconnectPendingPlayer(ServerPlayer entityplayer) { + Component msg = Component.translatable("multiplayer.disconnect.duplicate_login"); + entityplayer.networkManager.send(new net.minecraft.network.protocol.game.ClientboundDisconnectPacket(msg), (future) -> { + entityplayer.networkManager.disconnect(msg); + entityplayer.networkManager = null; + }); + } + + private void postChunkLoadJoin(ServerPlayer player, ServerLevel worldserver1, Connection networkmanager, ServerGamePacketListenerImpl playerconnection, CompoundTag nbttagcompound, String s1, String s) { + pendingPlayers.remove(player.getUUID(), player); + if (!networkmanager.isConnected()) { + return; + } + player.didPlayerJoinEvent = true; + // Paper end MutableComponent ichatmutablecomponent; if (player.getGameProfile().getName().equalsIgnoreCase(s)) { @@ -504,6 +560,7 @@ public abstract class PlayerList { protected void save(ServerPlayer player) { if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit + if (!player.didPlayerJoinEvent) return; // Paper - If we never fired PJE, we disconnected during login. Data has not changed, and additionally, our saved vehicle is not loaded! If we save now, we will lose our vehicle (CraftBukkit bug) this.playerIo.save(player); ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit @@ -531,7 +588,7 @@ public abstract class PlayerList { } PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(entityplayer.getBukkitEntity(), net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? entityplayer.getBukkitEntity().displayName() : net.kyori.adventure.text.Component.text(entityplayer.getScoreboardName()))); - this.cserver.getPluginManager().callEvent(playerQuitEvent); + if (entityplayer.didPlayerJoinEvent) this.cserver.getPluginManager().callEvent(playerQuitEvent); // Paper - if we disconnected before join ever fired, don't fire quit entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); if (server.isSameThread()) entityplayer.doTick(); // SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog) @@ -576,6 +633,13 @@ public abstract class PlayerList { // this.advancements.remove(uuid); // CraftBukkit end } + // Paper start + entityplayer1 = pendingPlayers.get(uuid); + if (entityplayer1 == entityplayer) { + pendingPlayers.remove(uuid); + } + entityplayer.networkManager = null; + // Paper end // CraftBukkit start // this.broadcastAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[]{entityplayer})); @@ -593,7 +657,7 @@ public abstract class PlayerList { this.cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity()); // CraftBukkit end - return playerQuitEvent.quitMessage(); // Paper - Adventure + return entityplayer.didPlayerJoinEvent ? playerQuitEvent.quitMessage() : null; // CraftBukkit // Paper - Adventure // Paper - don't print quit if we never printed join } // CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer @@ -612,6 +676,13 @@ public abstract class PlayerList { list.add(entityplayer); } } + // Paper start - check pending players too + entityplayer = pendingPlayers.get(uuid); + if (entityplayer != null) { + this.pendingPlayers.remove(uuid); + disconnectPendingPlayer(entityplayer); + } + // Paper end Iterator iterator = list.iterator();