mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-08 11:24:11 +01:00
375 lines
19 KiB
Diff
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;
|
|
}
|