Paper/patches/server/0418-Load-Chunks-for-Login-Asynchronously.patch
Nassim Jahnke 17f71ac87b
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#5904)
Upstream has released updates that appear 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:
70d24eb8 SPIGOT-6587: Update documentation/error of drop chance API

CraftBukkit Changes:
470050ad SPIGOT-6587: Update documentation/error of drop chance API
1c39efa3 Fix Inventory#getViewers on the player inventory not returning the player first time their inventory is opened
d161627d Fix PrepareItemCraftEvent#isRepair
aa1fae73 SPIGOT-6586: EntityChangeBlockEvent for falling block does not cancel properly
8a04072e SPIGOT-6583: Throwing eggs doesn't make sounds

Spigot Changes:
f773da84 Remove redundant patch
cd367234 Rebuild patches
2021-06-20 21:25:59 +02:00

259 lines
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/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 5c2a19b054558d260ddb2f7282ea3b54a4dc2c03..68ef26c0b3124aa1bd37df9f762401b700a6b8ba 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<T> {
public static final TicketType<ChunkPos> FORCED = TicketType.create("forced", Comparator.comparingLong(ChunkPos::toLong));
public static final TicketType<ChunkPos> LIGHT = TicketType.create("light", Comparator.comparingLong(ChunkPos::toLong));
public static final TicketType<BlockPos> PORTAL = TicketType.create("portal", Vec3i::compareTo, 300);
+ public static final TicketType<Long> LOGIN = create("login", Long::compareTo, 100); // Paper
public static final TicketType<Integer> POST_TELEPORT = TicketType.create("post_teleport", Integer::compareTo, 5);
public static final TicketType<ChunkPos> UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1);
public static final TicketType<Unit> 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 aed93f66cdf8c5a97b04da59129b2ee1371816de..35b2c45991e2470fc565e7206f94c67f54fcdcd6 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 12f945e91827470a9a61951e45c062deee8cf281..195f5d1519c3fc2fdd03ecd0d49d7fba74037692 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 aad3f28147b5ae9f3a81ec9b500559ed222daf93..95f229dcb1b003215c602847a351b313fb0980ba 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;
@@ -133,6 +134,7 @@ public abstract class PlayerList {
private final IpBanList ipBans;
private final ServerOpList ops;
private final UserWhiteList whitelist;
+ private final Map<UUID, ServerPlayer> pendingPlayers = Maps.newHashMap(); // Paper
// CraftBukkit start
// private final Map<UUID, ServerStatisticManager> stats;
// private final Map<UUID, AdvancementDataPlayer> advancements;
@@ -171,6 +173,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();
@@ -184,7 +191,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) {
@@ -258,6 +265,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.getEntityTickingChunkFuture();
+ } 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.playersByUUID.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)) {
@@ -501,6 +554,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
@@ -528,7 +582,7 @@ public abstract class PlayerList {
}
PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(entityplayer.getBukkitEntity(), 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)
@@ -573,6 +627,13 @@ public abstract class PlayerList {
// this.advancements.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}));
@@ -590,7 +651,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
@@ -609,6 +670,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 1a3d6368b84aa24b6d0361dd30bc06b79f1d5133..4d2209baf88a3f097e0df1645f88d4afd718c414 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) {