Paper/patches/server/0999-Use-distance-map-to-optimise-entity-tracker.patch
Jake Potrebic ac554ad46d
Updated Upstream (Bukkit/CraftBukkit) (#10691)
Updated Upstream (Bukkit/CraftBukkit)

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:
fa99e752 PR-1007: Add ItemMeta#getAsComponentString()
94a91782 Fix copy-pasted BlockType.Typed documentation
9b34ac8c Largely restore deprecated PotionData API
51a6449b PR-1008: Deprecate ITEMS_TOOLS, removed in 1.20.5
702d15fe Fix Javadoc reference
42f6cdf4 PR-919: Add internal ItemType and BlockType, delegate Material methods to them
237bb37b SPIGOT-1166, SPIGOT-7647: Expose Damager BlockState in EntityDamageByBlockEvent
035ea146 SPIGOT-6993: Allow #setVelocity to change the speed of a fireball and add a note to #setDirection about it
8c7880fb PR-1004: Improve field rename handling and centralize conversion between bukkit and string more
87c90e93 SPIGOT-7650: Add DamageSource for EntityDeathEvent and PlayerDeathEvent

CraftBukkit Changes:
4af0f22e8 SPIGOT-7664: Item meta should prevail over block states
c2ccc46ec SPIGOT-7666: Fix access to llama and horse special slot
124ac66d7 SPIGOT-7665: Fix ThrownPotion#getEffects() implementation only bringing custom effects
66f1f439a Restore null page behaviour of signed books even though not strictly allowed by API
6118e5398 Fix regression listening to minecraft:brand custom payloads
c1a26b366 Fix unnecessary and potential not thread-safe chat visibility check
12360a7ec Remove unused imports
147b098b4 PR-1397: Add ItemMeta#getAsComponentString()
428aefe0e Largely restore deprecated PotionData API
afe5b5ee9 PR-1275: Add internal ItemType and BlockType, delegate Material methods to them
8afeafa7d SPIGOT-1166, SPIGOT-7647: Expose Damager BlockState in EntityDamageByBlockEvent
4e7d749d4 SPIGOT-6993: Allow #setVelocity to change the speed of a fireball and add a note to #setDirection about it
441880757 Support both entity_data and bucket_entity_data on axolotl/fish buckets
0e22fdd1e Fix custom direct BlockState being not correctly set in DamageSource
f2182ed47 SPIGOT-7659: TropicalFishBucketMeta should use BUCKET_ENTITY_DATA
2a6207fe1 PR-1393: Improve field rename handling and centralize conversion between bukkit and string more
c024a5039 SPIGOT-7650: Add DamageSource for EntityDeathEvent and PlayerDeathEvent
741b84480 PR-1390: Improve internal handling of damage sources
0364df4e1 SPIGOT-7657: Error when loading angry entities
2024-05-11 23:48:37 +02:00

342 lines
17 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/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 4621c33ed73b0db64e78e7b9be7013a2ba7393c8..48f7997e8a20f5a5a77516cbde990d0aacc2078a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -149,6 +149,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper start - distance maps
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> 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();
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap[] playerEntityTrackerTrackMaps;
+ final int[] entityTrackerTrackRanges;
+ public final int getEntityTrackerRange(final int ordinal) {
+ return this.entityTrackerTrackRanges[ordinal];
+ }
+
+ private int convertSpigotRangeToVanilla(final int vanilla) {
+ return net.minecraft.server.MinecraftServer.getServer().getScaledTrackingDistance(vanilla);
+ }
+ // Paper end - use distance map to optimise tracker
void addPlayerToDistanceMaps(ServerPlayer player) {
int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX());
@@ -156,6 +173,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Note: players need to be explicitly added to distance maps before they can be updated
this.nearbyPlayers.addPlayer(player);
this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader
+ // 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, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
+ }
+ // Paper end - use distance map to optimise entity tracker
}
void removePlayerFromDistanceMaps(ServerPlayer player) {
@@ -164,6 +189,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Note: players need to be explicitly added to distance maps before they can be updated
this.nearbyPlayers.removePlayer(player);
this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader
+ // 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(ServerPlayer player) {
@@ -172,6 +202,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Note: players need to be explicitly added to distance maps before they can be updated
this.nearbyPlayers.tickPlayer(player);
this.level.playerChunkLoader.updatePlayer(player); // Paper - replace chunk loader
+ // 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, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
+ }
+ // Paper end - use distance map to optimise entity tracker
}
// Paper end
// Paper start
@@ -258,6 +296,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.add(this.dataRegionManager);
this.nearbyPlayers = new io.papermc.paper.util.player.NearbyPlayers(this.level);
// Paper end
+ // 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.level.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 = EntityType.ENDER_DRAGON.clientTrackingRange() * 16;
+ break;
+ case DISPLAY:
+ configuredSpigotValue = spigotWorldConfig.displayTrackingRange;
+ break;
+ default:
+ throw new IllegalStateException("Missing case for enum " + trackingRangeType);
+ }
+ configuredSpigotValue = convertSpigotRangeToVanilla(configuredSpigotValue);
+
+ 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
}
// Paper start
@@ -951,17 +1031,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void move(ServerPlayer player) {
- ObjectIterator objectiterator = this.entityMap.values().iterator();
-
- while (objectiterator.hasNext()) {
- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
-
- if (playerchunkmap_entitytracker.entity == player) {
- playerchunkmap_entitytracker.updatePlayers(this.level.players());
- } else {
- playerchunkmap_entitytracker.updatePlayer(player);
- }
- }
+ // Paper - delay this logic for the entity tracker tick, no need to duplicate it
SectionPos sectionposition = player.getLastSectionPos();
SectionPos sectionposition1 = SectionPos.of((EntityAccess) player);
@@ -1038,7 +1108,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
- playerchunkmap_entitytracker.updatePlayers(this.level.players());
+ playerchunkmap_entitytracker.updatePlayers(entity.getPlayersInTrackRange()); // Paper - don't search all players
if (entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
@@ -1080,9 +1150,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = null; // Paper - We're no longer tracked
}
- protected void tick() {
- // Paper - replaced by PlayerChunkLoader
+ // Paper start - optimised tracker
+ private final void processTrackQueue() {
+ this.level.timings.tracker1.startTiming();
+ try {
+ for (TrackedEntity tracker : this.entityMap.values()) {
+ // update tracker entry
+ tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
+ }
+ } finally {
+ this.level.timings.tracker1.stopTiming();
+ }
+
+
+ this.level.timings.tracker2.startTiming();
+ try {
+ for (TrackedEntity tracker : this.entityMap.values()) {
+ tracker.serverEntity.sendChanges();
+ }
+ } finally {
+ this.level.timings.tracker2.stopTiming();
+ }
+ }
+ // Paper end - optimised tracker
+ protected void tick() {
+ // Paper start - optimized tracker
+ if (true) {
+ this.processTrackQueue();
+ return;
+ }
+ // Paper end - optimized tracker
List<ServerPlayer> list = Lists.newArrayList();
List<ServerPlayer> list1 = this.level.players();
ObjectIterator objectiterator = this.entityMap.values().iterator();
@@ -1230,6 +1328,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastSectionPos = SectionPos.of((EntityAccess) entity);
}
+ // Paper start - use distance map to optimise tracker
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> lastTrackerCandidates;
+
+ final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newTrackerCandidates) {
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> 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 ServerPlayer)) {
+ continue;
+ }
+ ServerPlayer player = (ServerPlayer)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 (ServerPlayerConnection conn : this.seenBy.toArray(new ServerPlayerConnection[0])) { // avoid CME
+ if (newTrackerCandidates == null || !newTrackerCandidates.contains(conn.getPlayer())) {
+ this.updatePlayer(conn.getPlayer());
+ }
+ }
+ }
+ // Paper end - use distance map to optimise tracker
+
public boolean equals(Object object) {
return object instanceof ChunkMap.TrackedEntity ? ((ChunkMap.TrackedEntity) object).entity.getId() == this.entity.getId() : false;
}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index b57644317b5085d74d11ac6ba858c3747d703a47..257943b4c984d6faee29eca17c8f951e7f43168b 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -59,6 +59,7 @@ import net.minecraft.network.syncher.SyncedDataHolder;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
+import io.papermc.paper.util.MCUtil;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -471,6 +472,38 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.teleportTo(worldserver, null);
}
// Paper end - make end portalling safe
+ // Paper start - optimise entity tracking
+ final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this);
+
+ public boolean isLegacyTrackingEntity = false;
+
+ public final void setLegacyTrackingEntity(final boolean isLegacyTrackingEntity) {
+ this.isLegacyTrackingEntity = isLegacyTrackingEntity;
+ }
+
+ public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> getPlayersInTrackRange() {
+ // determine highest range of passengers
+ if (this.passengers.isEmpty()) {
+ return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()]
+ .getObjectsInRange(MCUtil.getCoordinateKey(this));
+ }
+ Iterable<Entity> passengers = this.getIndirectPassengers();
+ net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap;
+ org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType;
+ int range = chunkMap.getEntityTrackerRange(type.ordinal());
+
+ for (Entity passenger : passengers) {
+ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType;
+ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal());
+ if (passengerRange > range) {
+ type = passengerType;
+ range = passengerRange;
+ }
+ }
+
+ return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this));
+ }
+ // Paper end - optimise entity tracking
public float getBukkitYaw() {
return this.yRot;
}
diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java
index bb06f89a29f30144e7e2113e088a503db006a83c..e4425b242fe73d1fd2bd10c313aa16925432329f 100644
--- a/src/main/java/org/spigotmc/TrackingRange.java
+++ b/src/main/java/org/spigotmc/TrackingRange.java
@@ -55,4 +55,48 @@ 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 net.minecraft.world.entity.boss.enderdragon.EnderDragon) return TrackingRangeType.ENDERDRAGON; // Paper - enderdragon is exempt
+ if ( entity instanceof ServerPlayer )
+ {
+ return TrackingRangeType.PLAYER;
+ // Paper start - Simplify and set water mobs to animal tracking range
+ }
+ switch (entity.activationType) {
+ case RAIDER:
+ case MONSTER:
+ case FLYING_MONSTER:
+ return TrackingRangeType.MONSTER;
+ case WATER:
+ case VILLAGER:
+ case ANIMAL:
+ return TrackingRangeType.ANIMAL;
+ case MISC:
+ }
+ if ( entity instanceof ItemFrame || entity instanceof Painting || entity instanceof ItemEntity || entity instanceof ExperienceOrb )
+ // Paper end
+ {
+ return TrackingRangeType.MISC;
+ } else if (entity instanceof Display) {
+ return TrackingRangeType.DISPLAY;
+ } else
+ {
+ return TrackingRangeType.OTHER;
+ }
+ }
+
+ public static enum TrackingRangeType {
+ PLAYER,
+ ANIMAL,
+ MONSTER,
+ MISC,
+ OTHER,
+ ENDERDRAGON,
+ DISPLAY;
+ }
+ // Paper end - optimise entity tracking
}