Skip to content

Commit d73ff19

Browse files
authored
feat: do not wait for chunk loads when calling (#2912)
* feat: do not wait for chunk loads when calling * Add comment * It's ugly but it stops OOM * Fix copykey * Apply to all versions * Add config options * feat: add configurable per-thread target-size with programmatic default * Add for 1.21.3 * Abstractify bukkit get blocks a bit * Clean up some more duplication
1 parent 40d0422 commit d73ff19

File tree

37 files changed

+2988
-3246
lines changed

37 files changed

+2988
-3246
lines changed

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java

Lines changed: 385 additions & 509 deletions
Large diffs are not rendered by default.

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
44
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
55
import com.fastasyncworldedit.core.queue.IBlocks;
6+
import com.fastasyncworldedit.core.queue.IChunk;
67
import com.fastasyncworldedit.core.queue.IChunkGet;
78
import com.fastasyncworldedit.core.queue.IChunkSet;
9+
import com.fastasyncworldedit.core.queue.IQueueExtent;
810
import com.fastasyncworldedit.core.util.NbtUtils;
911
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
1012
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
@@ -273,7 +275,7 @@ public int[] getHeightMap(HeightMapType type) {
273275
}
274276

275277
@Override
276-
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
278+
public <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalize) {
277279
return null;
278280
}
279281

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
import net.minecraft.world.level.chunk.SingleValuePalette;
5858
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
5959
import org.apache.logging.log4j.Logger;
60-
import org.bukkit.Bukkit;
6160
import org.bukkit.craftbukkit.v1_20_R2.CraftChunk;
6261

6362
import javax.annotation.Nonnull;
@@ -79,9 +78,8 @@
7978
import java.util.Map;
8079
import java.util.Optional;
8180
import java.util.concurrent.CompletableFuture;
81+
import java.util.concurrent.ExecutionException;
8282
import java.util.concurrent.Semaphore;
83-
import java.util.concurrent.TimeUnit;
84-
import java.util.concurrent.TimeoutException;
8583
import java.util.function.IntFunction;
8684

8785
import static java.lang.invoke.MethodType.methodType;
@@ -276,12 +274,49 @@ static DelegateSemaphore applyLock(LevelChunkSection section) {
276274
}
277275
}
278276
} catch (Throwable e) {
279-
e.printStackTrace();
277+
LOGGER.error("Error apply DelegateSemaphore", e);
280278
throw new RuntimeException(e);
281279
}
282280
}
283281

284-
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
282+
public static CompletableFuture<LevelChunk> ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
283+
LevelChunk levelChunk = getChunkImmediatelyAsync(serverLevel, chunkX, chunkZ);
284+
if (levelChunk != null) {
285+
return CompletableFuture.completedFuture(levelChunk);
286+
}
287+
if (PaperLib.isPaper()) {
288+
CompletableFuture<LevelChunk> future = serverLevel
289+
.getWorld()
290+
.getChunkAtAsync(chunkX, chunkZ, true, true)
291+
.thenApply(chunk -> {
292+
addTicket(serverLevel, chunkX, chunkZ);
293+
try {
294+
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
295+
} catch (Throwable e) {
296+
LOGGER.error("Could not asynchronously load chunk at {},{}", chunkX, chunkZ, e);
297+
return null;
298+
}
299+
});
300+
try {
301+
if (!future.isCompletedExceptionally() || (future.isDone() && future.get() != null)) {
302+
return future;
303+
}
304+
Throwable t = future.exceptionNow();
305+
LOGGER.error("Asynchronous chunk load at {},{} exceptionally completed immediately", chunkX, chunkZ, t);
306+
} catch (InterruptedException | ExecutionException e) {
307+
LOGGER.error(
308+
"Unexpected error when getting completed future at chunk {},{}. Returning to default.",
309+
chunkX,
310+
chunkZ,
311+
e
312+
);
313+
}
314+
}
315+
return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)));
316+
}
317+
318+
319+
public static @Nullable LevelChunk getChunkImmediatelyAsync(ServerLevel serverLevel, int chunkX, int chunkZ) {
285320
if (!PaperLib.isPaper()) {
286321
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
287322
if (nmsChunk != null) {
@@ -290,6 +325,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c
290325
if (Fawe.isMainThread()) {
291326
return serverLevel.getChunk(chunkX, chunkZ);
292327
}
328+
return null;
293329
} else {
294330
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
295331
if (nmsChunk != null) {
@@ -305,30 +341,8 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c
305341
if (Fawe.isMainThread()) {
306342
return serverLevel.getChunk(chunkX, chunkZ);
307343
}
308-
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
309-
try {
310-
CraftChunk chunk;
311-
try {
312-
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
313-
} catch (TimeoutException e) {
314-
String world = serverLevel.getWorld().getName();
315-
// We've already taken 10 seconds we can afford to wait a little here.
316-
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
317-
if (loaded) {
318-
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
319-
// Retry chunk load
320-
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
321-
} else {
322-
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
323-
}
324-
}
325-
addTicket(serverLevel, chunkX, chunkZ);
326-
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
327-
} catch (Throwable e) {
328-
e.printStackTrace();
329-
}
344+
return null;
330345
}
331-
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
332346
}
333347

334348
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
@@ -672,7 +686,7 @@ static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
672686
}
673687
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
674688
} catch (Throwable throwable) {
675-
throwable.printStackTrace();
689+
LOGGER.error("Error removing beacon", throwable);
676690
}
677691
}
678692

0 commit comments

Comments
 (0)