shift extent queue methods

This commit is contained in:
Jesse Boyd 2019-07-19 02:07:31 +10:00
parent 2c22f02c20
commit 0a924915c8
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
15 changed files with 308 additions and 699 deletions

View File

@ -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);
/**

View File

@ -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 <T extends Future<T>> void complete(Future<T> task) {
try {
while (task != null) {
task = task.get();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public <T> Future<T> async(final Runnable run, final T value) {
return forkJoinPoolSecondary.submit(run, value);
}
public Future<?> async(final Runnable run) {
return forkJoinPoolSecondary.submit(run);
}
public <T> Future<T> async(final Callable<T> call) {
return forkJoinPoolSecondary.submit(call);
}

View File

@ -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 <B extends BlockStateHolder<B>> 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 <B extends BlockStateHolder<B>> 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<EditSession> sessions = getEditSessions();
sessions.add(session);
}
/**
* Add a progress task<br>
* - Progress type
* - Amount of type
*
* @param progressTask
*/
default void setProgressTracker(RunnableVal2<ProgressType, Integer> progressTask) {
this.setProgressTask(progressTask);
}
default Collection<EditSession> 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<int[]>() {
@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<FaweChunk> 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<ProgressType, Integer> getProgressTask();
void setProgressTask(RunnableVal2<ProgressType, Integer> progressTask);
void setChangeTask(RunnableVal2<FaweChunk, FaweChunk> changeTask);
RunnableVal2<FaweChunk, FaweChunk> 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<BlockVector3, BaseBlock> 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<BlockVector3, BaseBlock> 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);
}
}

View File

@ -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 <B extends BlockStateHolder<B>> 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 <B extends BlockStateHolder<B>> boolean setBlock(final BlockVector3 location, final B block) throws WorldEditException {
return setBlock(location.getBlockX(), location.getBlockY(), location.getBlockZ(), block);

View File

@ -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();

View File

@ -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<FaweChunk, FaweChunk>() {
@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<Short, CompoundTag> tiles = next.getTiles();
if (!tiles.isEmpty()) {
for (Map.Entry<Short, CompoundTag> entry : tiles.entrySet()) {
addTileCreate(entry.getValue());
}
}
// Tiles removed
tiles = previous.getTiles();
if (!tiles.isEmpty()) {
for (Map.Entry<Short, CompoundTag> entry : tiles.entrySet()) {
addTileRemove(entry.getValue());
}
}
}
// Entity changes
{
// Entities created
Set<CompoundTag> 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);
}
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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<Object>() {
@Override
public void run(Object value) {
queue.regenerateChunk(cx, cz, biome, seed);
regenerateChunk(cx, cz, biome, seed);
}
});
}

View File

@ -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
*/

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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<T extends Future<T>> implements Future<T> {
private Future<T> task;
public LinkedFuture(Future<T> 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;
}
}

View File

@ -272,4 +272,9 @@ public interface World extends Extent {
@Override
int hashCode();
@Override
default boolean isWorld() {
return true;
}
}