-
Notifications
You must be signed in to change notification settings - Fork 435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async chunk loading for spigot/paper 1.21.3/1.21.4 #4188
base: v3.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
import org.bukkit.World; | ||
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld; | ||
import org.dynmap.DynmapChunk; | ||
import org.dynmap.Log; | ||
import org.dynmap.bukkit.helper.BukkitWorld; | ||
import org.dynmap.common.BiomeMap; | ||
import org.dynmap.common.chunk.GenericChunk; | ||
|
@@ -18,6 +19,9 @@ | |
import java.util.List; | ||
import java.util.NoSuchElementException; | ||
import java.util.concurrent.CancellationException; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread | ||
|
@@ -31,6 +35,31 @@ public MapChunkCache121_3(GenericChunkCache cc) { | |
super(cc); | ||
} | ||
|
||
@Override | ||
protected Supplier<GenericChunk> getLoadedChunkAsync(DynmapChunk chunk) { | ||
CompletableFuture<GenericChunk> genericChunk = CompletableFuture.supplyAsync(() -> { | ||
CraftWorld cw = (CraftWorld) w; | ||
Chunk c = cw.getHandle().getChunkIfLoaded(chunk.x, chunk.z); | ||
if (c == null || !c.q) { //!c.loaded | ||
return null; | ||
} | ||
SerializableChunkData chunkData = SerializableChunkData.a(cw.getHandle(), c); //SerializableChunkData.copyOf | ||
NBTTagCompound nbt = chunkData.a(); // SerializableChunkData.write | ||
return parseChunkFromNBT(new NBT.NBTCompound(nbt)); | ||
}); | ||
return () -> { | ||
try { | ||
return genericChunk.get(); | ||
} catch (InterruptedException e) { | ||
Log.severe("Error loading cached chunk async", e); | ||
Thread.currentThread().interrupt(); | ||
} catch (ExecutionException e) { | ||
Log.severe("Error loading cached chunk async", e); | ||
} | ||
return null; | ||
}; | ||
} | ||
|
||
protected GenericChunk getLoadedChunk(DynmapChunk chunk) { | ||
CraftWorld cw = (CraftWorld) w; | ||
if (!cw.isChunkLoaded(chunk.x, chunk.z)) return null; | ||
|
@@ -41,6 +70,24 @@ protected GenericChunk getLoadedChunk(DynmapChunk chunk) { | |
return nbt != null ? parseChunkFromNBT(new NBT.NBTCompound(nbt)) : null; | ||
} | ||
|
||
@Override | ||
protected Supplier<GenericChunk> loadChunkAsync(DynmapChunk chunk){ | ||
CraftWorld cw = (CraftWorld) w; | ||
CompletableFuture<GenericChunk> genericChunk = cw.getHandle().m().a.d(new ChunkCoordIntPair(chunk.x, chunk.z)) // WorldServer.getChunkSource().chunkMap.read(new ChunkCoordIntPair(chunk.x, chunk.z)) | ||
.thenApply(opt -> opt.map(NBT.NBTCompound::new).map(this::parseChunkFromNBT).orElse(null)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this to supplier to make sure it's not gonna be executed on main thread There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not how async and cf work. All operations chained with "then" will get executed on the thread the load itself is executed. So basically the return value is not that elegant. In theory the cf itself should be returned, but we are bound to the given interface. So moving this to the supplier will result in getting it executed async when the supplier is requested, but why wait when we can start loading it right now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep, and that's what you should avoid, it's not a thread that you can say with confidence that you can stress with additional load. And this is the reason why you should move it to supplier, as you'll for sure be in async thread pool that you can load with stuff like parsing the chunk |
||
return () -> { | ||
try { | ||
return genericChunk.get(); | ||
} catch (InterruptedException e) { | ||
Log.severe("Error loading chunk async", e); | ||
Thread.currentThread().interrupt(); | ||
} catch (ExecutionException e) { | ||
Log.severe("Error loading chunk async", e); | ||
} | ||
return null; | ||
}; | ||
} | ||
|
||
protected GenericChunk loadChunk(DynmapChunk chunk) { | ||
CraftWorld cw = (CraftWorld) w; | ||
NBTTagCompound nbt = null; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kinda missed a bit of the impl description, up to
SerializableChunkData.a(cw.getHandle(), c)
needs to be executed on main thread, the part bellow it should be moved into the supplier so it would be executed asyncBasically the cf should return SerializableChunkData and not the parsed chunk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why? I can see no obvious reason to not load in on the parallel thread. No need to stress the main thread here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's saving data that is really not liking concurrent access, and accessing chunk in async in Minecraft is always calling for issues