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-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
2020-05-14 10:58:47 +02:00
index f20f798f0f7bb765ffdab8672f4bf77a60fa52d2..9eab570e48817e18d10ddde95b3f80f7e4ea4766 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-05-14 10:58:47 +02:00
@@ -1384,7 +1384,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
2020-04-22 08:52:17 +02:00
this.pitch = MathHelper.a(f1, -90.0F, 90.0F) % 360.0F;
this.lastYaw = this.yaw;
this.lastPitch = this.pitch;
- 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
}
public void setPositionRotation(BlockPosition blockposition, float f, float f1) {
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
2020-05-19 10:01:53 +02:00
index a87aa07b17205b52e85f7d082fa4d5169771cbb4..79c2187b7383336e7574709e6d4ad805e557976f 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
2020-04-24 11:33:33 +02:00
@@ -43,6 +43,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
private static final Logger LOGGER = LogManager.getLogger();
public String locale = null; // CraftBukkit - lowercase // Paper - default to null
public PlayerConnection playerConnection;
+ public NetworkManager networkManager; // Paper
public final MinecraftServer server;
public final PlayerInteractManager playerInteractManager;
public final Deque<Integer> removeQueue = new ArrayDeque<>(); // Paper
2020-05-06 09:44:47 +02:00
@@ -101,6 +102,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
2020-05-06 11:48:49 +02:00
index f1222fcb2bd52b8781d0f92c94e1472fa7b1e493..28f48f22522ef8c3c66381abcf017f0859b45290 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
@@ -66,7 +66,7 @@ public class LoginListener implements PacketLoginInListener {
}
// Paper end
} else if (this.g == LoginListener.EnumProtocolState.DELAY_ACCEPT) {
- EntityPlayer entityplayer = this.server.getPlayerList().a(this.i.getId());
+ EntityPlayer entityplayer = this.server.getPlayerList().getActivePlayer(this.i.getId()); // Paper
if (entityplayer == null) {
this.g = LoginListener.EnumProtocolState.READY_TO_ACCEPT;
@@ -165,7 +165,7 @@ public class LoginListener implements PacketLoginInListener {
}
this.networkManager.sendPacket(new PacketLoginOutSuccess(this.i));
- EntityPlayer entityplayer = this.server.getPlayerList().a(this.i.getId());
+ 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
2020-06-09 03:44:52 +02:00
index 4be93d12dbe12511628fd97af52d5cf78da17eaa..6dd4303c1c211ac4b0bb542ea96cc150581bf8c1 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
2020-04-29 10:14:47 +02:00
@@ -69,6 +69,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
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
2020-05-07 01:30:47 +02:00
@@ -142,6 +143,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();
2020-05-07 01:30:47 +02:00
@@ -183,7 +193,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
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
2020-06-09 03:44:52 +02:00
index 1c7aac029ae01afa127ca386278a4ff8520e3674..e11254d9e5a64ad02517e87d5c72a973affedfd9 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
2020-06-09 03:44:52 +02:00
@@ -16,6 +16,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -52,11 +53,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;
2020-06-09 03:44:52 +02:00
@@ -94,6 +96,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();
2020-06-09 03:44:52 +02:00
@@ -107,7 +114,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
if (nbttagcompound == null) entityplayer.moveToSpawn(worldserver); // Paper - only move to spawn on first login, otherwise, stay where you are....
// CraftBukkit end
2020-06-09 03:44:52 +02:00
@@ -160,6 +167,52 @@ public abstract class PlayerList {
2020-04-24 11:33:33 +02:00
entityplayer.B().a(entityplayer);
this.sendScoreboard(worldserver.getScoreboard(), entityplayer);
this.server.invalidatePingSample();
2020-04-19 10:32:04 +02:00
+ // Paper start - async load spawn in chunk
+ WorldServer finalWorldserver = worldserver;
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);
+ PlayerChunkMap playerChunkMap = finalWorldserver.getChunkProvider().playerChunkMap;
+ playerChunkMap.chunkDistanceManager.addTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
+ worldserver.getChunkProvider().tickDistanceManager();
+ worldserver.getChunkProvider().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> {
+ PlayerChunk updatingChunk = playerChunkMap.getUpdatingChunk(pos.pair());
+ if (updatingChunk != null) {
+ return updatingChunk.getEntityTickingFuture();
+ } else {
+ return CompletableFuture.completedFuture(chunk);
+ }
+ }).thenAccept(chunk -> {
2020-04-29 10:14:47 +02:00
+ playerconnection.playerJoinReady = () -> {
+ postChunkLoadJoin(
+ entityplayer, finalWorldserver, networkmanager, playerconnection,
+ nbttagcompound, networkmanager.getSocketAddress().toString(), lastKnownName
+ );
2020-06-09 03:44:52 +02:00
+ playerChunkMap.chunkDistanceManager.removeTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
2020-04-29 10:14:47 +02:00
+ };
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;
+ });
+ }
+
+ private void postChunkLoadJoin(EntityPlayer entityplayer, WorldServer worldserver, NetworkManager networkmanager, PlayerConnection playerconnection, NBTTagCompound nbttagcompound, String s1, String s) {
+ 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)) {
2020-06-09 03:44:52 +02:00
@@ -218,7 +271,6 @@ public abstract class PlayerList {
2020-04-29 10:14:47 +02:00
}
entityplayer.sentListPacket = true;
entityplayer.supressTrackerForLogin = false; // Paper
- ((WorldServer)entityplayer.world).getChunkProvider().playerChunkMap.addEntity(entityplayer); // Paper - track entity now
// CraftBukkit end
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(entityplayer.getId(), entityplayer.datawatcher, true)); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
2020-06-09 03:44:52 +02:00
@@ -245,6 +297,7 @@ public abstract class PlayerList {
2020-04-29 10:14:47 +02:00
}
// Paper start - move vehicle into method so it can be called above - short circuit around that code
+ ((WorldServer)entityplayer.world).getChunkProvider().playerChunkMap.addEntity(entityplayer); // track entity now
onPlayerJoinFinish(entityplayer, worldserver, s1);
}
private void mountSavedVehicle(EntityPlayer entityplayer, WorldServer worldserver, NBTTagCompound nbttagcompound) {
2020-06-09 03:44:52 +02:00
@@ -390,6 +443,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
2020-06-09 03:44:52 +02:00
@@ -414,7 +468,7 @@ public abstract class PlayerList {
2020-04-22 11:40:06 +02:00
org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(entityplayer, org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT); // Paper
PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(cserver.getPlayer(entityplayer), "\u00A7e" + entityplayer.getName() + " left the game");
- 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());
if (server.isMainThread()) entityplayer.playerTick();// SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog)
2020-06-09 03:44:52 +02:00
@@ -466,6 +520,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
// this.sendAll(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[]{entityplayer}));
2020-06-09 03:44:52 +02:00
@@ -483,7 +544,7 @@ public abstract class PlayerList {
2020-04-22 11:40:06 +02:00
cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity());
// CraftBukkit end
- return playerQuitEvent.getQuitMessage(); // CraftBukkit
+ return entityplayer.didPlayerJoinEvent ? playerQuitEvent.getQuitMessage() : null; // CraftBukkit // Paper - don't print quit if we never printed join
}
// CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer
2020-06-09 03:44:52 +02:00
@@ -502,6 +563,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