PaperMC/Spigot-Server-Patches/0502-Use-distance-map-to-optimise-entity-tracker.patch
2020-05-06 11:31:29 -05:00

366 lines
18 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 5 May 2020 20:18:05 -0700
Subject: [PATCH] Use distance map to optimise entity tracker
Use the distance map to find candidate players for tracking.
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 14ba037c1dc042f81123b3f31ad5c3485e59b773..8820e4b6506f645b98d23fe6eab044cc6cd54427 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -244,6 +244,21 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
}
// Paper end
+ // Paper start - optimise entity tracking
+ final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this);
+
+ boolean isLegacyTrackingEntity = false;
+
+ public final void setLegacyTrackingEntity(final boolean isLegacyTrackingEntity) {
+ this.isLegacyTrackingEntity = isLegacyTrackingEntity;
+ }
+
+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> getPlayersInTrackRange() {
+ return ((WorldServer)this.world).getChunkProvider().playerChunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()]
+ .getObjectsInRange(MCUtil.getCoordinateKey(this));
+ }
+ // Paper end - optimise entity tracking
+
public Entity(EntityTypes<?> entitytypes, World world) {
this.id = Entity.entityCount.incrementAndGet();
this.passengers = Lists.newArrayList();
diff --git a/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/src/main/java/net/minecraft/server/EntityTrackerEntry.java
index 3a88c9a67062eb73ad8257ea786efca7e7e99f65..6d3b34ead9cc95dcc1152dffa8c6c4a8c7f1d58b 100644
--- a/src/main/java/net/minecraft/server/EntityTrackerEntry.java
+++ b/src/main/java/net/minecraft/server/EntityTrackerEntry.java
@@ -70,6 +70,7 @@ public class EntityTrackerEntry {
this.r = entity.onGround;
}
+ public final void tick() { this.a(); } // Paper - OBFHELPER
public void a() {
List<Entity> list = this.tracker.getPassengers();
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b634c40e8 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -120,21 +120,51 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
// Paper start - distance maps
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<EntityPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
+ // Paper start - use distance map to optimise tracker
+ public static boolean isLegacyTrackingEntity(Entity entity) {
+ return entity.isLegacyTrackingEntity;
+ }
+
+ // inlined EnumMap, TrackingRange.TrackingRangeType
+ static final org.spigotmc.TrackingRange.TrackingRangeType[] TRACKING_RANGE_TYPES = org.spigotmc.TrackingRange.TrackingRangeType.values();
+ final com.destroystokyo.paper.util.misc.PlayerAreaMap[] playerEntityTrackerTrackMaps;
+ final int[] entityTrackerTrackRanges;
+ // Paper end - use distance map to optimise tracker
void addPlayerToDistanceMaps(EntityPlayer player) {
int chunkX = MCUtil.getChunkCoordinate(player.locX());
int chunkZ = MCUtil.getChunkCoordinate(player.locZ());
// Note: players need to be explicitly added to distance maps before they can be updated
+ // Paper start - use distance map to optimise entity tracker
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
+ int trackRange = this.entityTrackerTrackRanges[i];
+
+ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance()));
+ }
+ // Paper end - use distance map to optimise entity tracker
}
void removePlayerFromDistanceMaps(EntityPlayer player) {
-
+ // Paper start - use distance map to optimise tracker
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
+ this.playerEntityTrackerTrackMaps[i].remove(player);
+ }
+ // Paper end - use distance map to optimise tracker
}
void updateMaps(EntityPlayer player) {
int chunkX = MCUtil.getChunkCoordinate(player.locX());
int chunkZ = MCUtil.getChunkCoordinate(player.locZ());
// Note: players need to be explicitly added to distance maps before they can be updated
+ // Paper start - use distance map to optimise entity tracker
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
+ int trackRange = this.entityTrackerTrackRanges[i];
+
+ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance()));
+ }
+ // Paper end - use distance map to optimise entity tracker
}
@@ -172,6 +202,44 @@ 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
+ // Paper start - use distance map to optimise entity tracker
+ this.playerEntityTrackerTrackMaps = new com.destroystokyo.paper.util.misc.PlayerAreaMap[TRACKING_RANGE_TYPES.length];
+ this.entityTrackerTrackRanges = new int[TRACKING_RANGE_TYPES.length];
+
+ org.spigotmc.SpigotWorldConfig spigotWorldConfig = this.world.spigotConfig;
+
+ for (int ordinal = 0, len = TRACKING_RANGE_TYPES.length; ordinal < len; ++ordinal) {
+ org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = TRACKING_RANGE_TYPES[ordinal];
+ int configuredSpigotValue;
+ switch (trackingRangeType) {
+ case PLAYER:
+ configuredSpigotValue = spigotWorldConfig.playerTrackingRange;
+ break;
+ case ANIMAL:
+ configuredSpigotValue = spigotWorldConfig.animalTrackingRange;
+ break;
+ case MONSTER:
+ configuredSpigotValue = spigotWorldConfig.monsterTrackingRange;
+ break;
+ case MISC:
+ configuredSpigotValue = spigotWorldConfig.miscTrackingRange;
+ break;
+ case OTHER:
+ configuredSpigotValue = spigotWorldConfig.otherTrackingRange;
+ break;
+ case ENDERDRAGON:
+ configuredSpigotValue = 10 * 16; // default is 10 chunk range // TODO check on update
+ break;
+ default:
+ throw new IllegalStateException("Missing case for enum " + trackingRangeType);
+ }
+
+ int trackRange = (configuredSpigotValue >>> 4) + ((configuredSpigotValue & 15) != 0 ? 1 : 0);
+ this.entityTrackerTrackRanges[ordinal] = trackRange;
+
+ this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
+ }
+ // Paper end - use distance map to optimise entity tracker
}
public void updatePlayerMobTypeMap(Entity entity) {
@@ -1423,17 +1491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
}
public void movePlayer(EntityPlayer entityplayer) {
- ObjectIterator objectiterator = this.trackedEntities.values().iterator();
-
- while (objectiterator.hasNext()) {
- PlayerChunkMap.EntityTracker playerchunkmap_entitytracker = (PlayerChunkMap.EntityTracker) objectiterator.next();
-
- if (playerchunkmap_entitytracker.tracker == entityplayer) {
- playerchunkmap_entitytracker.track(this.world.getPlayers());
- } else {
- playerchunkmap_entitytracker.updatePlayer(entityplayer);
- }
- }
+ // Paper - delay this logic for the entity tracker tick, no need to duplicate it
int i = MathHelper.floor(entityplayer.locX()) >> 4;
int j = MathHelper.floor(entityplayer.locZ()) >> 4;
@@ -1550,7 +1608,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker
this.trackedEntities.put(entity.getId(), playerchunkmap_entitytracker);
- playerchunkmap_entitytracker.track(this.world.getPlayers());
+ playerchunkmap_entitytracker.updatePlayers(entity.getPlayersInTrackRange()); // Paper - don't search all players
if (entity instanceof EntityPlayer) {
EntityPlayer entityplayer = (EntityPlayer) entity;
@@ -1594,7 +1652,37 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
entity.tracker = null; // Paper - We're no longer tracked
}
+ // Paper start - optimised tracker
+ private final void processTrackQueue() {
+ this.world.timings.tracker1.startTiming();
+ try {
+ for (EntityTracker tracker : this.trackedEntities.values()) {
+ // update tracker entry
+ tracker.updatePlayers(tracker.tracker.getPlayersInTrackRange());
+ }
+ } finally {
+ this.world.timings.tracker1.stopTiming();
+ }
+
+
+ this.world.timings.tracker2.startTiming();
+ try {
+ for (EntityTracker tracker : this.trackedEntities.values()) {
+ tracker.trackerEntry.tick();
+ }
+ } finally {
+ this.world.timings.tracker2.stopTiming();
+ }
+ }
+ // Paper end - optimised tracker
+
protected void g() {
+ // Paper start - optimized tracker
+ if (true) {
+ this.processTrackQueue();
+ return;
+ }
+ // Paper end - optimized tracker
List<EntityPlayer> list = Lists.newArrayList();
List<EntityPlayer> list1 = this.world.getPlayers();
@@ -1662,23 +1750,31 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
PacketDebug.a(this.world, chunk.getPos());
List<Entity> list = Lists.newArrayList();
List<Entity> list1 = Lists.newArrayList();
- ObjectIterator objectiterator = this.trackedEntities.values().iterator();
+ // Paper start - optimise entity tracker
+ // use the chunk entity list, not the whole trackedEntities map...
+ Entity[] entities = chunk.entities.getRawData();
+ for (int i = 0, size = chunk.entities.size(); i < size; ++i) {
+ Entity entity = entities[i];
+ if (entity == entityplayer) {
+ continue;
+ }
+ PlayerChunkMap.EntityTracker tracker = this.trackedEntities.get(entity.getId());
+ if (tracker != null) { // dumb plugins... move on...
+ tracker.updatePlayer(entityplayer);
+ }
- while (objectiterator.hasNext()) {
- PlayerChunkMap.EntityTracker playerchunkmap_entitytracker = (PlayerChunkMap.EntityTracker) objectiterator.next();
- Entity entity = playerchunkmap_entitytracker.tracker;
+ // keep the vanilla logic here - this is REQUIRED or else passengers and their vehicles disappear!
+ // (and god knows what the leash thing is)
- if (entity != entityplayer && entity.chunkX == chunk.getPos().x && entity.chunkZ == chunk.getPos().z) {
- playerchunkmap_entitytracker.updatePlayer(entityplayer);
- if (entity instanceof EntityInsentient && ((EntityInsentient) entity).getLeashHolder() != null) {
- list.add(entity);
- }
+ if (entity instanceof EntityInsentient && ((EntityInsentient)entity).getLeashHolder() != null) {
+ list.add(entity);
+ }
- if (!entity.getPassengers().isEmpty()) {
- list1.add(entity);
- }
+ if (!entity.getPassengers().isEmpty()) {
+ list1.add(entity);
}
}
+ // Paper end - optimise entity tracker
Iterator iterator;
Entity entity1;
@@ -1716,7 +1812,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
public class EntityTracker {
- private final EntityTrackerEntry trackerEntry;
+ final EntityTrackerEntry trackerEntry; // Paper - private -> package private
private final Entity tracker;
private final int trackingDistance;
private SectionPosition e;
@@ -1733,6 +1829,42 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.e = SectionPosition.a(entity);
}
+ // Paper start - use distance map to optimise tracker
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> lastTrackerCandidates;
+
+ final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newTrackerCandidates) {
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> oldTrackerCandidates = this.lastTrackerCandidates;
+ this.lastTrackerCandidates = newTrackerCandidates;
+
+ if (newTrackerCandidates != null) {
+ Object[] rawData = newTrackerCandidates.getBackingSet();
+ for (int i = 0, len = rawData.length; i < len; ++i) {
+ Object raw = rawData[i];
+ if (!(raw instanceof EntityPlayer)) {
+ continue;
+ }
+ EntityPlayer player = (EntityPlayer)raw;
+ this.updatePlayer(player);
+ }
+ }
+
+ if (oldTrackerCandidates == newTrackerCandidates) {
+ // this is likely the case.
+ // means there has been no range changes, so we can just use the above for tracking.
+ return;
+ }
+
+ // stuff could have been removed, so we need to check the trackedPlayers set
+ // for players that were removed
+
+ for (EntityPlayer player : this.trackedPlayers.toArray(new EntityPlayer[0])) { // avoid CME
+ if (newTrackerCandidates == null || !newTrackerCandidates.contains(player)) {
+ this.updatePlayer(player);
+ }
+ }
+ }
+ // Paper end - use distance map to optimise tracker
+
public boolean equals(Object object) {
return object instanceof PlayerChunkMap.EntityTracker ? ((PlayerChunkMap.EntityTracker) object).tracker.getId() == this.tracker.getId() : false;
}
@@ -1829,7 +1961,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
int j = entity.getEntityType().getChunkRange() * 16;
j = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, j); // Paper
- if (j > i) {
+ if (j < i) { // Paper - we need the lowest range thanks to the fact that our tracker doesn't account for passenger logic
i = j;
}
}
diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java
index 765bdaf9b525a989ec8d37a2fe856dcfcbd06782..43b5ed8e396e5312f7de1f160f596f58baead28a 100644
--- a/src/main/java/org/spigotmc/TrackingRange.java
+++ b/src/main/java/org/spigotmc/TrackingRange.java
@@ -49,4 +49,43 @@ public class TrackingRange
return config.otherTrackingRange;
}
}
+
+ // Paper start - optimise entity tracking
+ // copied from above, TODO check on update
+ public static TrackingRangeType getTrackingRangeType(Entity entity)
+ {
+ if ( entity instanceof EntityPlayer )
+ {
+ return TrackingRangeType.PLAYER;
+ // Paper start - Simplify and set water mobs to animal tracking range
+ }
+ switch (entity.activationType) {
+ case RAIDER:
+ case MONSTER:
+ return TrackingRangeType.MONSTER;
+ case WATER:
+ case ANIMAL:
+ return TrackingRangeType.ANIMAL;
+ case MISC:
+ }
+ if ( entity instanceof EntityItemFrame || entity instanceof EntityPainting || entity instanceof EntityItem || entity instanceof EntityExperienceOrb )
+ // Paper end
+ {
+ return TrackingRangeType.MISC;
+ } else
+ {
+ if (entity instanceof EntityEnderDragon) return TrackingRangeType.ENDERDRAGON; // Paper - enderdragon is exempt
+ return TrackingRangeType.OTHER;
+ }
+ }
+
+ public static enum TrackingRangeType {
+ PLAYER,
+ ANIMAL,
+ MONSTER,
+ MISC,
+ OTHER,
+ ENDERDRAGON;
+ }
+ // Paper end - optimise entity tracking
}