Mirror von
https://github.com/PaperMC/Paper.git
synchronisiert 2024-11-15 20:40:07 +01:00
614a664bd3
Mark chunks that are blocking main thread for world generation as urgent Implements a general priority system so that chunks that are sorted in the generator queues can prioritize certain chunks over another. Urgent chunks will jump to the front of the line, ensuring that a sync chunk load on an ungenerated chunk does not lag the server for a long period of time if the servers generator queues are filled with lots of chunks already. This massively reduces the lag spikes from sync chunk gens. Then we further prioritize loading order so nearby chunks have higher priority than distant chunks, reducing the pressure a high no tick view distance holds on you. Chunks in front of the player have higher priority, to help with fast traveling players keep up with their movement. This commit also improves single core cpu scenarios in that we will now automatically disable Async Chunks as well as Minecrafts thread pool. It is never recommended to use async chunks on a single CPU as context switching will be slower than just running it all on main. This also bumps the number of server worker threads by default too. Mojang does not utilize the workers in an effecient manner, resulting in them using barely any sustained CPU. So give it more workers so more chunks can be processed concurrently This change also improves urgent chunk loading, so players flying into unloaded chunks will hurt a little bit less (but still hurt) Ping #3395 #3363 (Not marking as closed, we need to make prevent moving work)
259 Zeilen
14 KiB
Diff
259 Zeilen
14 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/Entity.java b/src/main/java/net/minecraft/server/Entity.java
|
|
index f20f798f0f7bb765ffdab8672f4bf77a60fa52d2..9eab570e48817e18d10ddde95b3f80f7e4ea4766 100644
|
|
--- a/src/main/java/net/minecraft/server/Entity.java
|
|
+++ b/src/main/java/net/minecraft/server/Entity.java
|
|
@@ -1384,7 +1384,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 a87aa07b17205b52e85f7d082fa4d5169771cbb4..79c2187b7383336e7574709e6d4ad805e557976f 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
|
|
@@ -101,6 +102,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 f1222fcb2bd52b8781d0f92c94e1472fa7b1e493..28f48f22522ef8c3c66381abcf017f0859b45290 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 1673e5b8f13978cd4a47555b9806d801cbbdf3c8..56bace816f5ea5b8b837df7e7707796f68b76f37 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 40f5d9fa9069a330b6999eefa50015daa4c19217..60af90bf8c376ab8ab61b16ae38886149faa88cc 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
|
|
|
|
@@ -160,6 +166,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)) {
|
|
@@ -218,7 +268,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
|
|
@@ -245,6 +294,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) {
|
|
@@ -390,6 +440,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
|
|
|
|
@@ -414,7 +465,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)
|
|
@@ -466,6 +517,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}));
|
|
@@ -483,7 +541,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
|
|
@@ -502,6 +560,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();
|
|
|