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 63f20e7adf80a1b81438262087e33c3a1e63ca7f..f0d574307b24d19d3006e5c53b650c75436bde38 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -172,6 +172,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; @@ -238,6 +239,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; // CraftBukkit end public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper 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 2e7b909750ee512dce40b8574dfb62ed68fbbfb6..0d8a8b3d7a13e2384f8614bdc190768c03bb98c2 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -221,6 +221,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser private static final int LATENCY_CHECK_INTERVAL = 15000; public final Connection connection; private final MinecraftServer server; + public Runnable playerJoinReady; // Paper public ServerPlayer player; private int tickCount; private long keepAliveTime = Util.getMillis(); @@ -295,6 +296,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 this.resetPosition(); this.player.xo = this.player.getX(); this.player.yo = this.player.getY(); @@ -336,7 +346,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 477117affabfe07d52d3b40404613492b0bcdc56..3a7cc4f8ee62c8ff726ecf3e669c9f9ba5651487 100644 --- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java @@ -88,7 +88,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; @@ -194,7 +194,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().processLogin(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 3a13c151066c8784fdc844e1d6310f77ff32e7f1..c4242a1602bbb02541c330bc02016f15c8644358 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -38,6 +38,7 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; import net.minecraft.network.protocol.game.ClientboundChatPacket; import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.game.ClientboundDisconnectPacket; import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; import net.minecraft.network.protocol.game.ClientboundInitializeBorderPacket; @@ -127,11 +128,12 @@ public abstract class PlayerList { private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); private final MinecraftServer server; public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety - private final Map playersByUUID = Maps.newHashMap(); + private final Map playersByUUID = Maps.newHashMap();Map getUUIDMap() { return playersByUUID; } // Paper - OBFHELPER private final UserBanList bans; private final IpBanList ipBans; private final ServerOpList ops; private final UserWhiteList whitelist; + private final Map pendingPlayers = Maps.newHashMap(); // Paper // CraftBukkit start // private final Map o; // private final Map p; @@ -170,6 +172,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(); @@ -183,7 +190,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) { @@ -257,6 +264,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.addTicketAtLevel(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.getEntityTickingFuture(); + } else { + return java.util.concurrent.CompletableFuture.completedFuture(chunk); + } + }).thenAccept(chunk -> { + playerconnection.playerJoinReady = () -> { + postChunkLoadJoin( + player, finalWorldserver, connection, playerconnection, + nbttagcompound, connection.getRemoteAddress().toString(), lastKnownName + ); + }; + }); + } + + public ServerPlayer getActivePlayer(UUID uuid) { + ServerPlayer player = this.getUUIDMap().get(uuid); + return player != null ? player : pendingPlayers.get(uuid); + } + + void disconnectPendingPlayer(ServerPlayer entityplayer) { + TranslatableComponent msg = new TranslatableComponent("multiplayer.disconnect.duplicate_login", new Object[0]); + entityplayer.networkManager.send(new 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 TranslatableComponent chatmessage; if (player.getGameProfile().getName().equalsIgnoreCase(s)) { @@ -495,6 +548,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 @@ -522,7 +576,7 @@ public abstract class PlayerList { } PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(this.cserver.getPlayer(entityplayer), net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, com.destroystokyo.paper.PaperConfig.useDisplayNameInQuit ? 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) @@ -567,6 +621,13 @@ public abstract class PlayerList { // this.p.remove(uuid); // CraftBukkit end } + // Paper start + entityplayer1 = pendingPlayers.get(uuid); + if (entityplayer1 == entityplayer) { + pendingPlayers.remove(uuid); + } + entityplayer.networkManager = null; + // Paper end // CraftBukkit start // this.sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[]{entityplayer})); @@ -584,7 +645,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 @@ -603,6 +664,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(); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index 7120400af3e6a36f9b097fe9dfaff655209b03ce..d9ed724d07309f23c91f20c9af888cce5127e323 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -1511,7 +1511,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n this.yo = y; this.zo = d4; this.setPos(d3, y, d4); - this.level.getChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4); // CraftBukkit + if (valid) this.level.getChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4); // CraftBukkit // Paper } public void moveTo(Vec3 pos) {