From e161abea56e00bd7d465e8fb8c01c87452df22e0 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 14 Sep 2024 18:32:37 +0100 Subject: [PATCH] feat: do not wait for chunk loads when calling --- .../fawe/v1_20_R2/PaperweightGetBlocks.java | 4 +- .../v1_20_R2/PaperweightGetBlocks_Copy.java | 4 +- .../fawe/v1_20_R3/PaperweightGetBlocks.java | 4 +- .../v1_20_R3/PaperweightGetBlocks_Copy.java | 4 +- .../fawe/v1_20_R4/PaperweightGetBlocks.java | 4 +- .../v1_20_R4/PaperweightGetBlocks_Copy.java | 4 +- .../fawe/v1_21_R1/PaperweightGetBlocks.java | 83 +++++++++++++------ .../v1_21_R1/PaperweightGetBlocks_Copy.java | 4 +- .../v1_21_R1/PaperweightPlatformAdapter.java | 65 +++++++++------ .../fastasyncworldedit/core/FaweCache.java | 3 +- .../core/queue/IBatchProcessor.java | 5 +- .../core/queue/IChunkGet.java | 6 +- .../core/queue/IQueueExtent.java | 10 ++- .../implementation/ParallelQueueExtent.java | 2 + .../queue/implementation/QueueHandler.java | 17 +++- .../SingleThreadQueueExtent.java | 16 +++- .../implementation/blocks/CharBlocks.java | 13 ++- .../implementation/blocks/NullChunkGet.java | 4 +- .../implementation/chunk/ChunkHolder.java | 8 +- .../queue/implementation/chunk/NullChunk.java | 4 +- 20 files changed, 185 insertions(+), 79 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index a325bb08b..7b5320956 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java @@ -9,8 +9,10 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; @@ -423,7 +425,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") - public synchronized > T call(IChunkSet set, Runnable finalizer) { + public synchronized > T call(IQueueExtent owner, IChunkSet set, Runnable finalizer) { if (!callLock.isHeldByCurrentThread()) { throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java index e655dd206..e9376b74e 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks_Copy.java @@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBlocks; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -254,7 +256,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public > T call(IChunkSet set, Runnable finalize) { + public > T call(IQueueExtent owner, IChunkSet set, Runnable finalize) { return null; } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index b542b3e08..b3b73ab69 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -9,8 +9,10 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; @@ -423,7 +425,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") - public synchronized > T call(IChunkSet set, Runnable finalizer) { + public synchronized > T call(IQueueExtent owner, IChunkSet set, Runnable finalizer) { if (!callLock.isHeldByCurrentThread()) { throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java index 1163c2d33..7abbbe579 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java @@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBlocks; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -254,7 +256,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public > T call(IChunkSet set, Runnable finalize) { + public > T call(IQueueExtent owner, IChunkSet set, Runnable finalize) { return null; } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java index ff05c453d..29d2e2824 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java @@ -9,8 +9,10 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; @@ -424,7 +426,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override @SuppressWarnings("rawtypes") - public synchronized > T call(IChunkSet set, Runnable finalizer) { + public synchronized > T call(IQueueExtent owner, IChunkSet set, Runnable finalizer) { if (!callLock.isHeldByCurrentThread()) { throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java index 7d199c7f6..c60958eb8 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks_Copy.java @@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBlocks; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -257,7 +259,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public > T call(IChunkSet set, Runnable finalize) { + public > T call(IQueueExtent owner, IChunkSet set, Runnable finalize) { return null; } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java index 70e7a4f22..4ddc9214e 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java @@ -6,11 +6,14 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.math.BitArrayUnstretched; import com.fastasyncworldedit.core.math.IntPair; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; @@ -21,6 +24,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -82,7 +86,9 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.ReadWriteLock; @@ -191,7 +197,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc try { fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition); } catch (Throwable e) { - e.printStackTrace(); + LOGGER.error("Error setting lighting to get", e); } } } @@ -203,7 +209,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc try { fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition); } catch (Throwable e) { - e.printStackTrace(); + LOGGER.error("Error setting sky lighting to get", e); } } } @@ -419,26 +425,42 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc entity.discard(); } - public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { + public CompletableFuture ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); } @Override @SuppressWarnings("rawtypes") - public synchronized > T call(IChunkSet set, Runnable finalizer) { + public synchronized > T call(IQueueExtent owner, IChunkSet set, Runnable finalizer) { if (!callLock.isHeldByCurrentThread()) { throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); } forceLoadSections = false; - LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ); - PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; - if (createCopy) { - if (copies.containsKey(copyKey)) { - throw new IllegalStateException("Copy key already used."); - } - copies.put(copyKey, copy); + final ServerLevel nmsWorld = serverLevel; + CompletableFuture nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ); + LevelChunk chunk = nmsChunkFuture.getNow(null); + // Run immediately if possible + if (chunk != null) { + return internalCall(set, finalizer, chunk, nmsWorld); } + nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) internalCall( + set, + finalizer, + nmsChunk, + nmsWorld + ))); + return (T) (Future) CompletableFuture.completedFuture(null); + } + + private > T internalCall(IChunkSet set, Runnable finalizer, LevelChunk nmsChunk, ServerLevel nmsWorld) { try { + PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null; + if (createCopy) { + if (copies.containsKey(copyKey)) { + throw new IllegalStateException("Copy key already used."); + } + copies.put(copyKey, copy); + } // Remove existing tiles. Create a copy so that we can remove blocks Map chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); List beacons = null; @@ -510,7 +532,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( - serverLevel.getWorld().getName(), + nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, @@ -586,7 +608,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData ); if (PaperweightPlatformAdapter.setSectionAtomic( - serverLevel.getWorld().getName(), + nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, null, @@ -650,7 +672,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc biomeData != null ? biomeData : (PalettedContainer>) existingSection.getBiomes() ); if (!PaperweightPlatformAdapter.setSectionAtomic( - serverLevel.getWorld().getName(), + nmsWorld.getWorld().getName(), chunkPos, levelChunkSections, existingSection, @@ -725,7 +747,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { for (UUID uuid : entityRemoves) { - Entity entity = serverLevel.getEntities().get(uuid); + Entity entity = nmsWorld.getEntities().get(uuid); if (entity != null) { removeEntity(entity); } @@ -764,7 +786,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc EntityType type = EntityType.byString(id).orElse(null); if (type != null) { - Entity entity = type.create(serverLevel); + Entity entity = type.create(nmsWorld); if (entity != null) { final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag); for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { @@ -773,11 +795,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc entity.load(tag); entity.absMoveTo(x, y, z, yaw, pitch); entity.setUUID(NbtUtils.uuid(nativeTag)); - if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { LOGGER.warn( "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", id, - serverLevel.getWorld().getName(), + nmsWorld.getWorld().getName(), x, y, z @@ -807,11 +829,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc final int z = blockHash.z() + bz; final BlockPos pos = new BlockPos(x, y, z); - synchronized (serverLevel) { - BlockEntity tileEntity = serverLevel.getBlockEntity(pos); + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); if (tileEntity == null || tileEntity.isRemoved()) { - serverLevel.removeBlockEntity(pos); - tileEntity = serverLevel.getBlockEntity(pos); + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); } if (tileEntity != null) { final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag()); @@ -866,7 +888,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc return queueHandler.async(callback, null); } } catch (Throwable e) { - e.printStackTrace(); + LOGGER.error("Error performing final chunk calling at {},{}", chunkX, chunkZ, e); throw e; } }; @@ -884,7 +906,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } return null; } catch (Throwable e) { - e.printStackTrace(); + LOGGER.error("Error calling chunk at {},{}", chunkX, chunkZ, e); return null; } finally { forceLoadSections = true; @@ -1029,7 +1051,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc } return data; } catch (IllegalAccessException | InterruptedException e) { - e.printStackTrace(); + LOGGER.error("Could not read block data from palette", e); throw new RuntimeException(e); } finally { lock.release(); @@ -1071,7 +1093,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc synchronized (this) { levelChunk = this.levelChunk; if (levelChunk == null) { - this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ); + try { + this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); + throw new FaweException( + TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()), + FaweException.Type.OTHER, + false + ); + } } } } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java index 18b557b97..c791c81a0 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks_Copy.java @@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBlocks; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.util.NbtUtils; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -257,7 +259,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet { } @Override - public > T call(IChunkSet set, Runnable finalize) { + public > T call(IQueueExtent owner, IChunkSet set, Runnable finalize) { return null; } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java index 410152251..77edf6f53 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightPlatformAdapter.java @@ -78,6 +78,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -265,7 +267,43 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } } - public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) { + public static CompletableFuture ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) { + LevelChunk levelChunk = getChunkImmediatelyAsync(serverLevel, chunkX, chunkZ); + if (levelChunk != null) { + return CompletableFuture.completedFuture(levelChunk); + } + if (PaperLib.isPaper()) { + CompletableFuture future = serverLevel + .getWorld() + .getChunkAtAsync(chunkX, chunkZ, true, true) + .thenApply(chunk -> { + addTicket(serverLevel, chunkX, chunkZ); + try { + return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + } catch (Throwable e) { + LOGGER.error("Could not asynchronously load chunk at {},{}", chunkX, chunkZ, e); + return null; + } + }); + try { + if (!future.isCompletedExceptionally() || (future.isDone() && future.get() != null)) { + return future; + } + Throwable t = future.exceptionNow(); + LOGGER.error("Asynchronous chunk load at {},{} exceptionally completed immediately", chunkX, chunkZ, t); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error( + "Unexpected error when getting completed future at chunk {},{}. Returning to default.", + chunkX, + chunkZ, + e + ); + } + } + return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ))); + } + + public static @Nullable LevelChunk getChunkImmediatelyAsync(ServerLevel serverLevel, int chunkX, int chunkZ) { if (!PaperLib.isPaper()) { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false); if (nmsChunk != null) { @@ -274,6 +312,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { if (Fawe.isMainThread()) { return serverLevel.getChunk(chunkX, chunkZ); } + return null; } else { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); if (nmsChunk != null) { @@ -289,30 +328,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { if (Fawe.isMainThread()) { return serverLevel.getChunk(chunkX, chunkZ); } - CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); - try { - CraftChunk chunk; - try { - chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); - } catch (TimeoutException e) { - String world = serverLevel.getWorld().getName(); - // We've already taken 10 seconds we can afford to wait a little here. - boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); - if (loaded) { - LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); - // Retry chunk load - chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); - } else { - throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); - } - } - addTicket(serverLevel, chunkX, chunkZ); - return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); - } catch (Throwable e) { - e.printStackTrace(); - } + return null; } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index 3ed494298..242d5f637 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -50,6 +50,7 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -645,7 +646,7 @@ public enum FaweCache implements Trimable { */ public ThreadPoolExecutor newBlockingExecutor(String name, Logger logger) { int nThreads = Settings.settings().QUEUE.PARALLEL_THREADS; - ArrayBlockingQueue queue = new ArrayBlockingQueue<>(nThreads, true); + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, queue, new ThreadFactoryBuilder().setNameFormat(name).build(), diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java index faec2f9f5..90d75c30e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java @@ -10,6 +10,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockTypesCache; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -127,9 +128,7 @@ public interface IBatchProcessor { char[] arr = set.loadIfPresent(layer); if (arr != null) { int index = (minY & 15) << 8; - for (int i = index; i < 4096; i++) { - arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; - } + Arrays.fill(arr, index, 4096, (char) BlockTypesCache.ReservedIDs.__RESERVED__); } set.setBlocks(layer, arr); } else if (layer == maxLayer) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java index b72690029..a260dae7f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java @@ -45,7 +45,7 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { } - > T call(IChunkSet set, Runnable finalize); + > T call(IQueueExtent owner, IChunkSet set, Runnable finalize); @Deprecated(forRemoval = true, since = "2.11.2") default CompoundTag getEntity(UUID uuid) { @@ -81,7 +81,7 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { } /** - * Lock the {@link IChunkGet#call(IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks + * Lock the {@link IChunkGet#call(IQueueExtent, IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks * related methods e.g. {@link IChunkGet#setCreateCopy(boolean)} * * @since 2.8.2 @@ -89,7 +89,7 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { default void lockCall() {} /** - * Unlock {@link IChunkGet#call(IChunkSet, Runnable)} (and other related methods) to executions from other threads + * Unlock {@link IChunkGet#call(IQueueExtent, IChunkSet, Runnable)} (and other related methods) to executions from other threads * * @since 2.8.2 */ diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java index d09b7f04b..fce6537e5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java @@ -10,10 +10,12 @@ import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nullable; import java.io.Flushable; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.Future; /** @@ -61,7 +63,7 @@ public interface IQueueExtent extends Flushable, Trimable, ICh IChunkSet getCachedSet(int chunkX, int chunkZ); /** - * Submit the chunk so that it's changes are applied to the world + * Submit the chunk so that its changes are applied to the world * * @return Future */ @@ -81,6 +83,12 @@ public interface IQueueExtent extends Flushable, Trimable, ICh boolean isFastMode(); + /** + * Submit a task to the extent to be queued as if it were a chunk + */ + @ApiStatus.Internal + > V submitTaskUnchecked(Callable callable); + /** * Create a new root IChunk object. Full chunks will be reused, so a more optimized chunk can be * returned in that case. diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 56a238166..995861514 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -149,6 +149,8 @@ public class ParallelQueueExtent extends PassthroughExtent { final SingleThreadQueueExtent queue = (SingleThreadQueueExtent) getNewQueue(); queue.setFastMode(fastmode); queue.setFaweExceptionArray(faweExceptionReasonsUsed); + int div = ((size + 1) * 3) >> 1; // Allow each thread to use 1.5x TARGET_SIZE / PARALLEL_THREADS + queue.setTargetSize(Settings.settings().QUEUE.TARGET_SIZE / div); enter(queue); synchronized (queue) { try { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index 7bdbf4645..315c90027 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -18,6 +18,7 @@ import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory; import com.fastasyncworldedit.core.wrappers.WorldWrapper; import com.google.common.util.concurrent.Futures; import com.sk89q.worldedit.world.World; +import org.jetbrains.annotations.ApiStatus; import java.lang.ref.WeakReference; import java.util.HashMap; @@ -41,14 +42,12 @@ import java.util.function.Supplier; @SuppressWarnings({"unchecked", "rawtypes"}) public abstract class QueueHandler implements Trimable, Runnable { - private static final int PROCESSORS = Runtime.getRuntime().availableProcessors(); - /** * Primary queue should be used for tasks that are unlikely to wait on other tasks, IO, etc. (i.e. spend most of their * time utilising CPU. */ private final ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool( - PROCESSORS, + Settings.settings().QUEUE.PARALLEL_THREADS, new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Primary - %s"), null, false @@ -59,7 +58,7 @@ public abstract class QueueHandler implements Trimable, Runnable { * primary queue. They may be IO-bound tasks. */ private final ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool( - PROCESSORS, + Settings.settings().QUEUE.PARALLEL_THREADS, new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Secondary - %s"), null, false @@ -93,6 +92,11 @@ public abstract class QueueHandler implements Trimable, Runnable { TaskManager.taskManager().repeat(this, 1); } + @ApiStatus.Internal + public ThreadPoolExecutor getBlockingExecutor() { + return blockingExecutor; + } + @Override public void run() { if (!Fawe.isMainThread()) { @@ -380,6 +384,11 @@ public abstract class QueueHandler implements Trimable, Runnable { return (T) blockingExecutor.submit(chunk); } + @ApiStatus.Internal + public > T submitToBlocking(Callable callable) { + return (T) blockingExecutor.submit(callable); + } + /** * Get or create the WorldChunkCache for a world */ diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index b0239a5a3..ebd97d5b5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.world.World; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import org.apache.logging.log4j.Logger; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -68,6 +69,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen private boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length]; private int lastException = Integer.MIN_VALUE; private int exceptionCount = 0; + private int targetSize = Settings.settings().QUEUE.TARGET_SIZE; public SingleThreadQueueExtent() { } @@ -120,6 +122,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen return maxY; } + public void setTargetSize(int targetSize) { + this.targetSize = targetSize; + } + /** * Sets the cached boolean array of length {@code FaweException.Type.values().length} that determines if a thrown * {@link FaweException} of type {@link FaweException.Type} should be output to console, rethrown to attempt to be visible @@ -154,6 +160,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen this.setPostProcessor(EmptyBatchProcessor.getInstance()); this.world = null; this.faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length]; + this.targetSize = Settings.settings().QUEUE.TARGET_SIZE; } /** @@ -246,6 +253,13 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen return (V) Fawe.instance().getQueueHandler().submit(chunk); } + @Override + public > V submitTaskUnchecked(Callable callable) { + V future = (V) Fawe.instance().getQueueHandler().submitToBlocking(callable); + submissions.add(future); + return future; + } + @Override public synchronized boolean trim(boolean aggressive) { cacheGet.trim(aggressive); @@ -308,7 +322,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen // If queueing is enabled AND either of the following // - memory is low & queue size > num threads + 8 // - queue size > target size and primary queue has less than num threads submissions - int targetSize = lowMem ? Settings.settings().QUEUE.PARALLEL_THREADS + 8 : Settings.settings().QUEUE.TARGET_SIZE; + int targetSize = lowMem ? Settings.settings().QUEUE.PARALLEL_THREADS + 8 : this.targetSize; if (enabledQueue && size > targetSize && (lowMem || Fawe.instance().getQueueHandler().isUnderutilized())) { chunk = chunks.removeFirst(); final Future future = submitUnchecked(chunk); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java index c338033f0..dc91132d0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java @@ -9,6 +9,8 @@ import com.sk89q.worldedit.world.block.BlockTypesCache; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; +import java.lang.invoke.VarHandle; +import java.util.Arrays; public abstract class CharBlocks implements IBlocks { @@ -20,7 +22,10 @@ public abstract class CharBlocks implements IBlocks { char[] arr = blocks.blocks[layer]; if (arr == null) { // Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues - return EMPTY.get(blocks, layer, false); + synchronized (blocks.sectionLocks[layer]) { + LOGGER.warn("Unexpected null section, please report this occurence alongside a debugpaste."); + return getSkipFull(blocks, layer, false); + } } return arr; } @@ -32,6 +37,7 @@ public abstract class CharBlocks implements IBlocks { if (arr == null) { // Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues synchronized (blocks.sectionLocks[layer]) { + LOGGER.warn("Unexpected null section, please report this occurence alongside a debugpaste."); return getSkipFull(blocks, layer, aggressive); } } @@ -118,6 +124,7 @@ public abstract class CharBlocks implements IBlocks { public synchronized IChunkSet reset() { for (int i = 0; i < sectionCount; i++) { sections[i] = EMPTY; + VarHandle.storeStoreFence(); blocks[i] = null; } return null; @@ -134,9 +141,7 @@ public abstract class CharBlocks implements IBlocks { if (data == null) { return new char[4096]; } - for (int i = 0; i < 4096; i++) { - data[i] = defaultOrdinal(); - } + Arrays.fill(data, defaultOrdinal()); return data; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java index 273f44952..6c73bc320 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java @@ -4,8 +4,10 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.IBlocks; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -114,7 +116,7 @@ public final class NullChunkGet implements IChunkGet { } @Nullable - public > T call(@Nonnull IChunkSet set, @Nonnull Runnable finalize) { + public > T call(IQueueExtent owner, @Nonnull IChunkSet set, @Nonnull Runnable finalize) { return null; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 7c36eb94c..2216444f9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -987,7 +987,8 @@ public class ChunkHolder> implements IQueueChunk { if (chunkSet != null && !chunkSet.isEmpty()) { chunkSet.setBitMask(bitMask); IChunkSet copy = chunkSet.createCopy(); - return this.call(copy, () -> { + + return this.call(extent, copy, () -> { // Do nothing }); } @@ -997,8 +998,9 @@ public class ChunkHolder> implements IQueueChunk { /** * This method should never be called from outside ChunkHolder */ + @Override - public synchronized T call(IChunkSet set, Runnable finalize) { + public > U call(IQueueExtent owner, IChunkSet set, Runnable finalize) { if (set != null) { IChunkGet get = getOrCreateGet(); try { @@ -1016,7 +1018,7 @@ public class ChunkHolder> implements IQueueChunk { } else { finalizer = finalize; } - return get.call(set, finalizer); + return get.call(extent, set, finalizer); } finally { get.unlockCall(); untrackExtent(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java index 14107e83a..656e067a8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java @@ -4,8 +4,10 @@ import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; import com.fastasyncworldedit.core.nbt.FaweCompoundTag; import com.fastasyncworldedit.core.queue.Filter; +import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; +import com.fastasyncworldedit.core.queue.IQueueExtent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; @@ -225,7 +227,7 @@ public final class NullChunk implements IQueueChunk { } @Nullable - public > T call(@Nullable IChunkSet set, @Nullable Runnable finalize) { + public > T call(IQueueExtent owner, @Nullable IChunkSet set, @Nullable Runnable finalize) { return null; }