From a0b008a31a0023a61ef39b1e1de09d532b409893 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Thu, 26 Dec 2024 22:08:49 +0000 Subject: [PATCH] Abstractify bukkit get blocks a bit --- .../fawe/v1_20_R2/PaperweightGetBlocks.java | 152 ++-------------- .../fawe/v1_20_R4/PaperweightGetBlocks.java | 154 ++-------------- .../fawe/v1_21_R1/PaperweightGetBlocks.java | 152 ++-------------- .../fawe/v1_21_3/PaperweightGetBlocks.java | 152 ++-------------- .../fawe/v1_21_4/PaperweightGetBlocks.java | 148 +-------------- .../adapter/AbstractBukkitGetBlocks.java | 168 ++++++++++++++++++ .../bukkit/adapter/BukkitGetBlocks.java | 7 - 7 files changed, 218 insertions(+), 715 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/AbstractBukkitGetBlocks.java delete mode 100644 worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitGetBlocks.java 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 0ac109d02f..9f7fb71b27 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 @@ -1,6 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; -import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; +import com.fastasyncworldedit.bukkit.adapter.AbstractBukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; @@ -10,14 +10,9 @@ 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; -import com.fastasyncworldedit.core.util.MemUtil; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -88,18 +83,16 @@ 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; -import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends AbstractBukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -111,84 +104,25 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); - private final ReentrantLock callLock = new ReentrantLock(); - private final ServerLevel serverLevel; - private final int chunkX; - private final int chunkZ; - private final IntPair chunkPos; - private final int minHeight; - private final int maxHeight; - private final int minSectionPosition; - private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; - private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); private final Object sendLock = new Object(); - private LevelChunkSection[] sections; private LevelChunk levelChunk; + private LevelChunkSection[] sections; private DataLayer[] blockLight; private DataLayer[] skyLight; - private boolean createCopy = false; - private boolean forceLoadSections = true; private boolean lightUpdate = false; - private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); } public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { - super(serverLevel.getMinBuildHeight() >> 4, (serverLevel.getMaxBuildHeight() - 1) >> 4); - this.serverLevel = serverLevel; - this.chunkX = chunkX; - this.chunkZ = chunkZ; - this.minHeight = serverLevel.getMinBuildHeight(); - this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.minSectionPosition = minHeight >> 4; - this.maxSectionPosition = maxHeight >> 4; + super(serverLevel, chunkX, chunkZ, serverLevel.getMinBuildHeight(), serverLevel.getMaxBuildHeight() - 1); this.skyLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); - this.chunkPos = new IntPair(chunkX, chunkZ); - } - - public int getChunkX() { - return chunkX; - } - - public int getChunkZ() { - return chunkZ; - } - - @Override - public boolean isCreateCopy() { - return createCopy; - } - - @Override - public int setCreateCopy(boolean createCopy) { - if (!callLock.isHeldByCurrentThread()) { - throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); - } - this.createCopy = createCopy; - // Increment regardless of whether copy will be created or not to return null from getCopy() - return ++this.copyKey; - } - - @Override - public IChunkGet getCopy(final int key) { - return copies.remove(key); - } - - @Override - public void lockCall() { - this.callLock.lock(); - } - - @Override - public void unlockCall() { - this.callLock.unlock(); } @Override @@ -225,16 +159,6 @@ public void setHeightmapToGet(HeightMapType type, int[] data) { heightMap.setRawData(getChunk(), nativeType, bitArray.getData()); } - @Override - public int getMaxY() { - return maxHeight; - } - - @Override - public int getMinY() { - return minHeight; - } - @Override public BiomeType getBiomeType(int x, int y, int z) { LevelChunkSection section = getSections(false)[(y >> 4) - getMinSectionPosition()]; @@ -353,7 +277,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public @Nullable FaweCompoundTag entity(final UUID uuid) { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; for (Entity e : entities) { @@ -376,7 +300,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { return Collections.emptyList(); @@ -425,69 +349,13 @@ private void removeEntity(Entity entity) { entity.discard(); } - public CompletableFuture ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { + @Override + public CompletableFuture ensureLoaded(ServerLevel nmsWorld) { return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); } @Override - @SuppressWarnings({"rawtypes", "unchecked"}) - 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; - final ServerLevel nmsWorld = serverLevel; - CompletableFuture nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ); - LevelChunk chunk = nmsChunkFuture.getNow(null); - if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { - try { - // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of - // memory usage - chunk = nmsChunkFuture.get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); - throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage())); - } - } - final int finalCopyKey = copyKey; - // Run immediately if possible - if (chunk != null) { - return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld); - } - // Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the - // target size - nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall( - set, - finalizer, - finalCopyKey, - nmsChunk, - nmsWorld - ))); - // If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the - // above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures - // submitted above will still be added to the STQE submissions. - return (T) (Future) CompletableFuture.completedFuture(null); - } - - private > T tryWrappedInternalCall( - IChunkSet set, - Runnable finalizer, - int copyKey, - LevelChunk nmsChunk, - ServerLevel nmsWorld - ) { - try { - return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld); - } catch (Throwable e) { - LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e); - return null; - } finally { - forceLoadSections = true; - } - } - - private > T internalCall( + protected > T internalCall( IChunkSet set, Runnable finalizer, int copyKey, @@ -1137,7 +1005,7 @@ public LevelChunk getChunk() { levelChunk = this.levelChunk; if (levelChunk == null) { try { - this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get(); + this.levelChunk = levelChunk = ensureLoaded(this.serverLevel).get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( 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 182394a42d..030e93a3ad 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 @@ -1,6 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4; -import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; +import com.fastasyncworldedit.bukkit.adapter.AbstractBukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; @@ -10,14 +10,9 @@ 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; -import com.fastasyncworldedit.core.util.MemUtil; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -89,18 +84,16 @@ 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; -import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends AbstractBukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -112,84 +105,25 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); - private final ReentrantLock callLock = new ReentrantLock(); - private final ServerLevel serverLevel; - private final int chunkX; - private final int chunkZ; - private final IntPair chunkPos; - private final int minHeight; - private final int maxHeight; - private final int minSectionPosition; - private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; - private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); private final Object sendLock = new Object(); - private LevelChunkSection[] sections; private LevelChunk levelChunk; + private LevelChunkSection[] sections; private DataLayer[] blockLight; private DataLayer[] skyLight; - private boolean createCopy = false; - private boolean forceLoadSections = true; private boolean lightUpdate = false; - private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); } public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { - super(serverLevel.getMinBuildHeight() >> 4, (serverLevel.getMaxBuildHeight() - 1) >> 4); - this.serverLevel = serverLevel; - this.chunkX = chunkX; - this.chunkZ = chunkZ; - this.minHeight = serverLevel.getMinBuildHeight(); - this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.minSectionPosition = minHeight >> 4; - this.maxSectionPosition = maxHeight >> 4; + super(serverLevel, chunkX, chunkZ, serverLevel.getMinBuildHeight(), serverLevel.getMaxBuildHeight() - 1); this.skyLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); - this.chunkPos = new IntPair(chunkX, chunkZ); - } - - public int getChunkX() { - return chunkX; - } - - public int getChunkZ() { - return chunkZ; - } - - @Override - public boolean isCreateCopy() { - return createCopy; - } - - @Override - public int setCreateCopy(boolean createCopy) { - if (!callLock.isHeldByCurrentThread()) { - throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); - } - this.createCopy = createCopy; - // Increment regardless of whether copy will be created or not to return null from getCopy() - return ++this.copyKey; - } - - @Override - public IChunkGet getCopy(final int key) { - return copies.remove(key); - } - - @Override - public void lockCall() { - this.callLock.lock(); - } - - @Override - public void unlockCall() { - this.callLock.unlock(); } @Override @@ -226,16 +160,6 @@ public void setHeightmapToGet(HeightMapType type, int[] data) { heightMap.setRawData(getChunk(), nativeType, bitArray.getData()); } - @Override - public int getMaxY() { - return maxHeight; - } - - @Override - public int getMinY() { - return minHeight; - } - @Override public BiomeType getBiomeType(int x, int y, int z) { LevelChunkSection section = getSections(false)[(y >> 4) - getMinSectionPosition()]; @@ -354,7 +278,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public @Nullable FaweCompoundTag entity(final UUID uuid) { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; for (Entity e : entities) { @@ -377,7 +301,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { return Collections.emptyList(); @@ -426,69 +350,13 @@ private void removeEntity(Entity entity) { entity.discard(); } - public CompletableFuture ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { + @Override + public CompletableFuture ensureLoaded(ServerLevel nmsWorld) { return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); } @Override - @SuppressWarnings({"rawtypes", "unchecked"}) - 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; - final ServerLevel nmsWorld = serverLevel; - CompletableFuture nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ); - LevelChunk chunk = nmsChunkFuture.getNow(null); - if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { - try { - // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of - // memory usage - chunk = nmsChunkFuture.get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); - throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage())); - } - } - final int finalCopyKey = copyKey; - // Run immediately if possible - if (chunk != null) { - return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld); - } - // Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the - // target size - nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall( - set, - finalizer, - finalCopyKey, - nmsChunk, - nmsWorld - ))); - // If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the - // above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures - // submitted above will still be added to the STQE submissions. - return (T) (Future) CompletableFuture.completedFuture(null); - } - - private > T tryWrappedInternalCall( - IChunkSet set, - Runnable finalizer, - int copyKey, - LevelChunk nmsChunk, - ServerLevel nmsWorld - ) { - try { - return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld); - } catch (Throwable e) { - LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e); - return null; - } finally { - forceLoadSections = true; - } - } - - private > T internalCall( + protected > T internalCall( IChunkSet set, Runnable finalizer, int copyKey, @@ -1096,7 +964,7 @@ public char[] update(int layer, char[] data, boolean aggressive) { } 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(); @@ -1139,7 +1007,7 @@ public LevelChunk getChunk() { levelChunk = this.levelChunk; if (levelChunk == null) { try { - this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get(); + this.levelChunk = levelChunk = ensureLoaded(this.serverLevel).get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( 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 04225a769d..d634d77abc 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 @@ -1,6 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1; -import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; +import com.fastasyncworldedit.bukkit.adapter.AbstractBukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; @@ -10,14 +10,9 @@ 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; -import com.fastasyncworldedit.core.util.MemUtil; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -89,18 +84,16 @@ 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; -import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends AbstractBukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -112,84 +105,25 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); - private final ReentrantLock callLock = new ReentrantLock(); - private final ServerLevel serverLevel; - private final int chunkX; - private final int chunkZ; - private final IntPair chunkPos; - private final int minHeight; - private final int maxHeight; - private final int minSectionPosition; - private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; - private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); private final Object sendLock = new Object(); - private LevelChunkSection[] sections; private LevelChunk levelChunk; + private LevelChunkSection[] sections; private DataLayer[] blockLight; private DataLayer[] skyLight; - private boolean createCopy = false; - private boolean forceLoadSections = true; private boolean lightUpdate = false; - private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); } public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { - super(serverLevel.getMinBuildHeight() >> 4, (serverLevel.getMaxBuildHeight() - 1) >> 4); - this.serverLevel = serverLevel; - this.chunkX = chunkX; - this.chunkZ = chunkZ; - this.minHeight = serverLevel.getMinBuildHeight(); - this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.minSectionPosition = minHeight >> 4; - this.maxSectionPosition = maxHeight >> 4; + super(serverLevel, chunkX, chunkZ, serverLevel.getMinBuildHeight(), serverLevel.getMaxBuildHeight() - 1); this.skyLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); - this.chunkPos = new IntPair(chunkX, chunkZ); - } - - public int getChunkX() { - return chunkX; - } - - public int getChunkZ() { - return chunkZ; - } - - @Override - public boolean isCreateCopy() { - return createCopy; - } - - @Override - public int setCreateCopy(boolean createCopy) { - if (!callLock.isHeldByCurrentThread()) { - throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); - } - this.createCopy = createCopy; - // Increment regardless of whether copy will be created or not to return null from getCopy() - return ++this.copyKey; - } - - @Override - public IChunkGet getCopy(final int key) { - return copies.remove(key); - } - - @Override - public void lockCall() { - this.callLock.lock(); - } - - @Override - public void unlockCall() { - this.callLock.unlock(); } @Override @@ -226,16 +160,6 @@ public void setHeightmapToGet(HeightMapType type, int[] data) { heightMap.setRawData(getChunk(), nativeType, bitArray.getData()); } - @Override - public int getMaxY() { - return maxHeight; - } - - @Override - public int getMinY() { - return minHeight; - } - @Override public BiomeType getBiomeType(int x, int y, int z) { LevelChunkSection section = getSections(false)[(y >> 4) - getMinSectionPosition()]; @@ -354,7 +278,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public @Nullable FaweCompoundTag entity(final UUID uuid) { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; for (Entity e : entities) { @@ -377,7 +301,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { return Collections.emptyList(); @@ -427,69 +351,13 @@ private void removeEntity(Entity entity) { entity.discard(); } - public CompletableFuture ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { + @Override + public CompletableFuture ensureLoaded(ServerLevel nmsWorld) { return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); } @Override - @SuppressWarnings({"rawtypes", "unchecked"}) - 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; - final ServerLevel nmsWorld = serverLevel; - CompletableFuture nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ); - LevelChunk chunk = nmsChunkFuture.getNow(null); - if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { - try { - // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of - // memory usage - chunk = nmsChunkFuture.get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); - throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage())); - } - } - final int finalCopyKey = copyKey; - // Run immediately if possible - if (chunk != null) { - return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld); - } - // Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the - // target size - nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall( - set, - finalizer, - finalCopyKey, - nmsChunk, - nmsWorld - ))); - // If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the - // above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures - // submitted above will still be added to the STQE submissions. - return (T) (Future) CompletableFuture.completedFuture(null); - } - - private > T tryWrappedInternalCall( - IChunkSet set, - Runnable finalizer, - int copyKey, - LevelChunk nmsChunk, - ServerLevel nmsWorld - ) { - try { - return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld); - } catch (Throwable e) { - LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e); - return null; - } finally { - forceLoadSections = true; - } - } - - private > T internalCall( + protected > T internalCall( IChunkSet set, Runnable finalizer, int copyKey, @@ -1134,7 +1002,7 @@ public LevelChunk getChunk() { levelChunk = this.levelChunk; if (levelChunk == null) { try { - this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get(); + this.levelChunk = levelChunk = ensureLoaded(this.serverLevel).get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( diff --git a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java index 46522944c5..245ac61c60 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java @@ -1,6 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3; -import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; +import com.fastasyncworldedit.bukkit.adapter.AbstractBukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; @@ -10,14 +10,9 @@ 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; -import com.fastasyncworldedit.core.util.MemUtil; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -90,18 +85,16 @@ 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; -import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends AbstractBukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -113,84 +106,25 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); - private final ReentrantLock callLock = new ReentrantLock(); - private final ServerLevel serverLevel; - private final int chunkX; - private final int chunkZ; - private final IntPair chunkPos; - private final int minHeight; - private final int maxHeight; - private final int minSectionPosition; - private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; - private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); private final Object sendLock = new Object(); - private LevelChunkSection[] sections; private LevelChunk levelChunk; + private LevelChunkSection[] sections; private DataLayer[] blockLight; private DataLayer[] skyLight; - private boolean createCopy = false; - private boolean forceLoadSections = true; private boolean lightUpdate = false; - private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); } public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { - super(serverLevel.getMinY() >> 4, (serverLevel.getMaxY() - 1) >> 4); - this.serverLevel = serverLevel; - this.chunkX = chunkX; - this.chunkZ = chunkZ; - this.minHeight = serverLevel.getMinY(); - this.maxHeight = serverLevel.getMaxY() - 1; // Minecraft max limit is exclusive. - this.minSectionPosition = minHeight >> 4; - this.maxSectionPosition = maxHeight >> 4; + super(serverLevel, chunkX, chunkZ, serverLevel.getMinY(), serverLevel.getMaxY() - 1); this.skyLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().lookupOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); - this.chunkPos = new IntPair(chunkX, chunkZ); - } - - public int getChunkX() { - return chunkX; - } - - public int getChunkZ() { - return chunkZ; - } - - @Override - public boolean isCreateCopy() { - return createCopy; - } - - @Override - public int setCreateCopy(boolean createCopy) { - if (!callLock.isHeldByCurrentThread()) { - throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); - } - this.createCopy = createCopy; - // Increment regardless of whether copy will be created or not to return null from getCopy() - return ++this.copyKey; - } - - @Override - public IChunkGet getCopy(final int key) { - return copies.remove(key); - } - - @Override - public void lockCall() { - this.callLock.lock(); - } - - @Override - public void unlockCall() { - this.callLock.unlock(); } @Override @@ -227,16 +161,6 @@ public void setHeightmapToGet(HeightMapType type, int[] data) { heightMap.setRawData(getChunk(), nativeType, bitArray.getData()); } - @Override - public int getMaxY() { - return maxHeight; - } - - @Override - public int getMinY() { - return minHeight; - } - @Override public BiomeType getBiomeType(int x, int y, int z) { LevelChunkSection section = getSections(false)[(y >> 4) - getMinSectionPosition()]; @@ -355,7 +279,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public @Nullable FaweCompoundTag entity(final UUID uuid) { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; for (Entity e : entities) { @@ -378,7 +302,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { return Collections.emptyList(); @@ -428,69 +352,13 @@ private void removeEntity(Entity entity) { entity.discard(); } - public CompletableFuture ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { + @Override + public CompletableFuture ensureLoaded(ServerLevel nmsWorld) { return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); } @Override - @SuppressWarnings({"rawtypes", "unchecked"}) - 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; - final ServerLevel nmsWorld = serverLevel; - CompletableFuture nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ); - LevelChunk chunk = nmsChunkFuture.getNow(null); - if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { - try { - // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of - // memory usage - chunk = nmsChunkFuture.get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); - throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage())); - } - } - final int finalCopyKey = copyKey; - // Run immediately if possible - if (chunk != null) { - return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld); - } - // Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the - // target size - nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall( - set, - finalizer, - finalCopyKey, - nmsChunk, - nmsWorld - ))); - // If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the - // above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures - // submitted above will still be added to the STQE submissions. - return (T) (Future) CompletableFuture.completedFuture(null); - } - - private > T tryWrappedInternalCall( - IChunkSet set, - Runnable finalizer, - int copyKey, - LevelChunk nmsChunk, - ServerLevel nmsWorld - ) { - try { - return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld); - } catch (Throwable e) { - LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e); - return null; - } finally { - forceLoadSections = true; - } - } - - private > T internalCall( + protected > T internalCall( IChunkSet set, Runnable finalizer, int copyKey, @@ -1133,7 +1001,7 @@ public LevelChunk getChunk() { levelChunk = this.levelChunk; if (levelChunk == null) { try { - this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get(); + this.levelChunk = levelChunk = ensureLoaded(this.serverLevel).get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java index 0ae51242b5..3bb2531612 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java @@ -1,6 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_4; -import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; +import com.fastasyncworldedit.bukkit.adapter.AbstractBukkitGetBlocks; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; @@ -10,14 +10,9 @@ 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; -import com.fastasyncworldedit.core.util.MemUtil; import com.fastasyncworldedit.core.util.NbtUtils; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -90,18 +85,16 @@ 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; -import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { +public class PaperweightGetBlocks extends AbstractBukkitGetBlocks { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -113,84 +106,27 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc .getInstance() .getBukkitImplAdapter()); private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); - private final ReentrantLock callLock = new ReentrantLock(); - private final ServerLevel serverLevel; - private final int chunkX; - private final int chunkZ; - private final IntPair chunkPos; - private final int minHeight; - private final int maxHeight; - private final int minSectionPosition; - private final int maxSectionPosition; private final Registry biomeRegistry; private final IdMap> biomeHolderIdMap; - private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); private final Object sendLock = new Object(); private LevelChunkSection[] sections; private LevelChunk levelChunk; private DataLayer[] blockLight; private DataLayer[] skyLight; - private boolean createCopy = false; - private boolean forceLoadSections = true; private boolean lightUpdate = false; - private int copyKey = 0; public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); } public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { - super(serverLevel.getMinY() >> 4, (serverLevel.getMaxY() - 1) >> 4); - this.serverLevel = serverLevel; - this.chunkX = chunkX; - this.chunkZ = chunkZ; - this.minHeight = serverLevel.getMinY(); - this.maxHeight = serverLevel.getMaxY() - 1; // Minecraft max limit is exclusive. + super(serverLevel, chunkX, chunkZ, serverLevel.getMinY(), serverLevel.getMaxY() - 1); this.minSectionPosition = minHeight >> 4; this.maxSectionPosition = maxHeight >> 4; this.skyLight = new DataLayer[getSectionCount()]; this.blockLight = new DataLayer[getSectionCount()]; this.biomeRegistry = serverLevel.registryAccess().lookupOrThrow(BIOME); this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); - this.chunkPos = new IntPair(chunkX, chunkZ); - } - - public int getChunkX() { - return chunkX; - } - - public int getChunkZ() { - return chunkZ; - } - - @Override - public boolean isCreateCopy() { - return createCopy; - } - - @Override - public int setCreateCopy(boolean createCopy) { - if (!callLock.isHeldByCurrentThread()) { - throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); - } - this.createCopy = createCopy; - // Increment regardless of whether copy will be created or not to return null from getCopy() - return ++this.copyKey; - } - - @Override - public IChunkGet getCopy(final int key) { - return copies.remove(key); - } - - @Override - public void lockCall() { - this.callLock.lock(); - } - - @Override - public void unlockCall() { - this.callLock.unlock(); } @Override @@ -227,16 +163,6 @@ public void setHeightmapToGet(HeightMapType type, int[] data) { heightMap.setRawData(getChunk(), nativeType, bitArray.getData()); } - @Override - public int getMaxY() { - return maxHeight; - } - - @Override - public int getMinY() { - return minHeight; - } - @Override public BiomeType getBiomeType(int x, int y, int z) { LevelChunkSection section = getSections(false)[(y >> 4) - getMinSectionPosition()]; @@ -355,7 +281,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public @Nullable FaweCompoundTag entity(final UUID uuid) { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); Entity entity = null; for (Entity e : entities) { @@ -378,7 +304,7 @@ public int[] getHeightMap(HeightMapType type) { @Override public Collection entities() { - ensureLoaded(serverLevel, chunkX, chunkZ); + ensureLoaded(serverLevel); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { return Collections.emptyList(); @@ -428,69 +354,13 @@ private void removeEntity(Entity entity) { entity.discard(); } - public CompletableFuture ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { + @Override + public CompletableFuture ensureLoaded(ServerLevel nmsWorld) { return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); } @Override - @SuppressWarnings({"rawtypes", "unchecked"}) - 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; - final ServerLevel nmsWorld = serverLevel; - CompletableFuture nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ); - LevelChunk chunk = nmsChunkFuture.getNow(null); - if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { - try { - // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of - // memory usage - chunk = nmsChunkFuture.get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); - throw new FaweException( - TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage())); - } - } - final int finalCopyKey = copyKey; - // Run immediately if possible - if (chunk != null) { - return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld); - } - // Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the - // target size - nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall( - set, - finalizer, - finalCopyKey, - nmsChunk, - nmsWorld - ))); - // If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the - // above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures - // submitted above will still be added to the STQE submissions. - return (T) (Future) CompletableFuture.completedFuture(null); - } - - private > T tryWrappedInternalCall( - IChunkSet set, - Runnable finalizer, - int copyKey, - LevelChunk nmsChunk, - ServerLevel nmsWorld - ) { - try { - return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld); - } catch (Throwable e) { - LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e); - return null; - } finally { - forceLoadSections = true; - } - } - - private > T internalCall( + protected > T internalCall( IChunkSet set, Runnable finalizer, int copyKey, @@ -1133,7 +1003,7 @@ public LevelChunk getChunk() { levelChunk = this.levelChunk; if (levelChunk == null) { try { - this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get(); + this.levelChunk = levelChunk = ensureLoaded(this.serverLevel).get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e); throw new FaweException( diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/AbstractBukkitGetBlocks.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/AbstractBukkitGetBlocks.java new file mode 100644 index 0000000000..08ed217b7b --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/AbstractBukkitGetBlocks.java @@ -0,0 +1,168 @@ +package com.fastasyncworldedit.bukkit.adapter; + +import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.internal.exception.FaweException; +import com.fastasyncworldedit.core.math.IntPair; +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.blocks.CharGetBlocks; +import com.fastasyncworldedit.core.util.MemUtil; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.locks.ReentrantLock; + +public abstract class AbstractBukkitGetBlocks extends CharGetBlocks { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + protected final ServerLevel serverLevel; + protected final int chunkX; + protected final int chunkZ; + protected final ReentrantLock callLock = new ReentrantLock(); + protected final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + protected final IntPair chunkPos; + protected final int minHeight; + protected final int maxHeight; + protected boolean createCopy = false; + protected boolean forceLoadSections = true; + protected int copyKey = 0; + + protected AbstractBukkitGetBlocks( + ServerLevel serverLevel, int chunkX, int chunkZ, int minY, int maxY + ) { + super(minY >> 4, maxY >> 4); + this.serverLevel = serverLevel; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.minHeight = minY; + this.maxHeight = maxY; // Minecraft max limit is exclusive + this.chunkPos = new IntPair(chunkX, chunkZ); + } + + protected abstract void send(); + + protected abstract CompletableFuture ensureLoaded(ServerLevel serverLevel); + + protected abstract > T internalCall( + IChunkSet set, + Runnable finalizer, + int copyKey, + LevelChunk nmsChunk, + ServerLevel nmsWorld + ) throws Exception; + + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + 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; + final ServerLevel nmsWorld = serverLevel; + CompletableFuture nmsChunkFuture = ensureLoaded(nmsWorld); + LevelChunk chunk = nmsChunkFuture.getNow(null); + if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { + try { + // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of + // memory usage + chunk = nmsChunkFuture.get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); + throw new FaweException( + TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage())); + } + } + final int finalCopyKey = copyKey; + // Run immediately if possible + if (chunk != null) { + return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld); + } + // Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the + // target size + nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall( + set, + finalizer, + finalCopyKey, + nmsChunk, + nmsWorld + ))); + // If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the + // above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures + // submitted above will still be added to the STQE submissions. + return (T) (Future) CompletableFuture.completedFuture(null); + } + + private > T tryWrappedInternalCall( + IChunkSet set, + Runnable finalizer, + int copyKey, + LevelChunk nmsChunk, + ServerLevel nmsWorld + ) { + try { + return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld); + } catch (Throwable e) { + LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e); + return null; + } finally { + forceLoadSections = true; + } + } + + public int getChunkX() { + return chunkX; + } + + public int getChunkZ() { + return chunkZ; + } + + @Override + public boolean isCreateCopy() { + return createCopy; + } + + @Override + public int setCreateCopy(boolean createCopy) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); + } + this.createCopy = createCopy; + // Increment regardless of whether copy will be created or not to return null from getCopy() + return ++this.copyKey; + } + + @Override + public IChunkGet getCopy(final int key) { + return copies.remove(key); + } + + @Override + public void lockCall() { + this.callLock.lock(); + } + + @Override + public void unlockCall() { + this.callLock.unlock(); + } + + @Override + public int getMaxY() { + return maxHeight; + } + + @Override + public int getMinY() { + return minHeight; + } + +} diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitGetBlocks.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitGetBlocks.java deleted file mode 100644 index 8a5cc9d8a9..0000000000 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitGetBlocks.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.fastasyncworldedit.bukkit.adapter; - -public interface BukkitGetBlocks { - - void send(); - -}