Improve exceptions (#1256)

- Kick more exceptions further up the pipeline to be more likely to be shown to player
- Try to avoid lots of console spamming when it's the same error multiple times
- Allow parsing of FaweExceptions during commands to better give information to players
This commit is contained in:
dordsor21 2021-09-01 15:36:03 +01:00 committed by GitHub
parent 0c9270dbc1
commit fb7e95c440
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 415 additions and 231 deletions

View File

@ -1,6 +1,7 @@
package com.fastasyncworldedit.core;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.util.CachedTextureUtil;
import com.fastasyncworldedit.core.util.CleanTextureUtil;
@ -346,4 +347,50 @@ public class Fawe {
return this.thread = Thread.currentThread();
}
/**
* Non-api. Handles an input FAWE exception if not already handled, given the input boolean array.
* Looks at the {@link FaweException.Type} and decides what to do (rethrows if we want to attempt to show the error to the
* player, outputs to console where necessary).
* @param faweExceptionReasonsUsed boolean array that should be cached where this method is called from of length {@code
* FaweException.Type.values().length}
* @param e {@link FaweException} to handle
* @param logger {@link Logger} of the calling class
*/
public static void handleFaweException(
boolean[] faweExceptionReasonsUsed,
FaweException e,
final Logger logger
) {
FaweException.Type type = e.getType();
switch (type) {
case OTHER:
logger.catching(e);
throw e;
case LOW_MEMORY:
if (!faweExceptionReasonsUsed[type.ordinal()]) {
logger.warn("FaweException: " + e.getMessage());
faweExceptionReasonsUsed[type.ordinal()] = true;
throw e;
}
case MAX_TILES:
case NO_REGION:
case MAX_CHECKS:
case MAX_CHANGES:
case MAX_ENTITIES:
case MAX_ITERATIONS:
case OUTSIDE_REGION:
if (!faweExceptionReasonsUsed[type.ordinal()]) {
faweExceptionReasonsUsed[type.ordinal()] = true;
throw e;
} else {
return;
}
default:
if (!faweExceptionReasonsUsed[type.ordinal()]) {
faweExceptionReasonsUsed[type.ordinal()] = true;
logger.warn("FaweException: " + e.getMessage());
}
}
}
}

View File

