mirror of
https://github.com/PaperMC/Paper.git
synced 2024-09-03 02:20:51 +02:00
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: 01aa02eb PR-858: Add LivingEntity#playHurtAnimation() 9421320f PR-884: Refinements to new ban API for improved compatibility and correctness 37a60b45 SPIGOT-6455, SPIGOT-7030, PR-750: Improve ban API 4eeb174b All smithing inventories are now the new smithing inventory f2bb168e PR-880: Add methods to get/set FallingBlock CancelDrop e7a807fa PR-879: Add Player#sendHealthUpdate() 692b8e96 SPIGOT-7370: Remove float value conversion in plugin.yml 2d033390 SPIGOT-7403: Add direct API for waxed signs 16a08373 PR-876: Add missing Raider API and 'no action ticks' CraftBukkit Changes: b60a95c8c PR-1189: Add LivingEntity#playHurtAnimation() 95c335c63 PR-1226: Fix VehicleEnterEvent not being called for certain entities 0a0fc3bee PR-1227: Refinements to new ban API for improved compatibility and correctness 0d0b1e5dc Revert bad change to PathfinderGoalSit causing all cats to sit 648196070 SPIGOT-6455, SPIGOT-7030, PR-1054: Improve ban API 31fe848d6 All smithing inventories are now the new smithing inventory 9a919a143 SPIGOT-7416: SmithItemEvent not firing in Smithing Table 9f64f0d22 PR-1221: Add methods to get/set FallingBlock CancelDrop 3be9ac171 PR-1220: Add Player#sendHealthUpdate() c1279f775 PR-1209: Clean up various patches c432e4397 Fix Raider#setCelebrating() implementation 504d96665 SPIGOT-7403: Add direct API for waxed signs c68c1f1b3 PR-1216: Add missing Raider API and 'no action ticks' 85b89c3dd Increase outdated build delay Spigot Changes: 9ebce8af Rebuild patches 64b565e6 Rebuild patches
382 lines
18 KiB
Diff
382 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/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index dadf403ac91887f0fae87889170deb6d5732cbc1..93dea79180ebaef3ffb6abffd47f0026a91406cd 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -157,6 +157,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) {
|
|
this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader
|
|
@@ -168,6 +185,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.playerMobDistanceMap.add(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player));
|
|
}
|
|
// Paper end - per player mob spawning
|
|
+ // 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) {
|
|
@@ -178,6 +203,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.playerMobDistanceMap.remove(player);
|
|
}
|
|
// Paper end - per player mob spawning
|
|
+ // 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) {
|
|
@@ -190,6 +220,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.playerMobDistanceMap.update(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player));
|
|
}
|
|
// Paper end - per player mob spawning
|
|
+ // 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
|
|
@@ -276,6 +314,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.regionManagers.add(this.dataRegionManager);
|
|
// Paper end
|
|
this.playerMobDistanceMap = this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : 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.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;
|
|
+ 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
|
|
}
|
|
|
|
protected ChunkGenerator generator() {
|
|
@@ -960,17 +1037,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
|
|
|
|
int i = SectionPos.blockToSectionCoord(player.getBlockX());
|
|
int j = SectionPos.blockToSectionCoord(player.getBlockZ());
|
|
@@ -1054,7 +1121,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;
|
|
|
|
@@ -1098,7 +1165,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
entity.tracker = null; // Paper - We're no longer tracked
|
|
}
|
|
|
|
+ // 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();
|
|
@@ -1205,46 +1302,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}));
|
|
// Paper end
|
|
DebugPackets.sendPoiPacketsForChunk(this.level, chunk.getPos());
|
|
- List<Entity> list = Lists.newArrayList();
|
|
- List<Entity> list1 = Lists.newArrayList();
|
|
- ObjectIterator objectiterator = this.entityMap.values().iterator();
|
|
-
|
|
- while (objectiterator.hasNext()) {
|
|
- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
|
|
- Entity entity = playerchunkmap_entitytracker.entity;
|
|
-
|
|
- if (entity != player && entity.chunkPosition().equals(chunk.getPos())) {
|
|
- playerchunkmap_entitytracker.updatePlayer(player);
|
|
- if (entity instanceof Mob && ((Mob) entity).getLeashHolder() != null) {
|
|
- list.add(entity);
|
|
- }
|
|
-
|
|
- if (!entity.getPassengers().isEmpty()) {
|
|
- list1.add(entity);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- Iterator iterator;
|
|
- Entity entity1;
|
|
-
|
|
- if (!list.isEmpty()) {
|
|
- iterator = list.iterator();
|
|
-
|
|
- while (iterator.hasNext()) {
|
|
- entity1 = (Entity) iterator.next();
|
|
- player.connection.send(new ClientboundSetEntityLinkPacket(entity1, ((Mob) entity1).getLeashHolder()));
|
|
- }
|
|
- }
|
|
-
|
|
- if (!list1.isEmpty()) {
|
|
- iterator = list1.iterator();
|
|
-
|
|
- while (iterator.hasNext()) {
|
|
- entity1 = (Entity) iterator.next();
|
|
- player.connection.send(new ClientboundSetPassengersPacket(entity1));
|
|
- }
|
|
- }
|
|
+ // Paper - no longer needed - this was used to account for clients bugging out since they needed a chunk to store entities, but they no longer need a chunk
|
|
|
|
}
|
|
|
|
@@ -1299,6 +1357,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 f5c1de9671bf823a7f2fa78acfa8f9065d6eb517..c683b5b35ff2bf850283f002c97a28bb6cf223bd 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -57,6 +57,7 @@ import net.minecraft.network.syncher.EntityDataSerializers;
|
|
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;
|
|
@@ -481,6 +482,38 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
|
|
public boolean updatingSectionStatus = false;
|
|
// Paper end
|
|
+ // 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 Entity(EntityType<?> type, Level world) {
|
|
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
|
|
diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java
|
|
index 172d231adecf043f9f06b7f5e0365ae82327998d..8fca15099952626ee537f5ce3eefdfaa6518dc1b 100644
|
|
--- a/src/main/java/org/spigotmc/TrackingRange.java
|
|
+++ b/src/main/java/org/spigotmc/TrackingRange.java
|
|
@@ -55,4 +55,45 @@ 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
|
|
+ {
|
|
+ return TrackingRangeType.OTHER;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static enum TrackingRangeType {
|
|
+ PLAYER,
|
|
+ ANIMAL,
|
|
+ MONSTER,
|
|
+ MISC,
|
|
+ OTHER,
|
|
+ ENDERDRAGON;
|
|
+ }
|
|
+ // Paper end - optimise entity tracking
|
|
}
|