mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-08 19:34:09 +01:00
831bf8fb7e
This will let you configure how far to track entities in range from players, so that the entity does not render on the client if out of this range. This has multiple benefits: 1) Less bandwidth. Not sending update packets for entities that are not even close to a player, or even close enough to clearly see. 2) Less lag by maps in item frames - Default range is 160 blocks... Many players can track that item frame and cause lag and not even see it. 3) Less lag in general - Less work for the server to do 4) Less client lag - Not trying to render distant item frames and paintings and entities will reduce entity count on the client, which is major for shop/town worlds which may use tons of item frames. By: Aikar <aikar@aikar.co>
224 lines
12 KiB
Diff
224 lines
12 KiB
Diff
--- a/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/net/minecraft/server/level/ChunkMap.java
|
|
@@ -104,6 +104,10 @@
|
|
import org.apache.commons.lang3.mutable.MutableBoolean;
|
|
import org.slf4j.Logger;
|
|
|
|
+// CraftBukkit start
|
|
+import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
|
+// CraftBukkit end
|
|
+
|
|
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider, GeneratingChunkMap {
|
|
|
|
private static final ChunkResult<List<ChunkAccess>> UNLOADED_CHUNK_LIST_RESULT = ChunkResult.error("Unloaded chunks found in range");
|
|
@@ -148,6 +152,27 @@
|
|
private final AtomicInteger activeChunkWrites;
|
|
public int serverViewDistance;
|
|
private final WorldGenContext worldGenContext;
|
|
+
|
|
+ // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
|
|
+ public final CallbackExecutor callbackExecutor = new CallbackExecutor();
|
|
+ public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable {
|
|
+
|
|
+ private final java.util.Queue<Runnable> queue = new java.util.ArrayDeque<>();
|
|
+
|
|
+ @Override
|
|
+ public void execute(Runnable runnable) {
|
|
+ this.queue.add(runnable);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ Runnable task;
|
|
+ while ((task = this.queue.poll()) != null) {
|
|
+ task.run();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // CraftBukkit end
|
|
|
|
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
|
|
super(new RegionStorageInfo(session.getLevelId(), world.dimension(), "chunk"), session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
|
|
@@ -170,7 +195,13 @@
|
|
RegistryAccess iregistrycustom = world.registryAccess();
|
|
long j = world.getSeed();
|
|
|
|
- if (chunkGenerator instanceof NoiseBasedChunkGenerator chunkgeneratorabstract) {
|
|
+ // CraftBukkit start - SPIGOT-7051: It's a rigged game! Use delegate for random state creation, otherwise it is not so random.
|
|
+ ChunkGenerator randomGenerator = chunkGenerator;
|
|
+ if (randomGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
|
+ randomGenerator = customChunkGenerator.getDelegate();
|
|
+ }
|
|
+ if (randomGenerator instanceof NoiseBasedChunkGenerator chunkgeneratorabstract) {
|
|
+ // CraftBukkit end
|
|
this.randomState = RandomState.create((NoiseGeneratorSettings) chunkgeneratorabstract.generatorSettings().value(), (HolderGetter) iregistrycustom.lookupOrThrow(Registries.NOISE), j);
|
|
} else {
|
|
this.randomState = RandomState.create(NoiseGeneratorSettings.dummy(), (HolderGetter) iregistrycustom.lookupOrThrow(Registries.NOISE), j);
|
|
@@ -325,7 +356,7 @@
|
|
throw this.debugFuturesAndCreateReportedException(new IllegalStateException("At least one of the chunk futures were null"), "n/a");
|
|
}
|
|
|
|
- ChunkAccess ichunkaccess = (ChunkAccess) chunkresult.orElse((Object) null);
|
|
+ ChunkAccess ichunkaccess = (ChunkAccess) chunkresult.orElse(null); // CraftBukkit - decompile error
|
|
|
|
if (ichunkaccess == null) {
|
|
return ChunkMap.UNLOADED_CHUNK_LIST_RESULT;
|
|
@@ -977,7 +1008,8 @@
|
|
return ichunkaccess instanceof LevelChunk ? Optional.of((LevelChunk) ichunkaccess) : Optional.empty();
|
|
});
|
|
|
|
- csvwriter.writeRow(chunkcoordintpair.x, chunkcoordintpair.z, playerchunk.getTicketLevel(), optional.isPresent(), optional.map(ChunkAccess::getPersistedStatus).orElse((Object) null), optional1.map(LevelChunk::getFullStatus).orElse((Object) null), ChunkMap.printFuture(playerchunk.getFullChunkFuture()), ChunkMap.printFuture(playerchunk.getTickingChunkFuture()), ChunkMap.printFuture(playerchunk.getEntityTickingChunkFuture()), this.distanceManager.getTicketDebugString(i), this.anyPlayerCloseEnoughForSpawning(chunkcoordintpair), optional1.map((chunk) -> {
|
|
+ // CraftBukkit - decompile error
|
|
+ csvwriter.writeRow(chunkcoordintpair.x, chunkcoordintpair.z, playerchunk.getTicketLevel(), optional.isPresent(), optional.map(ChunkAccess::getPersistedStatus).orElse(null), optional1.map(LevelChunk::getFullStatus).orElse(null), ChunkMap.printFuture(playerchunk.getFullChunkFuture()), ChunkMap.printFuture(playerchunk.getTickingChunkFuture()), ChunkMap.printFuture(playerchunk.getEntityTickingChunkFuture()), this.distanceManager.getTicketDebugString(i), this.anyPlayerCloseEnoughForSpawning(chunkcoordintpair), optional1.map((chunk) -> {
|
|
return chunk.getBlockEntities().size();
|
|
}).orElse(0), tickingtracker.getTicketDebugString(i), tickingtracker.getLevel(i), optional1.map((chunk) -> {
|
|
return chunk.getBlockTicks().count();
|
|
@@ -990,7 +1022,7 @@
|
|
|
|
private static String printFuture(CompletableFuture<ChunkResult<LevelChunk>> future) {
|
|
try {
|
|
- ChunkResult<LevelChunk> chunkresult = (ChunkResult) future.getNow((Object) null);
|
|
+ ChunkResult<LevelChunk> chunkresult = (ChunkResult) future.getNow(null); // CraftBukkit - decompile error
|
|
|
|
return chunkresult != null ? (chunkresult.isSuccess() ? "done" : "unloaded") : "not completed";
|
|
} catch (CompletionException completionexception) {
|
|
@@ -1002,12 +1034,14 @@
|
|
|
|
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
|
|
return this.read(chunkPos).thenApplyAsync((optional) -> {
|
|
- return optional.map(this::upgradeChunkTag);
|
|
+ return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
|
|
}, Util.backgroundExecutor().forName("upgradeChunk"));
|
|
}
|
|
|
|
- private CompoundTag upgradeChunkTag(CompoundTag nbt) {
|
|
- return this.upgradeChunkTag(this.level.dimension(), this.overworldDataStorage, nbt, this.generator().getTypeNameForDataFixer());
|
|
+ // CraftBukkit start
|
|
+ private CompoundTag upgradeChunkTag(CompoundTag nbttagcompound, ChunkPos chunkcoordintpair) {
|
|
+ return this.upgradeChunkTag(this.level.getTypeKey(), this.overworldDataStorage, nbttagcompound, this.generator().getTypeNameForDataFixer(), chunkcoordintpair, this.level);
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
void forEachSpawnCandidateChunk(Consumer<ChunkHolder> callback) {
|
|
@@ -1025,10 +1059,27 @@
|
|
}
|
|
|
|
public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) {
|
|
- return !this.distanceManager.hasPlayersNearby(pos.toLong()) ? false : this.anyPlayerCloseEnoughForSpawningInternal(pos);
|
|
+ // Spigot start
|
|
+ return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
|
}
|
|
|
|
+ boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
|
+ return !this.distanceManager.hasPlayersNearby(chunkcoordintpair.toLong()) ? false : this.anyPlayerCloseEnoughForSpawningInternal(chunkcoordintpair, reducedRange);
|
|
+ // Spigot end
|
|
+ }
|
|
+
|
|
private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos pos) {
|
|
+ // Spigot start
|
|
+ return this.anyPlayerCloseEnoughForSpawningInternal(pos, false);
|
|
+ }
|
|
+
|
|
+ private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
|
+ int chunkRange = this.level.spigotConfig.mobSpawnRange;
|
|
+ chunkRange = (chunkRange > this.level.spigotConfig.viewDistance) ? (byte) this.level.spigotConfig.viewDistance : chunkRange;
|
|
+ chunkRange = (chunkRange > 8) ? 8 : chunkRange;
|
|
+
|
|
+ double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D;
|
|
+ // Spigot end
|
|
Iterator iterator = this.playerMap.getAllPlayers().iterator();
|
|
|
|
ServerPlayer entityplayer;
|
|
@@ -1039,7 +1090,7 @@
|
|
}
|
|
|
|
entityplayer = (ServerPlayer) iterator.next();
|
|
- } while (!this.playerIsCloseEnoughForSpawning(entityplayer, pos));
|
|
+ } while (!this.playerIsCloseEnoughForSpawning(entityplayer, chunkcoordintpair, blockRange)); // Spigot
|
|
|
|
return true;
|
|
}
|
|
@@ -1056,7 +1107,7 @@
|
|
while (iterator.hasNext()) {
|
|
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
|
|
|
- if (this.playerIsCloseEnoughForSpawning(entityplayer, pos)) {
|
|
+ if (this.playerIsCloseEnoughForSpawning(entityplayer, pos, 16384.0D)) { // Spigot
|
|
builder.add(entityplayer);
|
|
}
|
|
}
|
|
@@ -1065,13 +1116,13 @@
|
|
}
|
|
}
|
|
|
|
- private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos pos) {
|
|
- if (player.isSpectator()) {
|
|
+ private boolean playerIsCloseEnoughForSpawning(ServerPlayer entityplayer, ChunkPos chunkcoordintpair, double range) { // Spigot
|
|
+ if (entityplayer.isSpectator()) {
|
|
return false;
|
|
} else {
|
|
- double d0 = ChunkMap.euclideanDistanceSquared(pos, player);
|
|
+ double d0 = ChunkMap.euclideanDistanceSquared(chunkcoordintpair, entityplayer);
|
|
|
|
- return d0 < 16384.0D;
|
|
+ return d0 < range; // Spigot
|
|
}
|
|
}
|
|
|
|
@@ -1215,9 +1266,11 @@
|
|
}
|
|
|
|
public void addEntity(Entity entity) {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
|
|
if (!(entity instanceof EnderDragonPart)) {
|
|
EntityType<?> entitytypes = entity.getType();
|
|
int i = entitytypes.clientTrackingRange() * 16;
|
|
+ i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
|
|
|
|
if (i != 0) {
|
|
int j = entitytypes.updateInterval();
|
|
@@ -1250,6 +1303,7 @@
|
|
}
|
|
|
|
protected void removeEntity(Entity entity) {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("entity untrack"); // Spigot
|
|
if (entity instanceof ServerPlayer entityplayer) {
|
|
this.updatePlayerStatus(entityplayer, false);
|
|
ObjectIterator objectiterator = this.entityMap.values().iterator();
|
|
@@ -1424,7 +1478,7 @@
|
|
public final Set<ServerPlayerConnection> seenBy = Sets.newIdentityHashSet();
|
|
|
|
public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) {
|
|
- this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast);
|
|
+ this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit
|
|
this.entity = entity;
|
|
this.range = i;
|
|
this.lastSectionPos = SectionPos.of((EntityAccess) entity);
|
|
@@ -1469,6 +1523,7 @@
|
|
}
|
|
|
|
public void removePlayer(ServerPlayer player) {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
|
|
if (this.seenBy.remove(player.connection)) {
|
|
this.serverEntity.removePairing(player);
|
|
}
|
|
@@ -1476,6 +1531,7 @@
|
|
}
|
|
|
|
public void updatePlayer(ServerPlayer player) {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
|
if (player != this.entity) {
|
|
Vec3 vec3d = player.position().subtract(this.entity.position());
|
|
int i = ChunkMap.this.getPlayerViewDistance(player);
|
|
@@ -1484,6 +1540,11 @@
|
|
double d2 = d0 * d0;
|
|
boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
|
|
|
|
+ // CraftBukkit start - respect vanish API
|
|
+ if (!player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) {
|
|
+ flag = false;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (flag) {
|
|
if (this.seenBy.add(player.connection)) {
|
|
this.serverEntity.addPairing(player);
|