@ -3,8 +3,8 @@ package com.fastasyncworldedit.core;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.internal.exception.FaweBlockBagException;
import com.fastasyncworldedit.core.internal.exception.FaweChunkLoadException;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.internal.exception.FaweException.Type;
import com.fastasyncworldedit.core.math.BitArray;
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
@ -132,20 +132,40 @@ public enum FaweCache implements Trimable {
/*
Exceptions
*/
public static final FaweChunkLoadException CHUNK = new FaweChunkLoadException();
public static final FaweBlockBagException BLOCK_BAG = new FaweBlockBagException();
public static final FaweException MANUAL = new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.manual"));
public static final FaweException NO_REGION = new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.no.region"));
public static final FaweException MANUAL = new FaweException(
Caption.of("fawe.cancel.worldedit.cancel.reason.manual"),
Type.MANUAL
);
public static final FaweException NO_REGION = new FaweException(
Caption.of("fawe.cancel.worldedit.cancel.reason.no.region"),
Type.NO_REGION
);
public static final FaweException OUTSIDE_REGION = new FaweException(Caption.of(
"fawe.cancel.worldedit.cancel.reason.outside.region"));
public static final FaweException MAX_CHECKS = new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.max.checks"));
public static final FaweException MAX_CHANGES = new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.max.changes"));
public static final FaweException LOW_MEMORY = new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.low.memory"));
"fawe.cancel.worldedit.cancel.reason.outside.region"),
Type.OUTSIDE_REGION);
public static final FaweException MAX_CHECKS = new FaweException(
Caption.of("fawe.cancel.worldedit.cancel.reason.max" + ".checks"),
Type.MAX_CHECKS
);
public static final FaweException MAX_CHANGES = new FaweException(
Caption.of("fawe.cancel.worldedit.cancel.reason.max" + ".changes"),
Type.MAX_CHANGES
);
public static final FaweException LOW_MEMORY = new FaweException(
Caption.of("fawe.cancel.worldedit.cancel.reason.low" + ".memory"),
Type.LOW_MEMORY
);
public static final FaweException MAX_ENTITIES = new FaweException(Caption.of(
"fawe.cancel.worldedit.cancel.reason.max.entities"));
public static final FaweException MAX_TILES = new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.max.tiles"));
"fawe.cancel.worldedit.cancel.reason.max.entities"),
Type.MAX_ENTITIES);
public static final FaweException MAX_TILES = new FaweException(Caption.of(
"fawe.cancel.worldedit.cancel.reason.max.tiles",
Type.MAX_TILES
));
public static final FaweException MAX_ITERATIONS = new FaweException(Caption.of(
"fawe.cancel.worldedit.cancel.reason.max.iterations"));
"fawe.cancel.worldedit.cancel.reason.max.iterations"),
Type.MAX_ITERATIONS);
/*
thread cache
@ -539,28 +559,55 @@ public enum FaweCache implements Trimable {
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
) {
protected void afterExecute(Runnable r, Throwable t) {
try {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Future<?> future = (Future<?>) r;
if (future.isDone()) {
future.get();
}
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
// Array for lazy avoidance of concurrent modification exceptions and needless overcomplication of code (synchronisation is
// not very important)
private final boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private int lastException = Integer.MIN_VALUE;
private int count = 0;
protected synchronized void afterExecute(Runnable runnable, Throwable throwable) {
super.afterExecute(runnable, throwable);
if (throwable == null && runnable instanceof Future<?>) {
try {
Future<?> future = (Future<?>) runnable;
if (future.isDone()) {
future.get();
}
} catch (CancellationException ce) {
throwable = ce;
} catch (ExecutionException ee) {
throwable = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (throwable != null) {
if (throwable instanceof FaweException) {
handleFaweException((FaweException) throwable);
} else if (throwable.getCause() instanceof FaweException) {
handleFaweException((FaweException) throwable.getCause());
} else {
int hash = throwable.getMessage().hashCode();
if (hash != lastException) {
lastException = hash;
LOGGER.catching(throwable);
count = 0;
} else if (count < Settings.IMP.QUEUE.PARALLEL_THREADS) {
LOGGER.warn(throwable.getMessage());
count++;
}
}
if (t != null) {
t.printStackTrace();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private void handleFaweException(FaweException e) {
FaweException.Type type = e.getType();
if (e.getType() == FaweException.Type.OTHER) {
LOGGER.catching(e);
} else if (!faweExceptionReasonsUsed[type.ordinal()]) {
faweExceptionReasonsUsed[type.ordinal()] = true;
LOGGER.warn("FaweException: " + e.getMessage());
}
}
};

View File

@ -60,11 +60,7 @@ public class CatenaryBrush implements Brush, ResettableTool {
}
List<BlockVector3> nodes = Arrays.asList(pos1, vertex, pos2);
vertex = null;
try {
editSession.drawSpline(pattern, nodes, 0, 0, 0, 10, size, !shell);
} catch (WorldEditException e) {
e.printStackTrace();
}
editSession.drawSpline(pattern, nodes, 0, 0, 0, 10, size, !shell);
player.print(Caption.of("fawe.worldedit.brush.brush.line.secondary"));
if (!select) {
pos1 = null;

View File

@ -78,11 +78,7 @@ public class SurfaceSpline implements Brush {
}
if (radius == 0) {
BlockVector3 set = mutable.setComponents(tipx, tipy, tipz);
try {
pattern.apply(editSession, set, set);
} catch (WorldEditException e) {
e.printStackTrace();
}
pattern.apply(editSession, set, set);
} else {
vset.add(tipx, tipy, tipz);
}

View File

@ -1,6 +1,9 @@
package com.fastasyncworldedit.core.extent.processor;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.queue.Filter;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk;
@ -9,6 +12,8 @@ import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.util.StringMan;
import com.google.common.cache.LoadingCache;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.ArrayList;
@ -26,9 +31,16 @@ import java.util.function.Supplier;
public class MultiBatchProcessor implements IBatchProcessor {
private IBatchProcessor[] processors;
private static final Logger LOGGER = LogManagerCompat.getLogger();
private final LoadingCache<Class<?>, Map<Long, Filter>> classToThreadIdToFilter =
FaweCache.IMP.createCache((Supplier<Map<Long, Filter>>) ConcurrentHashMap::new);
// Array for lazy avoidance of concurrent modification exceptions and needless overcomplication of code (synchronisation is
// not very important)
private boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private IBatchProcessor[] processors;
private int lastException = Integer.MIN_VALUE;
private int exceptionCount = 0;
public MultiBatchProcessor(IBatchProcessor... processors) {
this.processors = processors;
@ -72,41 +84,36 @@ public class MultiBatchProcessor implements IBatchProcessor {
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
Map<Integer, Set<IBatchProcessor>> ordered = new HashMap<>();
try {
IChunkSet chunkSet = set;
for (IBatchProcessor processor : processors) {
if (processor.getScope() != ProcessorScope.ADDING_BLOCKS) {
ordered.merge(
processor.getScope().intValue(),
new HashSet<>(Collections.singleton(processor)),
(existing, theNew) -> {
existing.add(processor);
return existing;
}
);
IChunkSet chunkSet = set;
for (IBatchProcessor processor : processors) {
if (processor.getScope() != ProcessorScope.ADDING_BLOCKS) {
ordered.merge(
processor.getScope().intValue(),
new HashSet<>(Collections.singleton(processor)),
(existing, theNew) -> {
existing.add(processor);
return existing;
}
);
continue;
}
chunkSet = processSet(processor, chunk, get, chunkSet);
}
if (ordered.size() > 0) {
for (int i = 1; i <= 4; i++) {
Set<IBatchProcessor> processors = ordered.get(i);
if (processors == null) {
continue;
}
chunkSet = processSet(processor, chunk, get, chunkSet);
}
if (ordered.size() > 0) {
for (int i = 1; i <= 4; i++) {
Set<IBatchProcessor> processors = ordered.get(i);
if (processors == null) {
continue;
}
for (IBatchProcessor processor : processors) {
chunkSet = processSet(processor, chunk, get, chunkSet);
if (chunkSet == null) {
return null;
}
for (IBatchProcessor processor : processors) {
chunkSet = processSet(processor, chunk, get, chunkSet);
if (chunkSet == null) {
return null;
}
}
}
return chunkSet;
} catch (Throwable e) {
e.printStackTrace();
throw e;
}
return chunkSet;
}
@Nullable
@ -139,7 +146,22 @@ public class MultiBatchProcessor implements IBatchProcessor {
}
return CompletableFuture.completedFuture(set);
} catch (Throwable e) {
e.printStackTrace();
if (e instanceof FaweException) {
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e, LOGGER);
} else if (e.getCause() instanceof FaweException) {
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
} else {
String message = e.getMessage();
int hash = message.hashCode();
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
LOGGER.catching(e);
} else if (exceptionCount < Settings.IMP.QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
}
return null;
}
}
@ -214,4 +236,16 @@ public class MultiBatchProcessor implements IBatchProcessor {
return ProcessorScope.valueOf(0);
}
/**
* Sets the cached boolean array of length {@code FaweException.Type.values().length} that determines if a thrown
* {@link FaweException} of type {@link FaweException.Type} should be output to console, rethrown to attempt to be visible
* to the player, etc. Allows the same array to be used as widely as possible across the edit to avoid spam to console.
*
* @param faweExceptionReasonsUsed boolean array that should be cached where this method is called from of length {@code
* FaweException.Type.values().length}
*/
public void setFaweExceptionArray(final boolean[] faweExceptionReasonsUsed) {
this.faweExceptionReasonsUsed = faweExceptionReasonsUsed;
}
}

