Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-12-25 18:40:05 +01:00
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
Dieser Commit ist enthalten in:
Ursprung
0c9270dbc1
Commit
fb7e95c440
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren