mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-30 12:11:47 +01:00
Reduce entity tracker updates on move
This commit is contained in:
parent
de5b093c05
commit
6bf04cd5ed
1 changed files with 213 additions and 0 deletions
|
@ -0,0 +1,213 @@
|
|||
From 76a36ebf16641e3a4a047c4fa2c0befdf501abdf Mon Sep 17 00:00:00 2001
|
||||
From: froobynooby <froobynooby@froobworld.com>
|
||||
Date: Thu, 20 Feb 2020 15:50:49 +0930
|
||||
Subject: [PATCH] Reduce entity tracker updates on move
|
||||
|
||||
With this patch, for each player we keep track of a set of
|
||||
entities that the player is tracking. This is used to split
|
||||
the entity tracker update logic in the movePlayer method in
|
||||
PlayerChunkMap in to two parts:
|
||||
* Full update: Run through all entity trackers and update them
|
||||
* Partial update: Run through all entity trackers for entities
|
||||
the player is already tracking and update them
|
||||
|
||||
Partial updates will always take less time than full updates,
|
||||
usually by a considerable amount if players and entities are
|
||||
spread out over the map. Assuming they are evenly spread,
|
||||
and given there are x many players, it would be expected to
|
||||
take 1/x the time of a full update.
|
||||
|
||||
Full updates are only run if the following conditions are met:
|
||||
* It has been 20 ticks since the last full update
|
||||
* The player has moved over set distance since the last full
|
||||
update (distance is configurable)
|
||||
|
||||
The motivation for the first condition is that the client
|
||||
sends the server its position once a second, which calls
|
||||
movePlayer, so at a minimum we want to be sending the player
|
||||
an updated set of entities it can see every second.
|
||||
|
||||
The motivation for the second condition is that looping
|
||||
through every entity in world to check if it is now within
|
||||
the tracking range after the player has moved 0.1 blocks is
|
||||
largely unnecessary. Checking only after the player has moved
|
||||
1 or 2 blocks is far better for performance, and very unlikely
|
||||
to give any noticeable side effects.
|
||||
|
||||
In testing, this has reduced the time taken for movement
|
||||
packet processing by up to 4x. Packet processing for movement
|
||||
packets often show up as a major contributor to TPS loss in
|
||||
servers with large player counts
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
index 7ca67a4aa5..e93176d8f2 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
@@ -668,4 +668,9 @@ public class PaperWorldConfig {
|
||||
private void zombieVillagerInfectionChance() {
|
||||
zombieVillagerInfectionChance = getDouble("zombie-villager-infection-chance", zombieVillagerInfectionChance);
|
||||
}
|
||||
+
|
||||
+ public double trackerUpdateDistance = 1;
|
||||
+ private void trackerUpdateDistance() {
|
||||
+ trackerUpdateDistance = getDouble("tracker-update-distance", trackerUpdateDistance);
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
|
||||
index e7bfbc3307..43774bc9a5 100644
|
||||
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
|
||||
@@ -86,6 +86,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
public final int[] mobCounts = new int[ENUMCREATURETYPE_TOTAL_ENUMS]; // Paper
|
||||
public final com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> cachedSingleMobDistanceMap;
|
||||
// Paper end
|
||||
+ // Paper start - Reduce entity tracker updates on move
|
||||
+ public Vec3D lastTrackedPosition = new Vec3D(0, 0, 0);
|
||||
+ public long lastTrackedTick;
|
||||
+ // Paper end
|
||||
|
||||
// CraftBukkit start
|
||||
public String displayName;
|
||||
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||
index 57bea926a6..91f4b70117 100644
|
||||
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||
@@ -133,6 +133,39 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
}
|
||||
|
||||
|
||||
+ // Paper end
|
||||
+
|
||||
+ // Paper start - Reduce entity tracker updates on move
|
||||
+ private double trackerUpdateDistanceSquared;
|
||||
+ private final Int2ObjectMap<Int2ObjectMap<PlayerChunkMap.EntityTracker>> playerTrackedEntities = new Int2ObjectOpenHashMap<>();
|
||||
+ private final Int2ObjectMap<Queue<Integer>> playerTrackedEntitiesRemoveQueue = new Int2ObjectOpenHashMap<>();
|
||||
+
|
||||
+ void flushRemoveQueue(EntityPlayer entityplayer) {
|
||||
+ Queue<Integer> removeQueue = getPlayerTrackedEntityMapRemoveQueue(entityplayer.getId());
|
||||
+ Int2ObjectMap<PlayerChunkMap.EntityTracker> entityMap = getPlayerTrackedEntityMap(entityplayer.getId());
|
||||
+ for (Integer id = removeQueue.poll(); id != null; id = removeQueue.poll()) {
|
||||
+ entityMap.remove(id);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ void flushRemoveQueues() {
|
||||
+ for (Int2ObjectMap.Entry<Queue<Integer>> entry : playerTrackedEntitiesRemoveQueue.int2ObjectEntrySet()) {
|
||||
+ Int2ObjectMap<EntityTracker> entityMap = getPlayerTrackedEntityMap(entry.getKey());
|
||||
+ Queue<Integer> removeQueue = entry.getValue();
|
||||
+ for (Integer id = removeQueue.poll(); id != null; id = removeQueue.poll()) {
|
||||
+ entityMap.remove(id);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Int2ObjectMap<EntityTracker> getPlayerTrackedEntityMap(int id) {
|
||||
+ return playerTrackedEntities.computeIfAbsent(id, i -> new Int2ObjectOpenHashMap<>());
|
||||
+ }
|
||||
+
|
||||
+ Queue<Integer> getPlayerTrackedEntityMapRemoveQueue(int id) {
|
||||
+ return playerTrackedEntitiesRemoveQueue.computeIfAbsent(id, i -> new java.util.ArrayDeque<>());
|
||||
+ }
|
||||
+
|
||||
// Paper end
|
||||
|
||||
public PlayerChunkMap(WorldServer worldserver, File file, DataFixer datafixer, DefinedStructureManager definedstructuremanager, Executor executor, IAsyncTaskHandler<Runnable> iasynctaskhandler, ILightAccess ilightaccess, ChunkGenerator<?> chunkgenerator, WorldLoadListener worldloadlistener, Supplier<WorldPersistentData> supplier, int i) {
|
||||
@@ -167,6 +200,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
this.m = new VillagePlace(new File(this.w, "poi"), datafixer, this.world); // Paper
|
||||
this.setViewDistance(i);
|
||||
this.playerMobDistanceMap = this.world.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper
|
||||
+ this.trackerUpdateDistanceSquared = Math.pow(this.world.paperConfig.trackerUpdateDistance, 2); // Paper
|
||||
}
|
||||
|
||||
public void updatePlayerMobTypeMap(Entity entity) {
|
||||
@@ -1334,8 +1368,19 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
}
|
||||
|
||||
public void movePlayer(EntityPlayer entityplayer) {
|
||||
- ObjectIterator objectiterator = this.trackedEntities.values().iterator();
|
||||
+ // Paper start
|
||||
+ // ObjectIterator objectiterator = this.trackedEntities.values().iterator();
|
||||
+ ObjectIterator objectiterator;
|
||||
|
||||
+ if (MinecraftServer.currentTick - entityplayer.lastTrackedTick >= 20
|
||||
+ || entityplayer.lastTrackedPosition.distanceSquared(entityplayer.getPositionVector()) >= trackerUpdateDistanceSquared) {
|
||||
+ entityplayer.lastTrackedPosition = entityplayer.getPositionVector();
|
||||
+ entityplayer.lastTrackedTick = MinecraftServer.currentTick;
|
||||
+ objectiterator = this.trackedEntities.values().iterator(); // Update all entity trackers
|
||||
+ } else {
|
||||
+ objectiterator = getPlayerTrackedEntityMap(entityplayer.getId()).values().iterator(); // Only update entity trackers for already tracked entities
|
||||
+ }
|
||||
+ // Paper end
|
||||
while (objectiterator.hasNext()) {
|
||||
PlayerChunkMap.EntityTracker playerchunkmap_entitytracker = (PlayerChunkMap.EntityTracker) objectiterator.next();
|
||||
|
||||
@@ -1345,6 +1390,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
playerchunkmap_entitytracker.updatePlayer(entityplayer);
|
||||
}
|
||||
}
|
||||
+ flushRemoveQueues(); // Paper
|
||||
|
||||
int i = MathHelper.floor(entityplayer.locX()) >> 4;
|
||||
int j = MathHelper.floor(entityplayer.locZ()) >> 4;
|
||||
@@ -1486,12 +1532,21 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
|
||||
playerchunkmap_entitytracker.clear(entityplayer);
|
||||
}
|
||||
+ // Paper start
|
||||
+ playerTrackedEntities.remove(entityplayer.getId());
|
||||
+ playerTrackedEntitiesRemoveQueue.remove(entityplayer.getId());
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
PlayerChunkMap.EntityTracker playerchunkmap_entitytracker1 = (PlayerChunkMap.EntityTracker) this.trackedEntities.remove(entity.getId());
|
||||
|
||||
if (playerchunkmap_entitytracker1 != null) {
|
||||
playerchunkmap_entitytracker1.a();
|
||||
+ // Paper start
|
||||
+ for (EntityPlayer player : playerchunkmap_entitytracker1.trackedPlayers) {
|
||||
+ getPlayerTrackedEntityMap(player.getId()).remove(playerchunkmap_entitytracker1.tracker.getId());
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
entity.tracker = null; // Paper - We're no longer tracked
|
||||
}
|
||||
@@ -1532,7 +1587,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
}
|
||||
world.timings.tracker2.stopTiming(); // Paper
|
||||
}
|
||||
-
|
||||
+ flushRemoveQueues(); // Paper
|
||||
|
||||
}
|
||||
|
||||
@@ -1581,6 +1636,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
}
|
||||
}
|
||||
}
|
||||
+ flushRemoveQueue(entityplayer); // Paper
|
||||
|
||||
Iterator iterator;
|
||||
Entity entity1;
|
||||
@@ -1677,6 +1733,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
|
||||
if (this.trackedPlayers.remove(entityplayer)) {
|
||||
this.trackerEntry.a(entityplayer);
|
||||
+ getPlayerTrackedEntityMap(entityplayer.getId()).remove(this.tracker.getId()); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1713,9 +1770,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
||||
|
||||
if (flag1 && this.trackedPlayerMap.putIfAbsent(entityplayer, true) == null) { // Paper
|
||||
this.trackerEntry.b(entityplayer);
|
||||
+ getPlayerTrackedEntityMap(entityplayer.getId()).put(this.tracker.getId(), this); // Paper
|
||||
}
|
||||
} else if (this.trackedPlayers.remove(entityplayer)) {
|
||||
this.trackerEntry.a(entityplayer);
|
||||
+ getPlayerTrackedEntityMapRemoveQueue(entityplayer.getId()).add(this.tracker.getId()); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
--
|
||||
2.26.0
|
||||
|
Loading…
Reference in a new issue