Rework async chunk api implementation

Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.

Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.

Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.

Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.

Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
This commit is contained in:
Spottedleaf
2024-11-18 22:34:32 -08:00
parent 76403d796b
commit 989fd77808
6 changed files with 449 additions and 505 deletions

View File

@@ -63,10 +63,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ */ + */
+ @Deprecated(since = "1.13.1") + @Deprecated(since = "1.13.1")
+ public default void getChunkAtAsync(int x, int z, @NotNull ChunkLoadCallback cb) { + public default void getChunkAtAsync(int x, int z, @NotNull ChunkLoadCallback cb) {
+ getChunkAtAsync(x, z, true).thenAccept(cb::onLoad).exceptionally((ex) -> { + this.getChunkAtAsync(x, z, (java.util.function.Consumer<Chunk>)cb);
+ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
+ return null;
+ });
+ } + }
+ +
+ /** + /**
@@ -89,10 +86,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ */ + */
+ @Deprecated(since = "1.13.1") + @Deprecated(since = "1.13.1")
+ public default void getChunkAtAsync(@NotNull Location loc, @NotNull ChunkLoadCallback cb) { + public default void getChunkAtAsync(@NotNull Location loc, @NotNull ChunkLoadCallback cb) {
+ getChunkAtAsync(loc, true).thenAccept(cb::onLoad).exceptionally((ex) -> { + this.getChunkAtAsync(loc.getBlockX() >> 4, loc.getBlockZ() >> 4, cb);
+ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
+ return null;
+ });
+ } + }
+ +
+ /** + /**
@@ -115,10 +109,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ */ + */
+ @Deprecated(since = "1.13.1") + @Deprecated(since = "1.13.1")
+ public default void getChunkAtAsync(@NotNull Block block, @NotNull ChunkLoadCallback cb) { + public default void getChunkAtAsync(@NotNull Block block, @NotNull ChunkLoadCallback cb) {
+ getChunkAtAsync(block, true).thenAccept(cb::onLoad).exceptionally((ex) -> { + this.getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, cb);
+ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
+ return null;
+ });
+ } + }
+ +
+ /** + /**
@@ -140,10 +131,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * will be executed synchronously + * will be executed synchronously
+ */ + */
+ default void getChunkAtAsync(final int x, final int z, final @NotNull Consumer<? super Chunk> cb) { + default void getChunkAtAsync(final int x, final int z, final @NotNull Consumer<? super Chunk> cb) {
+ this.getChunkAtAsync(x, z, true).thenAccept(cb).exceptionally((ex) -> { + this.getChunkAtAsync(x, z, true, cb);
+ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
+ return null;
+ });
+ } + }
+ +
+ /** + /**
@@ -166,13 +154,58 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * will be executed synchronously + * will be executed synchronously
+ */ + */
+ default void getChunkAtAsync(final int x, final int z, final boolean gen, final @NotNull Consumer<? super Chunk> cb) { + default void getChunkAtAsync(final int x, final int z, final boolean gen, final @NotNull Consumer<? super Chunk> cb) {
+ this.getChunkAtAsync(x, z, gen).thenAccept(cb).exceptionally((ex) -> { + this.getChunkAtAsync(x, z, gen, false, cb);
+ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
+ return null;
+ });
+ } + }
+ +
+ /** + /**
+ * Requests a {@link Chunk} to be loaded at the given coordinates
+ *
+ * This method makes no guarantee on how fast the chunk will load,
+ * and will return the chunk to the callback at a later time.
+ *
+ * You should use this method if you need a chunk but do not need it
+ * immediately, and you wish to let the server control the speed
+ * of chunk loads, keeping performance in mind.
+ *
+ * The {@link java.util.function.Consumer} will always be executed synchronously
+ * on the main Server Thread.
+ *
+ * @param x Chunk X-coordinate of the chunk - floor(world coordinate / 16)
+ * @param z Chunk Z-coordinate of the chunk - floor(world coordinate / 16)
+ * @param gen Should we generate a chunk if it doesn't exist or not
+ * @param urgent If true, the chunk may be prioritised to be loaded above other chunks in queue
+ * @param cb Callback to receive the chunk when it is loaded.
+ * will be executed synchronously
+ */
+ void getChunkAtAsync(final int x, final int z, final boolean gen, final boolean urgent, final @NotNull Consumer<? super Chunk> cb);
+
+ /**
+ * Requests all chunks with x between [minX, maxZ] and z
+ * between [minZ, maxZ] to be loaded.
+ *
+ * This method makes no guarantee on how fast the chunk will load,
+ * and will invoke the callback at possibly a later time.
+ *
+ * You should use this method if you need chunks loaded but do not need them
+ * immediately, and you wish to let the server control the speed
+ * of chunk loads, keeping performance in mind.
+ *
+ * The {@link Runnable} will always be executed synchronously
+ * on the main Server Thread, and when invoked all chunks requested will be loaded.
+ *
+ * @param minX Minimum chunk X-coordinate of the chunk - floor(world coordinate / 16)
+ * @param minZ Minimum chunk Z-coordinate of the chunk - floor(world coordinate / 16)
+ * @param maxX Maximum chunk X-coordinate of the chunk - floor(world coordinate / 16)
+ * @param maxZ Maximum chunk Z-coordinate of the chunk - floor(world coordinate / 16)
+ * @param urgent If true, the chunks may be prioritised to be loaded above other chunks in queue
+ * @param cb Callback to invoke when all chunks are loaded.
+ * Will be executed synchronously
+ * @see Chunk
+ */
+ void getChunksAtAsync(final int minX, final int minZ, final int maxX, final int maxZ, final boolean urgent,
+ final Runnable cb);
+
+ /**
+ * Requests a {@link Chunk} to be loaded at the given {@link Location} + * Requests a {@link Chunk} to be loaded at the given {@link Location}
+ * + *
+ * This method makes no guarantee on how fast the chunk will load, + * This method makes no guarantee on how fast the chunk will load,
@@ -478,7 +511,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return this.getChunkAtAsync(x, z, true, true); + return this.getChunkAtAsync(x, z, true, true);
+ } + }
+ +
+ java.util.concurrent.@NotNull CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent); + default @NotNull java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
+ java.util.concurrent.CompletableFuture<Chunk> ret = new java.util.concurrent.CompletableFuture<>();
+ this.getChunkAtAsync(x, z, gen, urgent, ret::complete);
+ return ret;
+ }
+ // Paper end - async chunks API + // Paper end - async chunks API
+ +
/** /**

View File

@@ -1,103 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 9 Jun 2020 03:33:03 -0400
Subject: [PATCH] Add Plugin Tickets to API Chunk Methods
Like previous versions, plugins loading chunks kept them loaded until
they garbage collected to avoid constant spamming of chunk loads
This adds tickets to a few more places so that they can be unloaded.
Additionally, this drops their ticket level to BORDER so they wont be ticking
so they will just sit inactive instead.
Using .loadChunk to keep a chunk ticking was a horrible idea for upstream
when we have TWO methods that are able to do that already in the API.
Also reduce their collection count down to a maximum of 1 second. Barely
anyone knows what chunk-gc is in bukkit.yml as its less relevant now, and
since this wasn't spigot behavior, this is safe to mostly ignore (unless someone
wants it to collect even faster, they can restore that setting back to 1 instead of 20+)
Not adding it to .getType() though to keep behavior consistent with vanilla for performance reasons.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -0,0 +0,0 @@ public final class CraftServer implements Server {
this.overrideSpawnLimits();
console.autosavePeriod = this.configuration.getInt("ticks-per.autosave");
this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose"));
- TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks");
+ TicketType.PLUGIN.timeout = Math.min(20, this.configuration.getInt("chunk-gc.period-in-ticks")); // Paper - cap plugin loads to 1 second
this.minimumAPI = ApiVersion.getOrCreateVersion(this.configuration.getString("settings.minimum-api"));
this.loadIcon();
this.loadCompatibilities();
@@ -0,0 +0,0 @@ public final class CraftServer implements Server {
this.console.setMotd(config.motd);
this.overrideSpawnLimits();
this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose"));
- TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks");
+ TicketType.PLUGIN.timeout = Math.min(20, configuration.getInt("chunk-gc.period-in-ticks")); // Paper - cap plugin loads to 1 second
this.minimumAPI = ApiVersion.getOrCreateVersion(this.configuration.getString("settings.minimum-api"));
this.printSaveWarning = false;
this.console.autosavePeriod = this.configuration.getInt("ticks-per.autosave");
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public Chunk getChunkAt(int x, int z) {
- net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) this.world.getChunk(x, z, ChunkStatus.FULL, true);
+ // Paper start - add ticket to hold chunk for a little while longer if plugin accesses it
+ net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
+ if (chunk == null) {
+ this.addTicket(x, z);
+ chunk = this.world.getChunkSource().getChunk(x, z, true);
+ }
+ // Paper end
return new CraftChunk(chunk);
}
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return new CraftChunk(this.getHandle(), x, z);
}
+ // Paper start
+ private void addTicket(int x, int z) {
+ io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE)); // Paper
+ }
+ // Paper end
+
@Override
public Chunk getChunkAt(Block block) {
Preconditions.checkArgument(block != null, "null block");
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public boolean unloadChunkRequest(int x, int z) {
org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
if (this.isChunkLoaded(x, z)) {
- this.world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE);
+ this.world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper
}
return true;
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE);
+ this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper
return true;
}
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
ca.spottedleaf.moonrise.common.util.ChunkSystem.scheduleChunkLoad(this.getHandle(), x, z, gen, ChunkStatus.FULL, true, priority, (c) -> {
net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c;
+ if (chunk != null) this.addTicket(x, z); // Paper
ret.complete(chunk == null ? null : new CraftChunk(chunk));
});
});

View File

@@ -102,329 +102,330 @@ new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch GIT binary patch
literal 16900 literal 16900
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
LFIp>X81%mY^Bg|U zpErH+ydwvGOs@R*Hs)&kpXVngrCvTXb;0-d@xj~oPM7_l|L;HY`3dlY6p$IBU;poR
XXyugpZ1#qMfq}u()z4*}Q$iB}^Bg|U
literal 0 literal 0
HcmV?d00001 HcmV?d00001

View File

@@ -3378,7 +3378,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ private static long chunkLoadCounter = 0L; + private static long chunkLoadCounter = 0L;
+ public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus, + public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
+ final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) { + final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
+ if (!org.bukkit.Bukkit.isPrimaryThread()) { + if (!org.bukkit.Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
+ scheduleChunkTask(level, chunkX, chunkZ, () -> { + scheduleChunkTask(level, chunkX, chunkZ, () -> {
+ scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete); + scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ }, priority); + }, priority);
@@ -3443,7 +3443,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status"); + throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
+ } + }
+ +
+ if (!org.bukkit.Bukkit.isPrimaryThread()) { + if (!org.bukkit.Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
+ scheduleChunkTask(level, chunkX, chunkZ, () -> { + scheduleChunkTask(level, chunkX, chunkZ, () -> {
+ scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete); + scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ }, priority); + }, priority);
@@ -5501,21 +5501,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }); + });
+ return; + return;
+ } + }
+ List<net.minecraft.world.level.chunk.ChunkAccess> ret = new java.util.ArrayList<>();
+ it.unimi.dsi.fastutil.ints.IntArrayList ticketLevels = new it.unimi.dsi.fastutil.ints.IntArrayList();
+
+ int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3; + int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3;
+ int maxBlockX = Mth.floor(axisalignedbb.maxX + 1.0E-7D) + 3;
+
+ int minBlockZ = Mth.floor(axisalignedbb.minZ - 1.0E-7D) - 3; + int minBlockZ = Mth.floor(axisalignedbb.minZ - 1.0E-7D) - 3;
+
+ int maxBlockX = Mth.floor(axisalignedbb.maxX + 1.0E-7D) + 3;
+ int maxBlockZ = Mth.floor(axisalignedbb.maxZ + 1.0E-7D) + 3; + int maxBlockZ = Mth.floor(axisalignedbb.maxZ + 1.0E-7D) + 3;
+ +
+ int minChunkX = minBlockX >> 4; + int minChunkX = minBlockX >> 4;
+ int maxChunkX = maxBlockX >> 4;
+
+ int minChunkZ = minBlockZ >> 4; + int minChunkZ = minBlockZ >> 4;
+
+ int maxChunkX = maxBlockX >> 4;
+ int maxChunkZ = maxBlockZ >> 4; + int maxChunkZ = maxBlockZ >> 4;
+ +
+ this.loadChunks(minChunkX, minChunkZ, maxChunkX, maxChunkZ, priority, onLoad);
+ }
+
+ public final void loadChunks(int minChunkX, int minChunkZ, int maxChunkX, int maxChunkZ,
+ ca.spottedleaf.concurrentutil.util.Priority priority,
+ java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
+ List<net.minecraft.world.level.chunk.ChunkAccess> ret = new java.util.ArrayList<>();
+ it.unimi.dsi.fastutil.ints.IntArrayList ticketLevels = new it.unimi.dsi.fastutil.ints.IntArrayList();
+ ServerChunkCache chunkProvider = this.getChunkSource(); + ServerChunkCache chunkProvider = this.getChunkSource();
+ +
+ int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1); + int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1);
@@ -6224,31 +6229,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
// Spigot end // Spigot end
+ // Paper start + // Paper start
+ public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) { + @Override
+ if (Bukkit.isPrimaryThread()) { + public void getChunkAtAsync(int x, int z, boolean gen, boolean urgent, @NotNull Consumer<? super Chunk> cb) {
+ net.minecraft.world.level.chunk.LevelChunk immediate = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z); + ca.spottedleaf.moonrise.common.util.ChunkSystem.scheduleChunkLoad(
+ if (immediate != null) { + this.getHandle(), x, z, gen, ChunkStatus.FULL, true,
+ return java.util.concurrent.CompletableFuture.completedFuture(new CraftChunk(immediate)); + urgent ? ca.spottedleaf.concurrentutil.util.Priority.HIGHER : ca.spottedleaf.concurrentutil.util.Priority.NORMAL,
+ (ChunkAccess chunk) -> {
+ cb.accept(chunk == null ? null : new CraftChunk((net.minecraft.world.level.chunk.LevelChunk)chunk));
+ } + }
+ } + );
+ +
+ ca.spottedleaf.concurrentutil.util.Priority priority; + }
+ if (urgent) {
+ priority = ca.spottedleaf.concurrentutil.util.Priority.HIGHER;
+ } else {
+ priority = ca.spottedleaf.concurrentutil.util.Priority.NORMAL;
+ }
+ +
+ java.util.concurrent.CompletableFuture<Chunk> ret = new java.util.concurrent.CompletableFuture<>(); + @Override
+ + public void getChunksAtAsync(int minX, int minZ, int maxX, int maxZ, boolean urgent, Runnable cb) {
+ ca.spottedleaf.moonrise.common.util.ChunkSystem.scheduleChunkLoad(this.getHandle(), x, z, gen, ChunkStatus.FULL, true, priority, (c) -> { + this.getHandle().loadChunks(
+ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> { + minX, minZ, maxX, maxZ,
+ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c; + urgent ? ca.spottedleaf.concurrentutil.util.Priority.HIGHER : ca.spottedleaf.concurrentutil.util.Priority.NORMAL,
+ ret.complete(chunk == null ? null : new CraftChunk(chunk)); + (List<ChunkAccess> chunks) -> {
+ }); + cb.run();
+ }); + }
+ + );
+ return ret;
+ } + }
+ +
+ @Override + @Override

View File

@@ -87,7 +87,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- private static long chunkLoadCounter = 0L; - private static long chunkLoadCounter = 0L;
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus, public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) { final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
- if (!org.bukkit.Bukkit.isPrimaryThread()) { - if (!org.bukkit.Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
- scheduleChunkTask(level, chunkX, chunkZ, () -> { - scheduleChunkTask(level, chunkX, chunkZ, () -> {
- scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete); - scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
- }, priority); - }, priority);
@@ -153,7 +153,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status"); - throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
- } - }
- -
- if (!org.bukkit.Bukkit.isPrimaryThread()) { - if (!org.bukkit.Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
- scheduleChunkTask(level, chunkX, chunkZ, () -> { - scheduleChunkTask(level, chunkX, chunkZ, () -> {
- scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete); - scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
- }, priority); - }, priority);
@@ -26821,17 +26821,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- }); - });
- return; - return;
- } - }
- List<net.minecraft.world.level.chunk.ChunkAccess> ret = new java.util.ArrayList<>();
- it.unimi.dsi.fastutil.ints.IntArrayList ticketLevels = new it.unimi.dsi.fastutil.ints.IntArrayList();
-
+ // Paper - rewrite chunk system + // Paper - rewrite chunk system
int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3; int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3;
int maxBlockX = Mth.floor(axisalignedbb.maxX + 1.0E-7D) + 3; int minBlockZ = Mth.floor(axisalignedbb.minZ - 1.0E-7D) - 3;
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
int minChunkZ = minBlockZ >> 4; public final void loadChunks(int minChunkX, int minChunkZ, int maxChunkX, int maxChunkZ,
int maxChunkZ = maxBlockZ >> 4; ca.spottedleaf.concurrentutil.util.Priority priority,
java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
- List<net.minecraft.world.level.chunk.ChunkAccess> ret = new java.util.ArrayList<>();
- it.unimi.dsi.fastutil.ints.IntArrayList ticketLevels = new it.unimi.dsi.fastutil.ints.IntArrayList();
- ServerChunkCache chunkProvider = this.getChunkSource(); - ServerChunkCache chunkProvider = this.getChunkSource();
+ this.moonrise$loadChunksAsync(minChunkX, maxChunkX, minChunkZ, maxChunkZ, priority, onLoad); // Paper - rewrite chunk system + this.moonrise$loadChunksAsync(minChunkX, maxChunkX, minChunkZ, maxChunkZ, priority, onLoad); // Paper - rewrite chunk system
+ } + }
@@ -26874,7 +26873,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder.ChunkCompletion lastCompletion = newChunkHolder.getLastChunkCompletion(); + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder.ChunkCompletion lastCompletion = newChunkHolder.getLastChunkCompletion();
+ return lastCompletion == null ? null : lastCompletion.chunk(); + return lastCompletion == null ? null : lastCompletion.chunk();
+ } + }
+
- int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1);
- int[] loadedChunks = new int[1];
+ @Override + @Override
+ public final ChunkAccess moonrise$getSpecificChunkIfLoaded(final int chunkX, final int chunkZ, final net.minecraft.world.level.chunk.status.ChunkStatus leastStatus) { + public final ChunkAccess moonrise$getSpecificChunkIfLoaded(final int chunkX, final int chunkZ, final net.minecraft.world.level.chunk.status.ChunkStatus leastStatus) {
+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkX, chunkZ); + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkX, chunkZ);
@@ -26883,26 +26884,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ return newChunkHolder.getChunkIfPresentUnchecked(leastStatus); + return newChunkHolder.getChunkIfPresentUnchecked(leastStatus);
+ } + }
+
- int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1);
- int[] loadedChunks = new int[1];
+ @Override + @Override
+ public final void moonrise$midTickTasks() { + public final void moonrise$midTickTasks() {
+ ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); + ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks();
+ } + }
+
- Long holderIdentifier = Long.valueOf(chunkProvider.chunkFutureAwaitCounter++);
+ @Override + @Override
+ public final ChunkAccess moonrise$syncLoadNonFull(final int chunkX, final int chunkZ, final net.minecraft.world.level.chunk.status.ChunkStatus status) { + public final ChunkAccess moonrise$syncLoadNonFull(final int chunkX, final int chunkZ, final net.minecraft.world.level.chunk.status.ChunkStatus status) {
+ return this.moonrise$getChunkTaskScheduler().syncLoadNonFull(chunkX, chunkZ, status); + return this.moonrise$getChunkTaskScheduler().syncLoadNonFull(chunkX, chunkZ, status);
+ } + }
- java.util.function.Consumer<net.minecraft.world.level.chunk.ChunkAccess> consumer = (net.minecraft.world.level.chunk.ChunkAccess chunk) -> { - Long holderIdentifier = Long.valueOf(chunkProvider.chunkFutureAwaitCounter++);
+ @Override + @Override
+ public final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler moonrise$getChunkTaskScheduler() { + public final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler moonrise$getChunkTaskScheduler() {
+ return this.chunkTaskScheduler; + return this.chunkTaskScheduler;
+ } + }
+
- java.util.function.Consumer<net.minecraft.world.level.chunk.ChunkAccess> consumer = (net.minecraft.world.level.chunk.ChunkAccess chunk) -> {
+ @Override + @Override
+ public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController moonrise$getChunkDataController() { + public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController moonrise$getChunkDataController() {
+ return this.chunkDataController; + return this.chunkDataController;
@@ -36084,11 +36083,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public Collection<Plugin> getPluginChunkTickets(int x, int z) { public Collection<Plugin> getPluginChunkTickets(int x, int z) {
DistanceManager chunkDistanceManager = this.world.getChunkSource().chunkMap.distanceManager; DistanceManager chunkDistanceManager = this.world.getChunkSource().chunkMap.distanceManager;
- SortedArraySet<Ticket<?>> tickets = chunkDistanceManager.tickets.get(ChunkPos.asLong(x, z)); - SortedArraySet<Ticket<?>> tickets = chunkDistanceManager.tickets.get(ChunkPos.asLong(x, z));
-
- if (tickets == null) { - if (tickets == null) {
- return Collections.emptyList(); - return Collections.emptyList();
- } - }
-
- ImmutableList.Builder<Plugin> ret = ImmutableList.builder(); - ImmutableList.Builder<Plugin> ret = ImmutableList.builder();
- for (Ticket<?> ticket : tickets) { - for (Ticket<?> ticket : tickets) {
- if (ticket.getType() == TicketType.PLUGIN_TICKET) { - if (ticket.getType() == TicketType.PLUGIN_TICKET) {

View File

@@ -43,9 +43,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@Override @Override
public Chunk getChunkAt(int x, int z) { public Chunk getChunkAt(int x, int z) {
+ warnUnsafeChunk("getting a faraway chunk", x, z); // Paper + warnUnsafeChunk("getting a faraway chunk", x, z); // Paper
// Paper start - add ticket to hold chunk for a little while longer if plugin accesses it net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) this.world.getChunk(x, z, ChunkStatus.FULL, true);
net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z); return new CraftChunk(chunk);
if (chunk == null) { }
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
if (!unloadChunk0(x, z, false)) { if (!unloadChunk0(x, z, false)) {
return false; return false;
@@ -87,10 +87,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z); return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z);
} }
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Spigot end
// Paper start // Paper start
public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) { @Override
public void getChunkAtAsync(int x, int z, boolean gen, boolean urgent, @NotNull Consumer<? super Chunk> cb) {
+ warnUnsafeChunk("getting a faraway chunk async", x, z); // Paper + warnUnsafeChunk("getting a faraway chunk async", x, z); // Paper
if (Bukkit.isPrimaryThread()) { ca.spottedleaf.moonrise.common.util.ChunkSystem.scheduleChunkLoad(
net.minecraft.world.level.chunk.LevelChunk immediate = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z); this.getHandle(), x, z, gen, ChunkStatus.FULL, true,
if (immediate != null) { urgent ? ca.spottedleaf.concurrentutil.util.Priority.HIGHER : ca.spottedleaf.concurrentutil.util.Priority.NORMAL,
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void getChunksAtAsync(int minX, int minZ, int maxX, int maxZ, boolean urgent, Runnable cb) {
+ warnUnsafeChunk("getting a faraway chunk async", minX, minZ); // Paper
+ warnUnsafeChunk("getting a faraway chunk async", maxX, maxZ); // Paper
this.getHandle().loadChunks(
minX, minZ, maxX, maxZ,
urgent ? ca.spottedleaf.concurrentutil.util.Priority.HIGHER : ca.spottedleaf.concurrentutil.util.Priority.NORMAL,