Add fluid ticking and refactor post-processing a bit (#1554)

* Make postProcessSet a default method and change to void

* Throwable#getMessage is nullable

* Move (re-)ticking to a post-processor per "platform"
 - Add fluid ticking

* chore: Ignore (for us) irrelevant rules

* chore: Fix correct toml syntax?

* Re-add removed method for API-compliance and refactor it to have a use

* Switch to javax annotations

* Switch to recalcBlockCounts for ticking blocks.

* No need to set air count anymore either

* We can still "not tick" in fast mode in 1.17.2

* update adapters

* Let paper create the chunk section if biomes are null

* Adjust notes to settings

* 1.17.2 didn't exist

* Add 1.18.2

* Don't attempt to cache plains biome ID

* Use correct annotation

Co-authored-by: NotMyFault <mc.cache@web.de>
This commit is contained in:
Jordan
2022-03-10 14:27:25 +00:00
committed by GitHub
parent 5d18e15128
commit e9db749e2f
47 changed files with 975 additions and 399 deletions

View File

@@ -604,7 +604,7 @@ public enum FaweCache implements Trimable {
} else if (throwable.getCause() instanceof FaweException) {
handleFaweException((FaweException) throwable.getCause());
} else {
int hash = throwable.getMessage().hashCode();
int hash = throwable.getMessage() != null ? throwable.getMessage().hashCode() : 0;
if (hash != lastException) {
lastException = hash;
LOGGER.catching(throwable);

View File

@@ -567,7 +567,8 @@ public class Settings extends Config {
public int DISCARD_AFTER_MS = 60000;
@Comment({
"When using fastmode also do not bother to fix existing ticking blocks"
"When using fastmode do not bother to tick existing/placed blocks/fluids",
"Only works in versions up to 1.17.1"
})
public boolean NO_TICK_FASTMODE = true;
@@ -625,16 +626,11 @@ public class Settings extends Config {
public boolean OTHER = false;
@Comment({
"Allow blocks placed by WorldEdit to tick. This could cause the big lags.",
"This has no effect on existing blocks one way or the other."
"Allow fluids placed by FAWE to tick (flow). This could cause the big lags.",
"This has no effect on existing blocks one way or the other.",
"Changes due to fluid flow will not be tracked by history, thus may have unintended consequences"
})
public boolean ALLOW_TICK_PLACED = false;
@Comment({
"Force re-ticking of existing blocks not edited by FAWE.",
"This will increase time taken slightly."
})
public boolean ALLOW_TICK_EXISTING = true;
public boolean ALLOW_TICK_FLUIDS = false;
@Comment({
"Sets a maximum limit (in kb) for the size of a player's schematics directory (per-player mode only)",

View File

@@ -167,11 +167,6 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB
return set;
}
@Override
public Future<IChunkSet> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
return CompletableFuture.completedFuture(set);
}
@Nullable
@Override
public Extent construct(final Extent child) {

View File

@@ -56,9 +56,4 @@ public class HeightBoundExtent extends FaweRegionExtent {
return null;
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return CompletableFuture.completedFuture(set);
}
}

View File

@@ -178,8 +178,13 @@ public class MultiRegionExtent extends FaweRegionExtent {
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
public Future<?> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
return intersection.postProcessSet(chunk, get, set);
}
@Override
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
intersection.postProcess(chunk, get, set);
}
}

View File

@@ -342,7 +342,12 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor {
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
throw reason;
}
@Override
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
throw reason;
}

View File

@@ -46,10 +46,17 @@ public class SingleRegionExtent extends FaweRegionExtent {
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
// Most likely will do nothing, but perhaps people will find some fun way of using this via API (though doubtful)
return region.postProcessSet(chunk, get, set);
}
@Override
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
// Most likely will do nothing, but perhaps people will find some fun way of using this via API (though doubtful)
region.postProcess(chunk, get, set);
}
@Override
public boolean processGet(int chunkX, int chunkZ) {
return region.containsChunk(chunkX, chunkZ);

View File

@@ -143,11 +143,6 @@ public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProc
return set;
}
@Override
public Future<IChunkSet> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
return CompletableFuture.completedFuture(set);
}
@Nullable
@Override
public Extent construct(final Extent child) {

View File

@@ -28,10 +28,15 @@ public class BatchProcessorHolder implements IBatchProcessorHolder {
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return getPostProcessor().postProcessSet(chunk, get, set);
}
@Override
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
getPostProcessor().postProcess(chunk, get, set);
}
@Override
public void flush() {
getProcessor().flush();

View File

@@ -29,13 +29,6 @@ public final class EmptyBatchProcessor implements IBatchProcessor {
return set;
}
@Override
@Nonnull
public Future<IChunkSet> postProcessSet(@Nullable IChunk chunk, @Nullable IChunkGet get, @Nullable IChunkSet set) {
// Doesn't need to do anything
return CompletableFuture.completedFuture(set);
}
@Nonnull
public IBatchProcessor join(@Nullable IBatchProcessor other) {
return other;

View File

@@ -31,10 +31,15 @@ public interface IBatchProcessorHolder extends IBatchProcessor {
}
@Override
default Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
default Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return getPostProcessor().postProcessSet(chunk, get, set);
}
@Override
default void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
getPostProcessor().postProcess(chunk, get, set);
}
@Override
default boolean processGet(int chunkX, int chunkZ) {
return getProcessor().processGet(chunkX, chunkZ);

View File

@@ -9,6 +9,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.util.MultiFuture;
import com.fastasyncworldedit.core.util.StringMan;
import com.google.common.cache.LoadingCache;
import com.sk89q.worldedit.extent.Extent;
@@ -24,7 +25,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.function.Supplier;
@@ -129,37 +129,64 @@ public class MultiBatchProcessor implements IBatchProcessor {
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
try {
for (IBatchProcessor processor : processors) {
public Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
List<Future<?>> futures = new ArrayList<>();
for (IBatchProcessor processor : processors) {
try {
// We do NOT want to edit blocks in post processing
if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) {
continue;
}
set = processor.postProcessSet(chunk, get, set).get();
if (set == null) {
return null;
futures.add(processor.postProcessSet(chunk, get, set));
} catch (Throwable e) {
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 != null ? message.hashCode() : 0;
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
LOGGER.catching(e);
} else if (exceptionCount < Settings.settings().QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
}
}
return CompletableFuture.completedFuture(set);
} catch (Throwable e) {
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.settings().QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
return new MultiFuture(futures);
}
@Override
public void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
for (IBatchProcessor processor : processors) {
try {
// We do NOT want to edit blocks in post processing
if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) {
continue;
}
processor.postProcess(chunk, get, set);
} catch (Throwable e) {
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 != null ? message.hashCode() : 0;
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
LOGGER.catching(e);
} else if (exceptionCount < Settings.settings().QUEUE.PARALLEL_THREADS) {
exceptionCount++;
LOGGER.warn(message);
}
}
}
return null;
}
}