View File

@ -10,6 +10,7 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ThreadLocalRandom;
public interface HeightMap {
@ -60,7 +61,7 @@ public interface HeightMap {
.getConstructors()[0].newInstance(5, 1));
int diameter = 2 * size + 1;
data[1] = filter.filter(data[1], diameter, diameter);
} catch (Throwable e) {
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}

View File

@ -864,25 +864,21 @@ public class NMSRelighter implements Relighter {
if (isEmpty()) {
return;
}
try {
if (sky) {
fixSkyLighting();
} else {
synchronized (this) {
Map<Long, RelightSkyEntry> map = getSkyMap();
Iterator<Map.Entry<Long, RelightSkyEntry>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> entry = iter.next();
chunksToSend.put(entry.getKey(), entry.getValue().bitmask);
iter.remove();
}
if (sky) {
fixSkyLighting();
} else {
synchronized (this) {
Map<Long, RelightSkyEntry> map = getSkyMap();
Iterator<Map.Entry<Long, RelightSkyEntry>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> entry = iter.next();
chunksToSend.put(entry.getKey(), entry.getValue().bitmask);
iter.remove();
}
}
fixBlockLighting();
sendChunks();
} catch (Throwable e) {
e.printStackTrace();
}
fixBlockLighting();
sendChunks();
}
public void fixBlockLighting() {
@ -930,11 +926,7 @@ public class NMSRelighter implements Relighter {
}
public void flush() {
try {
close();
} catch (Exception e) {
e.printStackTrace();
}
close();
}
public synchronized void sendChunks() {

View File

@ -59,33 +59,28 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
public int getHeight(Extent extent, int x, int y, int z) {
// return extent.getNearestSurfaceTerrainBlock(x, z, y, 0, maxY);
try {
int rx = x - cacheBotX + 16;
int rz = z - cacheBotZ + 16;
int index;
if (((rx & 0xFF) != rx || (rz & 0xFF) != rz)) {
cacheBotX = x - 16;
cacheBotZ = z - 16;
rx = x - cacheBotX + 16;
rz = z - cacheBotZ + 16;
index = rx + (rz << 8);
if (cacheHeights == null) {
cacheHeights = new byte[65536];
} else {
Arrays.fill(cacheHeights, (byte) 0);
}
int rx = x - cacheBotX + 16;
int rz = z - cacheBotZ + 16;
int index;
if (((rx & 0xFF) != rx || (rz & 0xFF) != rz)) {
cacheBotX = x - 16;
cacheBotZ = z - 16;
rx = x - cacheBotX + 16;
rz = z - cacheBotZ + 16;
index = rx + (rz << 8);
if (cacheHeights == null) {
cacheHeights = new byte[65536];
} else {
index = rx + (rz << 8);
Arrays.fill(cacheHeights, (byte) 0);
}
int result = cacheHeights[index] & 0xFF;
if (y > result) {
cacheHeights[index] = (byte) (result = lastY = extent.getNearestSurfaceTerrainBlock(x, z, lastY, minY, maxY));
}
return result;
} catch (Throwable e) {
e.printStackTrace();
throw e;
} else {
index = rx + (rz << 8);
}
int result = cacheHeights[index] & 0xFF;
if (y > result) {
cacheHeights[index] = (byte) (result = lastY = extent.getNearestSurfaceTerrainBlock(x, z, lastY, minY, maxY));
}
return result;
}
protected boolean testSlope(Extent extent, int x, int y, int z) {

View File

@ -54,9 +54,6 @@ public class ExpressionPattern extends AbstractPattern {
} catch (EvaluationException e) {
e.printStackTrace();
return BlockTypes.AIR.getDefaultState().toBaseBlock();
} catch (Throwable e) {
e.printStackTrace();
throw e;
}
}

View File

@ -5,7 +5,7 @@ import com.fastasyncworldedit.core.configuration.Caption;
public class FaweBlockBagException extends FaweException {
public FaweBlockBagException() {
super(Caption.of("fawe.error.worldedit.some.fails.blockbag"));
super(Caption.of("fawe.error.worldedit.some.fails.blockbag"), Type.BLOCK_BAG);
}
}

View File

@ -1,11 +0,0 @@
package com.fastasyncworldedit.core.internal.exception;
import com.fastasyncworldedit.core.configuration.Caption;
public class FaweChunkLoadException extends FaweException {
public FaweChunkLoadException() {
super(Caption.of("fawe.cancel.worldedit.failed.load.chunk"));
}
}

View File

@ -13,13 +13,28 @@ public class FaweException extends RuntimeException {
public static final FaweException _disableQueue = new FaweException("disableQueue");
private final Component message;
private final Type type;
/**
* New instance. Defaults to {@link FaweException.Type#OTHER}.
*/
public FaweException(String reason) {
this(TextComponent.of(reason));
}
/**
* New instance. Defaults to {@link FaweException.Type#OTHER}.
*/
public FaweException(Component reason) {
this(reason, Type.OTHER);
}
/**
* New instance of a given {@link FaweException.Type}
*/
public FaweException(Component reason, Type type) {
this.message = reason;
this.type = type;
}
@Override
@ -31,6 +46,14 @@ public class FaweException extends RuntimeException {
return message;
}
/**
* Get the {@link FaweException.Type}
* @return the {@link FaweException.Type}
*/
public Type getType() {
return type;
}
public static FaweException get(Throwable e) {
if (e instanceof FaweException) {
return (FaweException) e;
@ -52,4 +75,19 @@ public class FaweException extends RuntimeException {
return this;
}
public enum Type {
MANUAL,
NO_REGION,
OUTSIDE_REGION,
MAX_CHECKS,
MAX_CHANGES,
LOW_MEMORY,
MAX_ENTITIES,
MAX_TILES,
MAX_ITERATIONS,
BLOCK_BAG,
CHUNK,
OTHER
}
}

View File

@ -1,5 +1,6 @@
package com.fastasyncworldedit.core.queue.implementation;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.NullExtent;
@ -10,7 +11,9 @@ import com.fastasyncworldedit.core.extent.filter.DistrFilter;
import com.fastasyncworldedit.core.extent.filter.LinkedFilter;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.processor.BatchProcessorHolder;
import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor;
import com.fastasyncworldedit.core.function.mask.BlockMaskBuilder;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.queue.Filter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent;
@ -22,6 +25,7 @@ import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
@ -31,6 +35,7 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import org.apache.logging.log4j.Logger;
import java.util.Iterator;
import java.util.List;
@ -40,19 +45,32 @@ import java.util.stream.IntStream;
public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrapper {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private final World world;
private final QueueHandler handler;
private final BatchProcessorHolder processor;
private final BatchProcessorHolder postProcessor;
// Array for lazy avoidance of concurrent modification exceptions and needless overcomplication of code (synchronisation is
// not very important)
private final boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private int changes;
private final boolean fastmode;
private int lastException = Integer.MIN_VALUE;
private int exceptionCount = 0;
public ParallelQueueExtent(QueueHandler handler, World world, boolean fastmode) {
super(handler.getQueue(world, new BatchProcessorHolder(), new BatchProcessorHolder()));
this.world = world;
this.handler = handler;
this.processor = (BatchProcessorHolder) getExtent().getProcessor();
if (this.processor.getProcessor() instanceof MultiBatchProcessor) {
((MultiBatchProcessor) this.processor.getProcessor()).setFaweExceptionArray(faweExceptionReasonsUsed);
}
this.postProcessor = (BatchProcessorHolder) getExtent().getPostProcessor();
if (this.postProcessor.getProcessor() instanceof MultiBatchProcessor) {
((MultiBatchProcessor) this.postProcessor.getProcessor()).setFaweExceptionArray(faweExceptionReasonsUsed);
}
this.fastmode = fastmode;
}
@ -99,29 +117,49 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap
try {
final Filter newFilter = filter.fork();
// Create a chunk that we will reuse/reset for each operation
final IQueueExtent<IQueueChunk> queue = getNewQueue();
final SingleThreadQueueExtent queue = (SingleThreadQueueExtent) getNewQueue();
queue.setFastMode(fastmode);
queue.setFaweExceptionArray(faweExceptionReasonsUsed);
synchronized (queue) {
ChunkFilterBlock block = null;
try {
ChunkFilterBlock block = null;
while (true) {
// Get the next chunk posWeakChunk
final int chunkX;
final int chunkZ;
synchronized (chunksIter) {
if (!chunksIter.hasNext()) {
break;
while (true) {
// Get the next chunk posWeakChunk
final int chunkX;
final int chunkZ;
synchronized (chunksIter) {
if (!chunksIter.hasNext()) {
break;
}
final BlockVector2 pos = chunksIter.next();
chunkX = pos.getX();
chunkZ = pos.getZ();
}
final BlockVector2 pos = chunksIter.next();
chunkX = pos.getX();
chunkZ = pos.getZ();
block = queue.apply(block, newFilter, region, chunkX, chunkZ, full);
}
queue.flush();
} catch (Throwable t) {
if (t instanceof FaweException) {
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) t, LOGGER);
} else if (t.getCause() instanceof FaweException) {
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) t.getCause(), LOGGER);
} else {
throw t;
}
block = queue.apply(block, newFilter, region, chunkX, chunkZ, full);
}
queue.flush();
}
} catch (Throwable e) {
e.printStackTrace();
String message = e.getMessage();
int hash = message.hashCode();
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
LOGGER.catching(e);
} else if (exceptionCount < Settings.IMP.QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
}
})).toArray(ForkJoinTask[]::new);
// Join filters

View File

@ -41,8 +41,9 @@ import java.util.concurrent.locks.ReentrantLock;
* Single threaded implementation for IQueueExtent (still abstract) - Does not implement creation of
* chunks (that has to implemented by the platform e.g., Bukkit)
* <p>
* This queue is reusable {@link #init(Extent, IChunkCache, IChunkCache)} }
* This queue is reusable {@link #init(Extent, IChunkCache, IChunkCache)}
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implements IQueueExtent<IQueueChunk> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
@ -51,28 +52,28 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
// private static final ConcurrentLinkedQueue<IChunk> CHUNK_POOL = new ConcurrentLinkedQueue<>();
// Chunks currently being queued / worked on
private final Long2ObjectLinkedOpenHashMap<IQueueChunk> chunks = new Long2ObjectLinkedOpenHashMap<>();
private final ConcurrentLinkedQueue<Future> submissions = new ConcurrentLinkedQueue<>();
private final ReentrantLock getChunkLock = new ReentrantLock();
private World world = null;
private int minY = 0;
private int maxY = 255;
private IChunkCache<IChunkGet> cacheGet;
private IChunkCache<IChunkSet> cacheSet;
private boolean initialized;
private Thread currentThread;
private final ConcurrentLinkedQueue<Future> submissions = new ConcurrentLinkedQueue<>();
// Last access pointers
private IQueueChunk lastChunk;
private long lastPair = Long.MAX_VALUE;
private boolean enabledQueue = true;
private boolean fastmode = false;
// Array for lazy avoidance of concurrent modification exceptions and needless overcomplication of code (synchronisation is
// not very important)
private boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private int lastException = Integer.MIN_VALUE;
private int exceptionCount = 0;
private final ReentrantLock getChunkLock = new ReentrantLock();
public SingleThreadQueueExtent() {}
public SingleThreadQueueExtent() {
}
/**
* New instance given inclusive world height bounds.
@ -114,13 +115,13 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
}
@Override
public void setFastMode(boolean fastmode) {
this.fastmode = fastmode;
public boolean isFastMode() {
return fastmode;
}
@Override
public boolean isFastMode() {
return fastmode;
public void setFastMode(boolean fastmode) {
this.fastmode = fastmode;
}
@Override
@ -133,6 +134,18 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
return maxY;
}
/**
* Sets the cached boolean array of length {@code FaweException.Type.values().length} that determines if a thrown
* {@link FaweException} of type {@link FaweException.Type} should be output to console, rethrown to attempt to be visible
* to the player, etc. Allows the same array to be used as widely as possible across the edit to avoid spam to console.
*
* @param faweExceptionReasonsUsed boolean array that should be cached where this method is called from of length {@code
* FaweException.Type.values().length}
*/
public void setFaweExceptionArray(final boolean[] faweExceptionReasonsUsed) {
this.faweExceptionReasonsUsed = faweExceptionReasonsUsed;
}
/**
* Resets the queue.
*/
@ -372,41 +385,11 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
if (aggressive) {
if (targetSize == 0) {
while (!submissions.isEmpty()) {
Future future = submissions.poll();
try {
while (future != null) {
future = (Future) future.get();
}
} catch (FaweException messageOnly) {
LOGGER.warn(messageOnly.getMessage());
} catch (ExecutionException e) {
if (e.getCause() instanceof FaweException) {
LOGGER.warn(e.getCause().getClass().getCanonicalName() + ": " + e.getCause().getMessage());
} else {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
iterateSubmissions();
}
}
for (int i = 0; i < overflow; i++) {
Future first = submissions.poll();
try {
while (first != null) {
first = (Future) first.get();
}
} catch (FaweException messageOnly) {
LOGGER.warn(messageOnly.getMessage());
} catch (ExecutionException e) {
if (e.getCause() instanceof FaweException) {
LOGGER.warn(e.getCause().getClass().getCanonicalName() + ": " + e.getCause().getMessage());
} else {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
iterateSubmissions();
}
} else {
for (int i = 0; i < overflow; i++) {
@ -416,19 +399,23 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
Future after = null;
try {
after = (Future) next.get();
} catch (FaweException messageOnly) {
LOGGER.warn(messageOnly.getMessage());
} catch (ExecutionException e) {
} catch (FaweException e) {
Fawe.handleFaweException(faweExceptionReasonsUsed, e, LOGGER);
} catch (ExecutionException | InterruptedException e) {
if (e.getCause() instanceof FaweException) {
LOGGER.warn(e.getCause().getClass().getCanonicalName() + ": " + e.getCause().getMessage());
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
} else {
e.printStackTrace();
String message = e.getMessage();
int hash = message.hashCode();
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
LOGGER.catching(e);
} else if (exceptionCount < Settings.IMP.QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
}
LOGGER.error(
"Please report this error on our issue tracker: https://github.com/IntellectualSites/FastAsyncWorldEdit/issues");
e.getCause().printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/*
* If the execution failed, namely next.get() threw an exception,
@ -446,6 +433,32 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
}
}
private void iterateSubmissions() {
Future first = submissions.poll();
try {
while (first != null) {
first = (Future) first.get();
}
} catch (FaweException e) {
Fawe.handleFaweException(faweExceptionReasonsUsed, e, LOGGER);
} catch (ExecutionException | InterruptedException e) {
if (e.getCause() instanceof FaweException) {
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
} else {
String message = e.getMessage();
int hash = message.hashCode();
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
LOGGER.catching(e);
} else if (exceptionCount < Settings.IMP.QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
}
}
}
@Override
public synchronized void flush() {
if (!chunks.isEmpty()) {

View File

@ -193,7 +193,7 @@ public class ClipboardCommands {
.getZ() + 1));
FaweLimit limit = actor.getLimit();
if (volume >= limit.MAX_CHECKS) {
throw new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.max.checks"));
throw FaweCache.MAX_CHECKS;
}
session.setClipboard(null);
ReadOnlyClipboard lazyClipboard = ReadOnlyClipboard.of(region, !skipEntities, copyBiomes);
@ -558,7 +558,7 @@ public class ClipboardCommands {
PasteEvent event = new PasteEvent(player, clipboard, uri, editSession, to);
WorldEdit.getInstance().getEventBus().post(event);
if (event.isCancelled()) {
throw new FaweException(Caption.of("fawe.cancel.worldedit.cancel.reason.manual"));
throw FaweCache.MANUAL;
}
}
//FAWE end

View File

@ -601,18 +601,13 @@ public class GenerationCommands {
int[] count = new int[1];
final BufferedImage finalImage = image;
RegionVisitor visitor = new RegionVisitor(region, pos -> {
try {
int x = pos.getBlockX() - pos1.getBlockX();
int z = pos.getBlockZ() - pos1.getBlockZ();
int color = finalImage.getRGB(x, z);
BlockType block = tu.getNearestBlock(color);
count[0]++;
if (block != null) {
return editSession.setBlock(pos, block.getDefaultState());
}
return false;
} catch (Throwable e) {
e.printStackTrace();
int x = pos.getBlockX() - pos1.getBlockX();
int z = pos.getBlockZ() - pos1.getBlockZ();
int color = finalImage.getRGB(x, z);
BlockType block = tu.getNearestBlock(color);
count[0]++;
if (block != null) {
return editSession.setBlock(pos, block.getDefaultState());
}
return false;
}, editSession);

View File

@ -774,19 +774,18 @@ public final class PlatformCommandManager {
}
actor.printError(e.getRichMessage());
} catch (CommandExecutionException e) {
handleUnknownException(actor, e.getCause());
} catch (CommandException e) {
if (e.getCause() instanceof FaweException) {
actor.print(Caption.of("fawe.cancel.worldedit.cancel.reason", ((FaweException) e.getCause()).getComponent()));
} else {
handleUnknownException(actor, e.getCause());
}
} catch (CommandException e) {
Component msg = e.getRichMessage();
if (msg != TextComponent.empty()) {
actor.print(TextComponent.builder("")
.append(e.getRichMessage())
.build());
List<String> argList = parseArgs(event.getArguments()).map(Substring::getSubstring).collect(Collectors.toList());
printUsage(actor, argList);
Component msg = e.getRichMessage();
if (msg != TextComponent.empty()) {
List<String> argList = parseArgs(event.getArguments())
.map(Substring::getSubstring)
.collect(Collectors.toList());
printUsage(actor, argList);
}
}
} catch (Throwable t) {
handleUnknownException(actor, t);

View File

@ -273,10 +273,9 @@ public interface Clipboard extends Extent, Iterable<BlockVector3>, Closeable {
}
try {
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
e.printStackTrace();
} finally {
editSession.close(); // Make sure editsession is always closed
}
editSession.flushQueue();
return editSession;
}

View File

@ -20,6 +20,7 @@
package com.sk89q.worldedit.internal.command.exception;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.DisallowedItemException;
import com.sk89q.worldedit.EmptyClipboardException;
@ -197,4 +198,11 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper {
throw e;
}
//FAWE start
@ExceptionMatch
public void convert(FaweException e) throws CommandException {
throw newCommandException(e.getComponent(), e);
}
//FAWE end
}