2020-05-06 11:48:49 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2020-04-19 10:32:04 +02:00
From: Aikar <aikar@aikar.co>
Date: Sun, 19 Apr 2020 04:28:29 -0400
Subject: [PATCH] Load Chunks for Login Asynchronously
2020-06-26 03:53:21 +02:00
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
2021-02-21 21:55:01 +01:00
index 75500aaae0dc44f0da3b4d5ed94da6fef9f96c38..322d0a7d127878ba49362454808b82172acf1600 100644
2020-06-26 03:53:21 +02:00
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
2021-02-21 21:55:01 +01:00
@@ -604,7 +604,7 @@ public class ChunkProviderServer extends IChunkProvider {
2020-06-26 03:53:21 +02:00
return this.serverThreadQueue.executeNext();
}
- private boolean tickDistanceManager() {
+ public boolean tickDistanceManager() { // Paper - private -> public
boolean flag = this.chunkMapDistance.a(this.playerChunkMap);
boolean flag1 = this.playerChunkMap.b();
2020-04-22 08:52:17 +02:00
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
2021-02-21 21:55:01 +01:00
index 7da08dd7c2396ae1048d857bfae4fee2a9f3b4d9..1ed26fa55b149e3717cf190abde8f865a4c076eb 100644
2020-04-22 08:52:17 +02:00
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
2020-11-03 03:22:15 +01:00
@@ -1283,7 +1283,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
2020-08-25 04:22:08 +02:00
this.lastY = d1;
this.lastZ = d4;
this.setPosition(d3, d1, d4);
2020-06-26 03:53:21 +02:00
- world.getChunkAt((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4); // CraftBukkit
+ if (valid) world.getChunkAt((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4); // CraftBukkit // Paper
2020-04-22 08:52:17 +02:00
}
2020-08-25 04:22:08 +02:00
public void d(Vec3D vec3d) {
2020-04-22 11:40:06 +02:00
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
2021-02-21 20:45:33 +01:00
index 03f13cc10de79137c1e186a4207ad6cc7d68c843..e364576955ce033f732bc9348ab653c5db71487f 100644
2020-04-22 11:40:06 +02:00
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
2021-02-21 20:45:33 +01:00
@@ -46,6 +46,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
2020-06-26 03:53:21 +02:00
2020-04-24 11:33:33 +02:00
private static final Logger LOGGER = LogManager.getLogger();
public PlayerConnection playerConnection;
+ public NetworkManager networkManager; // Paper
public final MinecraftServer server;
public final PlayerInteractManager playerInteractManager;
public final Deque<Integer> removeQueue = new ArrayDeque<>(); // Paper
2021-02-21 20:45:33 +01:00
@@ -112,6 +113,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
2020-04-22 11:40:06 +02:00
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
2020-04-24 11:33:33 +02:00
diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java
2021-02-21 20:45:33 +01:00
index e5790c2aeaa380a3acc26434f5de78ac746c6d57..bdd4766976902e11411728e6faa93b7e43a7ba22 100644
2020-04-24 11:33:33 +02:00
--- a/src/main/java/net/minecraft/server/LoginListener.java
+++ b/src/main/java/net/minecraft/server/LoginListener.java
2021-02-21 20:45:33 +01:00
@@ -68,7 +68,7 @@ public class LoginListener implements PacketLoginInListener {
2020-04-24 11:33:33 +02:00
}
// Paper end
} else if (this.g == LoginListener.EnumProtocolState.DELAY_ACCEPT) {
2020-06-26 03:53:21 +02:00
- EntityPlayer entityplayer = this.server.getPlayerList().getPlayer(this.i.getId());
2020-04-24 11:33:33 +02:00
+ EntityPlayer entityplayer = this.server.getPlayerList().getActivePlayer(this.i.getId()); // Paper
if (entityplayer == null) {
this.g = LoginListener.EnumProtocolState.READY_TO_ACCEPT;
2021-02-21 20:45:33 +01:00
@@ -167,7 +167,7 @@ public class LoginListener implements PacketLoginInListener {
2020-04-24 11:33:33 +02:00
}
this.networkManager.sendPacket(new PacketLoginOutSuccess(this.i));
2020-06-26 03:53:21 +02:00
- EntityPlayer entityplayer = this.server.getPlayerList().getPlayer(this.i.getId());
2020-04-24 11:33:33 +02:00
+ EntityPlayer entityplayer = this.server.getPlayerList().getActivePlayer(this.i.getId()); // Paper
if (entityplayer != null) {
this.g = LoginListener.EnumProtocolState.DELAY_ACCEPT;
2020-04-22 08:52:17 +02:00
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
2021-02-22 09:03:03 +01:00
index 503b090870c4307ab11ef5fb7e2692863d7dc9ea..11e63ee879fa8eaa502981bf3fe7fa4f5a6f5668 100644
2020-04-22 08:52:17 +02:00
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
2021-02-21 20:45:33 +01:00
@@ -76,6 +76,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
2020-04-29 10:14:47 +02:00
private static final Logger LOGGER = LogManager.getLogger();
public final NetworkManager networkManager;
private final MinecraftServer minecraftServer;
+ Runnable playerJoinReady; // Paper
public EntityPlayer player;
private int e;
private long lastKeepAlive = SystemUtils.getMonotonicMillis(); private void setLastPing(long lastPing) { this.lastKeepAlive = lastPing;}; private long getLastPing() { return this.lastKeepAlive;}; // Paper - OBFHELPER
2021-02-21 20:45:33 +01:00
@@ -154,6 +155,15 @@ public class PlayerConnection implements PacketListenerPlayIn {
2020-04-22 08:52:17 +02:00
// CraftBukkit end
public void tick() {
2020-04-29 10:14:47 +02:00
+ // 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
2020-05-07 01:30:47 +02:00
+ if (this.player.valid) {
2020-04-29 10:14:47 +02:00
+ // Paper end
2020-04-22 08:52:17 +02:00
this.syncPosition();
this.player.lastX = this.player.locX();
this.player.lastY = this.player.locY();
2021-02-21 20:45:33 +01:00
@@ -195,7 +205,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
2020-05-07 01:30:47 +02:00
this.r = null;
this.D = false;
this.E = 0;
- }
+ }} // Paper - end if (valid)
this.minecraftServer.getMethodProfiler().enter("keepAlive");
// Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings
2020-04-19 10:32:04 +02:00
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
2021-02-22 09:03:03 +01:00
index a45390e8a8782dc4295515aab033ba93a3a5a34b..992e3e932133c60b448ec62a322e74ef8deb7599 100644
2020-04-19 10:32:04 +02:00
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
2021-02-21 20:45:33 +01:00
@@ -56,11 +56,12 @@ public abstract class PlayerList {
2020-04-24 11:33:33 +02:00
private static final SimpleDateFormat g = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
private final MinecraftServer server;
public final List<EntityPlayer> players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety
- private final Map<UUID, EntityPlayer> j = Maps.newHashMap();
+ private final Map<UUID, EntityPlayer> j = Maps.newHashMap();Map<UUID, EntityPlayer> getUUIDMap() { return j; } // Paper - OBFHELPER
private final GameProfileBanList k;
private final IpBanList l;
private final OpList operators;
private final WhiteList whitelist;
+ private final Map<UUID, EntityPlayer> pendingPlayers = Maps.newHashMap(); // Paper
// CraftBukkit start
// private final Map<UUID, ServerStatisticManager> o;
// private final Map<UUID, AdvancementDataPlayer> p;
2021-02-21 20:45:33 +01:00
@@ -99,6 +100,11 @@ public abstract class PlayerList {
2020-04-24 11:33:33 +02:00
}
public void a(NetworkManager networkmanager, EntityPlayer entityplayer) {
+ EntityPlayer prev = pendingPlayers.put(entityplayer.getUniqueID(), entityplayer);// Paper
+ if (prev != null) {
+ disconnectPendingPlayer(prev);
+ }
+ entityplayer.networkManager = networkmanager; // Paper
entityplayer.loginTime = System.currentTimeMillis(); // Paper
GameProfile gameprofile = entityplayer.getProfile();
UserCache usercache = this.server.getUserCache();
2021-02-21 20:45:33 +01:00
@@ -112,7 +118,7 @@ public abstract class PlayerList {
2020-04-24 11:33:33 +02:00
if (nbttagcompound != null && nbttagcompound.hasKey("bukkit")) {
NBTTagCompound bukkit = nbttagcompound.getCompound("bukkit");
s = bukkit.hasKeyOfType("lastKnownName", 8) ? bukkit.getString("lastKnownName") : s;
- }
+ }String lastKnownName = s; // Paper
// CraftBukkit end
2020-06-26 03:53:21 +02:00
if (nbttagcompound != null) {
2021-02-21 20:45:33 +01:00
@@ -185,6 +191,51 @@ public abstract class PlayerList {
2020-08-25 04:22:08 +02:00
entityplayer.getRecipeBook().a(entityplayer);
2020-06-26 03:53:21 +02:00
this.sendScoreboard(worldserver1.getScoreboard(), entityplayer);
2020-04-24 11:33:33 +02:00
this.server.invalidatePingSample();
2020-04-19 10:32:04 +02:00
+ // Paper start - async load spawn in chunk
2020-06-29 00:44:34 +02:00
+ WorldServer finalWorldserver = worldserver1;
2020-04-22 11:40:06 +02:00
+ int chunkX = loc.getBlockX() >> 4;
+ int chunkZ = loc.getBlockZ() >> 4;
2020-06-09 03:44:52 +02:00
+ final ChunkCoordIntPair pos = new ChunkCoordIntPair(chunkX, chunkZ);
2020-06-29 00:44:34 +02:00
+ PlayerChunkMap playerChunkMap = worldserver1.getChunkProvider().playerChunkMap;
2020-06-09 03:44:52 +02:00
+ playerChunkMap.chunkDistanceManager.addTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
2020-06-29 00:44:34 +02:00
+ worldserver1.getChunkProvider().tickDistanceManager();
+ worldserver1.getChunkProvider().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> {
2020-06-09 03:44:52 +02:00
+ PlayerChunk updatingChunk = playerChunkMap.getUpdatingChunk(pos.pair());
+ if (updatingChunk != null) {
+ return updatingChunk.getEntityTickingFuture();
+ } else {
2020-06-29 00:44:34 +02:00
+ return java.util.concurrent.CompletableFuture.completedFuture(chunk);
2020-06-09 03:44:52 +02:00
+ }
+ }).thenAccept(chunk -> {
2020-04-29 10:14:47 +02:00
+ playerconnection.playerJoinReady = () -> {
+ postChunkLoadJoin(
2020-06-29 00:44:34 +02:00
+ entityplayer, finalWorldserver, networkmanager, playerconnection,
2020-04-29 10:14:47 +02:00
+ nbttagcompound, networkmanager.getSocketAddress().toString(), lastKnownName
+ );
+ };
2020-06-09 03:44:52 +02:00
+ });
2020-04-24 11:33:33 +02:00
+ }
2020-04-22 11:40:06 +02:00
+
2020-04-24 11:33:33 +02:00
+ EntityPlayer getActivePlayer(UUID uuid) {
+ EntityPlayer player = this.getUUIDMap().get(uuid);
+ return player != null ? player : pendingPlayers.get(uuid);
2020-04-19 10:32:04 +02:00
+ }
2020-04-24 11:33:33 +02:00
+
+ void disconnectPendingPlayer(EntityPlayer entityplayer) {
+ ChatMessage msg = new ChatMessage("multiplayer.disconnect.duplicate_login", new Object[0]);
+ entityplayer.networkManager.sendPacket(new PacketPlayOutKickDisconnect(msg), (future) -> {
+ entityplayer.networkManager.close(msg);
+ entityplayer.networkManager = null;
+ });
+ }
+
2020-06-29 00:44:34 +02:00
+ private void postChunkLoadJoin(EntityPlayer entityplayer, WorldServer worldserver1, NetworkManager networkmanager, PlayerConnection playerconnection, NBTTagCompound nbttagcompound, String s1, String s) {
2020-04-24 11:33:33 +02:00
+ pendingPlayers.remove(entityplayer.getUniqueID(), entityplayer);
+ if (!networkmanager.isConnected()) {
2020-04-22 08:52:17 +02:00
+ return;
+ }
2020-04-22 11:40:06 +02:00
+ entityplayer.didPlayerJoinEvent = true;
2020-04-19 10:32:04 +02:00
+ // Paper end
2020-04-24 11:33:33 +02:00
ChatMessage chatmessage;
2020-04-22 08:52:17 +02:00
2020-04-24 11:33:33 +02:00
if (entityplayer.getProfile().getName().equalsIgnoreCase(s)) {
2021-02-22 09:03:03 +01:00
@@ -422,6 +473,7 @@ public abstract class PlayerList {
2020-04-22 11:40:06 +02:00
protected void savePlayerFile(EntityPlayer entityplayer) {
if (!entityplayer.getBukkitEntity().isPersistent()) return; // CraftBukkit
+ if (!entityplayer.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.playerFileData.save(entityplayer);
ServerStatisticManager serverstatisticmanager = (ServerStatisticManager) entityplayer.getStatisticManager(); // CraftBukkit
2021-02-22 09:03:03 +01:00
@@ -449,7 +501,7 @@ public abstract class PlayerList {
2020-10-01 15:16:32 +02:00
}
2020-04-22 11:40:06 +02:00
2021-02-21 20:45:33 +01:00
PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(cserver.getPlayer(entityplayer), net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW));
2020-04-22 11:40:06 +02:00
- cserver.getPluginManager().callEvent(playerQuitEvent);
+ if (entityplayer.didPlayerJoinEvent) cserver.getPluginManager().callEvent(playerQuitEvent); // Paper - if we disconnected before join ever fired, don't fire quit
entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
2020-06-24 02:25:28 +02:00
if (server.isMainThread()) entityplayer.playerTick(); // SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog)
2021-02-22 09:03:03 +01:00
@@ -502,6 +554,13 @@ public abstract class PlayerList {
2020-04-24 11:33:33 +02:00
// 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
2020-06-26 03:53:21 +02:00
// this.sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[]{entityplayer}));
2021-02-22 09:03:03 +01:00
@@ -519,7 +578,7 @@ public abstract class PlayerList {
2020-04-22 11:40:06 +02:00
cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity());
// CraftBukkit end
2021-02-21 20:45:33 +01:00
- return playerQuitEvent.quitMessage(); // Paper - Adventure
+ return entityplayer.didPlayerJoinEvent ? playerQuitEvent.quitMessage() : null; // CraftBukkit // Paper - Adventure // Paper - don't print quit if we never printed join
2020-04-22 11:40:06 +02:00
}
// CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer
2021-02-22 09:03:03 +01:00
@@ -538,6 +597,13 @@ public abstract class PlayerList {
2020-04-24 11:33:33 +02:00
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();
2020-06-09 03:44:52 +02:00
diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java
index 8055f5998213ab1c6c10d03d88d2b14d220a5e40..d7b9d9fd3a3b607278a3d72b0b306b0be2aa30ad 100644
--- a/src/main/java/net/minecraft/server/TicketType.java
+++ b/src/main/java/net/minecraft/server/TicketType.java
@@ -17,6 +17,7 @@ public class TicketType<T> {
public static final TicketType<ChunkCoordIntPair> FORCED = a("forced", Comparator.comparingLong(ChunkCoordIntPair::pair));
public static final TicketType<ChunkCoordIntPair> LIGHT = a("light", Comparator.comparingLong(ChunkCoordIntPair::pair));
public static final TicketType<BlockPosition> PORTAL = a("portal", BaseBlockPosition::compareTo, 300);
+ public static final TicketType<Long> LOGIN = a("login", Long::compareTo, 100); // Paper
public static final TicketType<Integer> POST_TELEPORT = a("post_teleport", Integer::compareTo, 5);
public static final TicketType<ChunkCoordIntPair> UNKNOWN = a("unknown", Comparator.comparingLong(ChunkCoordIntPair::pair), 1);
public static final TicketType<Unit> PLUGIN = a("plugin", (a, b) -> 0); // CraftBukkit