Paper/Spigot-Server-Patches/0463-Load-Chunks-for-Login-Asynchronously.patch
Aikar ab347c4c96
Updated Upstream (Bukkit/CraftBukkit/Spigot)
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:
42d5a714 SPIGOT-5899: Hoglins API similar to Piglins
2c1ee10e SPIGOT-5887: ClickType doesn't include off hand swaps
5ff7c7ce SPIGOT-5886: Missing BlockData

CraftBukkit Changes:
7560f5f5 SPIGOT-5905: Fix hex colours not being allowed in MOTD
d47c47ee SPIGOT-5889: Villager using composter should call EntityChangeBlockEvent
2fe6b4a3 SPIGOT-5899: Hoglins API similar to Piglins
e09dbeca SPIGOT-5887: ClickType doesn't include off hand swaps
23aac2a5 SPIGOT-5903: EntityDismountEvent cannot be triggered asynchronously
92cbf656 SPIGOT-5884: Tab completions lost on reloadData / minecraft:reload
fb4e54ad SPIGOT-5902: PlayerRespawnEvent places player at spawn before event is called
aa8f3d5a SPIGOT-5901: Structures are generated in all worlds based on the setting for the main world
a0c35937 SPIGOT-5895: PlayerChangedWorldEvent#getFrom is incorrect
89c0a5c3 SPIGOT-5886: Missing BlockData

Spigot Changes:
0287a20d SPIGOT-5903: EntityDismountEvent cannot be triggered asynchronously
2020-06-30 01:20:29 -04:00

269 Zeilen
15 KiB
Diff

From 0000000000000000000000000000000000000000 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/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 0080a0cbe58d1a81c434ef97659428c8bf1ec290..eebd4c50a7324250d3ebe7060739a71af4243f72 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -604,7 +604,7 @@ public class ChunkProviderServer extends IChunkProvider {
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();
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index a0249d7a60c1381ebd7131d735a73fa2e9250543..561610ee1a99c781ccabd600812a0d10cd4ba785 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -1326,7 +1326,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 c(Vec3D vec3d) {
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
index b90d5fdd58ede1cf57e36b6f65ce259cf55615ba..658f5d8515db84a55d9cea092163feac596ea956 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 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 50163f7291474062bde81631c9220e8a9f5bb718..ebab6961b752ca0b1c1b28f85abb33c7b4d70e3c 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().getPlayer(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().getPlayer(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 6faa8764d19f3e58808de9dc84145f765c8958a8..22ded747e9e8683faa17924149f9b279752e47ea 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,15 @@ 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) {
+ // Paper end
this.syncPosition();
this.player.lastX = this.player.locX();
this.player.lastY = this.player.locY();
@@ -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
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index 18272d56bdac0ff76092d8d834c53474516732a8..045d85016edff8e1e8c775f8c701ac806fb3d4e1 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
@@ -54,11 +54,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;
@@ -97,6 +98,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();
@@ -110,7 +116,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
// CraftBukkit end
if (nbttagcompound != null) {
@@ -183,6 +189,51 @@ public abstract class PlayerList {
entityplayer.B().a(entityplayer);
this.sendScoreboard(worldserver1.getScoreboard(), entityplayer);
this.server.invalidatePingSample();
+ // Paper start - async load spawn in chunk
+ WorldServer finalWorldserver = worldserver1;
+ int chunkX = loc.getBlockX() >> 4;
+ int chunkZ = loc.getBlockZ() >> 4;
+ final ChunkCoordIntPair pos = new ChunkCoordIntPair(chunkX, chunkZ);
+ PlayerChunkMap playerChunkMap = worldserver1.getChunkProvider().playerChunkMap;
+ playerChunkMap.chunkDistanceManager.addTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
+ worldserver1.getChunkProvider().tickDistanceManager();
+ worldserver1.getChunkProvider().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> {
+ PlayerChunk updatingChunk = playerChunkMap.getUpdatingChunk(pos.pair());
+ if (updatingChunk != null) {
+ return updatingChunk.getEntityTickingFuture();
+ } else {
+ return java.util.concurrent.CompletableFuture.completedFuture(chunk);
+ }
+ }).thenAccept(chunk -> {
+ playerconnection.playerJoinReady = () -> {
+ postChunkLoadJoin(
+ entityplayer, finalWorldserver, networkmanager, playerconnection,
+ nbttagcompound, networkmanager.getSocketAddress().toString(), lastKnownName
+ );
+ };
+ });
+ }
+
+ 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 worldserver1, 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)) {
@@ -419,6 +470,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
@@ -443,7 +495,7 @@ public abstract class PlayerList {
entityplayer.closeInventory(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)
@@ -496,6 +548,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}));
@@ -513,7 +572,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
@@ -532,6 +591,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/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