Paper/Spigot-Server-Patches/0486-Load-Chunks-for-Login-Asynchronously.patch
Aikar 57dd397155
Updated Upstream (Bukkit/CraftBukkit)
Upstream has released updates that appears to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
b999860d SPIGOT-2304: Add LootGenerateEvent

CraftBukkit Changes:
77fd87e4 SPIGOT-2304: Implement LootGenerateEvent
a1a705ee SPIGOT-5566: Doused campfires & fires should call EntityChangeBlockEvent
41712edd SPIGOT-5707: PersistentDataHolder not Persistent on API dropped Item
2020-05-01 18:03:57 -04:00

255 lines
14 KiB
Diff

From 8a70af59fcb23c04a33296165224d604fdbf692e Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 324fd07bce..01330045c0 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -1382,7 +1382,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
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) {
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
index a48e113b53..0f9bca8b8b 100644
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
@@ -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
@@ -105,6 +106,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
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/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java
index f1222fcb2b..28f48f2252 100644
--- 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;
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
index 2e81f3a1d9..8800a8fcf9 100644
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
@@ -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
@@ -142,6 +143,17 @@ public class PlayerConnection implements PacketListenerPlayIn {
// 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) {
+ return;
+ }
+ // Paper end
this.syncPosition();
this.player.lastX = this.player.locX();
this.player.lastY = this.player.locY();
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index ec45c30dd3..edf9df8c8a 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
@@ -52,11 +52,12 @@ public abstract class PlayerList {
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;
@@ -94,6 +95,11 @@ public abstract class PlayerList {
}
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();
@@ -107,7 +113,7 @@ public abstract class PlayerList {
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
@@ -158,6 +164,50 @@ public abstract class PlayerList {
entityplayer.B().a(entityplayer);
this.sendScoreboard(worldserver.getScoreboard(), entityplayer);
this.server.invalidatePingSample();
+ // Paper start - async load spawn in chunk
+ WorldServer finalWorldserver = worldserver;
+ int chunkX = loc.getBlockX() >> 4;
+ int chunkZ = loc.getBlockZ() >> 4;
+ worldserver.getChunkProvider().getTickingChunkAsync(chunkX, chunkZ, (chunk -> { // use ticking - as it has at least 1 neighbours loaded
+ playerconnection.playerJoinReady = () -> {
+ postChunkLoadJoin(
+ entityplayer, finalWorldserver, networkmanager, playerconnection,
+ nbttagcompound, networkmanager.getSocketAddress().toString(), lastKnownName
+ );
+ };
+ }));
+ // boost the priorities
+ worldserver.asyncChunkTaskManager.raisePriority(chunkX, chunkZ, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY);
+ for (int cx = -1; cx <= 1; cx++) {
+ for (int cz = -1; cz <= 1; cz++) {
+ if (cx == 0 && cz == 0) continue;
+ // we have to directly request it otherwise the task won't be started yet to boost priority
+ worldserver.getChunkProvider().getFullChunkAsync(chunkX + cx, chunkZ + cz, null);
+ worldserver.asyncChunkTaskManager.raisePriority(chunkX + cx, chunkZ + cz, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY);
+ }
+ }
+ }
+
+ EntityPlayer getActivePlayer(UUID uuid) {
+ EntityPlayer player = this.getUUIDMap().get(uuid);
+ return player != null ? player : pendingPlayers.get(uuid);
+ }
+
+ 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()) {
+ return;
+ }
+ entityplayer.didPlayerJoinEvent = true;
+ // Paper end
ChatMessage chatmessage;
if (entityplayer.getProfile().getName().equalsIgnoreCase(s)) {
@@ -216,7 +266,6 @@ public abstract class PlayerList {
}
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
@@ -243,6 +292,7 @@ public abstract class PlayerList {
}
// 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) {
@@ -388,6 +438,7 @@ public abstract class PlayerList {
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
@@ -412,7 +463,7 @@ public abstract class PlayerList {
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)
@@ -464,6 +515,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(EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[]{entityplayer}));
@@ -481,7 +539,7 @@ public abstract class PlayerList {
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
@@ -500,6 +558,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();
--
2.26.2