View File

@@ -24,11 +24,6 @@ public final class NullProcessor implements IBatchProcessor {
return null;
}
@Nullable
public Future<IChunkSet> postProcessSet(@Nonnull IChunk chunk, @Nonnull IChunkGet get, @Nonnull IChunkSet set) {
return null;
}
@Nonnull
public Extent construct(@Nonnull Extent child) {
return new NullExtent();

View File

@@ -136,11 +136,6 @@ public class HeightmapProcessor implements IBatchProcessor {
return set;
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return CompletableFuture.completedFuture(set);
}
@Override
@Nullable
public Extent construct(Extent child) {

View File

@@ -46,11 +46,6 @@ public class RelightProcessor implements IBatchProcessor {
return set;
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return CompletableFuture.completedFuture(set);
}
@Override
public @Nullable
Extent construct(Extent child) {

View File

@@ -228,8 +228,13 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
}
@Override
public Future<IChunkSet> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
return (Future<IChunkSet>) addWriteTask(() -> processSet(chunk, get, set));
public void postProcess(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
addWriteTask(() -> processSet(chunk, get, set));
}
@Override
public Future<?> postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
return addWriteTask(() -> processSet(chunk, get, set));
}
@Override

View File

@@ -6,13 +6,12 @@ import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.Function;
@@ -23,7 +22,27 @@ public interface IBatchProcessor {
*/
IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set);
Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set);
/**
* Post-process a chunk that has been edited. Set should NOT be modified here, changes will NOT be flushed to the world,
* but MAY be flushed to history. Defaults to nothing as most Processors will not use it. Post-processors that are not
* technically blocking should override this method to allow post-processors to become blocking if required.
*/
default Future<?> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
// Do not need to default to below method. FAWE itself by default will only call the method below.
return CompletableFuture.completedFuture(null);
}
/**
* Post-process a chunk that has been edited. Set should NOT be modified here, changes will NOT be flushed to the world,
* but MAY be flushed to history. Defaults to nothing as most Processors will not use it. If the post-processor will run
* tasks asynchronously/not be blocking, use {@link IBatchProcessor#postProcessSet} to return a Future.
*
* @since TODO
*/
default void postProcess(IChunk chunk, IChunkGet get, IChunkSet set) {
// Default to above for compatibility and to ensure whatever method is overridden by child classes is called
postProcessSet(chunk, get, set);
}
default boolean processGet(int chunkX, int chunkZ) {
return true;

View File

@@ -145,7 +145,7 @@ public class ParallelQueueExtent extends PassthroughExtent {
}
} catch (Throwable e) {
String message = e.getMessage();
int hash = message.hashCode();
int hash = message != null ? message.hashCode() : 0;
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;

View File

@@ -401,7 +401,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
} else {
String message = e.getMessage();
int hash = message.hashCode();
int hash = message != null ? message.hashCode() : 0;
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;
@@ -441,7 +441,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
Fawe.handleFaweException(faweExceptionReasonsUsed, (FaweException) e.getCause(), LOGGER);
} else {
String message = e.getMessage();
int hash = message.hashCode();
int hash = message != null ? message.hashCode() : 0;
if (lastException != hash) {
lastException = hash;
exceptionCount = 0;

View File

@@ -1010,7 +1010,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return get.call(set, finalize);
} finally {
if (postProcess) {
getExtent().postProcessSet(this, get.getCopy(), set);
getExtent().postProcess(this, get.getCopy(), set);
}
}
}

View File

@@ -0,0 +1,55 @@
package com.fastasyncworldedit.core.util;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class MultiFuture implements Future<Object[]> {
private final List<Future<?>> futures;
public MultiFuture(List<Future<?>> futures) {
this.futures = futures;
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
return futures.stream().allMatch(f -> f.cancel(mayInterruptIfRunning));
}
@Override
public boolean isCancelled() {
return futures.stream().allMatch(Future::isCancelled);
}
@Override
public boolean isDone() {
return futures.stream().allMatch(Future::isDone);
}
@Override
public Object[] get() {
return futures.stream().map(f -> {
try {
return f.get();
} catch (InterruptedException | ExecutionException e) {
return e;
}
}).toArray();
}
@Override
public Object[] get(final long timeout, @Nonnull final TimeUnit unit) {
return futures.stream().map(f -> {
try {
return f.get(timeout / futures.size(), unit);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
return e;
}
}).toArray();
}
}