From 0a924915c861f6354a80eac0845a94750b56fc37 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Fri, 19 Jul 2019 02:07:31 +1000 Subject: [PATCH] shift extent queue methods --- .../com/boydti/fawe/beta/IQueueExtent.java | 19 + .../beta/implementation/QueueHandler.java | 17 + .../com/boydti/fawe/object/FaweQueue.java | 521 ------------------ .../com/boydti/fawe/object/HistoryExtent.java | 7 +- .../brush/visualization/VirtualWorld.java | 17 +- .../fawe/object/changeset/FaweChangeSet.java | 142 +---- .../fawe/object/exception/FaweException.java | 25 +- .../boydti/fawe/util/EditSessionBuilder.java | 5 + .../java/com/sk89q/worldedit/EditSession.java | 83 +-- .../extent/AbstractDelegateExtent.java | 58 ++ .../com/sk89q/worldedit/extent/Extent.java | 38 ++ .../extent/buffer/ForgetfulExtentBuffer.java | 5 + .../function/visitor/DownwardVisitor.java | 10 +- .../worldedit/util/task/LinkedFuture.java | 55 ++ .../java/com/sk89q/worldedit/world/World.java | 5 + 15 files changed, 308 insertions(+), 699 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java index 5e9bfffbe..8fe5d387a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java @@ -1,6 +1,7 @@ package com.boydti.fawe.beta; import com.boydti.fawe.beta.implementation.WorldChunkCache; +import com.boydti.fawe.object.exception.FaweException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; @@ -17,6 +18,24 @@ import java.util.function.Supplier; * Interface for a queue based extent which uses chunks */ public interface IQueueExtent extends Flushable, Trimable, Extent { + + @Override + default boolean isQueueEnabled() { + return true; + } + + /** + * Must ensure that it is enqueued with QueueHandler + */ + @Override + void enableQueue(); + + /** + * Must ensure it is not in the queue handler + */ + @Override + void disableQueue(); + void init(WorldChunkCache world); /** diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java index c323463f0..e3f14bc10 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.Future; @@ -113,10 +114,26 @@ public abstract class QueueHandler implements Trimable, Runnable { } } + public > void complete(Future task) { + try { + while (task != null) { + task = task.get(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + public Future async(final Runnable run, final T value) { return forkJoinPoolSecondary.submit(run, value); } + public Future async(final Runnable run) { + return forkJoinPoolSecondary.submit(run); + } + public Future async(final Callable call) { return forkJoinPoolSecondary.submit(call); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java deleted file mode 100644 index 5b7446848..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java +++ /dev/null @@ -1,521 +0,0 @@ -package com.boydti.fawe.object; - -import com.boydti.fawe.Fawe; -import com.boydti.fawe.config.BBC; -import com.boydti.fawe.config.Settings; -import com.boydti.fawe.example.NullRelighter; -import com.boydti.fawe.example.Relighter; -import com.boydti.fawe.object.exception.FaweException; -import com.boydti.fawe.util.MainUtil; -import com.boydti.fawe.util.MathMan; -import com.boydti.fawe.util.MemUtil; -import com.boydti.fawe.util.SetQueue; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.MutableBlockVector3; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; - -import java.io.File; -import java.util.Collection; -import java.util.Collections; -import java.util.UUID; -import javax.annotation.Nullable; - -/** - * A queue based Extent capable of queing chunk and region changes - */ -public interface FaweQueue extends HasFaweQueue, Extent { - - enum ProgressType { - QUEUE, - DISPATCH, - DONE, - } - - enum RelightMode { - NONE, - OPTIMAL, - ALL, - } - - enum Capability { - // If history can be recorded in an async task by the dispatcher - CHANGE_TASKS, - // If custom chunk packets can be sent - CHUNK_PACKETS - // - } - - default Relighter getRelighter() { - return NullRelighter.INSTANCE; - } - - @Override - default BlockVector3 getMinimumPoint() { - return BlockVector3.at(-30000000, 0, -30000000); - } - - @Override - default BlockVector3 getMaximumPoint() { - return BlockVector3.at(30000000, getMaxY(), 30000000); - } - - @Override - default BlockState getLazyBlock(int x, int y, int z) { - int combinedId4Data = getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId()); - try { - return BlockState.getFromInternalId(combinedId4Data); - } catch (Throwable e) { - e.printStackTrace(); - return BlockTypes.AIR.getDefaultState(); - } - } - - @Override - default > boolean setBlock(int x, int y, int z, B block) throws WorldEditException { - return setBlock(x, y, z, block.getInternalId(), block instanceof BaseBlock ? block.getNbtData() : null); - } - - @Override - default BaseBlock getFullBlock(BlockVector3 position) { - int combinedId4Data = getCachedCombinedId4Data(position.getBlockX(), position.getBlockY(), position.getBlockZ(), BlockTypes.AIR.getInternalId()); - try { - BaseBlock block = BaseBlock.getFromInternalId(combinedId4Data, null); - if (block.getMaterial().hasContainer()) { - CompoundTag tile = getTileEntity(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - if (tile != null) { - return BaseBlock.getFromInternalId(combinedId4Data, tile); - } - } - return block; - } catch (Throwable e) { - e.printStackTrace(); - return BlockTypes.AIR.getDefaultState().toBaseBlock(); - } - } - - @Override - default BiomeType getBiome(BlockVector2 position) { - return null; - } - - @Override - default > boolean setBlock(BlockVector3 position, B block) throws WorldEditException { - return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); - } - - boolean setBlock(final int x, final int y, final int z, int combinedId); - - default boolean setBlock(final int x, final int y, final int z, int combinedId, CompoundTag nbtData) { - if (setBlock(x, y, z, combinedId)) { - if (nbtData != null) setTile(x, y, z, nbtData); - return true; - } - return false; - } - - @Override - default boolean setBiome(BlockVector2 position, BiomeType biome) { - return setBiome(position.getBlockX(), position.getBlockZ(), biome); - } - - @Override - default FaweQueue getQueue() { - return this; - } - - - - default void addEditSession(EditSession session) { - if (session == null) { - return; - } - Collection sessions = getEditSessions(); - sessions.add(session); - } - - /** - * Add a progress task
- * - Progress type - * - Amount of type - * - * @param progressTask - */ - default void setProgressTracker(RunnableVal2 progressTask) { - this.setProgressTask(progressTask); - } - - default Collection getEditSessions() { - return Collections.emptySet(); - } - - default boolean supports(Capability capability) { - return false; - } - - default void optimize() {} - - default int setBlocks(CuboidRegion cuboid, int combinedId) { - RegionWrapper current = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint()); - final int minY = cuboid.getMinimumY(); - final int maxY = cuboid.getMaximumY(); - - final FaweChunk fc = getFaweChunk(0, 0); - fc.fillCuboid(0, 15, minY, maxY, 0, 15, combinedId); - fc.optimize(); - - MainUtil.chunkTaskSync(current, new RunnableVal() { - @Override - public void run(int[] value) { - FaweChunk newChunk; - if (value[6] == 0) { - newChunk = fc.copy(true); - newChunk.setLoc(FaweQueue.this, value[0], value[1]); - } else { - int bx = value[2] & 15; - int tx = value[4] & 15; - int bz = value[3] & 15; - int tz = value[5] & 15; - if (bx == 0 && tx == 15 && bz == 0 && tz == 15) { - newChunk = fc.copy(true); - newChunk.setLoc(FaweQueue.this, value[0], value[1]); - } else { - newChunk = FaweQueue.this.getFaweChunk(value[0], value[1]); - newChunk.fillCuboid(value[2] & 15, value[4] & 15, minY, maxY, value[3] & 15, value[5] & 15, combinedId); - } - } - newChunk.addToQueue(); - } - }); - return cuboid.getArea(); - } - - void setTile(int x, int y, int z, CompoundTag tag); - - void setEntity(int x, int y, int z, CompoundTag tag); - - void removeEntity(int x, int y, int z, UUID uuid); - - boolean setBiome(final int x, final int z, final BiomeType biome); - - FaweChunk getFaweChunk(int x, int z); - - Collection getFaweChunks(); - - default boolean setMCA(int mcaX, int mcaZ, RegionWrapper region, Runnable whileLocked, boolean save, boolean load) { - if (whileLocked != null) whileLocked.run(); - return true; - } - - void setChunk(final FaweChunk chunk); - - File getSaveFolder(); - - @Override - default int getMaxY() { - World weWorld = getWEWorld(); - return weWorld == null ? 255 : weWorld.getMaxY(); - } - - default Settings getSettings() { - return Settings.IMP; - } - - default void setSettings(Settings settings) { - - } - - void setWorld(String world); - - World getWEWorld(); - - String getWorldName(); - - long getModified(); - - void setModified(long modified); - - RunnableVal2 getProgressTask(); - - void setProgressTask(RunnableVal2 progressTask); - - void setChangeTask(RunnableVal2 changeTask); - - RunnableVal2 getChangeTask(); - - SetQueue.QueueStage getStage(); - - void setStage(SetQueue.QueueStage stage); - - void addNotifyTask(Runnable runnable); - - void runTasks(); - - void addTask(Runnable whenFree); - - default void forEachBlockInChunk(int cx, int cz, RunnableVal2 onEach) { - int bx = cx << 4; - int bz = cz << 4; - MutableBlockVector3 mutable = new MutableBlockVector3(0, 0, 0); - for (int x = 0; x < 16; x++) { - int xx = x + bx; - mutable.mutX(xx); - for (int z = 0; z < 16; z++) { - int zz = z + bz; - mutable.mutZ(zz); - for (int y = 0; y <= getMaxY(); y++) { - int combined = getCombinedId4Data(xx, y, zz); - BaseBlock block = BlockState.getFromInternalId(combined).toBaseBlock(); - BlockType type = block.getBlockType(); - if (type.getMaterial().isAir()) { - continue; - } - mutable.mutY(y); - CompoundTag tile = getTileEntity(x, y, z); - if (tile != null) { - onEach.run(mutable, block.toBaseBlock(tile)); - } else { - onEach.run(mutable, block); - } - } - } - } - } - - default void forEachTileInChunk(int cx, int cz, RunnableVal2 onEach) { - int bx = cx << 4; - int bz = cz << 4; - MutableBlockVector3 mutable = new MutableBlockVector3(0, 0, 0); - for (int x = 0; x < 16; x++) { - int xx = x + bx; - for (int z = 0; z < 16; z++) { - int zz = z + bz; - for (int y = 0; y < getMaxY(); y++) { - int combined = getCombinedId4Data(xx, y, zz); - if (combined == 0) { - continue; - } - BlockType type = BlockTypes.getFromStateId(combined); - if (type.getMaterial().hasContainer()) { - CompoundTag tile = getTileEntity(x, y, z); - if (tile != null) { - mutable.mutX(xx); - mutable.mutZ(zz); - mutable.mutY(y); - BaseBlock block = BaseBlock.getFromInternalId(combined, tile); - onEach.run(mutable, block); - } - } - } - } - } - } - - @Deprecated - default boolean regenerateChunk(int x, int z) { - return regenerateChunk(x, z, null, null); - } - - boolean regenerateChunk(int x, int z, @Nullable BiomeType biome, @Nullable Long seed); - - default void startSet(boolean parallel) { - } - - default void endSet(boolean parallel) { - } - - default int cancel() { - clear(); - int count = 0; - for (EditSession session : getEditSessions()) { - if (session.cancel()) { - count++; - } - } - return count; - } - - void sendBlockUpdate(FaweChunk chunk, FawePlayer... players); - - default void sendChunkUpdate(FaweChunk chunk, FawePlayer... players) { - sendBlockUpdate(chunk, players); - } - - @Deprecated - default boolean next() { - int amount = Settings.IMP.QUEUE.PARALLEL_THREADS; - long time = 20; // 30ms - return next(amount, time); - } - - /** - * Gets the FaweChunk and sets the requested blocks - * - * @return - */ - boolean next(int amount, long time); - - default void saveMemory() { - MainUtil.sendAdmin(BBC.OOM.s()); - // Set memory limited - MemUtil.memoryLimitedTask(); - // Clear block placement - clear(); - Fawe.get().getWorldEdit().getSessionManager().clear(); - // GC - System.gc(); - System.gc(); - // Unload chunks - } - - void sendChunk(FaweChunk chunk); - - void sendChunk(int x, int z, int bitMask); - - /** - * This method is called when the server is < 1% available memory - */ - void clear(); - - default boolean hasBlock(int x, int y, int z) throws FaweException.FaweChunkLoadException { - return getCombinedId4Data(x, y, z) != 0; - } - - BiomeType getBiomeType(int x, int z) throws FaweException.FaweChunkLoadException; - - int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException; - - int getCachedCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException; - - default int getAdjacentLight(int x, int y, int z) { - int light = 0; - if ((light = Math.max(light, getSkyLight(x - 1, y, z))) == 15) { - return light; - } - if ((light = Math.max(light, getSkyLight(x + 1, y, z))) == 15) { - return light; - } - if ((light = Math.max(light, getSkyLight(x, y, z - 1))) == 15) { - return light; - } - return Math.max(light, getSkyLight(x, y, z + 1)); - } - - boolean hasSky(); - - int getSkyLight(int x, int y, int z); - - default int getLight(int x, int y, int z) { - if (!hasSky()) { - return getEmmittedLight(x, y, z); - } - return Math.max(getSkyLight(x, y, z), getEmmittedLight(x, y, z)); - } - - int getEmmittedLight(int x, int y, int z); - - CompoundTag getTileEntity(int x, int y, int z) throws FaweException.FaweChunkLoadException; - - default int getCombinedId4Data(int x, int y, int z, int def) { - try { - return getCombinedId4Data(x, y, z); - } catch (FaweException ignore) { - return def; - } - } - - default int getCachedCombinedId4Data(int x, int y, int z, int def) { - try { - return getCachedCombinedId4Data(x, y, z); - } catch (FaweException ignore) { - return def; - } - } - - default int getCombinedId4DataDebug(int x, int y, int z, int def, EditSession session) { - try { - return getCombinedId4Data(x, y, z); - } catch (FaweException ignore) { - BBC.WORLDEDIT_FAILED_LOAD_CHUNK.send(session.getPlayer(),x >> 4, z >> 4); - return def; - } catch (Throwable e) { - e.printStackTrace(); - return BlockTypes.AIR.getInternalId(); - } - } - - default int getBrightness(int x, int y, int z) { - int combined = getCombinedId4Data(x, y, z); - if (combined == 0) { - return 0; - } - return BlockTypes.getFromStateId(combined).getMaterial().getLightValue(); - } - - default int getOpacityBrightnessPair(int x, int y, int z) { - return MathMan.pair16(Math.min(15, getOpacity(x, y, z)), getBrightness(x, y, z)); - } - - default int getOpacity(int x, int y, int z) { - int combined = getCombinedId4Data(x, y, z); - if (combined == 0) { - return 0; - } - return BlockTypes.getFromStateId(combined).getMaterial().getLightOpacity(); - } - - int size(); - - default boolean isEmpty() { - return size() == 0; - } - - /** - * Lock the thread until the queue is empty - */ - default void flush() { - flush(10000); - } - - /** - * Lock the thread until the queue is empty - */ - default void flush(int time) { - if (size() > 0) { - if (Fawe.isMainThread()) { - SetQueue.IMP.flush(this); - } else { - if (enqueue()) { - while (!isEmpty() && getStage() == SetQueue.QueueStage.ACTIVE) { - synchronized (this) { - try { - this.wait(time); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - } - } - } - } - - default boolean enqueue() { - return SetQueue.IMP.enqueue(this); - } - - default void dequeue() { - SetQueue.IMP.dequeue(this); - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java index 058724112..468193800 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java @@ -28,7 +28,6 @@ import static com.google.common.base.Preconditions.checkNotNull; public class HistoryExtent extends AbstractDelegateExtent { private FaweChangeSet changeSet; - private final FaweQueue queue; /** * Create a new instance. @@ -36,10 +35,9 @@ public class HistoryExtent extends AbstractDelegateExtent { * @param extent the extent * @param changeSet the change set */ - public HistoryExtent(final Extent extent, final FaweChangeSet changeSet, FaweQueue queue) { + public HistoryExtent(final Extent extent, final FaweChangeSet changeSet) { super(extent); checkNotNull(changeSet); - this.queue = queue; this.changeSet = changeSet; } @@ -53,7 +51,7 @@ public class HistoryExtent extends AbstractDelegateExtent { @Override public > boolean setBlock(int x, int y, int z, B block) throws WorldEditException { - BaseBlock previous = queue.getFullBlock(x, y, z); + BaseBlock previous = getFullBlock(x, y, z); if (previous.getInternalId() == block.getInternalId()) { if (!previous.hasNbtData() && (block instanceof BaseBlock && !block.hasNbtData())) { return false; @@ -63,6 +61,7 @@ public class HistoryExtent extends AbstractDelegateExtent { return getExtent().setBlock(x, y, z, block); } + @Override public > boolean setBlock(final BlockVector3 location, final B block) throws WorldEditException { return setBlock(location.getBlockX(), location.getBlockY(), location.getBlockZ(), block); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VirtualWorld.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VirtualWorld.java index 4a2009b58..31794a8b8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VirtualWorld.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VirtualWorld.java @@ -1,8 +1,6 @@ package com.boydti.fawe.object.brush.visualization; -import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.BlockInteractEvent; @@ -11,17 +9,14 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.SimpleWorld; import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import java.io.Closeable; import java.io.IOException; -public interface VirtualWorld extends SimpleWorld, FaweQueue, Closeable { +public interface VirtualWorld extends SimpleWorld, Closeable { Vector3 getOrigin(); - FaweChunk getSnapshot(int chunkX, int chunkZ); - @Override default BaseBlock getFullBlock(BlockVector3 position) { return getBlock(position).toBaseBlock(); @@ -38,16 +33,6 @@ public interface VirtualWorld extends SimpleWorld, FaweQueue, Closeable { @Override boolean setBlock(BlockVector3 pt, BlockStateHolder block) throws WorldEditException; - @Override - default BlockVector3 getMaximumPoint() { - return FaweQueue.super.getMaximumPoint(); - } - - @Override - default BlockVector3 getMinimumPoint() { - return FaweQueue.super.getMinimumPoint(); - } - FawePlayer getPlayer(); void update(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java index 74df79490..3631eeafd 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -6,10 +6,10 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.TaskManager; +import com.google.common.util.concurrent.Futures; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.extent.inventory.BlockBag; @@ -20,6 +20,7 @@ import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.task.LinkedFuture; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -29,6 +30,8 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; public abstract class FaweChangeSet implements ChangeSet { @@ -244,122 +247,31 @@ public abstract class FaweChangeSet implements ChangeSet { } } - public void addChangeTask(FaweQueue queue) { - queue.setChangeTask(new RunnableVal2() { - @Override - public void run(final FaweChunk previous, final FaweChunk next) { - FaweChangeSet.this.waitingCombined.incrementAndGet(); - Runnable run = () -> { - try { - int cx = previous.getX(); - int cz = previous.getZ(); - int bx = cx << 4; - int bz = cz << 4; - synchronized (FaweChangeSet.this) { - BiomeType[] previousBiomes = previous.getBiomeArray(); - if (previousBiomes != null) { - BiomeType[] nextBiomes = next.getBiomeArray(); - int index = 0; - for (int z = 0; z < 16; z++) { - int zz = bz + z; - for (int x = 0; x < 16; x++) { - BiomeType idFrom = previousBiomes[index]; - BiomeType idTo = nextBiomes[index]; - if (idFrom != idTo && idTo != null) { - addBiomeChange(bx + x, zz, idFrom, idTo); - } - index++; - } - } - } - // Block changes - for (int layer = 0; layer < layers; layer++) { - int[] currentLayer = next.getIdArray(layer); - int[] previousLayer = previous.getIdArray(layer); - if (currentLayer == null) { - continue; - } - int startY = layer << 4; - int index = 0; - for (int y = 0; y < 16; y++) { - int yy = y + startY; - for (int z = 0; z < 16; z++) { - int zz = z + bz; - for (int x = 0; x < 16; x++, index++) { - int xx = x + bx; - int combinedIdCurrent = currentLayer[index]; - if (combinedIdCurrent != 0) { - int combinedIdPrevious; - if (previousLayer != null) { - combinedIdPrevious = previousLayer[index]; - if (combinedIdPrevious == 0) { - combinedIdPrevious = BlockID.AIR; - } - } else { - combinedIdPrevious = BlockID.AIR; - } - if (combinedIdCurrent != combinedIdPrevious) { - add(xx, yy, zz, combinedIdPrevious, combinedIdCurrent); - } - } - } - } - } - } - // Tile changes - { - // Tiles created - Map tiles = next.getTiles(); - if (!tiles.isEmpty()) { - for (Map.Entry entry : tiles.entrySet()) { - addTileCreate(entry.getValue()); - } - } - // Tiles removed - tiles = previous.getTiles(); - if (!tiles.isEmpty()) { - for (Map.Entry entry : tiles.entrySet()) { - addTileRemove(entry.getValue()); - } - } - } - // Entity changes - { - // Entities created - Set entities = next.getEntities(); - if (!entities.isEmpty()) { - for (CompoundTag entityTag : entities) { - addEntityCreate(entityTag); - } - } - // Entities removed - entities = previous.getEntities(); - if (!entities.isEmpty()) { - for (CompoundTag entityTag : entities) { - addEntityRemove(entityTag); - } - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - } finally { - if (FaweChangeSet.this.waitingCombined.decrementAndGet() <= 0) { - synchronized (FaweChangeSet.this.waitingAsync) { - FaweChangeSet.this.waitingAsync.notifyAll(); - } - synchronized (FaweChangeSet.this.waitingCombined) { - FaweChangeSet.this.waitingCombined.notifyAll(); - } - } + public Future addWriteTask(Runnable writeTask) { + return addWriteTask(writeTask, Fawe.isMainThread()); + } + + public Future addWriteTask(Runnable writeTask, boolean completeNow) { + FaweChangeSet.this.waitingCombined.incrementAndGet(); + Runnable wrappedTask = () -> { + try { + writeTask.run(); + } finally { + if (FaweChangeSet.this.waitingCombined.decrementAndGet() <= 0) { + synchronized (FaweChangeSet.this.waitingAsync) { + FaweChangeSet.this.waitingAsync.notifyAll(); + } + synchronized (FaweChangeSet.this.waitingCombined) { + FaweChangeSet.this.waitingCombined.notifyAll(); } - }; - if (mainThread) { - run.run(); - } else { - TaskManager.IMP.getPublicForkJoinPool().submit(run); } } - }); + }; + if (completeNow) { + wrappedTask.run(); + return Futures.immediateCancelledFuture(); + } else { + return Fawe.get().getQueueHandler().async(wrappedTask); + } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/exception/FaweException.java b/worldedit-core/src/main/java/com/boydti/fawe/object/exception/FaweException.java index 1c3726905..3b959fa0a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/exception/FaweException.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/exception/FaweException.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.exception; import com.boydti.fawe.config.BBC; +import com.sk89q.worldedit.extent.Extent; public class FaweException extends RuntimeException { public static final FaweChunkLoadException CHUNK = new FaweChunkLoadException(); @@ -14,15 +15,33 @@ public class FaweException extends RuntimeException { public static final FaweException MAX_ENTITIES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES); public static final FaweException MAX_TILES = new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES); - private final BBC message; + // DEBUG + public static final FaweException _enableQueue; + public static final FaweException _disableQueue; + + static { + try { + _enableQueue = new FaweException(Extent.class.getDeclaredMethod("enableQueue").toString()); + _disableQueue = new FaweException(Extent.class.getDeclaredMethod("disableQueue").toString()); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + private final String message; + + public FaweException(String reason) { + this.message = reason; + } public FaweException(BBC reason) { - this.message = reason; + this(reason.format()); } @Override public String getMessage() { - return message == null ? null : message.format(); + return message; } public static FaweException get(Throwable e) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java index 225960da1..c30842127 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java @@ -199,6 +199,11 @@ public class EditSessionBuilder { return this; } + public EditSessionBuilder eventBus(@Nullable EventBus eventBus) { + this.eventBus = eventBus; + return this; + } + public EditSessionBuilder event(@Nullable EditSessionEvent event) { this.event = event; return this; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 69d6965a5..b463d3d7e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -29,7 +29,6 @@ import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.HistoryExtent; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; @@ -365,7 +364,6 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, * chunk batching}. */ public void enableStandardMode() { - setBatchingChunks(true); } /** @@ -374,17 +372,29 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, * @param reorderMode The reorder mode */ public void setReorderMode(ReorderMode reorderMode) { - //TODO Not working yet. - It shouldn't need to work. FAWE doesn't need reordering. + switch (reorderMode) { + case MULTI_STAGE: + enableQueue(); + break; + case NONE: // Functionally the same, since FAWE doesn't perform physics + case FAST: + disableQueue(); + break; + default: + throw new UnsupportedOperationException("Not implemented: " + reorderMode); + } } - //TODO: Reorder mode. /** * Get the reorder mode. * * @return the reorder mode */ public ReorderMode getReorderMode() { - return null; + if (isQueueEnabled()) { + return ReorderMode.MULTI_STAGE; + } + return ReorderMode.FAST; } /** @@ -463,6 +473,7 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, */ @Deprecated public void enableQueue() { + super.enableQueue(); } /** @@ -470,9 +481,7 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, */ @Deprecated public void disableQueue() { - if (isQueueEnabled()) { - this.flushQueue(); - } + super.disableQueue(); } /** @@ -730,6 +739,11 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, * @param batchingChunks {@code true} to enable, {@code false} to disable */ public void setBatchingChunks(boolean batchingChunks) { + if (batchingChunks) { + enableQueue(); + } else { + disableQueue(); + } } /** @@ -739,6 +753,7 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, * @see #setBatchingChunks(boolean) */ public void disableBuffering() { + disableQueue(); } /** @@ -1028,7 +1043,7 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, UndoContext context = new UndoContext(); context.setExtent(editSession.bypassAll); ChangeSet changeSet = getChangeSet(); - editSession.getQueue().setChangeTask(null); + setChangeSet(null); Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.UNDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE)); flushQueue(); editSession.changes = 1; @@ -1052,7 +1067,7 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, UndoContext context = new UndoContext(); context.setExtent(editSession.bypassAll); ChangeSet changeSet = getChangeSet(); - editSession.getQueue().setChangeTask(null); + setChangeSet(null); Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.REDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE)); flushQueue(); editSession.changes = 1; @@ -1146,28 +1161,31 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, final int startPerformY = region.getMinimumPoint().getBlockY(); final int startCheckY = fullHeight ? 0 : startPerformY; final int endY = region.getMaximumPoint().getBlockY(); - RegionVisitor visitor = new RegionVisitor(flat, pos -> { - int x = pos.getX(); - int z = pos.getZ(); - int freeSpot = startCheckY; - for (int y = startCheckY; y <= endY; y++) { - if (y < startPerformY) { - if (!getBlockType(x, y, z).getMaterial().isAir()) { - freeSpot = y + 1; + RegionVisitor visitor = new RegionVisitor(flat, new RegionFunction() { + @Override + public boolean apply(BlockVector3 pos) throws WorldEditException { + int x = pos.getX(); + int z = pos.getZ(); + int freeSpot = startCheckY; + for (int y = startCheckY; y <= endY; y++) { + if (y < startPerformY) { + if (!getBlockType(x, y, z).getMaterial().isAir()) { + freeSpot = y + 1; + } + continue; } - continue; - } - BlockType block = getBlockType(x, y, z); - if (!block.getMaterial().isAir()) { - if (freeSpot != y) { - setBlock(x, freeSpot, z, block); - setBlock(x, y, z, replace); + BlockType block = getBlockType(x, y, z); + if (!block.getMaterial().isAir()) { + if (freeSpot != y) { + setBlock(x, freeSpot, z, block); + setBlock(x, y, z, replace); + } + freeSpot++; } - freeSpot++; } - } - return true; - }, this); + return true; + } + }); Operations.completeBlindly(visitor); return this.changes; } @@ -1612,7 +1630,7 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, if (disAbs.getBlockX() < size.getBlockX() && disAbs.getBlockY() < size.getBlockY() && disAbs.getBlockZ() < size.getBlockZ()) { // Buffer if overlapping - queue.dequeue(); + disableQueue(); } ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to); @@ -3029,9 +3047,8 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, public boolean regenerate(final Region region, final BiomeType biome, final Long seed) { //TODO Optimize - avoid Vector2D creation (make mutable) - final FaweQueue queue = this.getQueue(); - queue.setChangeTask(null); final FaweChangeSet fcs = (FaweChangeSet) this.getChangeSet(); + this.setChangeSet(null); final FaweRegionExtent fe = this.getRegionExtent(); final boolean cuboid = region instanceof CuboidRegion; if (fe != null && cuboid) { @@ -3117,7 +3134,7 @@ public class EditSession extends AbstractDelegateExtent implements SimpleWorld, TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { - queue.regenerateChunk(cx, cz, biome, seed); + regenerateChunk(cx, cz, biome, seed); } }); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 01ce94d25..8c8cafc41 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -21,12 +21,18 @@ package com.sk89q.worldedit.extent; import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.Fawe; import com.boydti.fawe.jnbt.anvil.generator.GenBase; import com.boydti.fawe.jnbt.anvil.generator.Resource; +import com.boydti.fawe.object.HistoryExtent; +import com.boydti.fawe.object.changeset.FaweChangeSet; +import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.extent.LightingExtent; +import com.boydti.fawe.util.ExtentTraverser; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.buffer.ForgetfulExtentBuffer; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.OperationQueue; @@ -69,6 +75,58 @@ public class AbstractDelegateExtent implements Extent, LightingExtent { return extent; } + /* + Queue based methods + TODO NOT IMPLEMENTED: IQueueExtent and such need to implement these + */ + public boolean isQueueEnabled() { + return extent.isQueueEnabled(); + } + + @Override + public void disableQueue() { + try { + if (!(extent instanceof ForgetfulExtentBuffer)) { // placeholder + extent.disableQueue(); + } + } catch (FaweException disableQueue) {} + if (extent instanceof AbstractDelegateExtent) { + Extent next = ((AbstractDelegateExtent) extent).getExtent(); + new ExtentTraverser(this).setNext(next); + } else { + Fawe.debug("Cannot disable queue"); + } + } + + @Override + public void enableQueue() { + try { + extent.enableQueue(); + } catch (FaweException enableQueue) { + // TODO NOT IMPLEMENTED - THIS IS IMPORTANT (ForgetfulExtentBuffer is just a placeholder for now, it won't work) + new ExtentTraverser<>(this).setNext(new ForgetfulExtentBuffer(extent)); + } + } + + /* + History + */ + public void setChangeSet(FaweChangeSet changeSet) { + if (extent instanceof HistoryExtent) { + HistoryExtent history = ((HistoryExtent) extent); + if (changeSet == null) { + new ExtentTraverser(this).setNext(history.getExtent()); + } else { + history.setChangeSet(changeSet); + } + } + else if (extent instanceof AbstractDelegateExtent) { + ((AbstractDelegateExtent) extent).setChangeSet(changeSet); + } else if (changeSet != null) { + new ExtentTraverser<>(this).setNext(new HistoryExtent(extent, changeSet)); + } + } + /* Bounds */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index c4059a4fe..66a688d01 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -25,6 +25,7 @@ import com.boydti.fawe.jnbt.anvil.generator.OreGen; import com.boydti.fawe.jnbt.anvil.generator.Resource; import com.boydti.fawe.jnbt.anvil.generator.SchemGen; import com.boydti.fawe.object.clipboard.WorldCopyClipboard; +import com.boydti.fawe.object.exception.FaweException; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; @@ -51,6 +52,7 @@ import com.sk89q.worldedit.registry.state.PropertyGroup; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -133,6 +135,42 @@ public interface Extent extends InputExtent, OutputExtent { return null; } + /* + Queue based methods + TODO NOT IMPLEMENTED: + */ + default boolean isQueueEnabled() { + return false; + } + + default void enableQueue() { + if (!isQueueEnabled()) throw FaweException._enableQueue; + } + + default void disableQueue() { + if (isQueueEnabled()) throw FaweException._disableQueue; + } + + /* + World based methods + TODO NOT IMPLEMENTED: + */ + + default boolean isWorld() { + return false; + } + + default boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) { + throw new UnsupportedOperationException("TODO NOT IMPLEMENTED: " + isWorld()); + } + + /* + Shifting operations down the pipeline from EditSession -> Extent + - This allows certain extents (e.g. multithreaded extent) to override and optimize as needed + - The EditSession shouldn't need to worry about implementation details + - TODO: actually optimize these + */ + default int getHighestTerrainBlock(final int x, final int z, int minY, int maxY) { maxY = Math.min(maxY, Math.max(0, maxY)); minY = Math.max(0, minY); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java index 0aa410916..274e7d03d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java @@ -62,6 +62,11 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat this(delegate, Masks.alwaysTrue()); } + @Override + public boolean isQueueEnabled() { + return true; + } + /** * Create a new extent buffer that will buffer changes that meet the criteria * of the given mask. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java index 33a1dde10..3d03c6c49 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java @@ -19,16 +19,12 @@ package com.sk89q.worldedit.function.visitor; -import com.boydti.fawe.object.HasFaweQueue; - import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; -import java.util.Collection; - /** * Visits adjacent points on the same X-Z plane as long as the points * pass the given mask, and then executes the provided region @@ -48,11 +44,11 @@ public class DownwardVisitor extends RecursiveVisitor { * @param baseY the base Y */ public DownwardVisitor(Mask mask, RegionFunction function, int baseY) { - this(mask, function, baseY, Integer.MAX_VALUE, null); + this(mask, function, baseY, Integer.MAX_VALUE); } - public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, HasFaweQueue hasFaweQueue) { - super(mask, function, depth, hasFaweQueue); + public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth) { + super(mask, function, depth); checkNotNull(mask); this.baseY = baseY; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java new file mode 100644 index 000000000..9923e8c87 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java @@ -0,0 +1,55 @@ +package com.sk89q.worldedit.util.task; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class LinkedFuture> implements Future { + private Future task; + + public LinkedFuture(Future task) { + this.task = task; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return task.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return task.isCancelled(); + } + + @Override + public boolean isDone() { + return task.isDone(); + } + + @Override + public synchronized T get() throws InterruptedException, ExecutionException { + if (task != null) { + task = task.get(); + if (task != null) { + return (T) this; + } + } + return null; + } + + @Override + public synchronized T get(long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + if (task != null) { + T result = task.get(timeout, unit); + if (task != null || !task.isDone()) { + return (T) this; + } + task = null; + + } + return null; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index 26858d7a2..cc2f876d7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -272,4 +272,9 @@ public interface World extends Extent { @Override int hashCode(); + @Override + default boolean isWorld() { + return true; + } + }