PaperMC/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch
2024-12-14 22:35:35 -08:00

375 lines
19 KiB
Diff

--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -145,6 +_,33 @@
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
+
+ // Paper start
+ public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) {
+ return this.pendingUnloads.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
+ }
+ // Paper end
+
public ChunkMap(
ServerLevel level,
LevelStorageSource.LevelStorageAccess levelStorageAccess,
@@ -171,13 +_,19 @@
this.level = level;
RegistryAccess registryAccess = level.registryAccess();
long seed = level.getSeed();
- if (generator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
+ // CraftBukkit start - SPIGOT-7051: It's a rigged game! Use delegate for random state creation, otherwise it is not so random.
+ ChunkGenerator randomGenerator = generator;
+ if (randomGenerator instanceof org.bukkit.craftbukkit.generator.CustomChunkGenerator customChunkGenerator) {
+ randomGenerator = customChunkGenerator.getDelegate();
+ }
+ if (randomGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
+ // CraftBukkit end
this.randomState = RandomState.create(noiseBasedChunkGenerator.generatorSettings().value(), registryAccess.lookupOrThrow(Registries.NOISE), seed);
} else {
this.randomState = RandomState.create(NoiseGeneratorSettings.dummy(), registryAccess.lookupOrThrow(Registries.NOISE), seed);
}
- this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, seed);
+ this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, seed, level.spigotConfig); // Spigot
this.mainThreadExecutor = mainThreadExecutor;
ConsecutiveExecutor consecutiveExecutor = new ConsecutiveExecutor(dispatcher, "worldgen");
this.progressListener = progressListener;
@@ -207,6 +_,12 @@
this.chunksToEagerlySave.add(chunkPos.toLong());
}
+ // Paper start
+ public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
+ return -1;
+ }
+ // Paper end
+
protected ChunkGenerator generator() {
return this.worldGenContext.generator();
}
@@ -354,9 +_,9 @@
}
);
stringBuilder.append("Updating:").append(System.lineSeparator());
- this.updatingChunkMap.values().forEach(consumer);
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.getUpdatingChunkHolders(this.level).forEach(consumer); // Paper
stringBuilder.append("Visible:").append(System.lineSeparator());
- this.visibleChunkMap.values().forEach(consumer);
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level).forEach(consumer); // Paper
CrashReport crashReport = CrashReport.forThrowable(exception, "Chunk loading");
CrashReportCategory crashReportCategory = crashReport.addCategory("Chunk loading");
crashReportCategory.setDetail("Details", details);
@@ -392,6 +_,9 @@
holder.setTicketLevel(newLevel);
} else {
holder = new ChunkHolder(new ChunkPos(chunkPos), newLevel, this.level, this.lightEngine, this::onLevelChange, this);
+ // Paper start
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderCreate(this.level, holder);
+ // Paper end
}
this.updatingChunkMap.put(chunkPos, holder);
@@ -420,8 +_,8 @@
protected void saveAllChunks(boolean flush) {
if (flush) {
- List<ChunkHolder> list = this.visibleChunkMap
- .values()
+ List<ChunkHolder> list = ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level) // Paper - moonrise
+ //.values() // Paper - moonrise
.stream()
.filter(ChunkHolder::wasAccessibleSinceLastSave)
.peek(ChunkHolder::refreshAccessibility)
@@ -447,7 +_,7 @@
this.nextChunkSaveTime.clear();
long millis = Util.getMillis();
- for (ChunkHolder chunkHolder : this.visibleChunkMap.values()) {
+ for (ChunkHolder chunkHolder : ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level)) { // Paper
this.saveChunkIfNeeded(chunkHolder, millis);
}
}
@@ -468,6 +_,7 @@
public boolean hasWork() {
return this.lightEngine.hasLightWork()
|| !this.pendingUnloads.isEmpty()
+ || ca.spottedleaf.moonrise.common.util.ChunkSystem.hasAnyChunkHolders(this.level) // Paper - moonrise
|| !this.updatingChunkMap.isEmpty()
|| this.poiManager.hasWork()
|| !this.toDrop.isEmpty()
@@ -526,7 +_,11 @@
this.scheduleUnload(chunkPos, chunkHolder);
} else {
ChunkAccess latestChunk = chunkHolder.getLatestChunk();
- if (this.pendingUnloads.remove(chunkPos, chunkHolder) && latestChunk != null) {
+ // Paper start
+ boolean removed;
+ if ((removed = this.pendingUnloads.remove(chunkPos, chunkHolder)) && latestChunk != null) {
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderDelete(this.level, chunkHolder);
+ // Paper end
if (latestChunk instanceof LevelChunk levelChunk) {
levelChunk.setLoaded(false);
}
@@ -540,7 +_,9 @@
this.lightEngine.tryScheduleUpdate();
this.progressListener.onStatusChange(latestChunk.getPos(), null);
this.nextChunkSaveTime.remove(latestChunk.getPos().toLong());
- }
+ } else if (removed) { // Paper start
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.onChunkHolderDelete(this.level, chunkHolder);
+ } // Paper end
}
}, this.unloadQueue::add).whenComplete((_void, error) -> {
if (error != null) {
@@ -818,7 +_,7 @@
}
}
- protected void setServerViewDistance(int viewDistance) {
+ public void setServerViewDistance(int viewDistance) { // Paper - publi
int i = Mth.clamp(viewDistance, 2, 32);
if (i != this.serverViewDistance) {
this.serverViewDistance = i;
@@ -856,7 +_,7 @@
}
public int size() {
- return this.visibleChunkMap.size();
+ return ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolderCount(this.level); // Paper
}
public net.minecraft.server.level.DistanceManager getDistanceManager() {
@@ -864,7 +_,7 @@
}
protected Iterable<ChunkHolder> getChunks() {
- return Iterables.unmodifiableIterable(this.visibleChunkMap.values());
+ return Iterables.unmodifiableIterable(ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level)); // Paper
}
void dumpChunks(Writer writer) throws IOException {
@@ -888,10 +_,10 @@
.build(writer);
TickingTracker tickingTracker = this.distanceManager.tickingTracker();
- for (Entry<ChunkHolder> entry : this.visibleChunkMap.long2ObjectEntrySet()) {
- long longKey = entry.getLongKey();
+ for (ChunkHolder entry : ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.level)) { // Paper - Moonrise
+ long longKey = entry.pos.toLong(); // Paper - Moonrise
ChunkPos chunkPos = new ChunkPos(longKey);
- ChunkHolder chunkHolder = entry.getValue();
+ ChunkHolder chunkHolder = entry; // Paper - Moonrise
Optional<ChunkAccess> optional = Optional.ofNullable(chunkHolder.getLatestChunk());
Optional<LevelChunk> optional1 = optional.flatMap(chunk -> chunk instanceof LevelChunk ? Optional.of((LevelChunk)chunk) : Optional.empty());
csvOutput.writeRow(
@@ -931,11 +_,13 @@
}
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos pos) {
- return this.read(pos).thenApplyAsync(optional -> optional.map(this::upgradeChunkTag), Util.backgroundExecutor().forName("upgradeChunk"));
+ return this.read(pos).thenApplyAsync(optional -> optional.map(tag -> upgradeChunkTag(tag, pos)), Util.backgroundExecutor().forName("upgradeChunk")); // CraftBukkit
}
- private CompoundTag upgradeChunkTag(CompoundTag tag) {
- return this.upgradeChunkTag(this.level.dimension(), this.overworldDataStorage, tag, this.generator().getTypeNameForDataFixer());
+ // CraftBukkit start
+ private CompoundTag upgradeChunkTag(CompoundTag tag, ChunkPos pos) {
+ return this.upgradeChunkTag(this.level.getTypeKey(), this.overworldDataStorage, tag, this.generator().getTypeNameForDataFixer(), pos, this.level);
+ // CraftBukkit end
}
void forEachSpawnCandidateChunk(Consumer<ChunkHolder> action) {
@@ -951,12 +_,34 @@
}
public boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos) {
- return this.distanceManager.hasPlayersNearby(chunkPos.toLong()) && this.anyPlayerCloseEnoughForSpawningInternal(chunkPos);
+ // Spigot start
+ return this.anyPlayerCloseEnoughForSpawning(chunkPos, false);
+ }
+
+ boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos, boolean reducedRange) {
+ return this.distanceManager.hasPlayersNearby(chunkPos.toLong()) && this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, reducedRange);
+ // Spigot end
}
private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkPos) {
+ // Spigot start
+ return this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, false);
+ }
+
+ private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkPos, boolean reducedRange) {
+ double blockRange; // Paper - use from event
+ // Spigot end
for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) {
- if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos)) {
+ // Paper start - PlayerNaturallySpawnCreaturesEvent
+ com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
+ blockRange = 16384.0D;
+ if (reducedRange) {
+ event = serverPlayer.playerNaturallySpawnedEvent;
+ if (event == null || event.isCancelled()) continue;
+ blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4));
+ }
+ // Paper end - PlayerNaturallySpawnCreaturesEvent
+ if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, blockRange)) {
return true;
}
}
@@ -972,7 +_,7 @@
Builder<ServerPlayer> builder = ImmutableList.builder();
for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) {
- if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos)) {
+ if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, 16384.0D)) { // Spigot
builder.add(serverPlayer);
}
}
@@ -981,12 +_,12 @@
}
}
- private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos) {
+ private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos, double range) { // Spigot
if (player.isSpectator()) {
return false;
} else {
double d = euclideanDistanceSquared(chunkPos, player);
- return d < 16384.0;
+ return d < range; // Spigot
}
}
@@ -1100,9 +_,19 @@
}
public void addEntity(Entity entity) {
+ org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
+ // Paper start - ignore and warn about illegal addEntity calls instead of crashing server
+ if (!entity.valid || entity.level() != this.level || this.entityMap.containsKey(entity.getId())) {
+ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName()
+ + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
+ return;
+ }
+ // Paper end - ignore and warn about illegal addEntity calls instead of crashing server
+ if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Paper - Fire PlayerJoinEvent when Player is actually ready; Delayadding to tracker until after list packets
if (!(entity instanceof EnderDragonPart)) {
EntityType<?> type = entity.getType();
int i = type.clientTrackingRange() * 16;
+ i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
if (i != 0) {
int updateInterval = type.updateInterval();
if (this.entityMap.containsKey(entity.getId())) {
@@ -1126,6 +_,7 @@
}
protected void removeEntity(Entity entity) {
+ org.spigotmc.AsyncCatcher.catchOp("entity untrack"); // Spigot
if (entity instanceof ServerPlayer serverPlayer) {
this.updatePlayerStatus(serverPlayer, false);
@@ -1230,7 +_,7 @@
});
}
- class DistanceManager extends net.minecraft.server.level.DistanceManager {
+ public class DistanceManager extends net.minecraft.server.level.DistanceManager { // Paper - public
protected DistanceManager(final Executor dispatcher, final Executor mainThreadExecutor) {
super(dispatcher, mainThreadExecutor);
}
@@ -1258,10 +_,10 @@
final Entity entity;
private final int range;
SectionPos lastSectionPos;
- public final Set<ServerPlayerConnection> seenBy = Sets.newIdentityHashSet();
+ public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
public TrackedEntity(final Entity entity, final int range, final int updateInterval, final boolean trackDelta) {
- this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this::broadcast);
+ this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this::broadcast, this.seenBy); // CraftBukkit
this.entity = entity;
this.range = range;
this.lastSectionPos = SectionPos.of(entity);
@@ -1297,24 +_,47 @@
}
public void removePlayer(ServerPlayer player) {
+ org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
}
}
public void updatePlayer(ServerPlayer player) {
+ org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
if (player != this.entity) {
- Vec3 vec3 = player.position().subtract(this.entity.position());
+ // Paper start - remove allocation of Vec3D here
+ // Vec3 vec3d = player.position().subtract(this.entity.position());
+ double vec3d_dx = player.getX() - this.entity.getX();
+ double vec3d_dz = player.getZ() - this.entity.getZ();
+ // Paper end - remove allocation of Vec3D here
int playerViewDistance = ChunkMap.this.getPlayerViewDistance(player);
double d = Math.min(this.getEffectiveRange(), playerViewDistance * 16);
- double d1 = vec3.x * vec3.x + vec3.z * vec3.z;
+ double d1 = vec3d_dx * vec3d_dx + vec3d_dz * vec3d_dz; // Paper
double d2 = d * d;
- boolean flag = d1 <= d2
- && this.entity.broadcastToPlayer(player)
- && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
+ // Paper start - Configurable entity tracking range by Y
+ boolean flag = d1 <= d2;
+ if (flag && level.paperConfig().entities.trackingRangeY.enabled) {
+ double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
+ if (rangeY != -1) {
+ double vec3d_dy = player.getY() - this.entity.getY();
+ flag = vec3d_dy * vec3d_dy <= rangeY * rangeY;
+ }
+ }
+ flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
+ // Paper end - Configurable entity tracking range by Y
+ // CraftBukkit start - respect vanish API
+ if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits
+ flag = false;
+ }
+ // CraftBukkit end
if (flag) {
if (this.seenBy.add(player.connection)) {
+ // Paper start - entity tracking events
+ if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) {
this.serverEntity.addPairing(player);
+ }
+ // Paper end - entity tracking events
}
} else if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
@@ -1331,6 +_,7 @@
for (Entity entity : this.entity.getIndirectPassengers()) {
int i1 = entity.getType().clientTrackingRange() * 16;
+ i1 = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i1); // Paper
if (i1 > i) {
i = i1;
}