326 lines
16 KiB
Diff
326 lines
16 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
|
Date: Mon, 31 Aug 2020 11:08:17 -0700
|
|
Subject: [PATCH] Actually unload POI data
|
|
|
|
While it's not likely for a poi data leak to be meaningful,
|
|
sometimes it is.
|
|
|
|
This patch also prevents the saving/unloading of POI data when
|
|
world saving is disabled.
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 6bae8378c14aa1ae35cf31f284bda2fcc61f1322..1794893cab3f26faa58745a95cb50d239f8a1484 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -864,6 +864,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
// Paper end
|
|
}
|
|
+ this.getPoiManager().dequeueUnload(holder.pos.longKey); // Paper - unload POI data
|
|
|
|
this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy
|
|
this.modified = true;
|
|
@@ -1015,7 +1016,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
private void processUnloads(BooleanSupplier shouldKeepTicking) {
|
|
LongIterator longiterator = this.toDrop.iterator();
|
|
- for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) {
|
|
+ for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) { // Paper - diff on change
|
|
long j = longiterator.nextLong();
|
|
ChunkHolder playerchunk = this.updatingChunks.queueRemove(j); // Paper - Don't copy
|
|
|
|
@@ -1063,6 +1064,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
|
|
}
|
|
// Paper end
|
|
+ this.getPoiManager().queueUnload(holder.pos.longKey, MinecraftServer.currentTickLong + 1); // Paper - unload POI data
|
|
if (ichunkaccess instanceof LevelChunk) {
|
|
((LevelChunk) ichunkaccess).setLoaded(false);
|
|
}
|
|
@@ -1092,6 +1094,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
for (int index = 0, len = this.regionManagers.size(); index < len; ++index) {
|
|
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
|
|
}
|
|
+ this.getPoiManager().queueUnload(holder.pos.longKey, MinecraftServer.currentTickLong + 1); // Paper - unload POI data
|
|
} // Paper end
|
|
} finally { this.unloadingPlayerChunk = unloadingBefore; } // Paper - do not allow ticket level changes while unloading chunks
|
|
|
|
@@ -1167,6 +1170,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
this.poiManager.loadInData(pos, chunkHolder.poiData);
|
|
chunkHolder.tasks.forEach(Runnable::run);
|
|
+ this.getPoiManager().dequeueUnload(pos.longKey); // Paper
|
|
|
|
if (chunkHolder.protoChunk != null) {
|
|
ProtoChunk protochunk = chunkHolder.protoChunk;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
|
|
index ab9bb440c8e91ecb49c1e14a427d35087a87ac80..400732788820a94acebccb1ad64789e0f6defdb1 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
|
|
@@ -1,5 +1,6 @@
|
|
package net.minecraft.world.entity.ai.village.poi;
|
|
|
|
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Paper
|
|
import com.mojang.datafixers.DataFixer;
|
|
import com.mojang.datafixers.util.Pair;
|
|
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
|
|
@@ -38,16 +39,145 @@ import net.minecraft.world.level.chunk.storage.SectionStorage;
|
|
public class PoiManager extends SectionStorage<PoiSection> {
|
|
public static final int MAX_VILLAGE_DISTANCE = 6;
|
|
public static final int VILLAGE_SECTION_SIZE = 1;
|
|
- private final PoiManager.DistanceTracker distanceTracker;
|
|
+ // Paper start - unload poi data
|
|
+ // the vanilla tracker needs to be replaced because it does not support level removes
|
|
+ private final io.papermc.paper.util.misc.Delayed26WayDistancePropagator3D villageDistanceTracker = new io.papermc.paper.util.misc.Delayed26WayDistancePropagator3D();
|
|
+ static final int POI_DATA_SOURCE = 7;
|
|
+ public static int convertBetweenLevels(final int level) {
|
|
+ return POI_DATA_SOURCE - level;
|
|
+ }
|
|
+
|
|
+ protected void updateDistanceTracking(long section) {
|
|
+ if (this.isVillageCenter(section)) {
|
|
+ this.villageDistanceTracker.setSource(section, POI_DATA_SOURCE);
|
|
+ } else {
|
|
+ this.villageDistanceTracker.removeSource(section);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - unload poi data
|
|
private final LongSet loadedChunks = new LongOpenHashSet();
|
|
private final net.minecraft.server.level.ServerLevel world; // Paper
|
|
|
|
public PoiManager(Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) {
|
|
super(path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world);
|
|
+ if (world == null) { throw new IllegalStateException("world must be non-null"); } // Paper - require non-null
|
|
this.world = (net.minecraft.server.level.ServerLevel)world; // Paper
|
|
- this.distanceTracker = new PoiManager.DistanceTracker();
|
|
}
|
|
|
|
+ // Paper start - actually unload POI data
|
|
+ private final java.util.TreeSet<QueuedUnload> queuedUnloads = new java.util.TreeSet<>();
|
|
+ private final Long2ObjectOpenHashMap<QueuedUnload> queuedUnloadsByCoordinate = new Long2ObjectOpenHashMap<>();
|
|
+
|
|
+ static final class QueuedUnload implements Comparable<QueuedUnload> {
|
|
+
|
|
+ private final long unloadTick;
|
|
+ private final long coordinate;
|
|
+
|
|
+ public QueuedUnload(long unloadTick, long coordinate) {
|
|
+ this.unloadTick = unloadTick;
|
|
+ this.coordinate = coordinate;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int compareTo(QueuedUnload other) {
|
|
+ if (other.unloadTick == this.unloadTick) {
|
|
+ return Long.compare(this.coordinate, other.coordinate);
|
|
+ } else {
|
|
+ return Long.compare(this.unloadTick, other.unloadTick);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int hashCode() {
|
|
+ int hash = 1;
|
|
+ hash = hash * 31 + Long.hashCode(this.unloadTick);
|
|
+ hash = hash * 31 + Long.hashCode(this.coordinate);
|
|
+ return hash;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean equals(Object obj) {
|
|
+ if (obj == null || obj.getClass() != QueuedUnload.class) {
|
|
+ return false;
|
|
+ }
|
|
+ QueuedUnload other = (QueuedUnload)obj;
|
|
+ return other.unloadTick == this.unloadTick && other.coordinate == this.coordinate;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ long determineDelay(long coordinate) {
|
|
+ if (this.isEmpty(coordinate)) {
|
|
+ return 5 * 60 * 20;
|
|
+ } else {
|
|
+ return 60 * 20;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void queueUnload(long coordinate, long minTarget) {
|
|
+ io.papermc.paper.util.TickThread.softEnsureTickThread("async poi unload queue");
|
|
+ QueuedUnload unload = new QueuedUnload(minTarget + this.determineDelay(coordinate), coordinate);
|
|
+ QueuedUnload existing = this.queuedUnloadsByCoordinate.put(coordinate, unload);
|
|
+ if (existing != null) {
|
|
+ this.queuedUnloads.remove(existing);
|
|
+ }
|
|
+ this.queuedUnloads.add(unload);
|
|
+ }
|
|
+
|
|
+ public void dequeueUnload(long coordinate) {
|
|
+ io.papermc.paper.util.TickThread.softEnsureTickThread("async poi unload dequeue");
|
|
+ QueuedUnload unload = this.queuedUnloadsByCoordinate.remove(coordinate);
|
|
+ if (unload != null) {
|
|
+ this.queuedUnloads.remove(unload);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void pollUnloads(BooleanSupplier canSleepForTick) {
|
|
+ io.papermc.paper.util.TickThread.softEnsureTickThread("async poi unload");
|
|
+ long currentTick = net.minecraft.server.MinecraftServer.currentTickLong;
|
|
+ net.minecraft.server.level.ServerChunkCache chunkProvider = this.world.getChunkSource();
|
|
+ net.minecraft.server.level.ChunkMap playerChunkMap = chunkProvider.chunkMap;
|
|
+ // copied target determination from PlayerChunkMap
|
|
+
|
|
+ java.util.Iterator<QueuedUnload> iterator = this.queuedUnloads.iterator();
|
|
+ for (int i = 0; iterator.hasNext() && (i < 200 || this.queuedUnloads.size() > 2000 || canSleepForTick.getAsBoolean()); i++) {
|
|
+ QueuedUnload unload = iterator.next();
|
|
+ if (unload.unloadTick > currentTick) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ long coordinate = unload.coordinate;
|
|
+
|
|
+ iterator.remove();
|
|
+ this.queuedUnloadsByCoordinate.remove(coordinate);
|
|
+
|
|
+ if (playerChunkMap.getUnloadingChunkHolder(net.minecraft.server.MCUtil.getCoordinateX(coordinate), net.minecraft.server.MCUtil.getCoordinateZ(coordinate)) != null
|
|
+ || playerChunkMap.getUpdatingChunkIfPresent(coordinate) != null) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ this.unloadData(coordinate);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void unloadData(long coordinate) {
|
|
+ io.papermc.paper.util.TickThread.softEnsureTickThread("async unloading poi data");
|
|
+ super.unloadData(coordinate);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onUnload(long coordinate) {
|
|
+ io.papermc.paper.util.TickThread.softEnsureTickThread("async poi unload callback");
|
|
+ this.loadedChunks.remove(coordinate);
|
|
+ int chunkX = net.minecraft.server.MCUtil.getCoordinateX(coordinate);
|
|
+ int chunkZ = net.minecraft.server.MCUtil.getCoordinateZ(coordinate);
|
|
+ for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
|
|
+ long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
|
|
+ this.updateDistanceTracking(sectionPos);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - actually unload POI data
|
|
+
|
|
public void add(BlockPos pos, Holder<PoiType> type) {
|
|
this.getOrCreate(SectionPos.asLong(pos)).add(pos, type);
|
|
}
|
|
@@ -182,8 +312,8 @@ public class PoiManager extends SectionStorage<PoiSection> {
|
|
}
|
|
|
|
public int sectionsToVillage(SectionPos pos) {
|
|
- this.distanceTracker.runAllUpdates();
|
|
- return this.distanceTracker.getLevel(pos.asLong());
|
|
+ this.villageDistanceTracker.propagateUpdates(); // Paper - replace distance tracking util
|
|
+ return convertBetweenLevels(this.villageDistanceTracker.getLevel(io.papermc.paper.util.CoordinateUtils.getChunkSectionKey(pos))); // Paper - replace distance tracking util
|
|
}
|
|
|
|
boolean isVillageCenter(long pos) {
|
|
@@ -198,7 +328,7 @@ public class PoiManager extends SectionStorage<PoiSection> {
|
|
@Override
|
|
public void tick(BooleanSupplier shouldKeepTicking) {
|
|
// Paper start - async chunk io
|
|
- while (!this.dirty.isEmpty() && shouldKeepTicking.getAsBoolean()) {
|
|
+ while (!this.dirty.isEmpty() && shouldKeepTicking.getAsBoolean() && !this.world.noSave()) { // Paper - unload POI data - don't write to disk if saving is disabled
|
|
ChunkPos chunkcoordintpair = SectionPos.of(this.dirty.firstLong()).chunk();
|
|
|
|
net.minecraft.nbt.CompoundTag data;
|
|
@@ -208,19 +338,24 @@ public class PoiManager extends SectionStorage<PoiSection> {
|
|
com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.world,
|
|
chunkcoordintpair.x, chunkcoordintpair.z, data, null, com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY);
|
|
}
|
|
+ // Paper start - unload POI data
|
|
+ if (!this.world.noSave()) { // don't write to disk if saving is disabled
|
|
+ this.pollUnloads(shouldKeepTicking);
|
|
+ }
|
|
+ // Paper end - unload POI data
|
|
// Paper end
|
|
- this.distanceTracker.runAllUpdates();
|
|
+ this.villageDistanceTracker.propagateUpdates(); // Paper - replace distance tracking until
|
|
}
|
|
|
|
@Override
|
|
protected void setDirty(long pos) {
|
|
super.setDirty(pos);
|
|
- this.distanceTracker.update(pos, this.distanceTracker.getLevelFromSource(pos), false);
|
|
+ this.updateDistanceTracking(pos); // Paper - move to new distance tracking util
|
|
}
|
|
|
|
@Override
|
|
protected void onSectionLoad(long pos) {
|
|
- this.distanceTracker.update(pos, this.distanceTracker.getLevelFromSource(pos), false);
|
|
+ this.updateDistanceTracking(pos); // Paper - move to new distance tracking util
|
|
}
|
|
|
|
public void checkConsistencyWithBlocks(ChunkPos chunkPos, LevelChunkSection chunkSection) {
|
|
@@ -278,7 +413,7 @@ public class PoiManager extends SectionStorage<PoiSection> {
|
|
|
|
@Override
|
|
protected int getLevelFromSource(long id) {
|
|
- return PoiManager.this.isVillageCenter(id) ? 0 : 7;
|
|
+ return PoiManager.this.isVillageCenter(id) ? 0 : 7; // Paper - unload poi data - diff on change, this specifies the source level to use for distance tracking
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
|
|
index 72be000b43c0026bc3b9236171f00a73cc4078e9..0f0718f0ee729715afcc4ba48307f5768a1a088d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
|
|
@@ -58,6 +58,40 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
|
|
// Paper - remove mojang I/O thread
|
|
}
|
|
|
|
+ // Paper start - actually unload POI data
|
|
+ public void unloadData(long coordinate) {
|
|
+ ChunkPos chunkPos = new ChunkPos(coordinate);
|
|
+ this.flush(chunkPos);
|
|
+
|
|
+ Long2ObjectMap<Optional<R>> data = this.storage;
|
|
+ int before = data.size();
|
|
+
|
|
+ for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
|
|
+ data.remove(SectionPos.asLong(chunkPos.x, section, chunkPos.z));
|
|
+ }
|
|
+
|
|
+ if (before != data.size()) {
|
|
+ this.onUnload(coordinate);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ protected void onUnload(long coordinate) {}
|
|
+
|
|
+ public boolean isEmpty(long coordinate) {
|
|
+ Long2ObjectMap<Optional<R>> data = this.storage;
|
|
+ int x = net.minecraft.server.MCUtil.getCoordinateX(coordinate);
|
|
+ int z = net.minecraft.server.MCUtil.getCoordinateZ(coordinate);
|
|
+ for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
|
|
+ Optional<R> optional = data.get(SectionPos.asLong(x, section, z));
|
|
+ if (optional != null && optional.orElse(null) != null) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ // Paper end - actually unload POI data
|
|
+
|
|
protected void tick(BooleanSupplier shouldKeepTicking) {
|
|
while(this.hasWork() && shouldKeepTicking.getAsBoolean()) {
|
|
ChunkPos chunkPos = SectionPos.of(this.dirty.firstLong()).chunk();
|
|
@@ -175,6 +209,7 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
|
|
});
|
|
}
|
|
}
|
|
+ if (this instanceof net.minecraft.world.entity.ai.village.poi.PoiManager) { ((net.minecraft.world.entity.ai.village.poi.PoiManager)this).queueUnload(pos.longKey, net.minecraft.server.MinecraftServer.currentTickLong + 1); } // Paper - unload POI data
|
|
|
|
}
|
|
|