Add basic preloading (#1221)

This commit is contained in:
dordsor21 2021-08-17 01:47:09 +01:00 committed by GitHub
parent d4d98708f9
commit da7aca8ef8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 532 additions and 119 deletions

View File

@ -58,6 +58,7 @@ public class FaweBukkit implements IFawe, Listener {
private boolean listeningImages;
private final boolean chunksStretched;
private final FAWEPlatformAdapterImpl platformAdapter;
private Preloader preloader;
public FaweBukkit(Plugin plugin) {
this.plugin = plugin;
@ -277,9 +278,12 @@ public class FaweBukkit implements IFawe, Listener {
}
@Override
public Preloader getPreloader() {
public Preloader getPreloader(boolean initialise) {
if (PaperLib.isPaper()) {
return new AsyncPreloader();
if (preloader == null && initialise) {
return preloader = new AsyncPreloader();
}
return preloader;
}
return null;
}

View File

@ -264,7 +264,7 @@ public class FaweDelegateRegionManager {
.autoQueue(false)
.build();
FlatRegionFunction replace = new BiomeReplace(editSession, biome);
FlatRegionVisitor visitor = new FlatRegionVisitor(region, replace);
FlatRegionVisitor visitor = new FlatRegionVisitor(region, replace, editSession);
try {
Operations.completeLegacy(visitor);
editSession.flushQueue();

View File

@ -353,7 +353,7 @@ public class BukkitWorld extends AbstractWorld {
int Z = pt.getBlockZ() >> 4;
if (Fawe.isMainThread()) {
world.getChunkAt(X, Z);
} else {
} else if (PaperLib.isPaper()) {
PaperLib.getChunkAtAsync(world, X, Z, true);
}
//FAWE end

View File

@ -163,6 +163,9 @@ public class Fawe {
}
public void onDisable() {
if (imp().getPreloader(false) != null) {
imp().getPreloader(false).cancel();
}
}
public QueueHandler getQueueHandler() {

View File

@ -35,7 +35,13 @@ public interface IFawe {
QueueHandler getQueueHandler();
Preloader getPreloader();
/**
* Get the preloader instance and initialise if needed
*
* @param initialise if the preloader should be initialised if null
* @return preloader instance
*/
Preloader getPreloader(boolean initialise);
default boolean isChunksStretched() {
return true;

View File

@ -319,9 +319,12 @@ public class Settings extends Config {
"Loading the right amount of chunks beforehand can speed up operations",
" - Low values may result in FAWE waiting on requests to the main thread",
" - Higher values use more memory and isn't noticeably faster",
" - A good (relatively) safe way to set this is",
" - Use 32 x GB of RAM / number of players expected to be using WE at the same time"
})
//TODO Find out where this was used and why the usage was removed
public int PRELOAD_CHUNKS = 100000;
// Renamed from PRELOAD_CHUNK because it was set to 100000... something that lots of servers will now have which is
// wayyy too much...
public int PRELOAD_CHUNK_COUNT = 128;
@Comment({
"If pooling is enabled (reduces GC, higher memory usage)",

View File

@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.image.ImageUtil;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.annotation.Confirm;
import com.sk89q.worldedit.command.util.annotation.Preload;
import com.sk89q.worldedit.command.util.annotation.Time;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extension.input.InputParseException;
@ -98,6 +99,17 @@ public class ConsumeBindings extends Bindings {
return radius;
}
@Binding
@Preload(Preload.PreloadCheck.PRELOAD)
public void checkPreload(Actor actor, InjectedValueAccess context) {
Preload.PreloadCheck.PRELOAD.preload(actor, context);
}
@Binding
@Preload(Preload.PreloadCheck.NEVER)
public void neverPreload(Actor actor, InjectedValueAccess context) {
}
@Binding
public UUID playerUUID(Actor actor, String argument) {
if (argument.equals("me")) {

View File

@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.queue.implementation;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.PassthroughExtent;
import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor;
@ -19,9 +20,15 @@ import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkHolder;
import com.fastasyncworldedit.core.queue.implementation.chunk.NullChunk;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.MemUtil;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.google.common.util.concurrent.Futures;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extent.Extent;
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;
import com.sk89q.worldedit.world.World;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import org.apache.logging.log4j.Logger;
@ -61,6 +68,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
private final ReentrantLock getChunkLock = new ReentrantLock();
private World world = null;
/**
* Safety check to ensure that the thread being used matches the one being initialized on. - Can
* be removed later
@ -124,6 +133,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
this.initialized = false;
this.setProcessor(EmptyBatchProcessor.getInstance());
this.setPostProcessor(EmptyBatchProcessor.getInstance());
this.world = null;
}
/**
@ -146,6 +156,14 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
this.setProcessor(EmptyBatchProcessor.getInstance());
this.setPostProcessor(EmptyBatchProcessor.getInstance());
initialized = true;
if (extent.isWorld()) {
world = (World) ((extent instanceof PassthroughExtent) ? ((PassthroughExtent) extent).getExtent() : extent);
} else if (extent instanceof EditSession) {
world = ((EditSession) extent).getWorld();
} else {
world = WorldWrapper.unwrap(extent);
}
}
@Override
@ -289,6 +307,37 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
}
}
/**
* Load a chunk in the world associated with this {@link SingleThreadQueueExtent} instance
*
* @param cx chunk X coordinate
* @param cz chunk Z coordinate
*/
public void addChunkLoad(int cx, int cz) {
if (world == null) {
return;
}
world.checkLoadedChunk(BlockVector3.at(cx << 4, 0, cz << 4));
}
/**
* Define a region to be "preloaded" to the number of chunks provided by {@link Settings.QUEUE#PRELOAD_CHUNK_COUNT}
*
* @param region region of chunks
*/
public void preload(Region region) {
if (Settings.IMP.QUEUE.PRELOAD_CHUNK_COUNT > 1) {
int loadCount = 0;
for (BlockVector2 from : region.getChunks()) {
if (loadCount >= Settings.IMP.QUEUE.PRELOAD_CHUNK_COUNT) {
break;
}
loadCount++;
addChunkLoad(from.getBlockX(), from.getBlockZ());
}
}
}
@Override
public ChunkHolder create(boolean isFull) {
return ChunkHolder.newInstance();

View File

@ -2,41 +2,50 @@ package com.fastasyncworldedit.core.queue.implementation.preloader;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.util.FaweTimer;
import com.fastasyncworldedit.core.util.TaskManager;
import com.fastasyncworldedit.core.util.collection.MutablePair;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nonnull;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class AsyncPreloader implements Preloader, Runnable {
private final ConcurrentHashMap<UUID, MutablePair<World, Set<BlockVector2>>> update;
private final AtomicBoolean cancelled = new AtomicBoolean(false);
public AsyncPreloader() {
this.update = new ConcurrentHashMap<>();
Fawe.get().getQueueHandler().async(this);
TaskManager.IMP.laterAsync(this, 1);
}
@Override
public void cancel(Player player) {
cancelAndGet(player);
public void cancel() {
cancelled.set(true);
synchronized (update) {
update.clear();
}
}
private MutablePair<World, Set<BlockVector2>> cancelAndGet(Actor player) {
MutablePair<World, Set<BlockVector2>> existing = update.get(player.getUniqueId());
@Override
public void cancel(@Nonnull Actor actor) {
cancelAndGet(actor);
}
private MutablePair<World, Set<BlockVector2>> cancelAndGet(@Nonnull Actor actor) {
MutablePair<World, Set<BlockVector2>> existing = update.get(actor.getUniqueId());
if (existing != null) {
existing.setValue(null);
}
@ -44,34 +53,29 @@ public class AsyncPreloader implements Preloader, Runnable {
}
@Override
public void update(Player player) {
LocalSession session = WorldEdit.getInstance().getSessionManager().getIfPresent(player);
public void update(@Nonnull Actor actor, @Nonnull World world) {
LocalSession session = WorldEdit.getInstance().getSessionManager().getIfPresent(actor);
if (session == null) {
return;
}
World world = player.getWorld();
MutablePair<World, Set<BlockVector2>> existing = cancelAndGet(player);
MutablePair<World, Set<BlockVector2>> existing = cancelAndGet(actor);
try {
Region region = session.getSelection(world);
if (!(region instanceof CuboidRegion) || region.getVolume() > 50466816) {
// TOO LARGE or NOT CUBOID
if (region == null) {
return;
}
if (existing == null) {
MutablePair<World, Set<BlockVector2>> previous = update.putIfAbsent(
player.getUniqueId(),
update.put(
actor.getUniqueId(),
existing = new MutablePair<>()
);
if (previous != null) {
existing = previous;
}
synchronized (existing) { // Ensure key & value are mutated together
existing.setKey(world);
existing.setValue(region.getChunks());
}
synchronized (update) {
update.notify();
}
}
synchronized (existing) { // Ensure key & value are mutated together
existing.setKey(world);
existing.setValue(region.getChunks());
}
synchronized (update) {
update.notify();
}
} catch (IncompleteRegionException ignored) {
}
@ -80,38 +84,38 @@ public class AsyncPreloader implements Preloader, Runnable {
@Override
public void run() {
FaweTimer timer = Fawe.get().getTimer();
try {
while (true) {
if (!update.isEmpty()) {
if (timer.getTPS() > 19) {
Iterator<Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>>> plrIter = update.entrySet().iterator();
Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>> entry = plrIter.next();
MutablePair<World, Set<BlockVector2>> pair = entry.getValue();
World world = pair.getKey();
Set<BlockVector2> chunks = pair.getValue();
if (chunks != null) {
Iterator<BlockVector2> chunksIter = chunks.iterator();
while (chunksIter.hasNext() && pair.getValue() == chunks) { // Ensure the queued load is still valid
BlockVector2 chunk = chunksIter.next();
queueLoad(world, chunk);
}
}
plrIter.remove();
} else {
Thread.sleep(1000);
}
} else {
synchronized (update) {
update.wait();
}
if (cancelled.get()) {
return;
}
if (update.isEmpty()) {
TaskManager.IMP.laterAsync(this, 1);
return;
}
Iterator<Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>>> plrIter = update.entrySet().iterator();
while (timer.getTPS() > 18 && plrIter.hasNext()) {
if (cancelled.get()) {
return;
}
Map.Entry<UUID, MutablePair<World, Set<BlockVector2>>> entry = plrIter.next();
MutablePair<World, Set<BlockVector2>> pair = entry.getValue();
World world = pair.getKey();
Set<BlockVector2> chunks = pair.getValue();
if (chunks != null) {
Iterator<BlockVector2> chunksIter = chunks.iterator();
while (chunksIter.hasNext() && pair.getValue() == chunks) { // Ensure the queued load is still valid
BlockVector2 chunk = chunksIter.next();
queueLoad(world, chunk);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
plrIter.remove();
}
if (cancelled.get()) {
return;
}
TaskManager.IMP.laterAsync(this, 20);
}
public void queueLoad(World world, BlockVector2 chunk) {
private void queueLoad(World world, BlockVector2 chunk) {
world.checkLoadedChunk(BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4));
}

View File

@ -1,11 +1,30 @@
package com.fastasyncworldedit.core.queue.implementation.preloader;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nonnull;
public interface Preloader {
void cancel(Player player);
/**
* Tell the preloader to stop attempting to preload chunks
*/
void cancel();
void update(Player player);
/**
* Cancel any preloading related to the given Actor
*
* @param actor Actor to cancel preloading of
*/
void cancel(@Nonnull Actor actor);
/**
* Update the preloading for the given player, in the given world. Uses the player's current selection.
*
* @param actor Actor to update
* @param world World to use
*/
void update(@Nonnull Actor actor, @Nonnull World world);
}

View File

@ -1408,11 +1408,13 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
// Pick how we're going to visit blocks
RecursiveVisitor visitor;
//FAWE start - provide extent for preloading
if (recursive) {
visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1));
visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this);
} else {
visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1));
visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), this);
}
//FAWE end
// Start at the origin
visitor.visit(origin);
@ -1667,7 +1669,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
int minY = region.getMinimumPoint().getBlockY();
int maxY = Math.min(getMaximumPoint().getBlockY(), region.getMaximumPoint().getBlockY() + 1);
SurfaceRegionFunction surface = new SurfaceRegionFunction(this, offset, minY, maxY);
FlatRegionVisitor visitor = new FlatRegionVisitor(asFlatRegion(region), surface);
FlatRegionVisitor visitor = new FlatRegionVisitor(asFlatRegion(region), surface, this);
//FAWE end
Operations.completeBlindly(visitor);
return this.changes = visitor.getAffected();
@ -1686,7 +1688,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
Naturalizer naturalizer = new Naturalizer(this);
FlatRegion flatRegion = Regions.asFlatRegion(region);
LayerVisitor visitor = new LayerVisitor(flatRegion, minimumBlockY(region), maximumBlockY(region), naturalizer);
//FAWE start - provide extent for preloading
LayerVisitor visitor = new LayerVisitor(flatRegion, minimumBlockY(region), maximumBlockY(region), naturalizer, this);
//FAWE end
Operations.completeBlindly(visitor);
return this.changes = naturalizer.getAffected();
}
@ -1937,7 +1941,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
} else {
replace = new BlockReplace(this, BlockTypes.AIR.getDefaultState());
}
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1));
//FAWE start - provide extent for preloading
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this);
//FAWE end
// Around the origin in a 3x3 block
for (BlockVector3 position : CuboidRegion.fromCenter(origin, 1)) {
@ -1980,7 +1986,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
);
BlockReplace replace = new BlockReplace(this, fluid.getDefaultState());
NonRisingVisitor visitor = new NonRisingVisitor(mask, replace);
//FAWE start - provide extent for preloading
NonRisingVisitor visitor = new NonRisingVisitor(mask, replace, Integer.MAX_VALUE, this);
//FAWE end
// Around the origin in a 3x3 block
for (BlockVector3 position : CuboidRegion.fromCenter(origin, 1)) {
@ -2586,7 +2594,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
checkNotNull(region);
SnowSimulator snowSimulator = new SnowSimulator(this, stack);
LayerVisitor layerVisitor = new LayerVisitor(region, region.getMinimumY(), region.getMaximumY(), snowSimulator);
//FAWE start - provide extent for preloading
LayerVisitor layerVisitor = new LayerVisitor(region, region.getMinimumY(), region.getMaximumY(), snowSimulator, this);
//FAWE end
Operations.completeLegacy(layerVisitor);
return snowSimulator.getAffected();
}
@ -2698,7 +2708,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
);
GroundFunction ground = new GroundFunction(new ExistingBlockMask(this), generator);
LayerVisitor visitor = new LayerVisitor(region, minimumBlockY(region), maximumBlockY(region), ground);
LayerVisitor visitor = new LayerVisitor(region, minimumBlockY(region), maximumBlockY(region), ground, this);
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
Operations.completeLegacy(visitor);
return this.changes = ground.getAffected();
@ -2732,7 +2742,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
public int makeForest(Region region, double density, TreeGenerator.TreeType treeType) throws MaxChangedBlocksException {
ForestGenerator generator = new ForestGenerator(this, treeType);
GroundFunction ground = new GroundFunction(new ExistingBlockMask(this), generator);
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
//FAWE start - provide extent for preloading
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground, this);
//FAWE end
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
Operations.completeLegacy(visitor);
return ground.getAffected();

View File

@ -28,6 +28,8 @@ import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.Logging;
import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder;
import com.sk89q.worldedit.command.util.annotation.Confirm;
import com.sk89q.worldedit.command.util.annotation.Preload;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Capability;
@ -162,6 +164,8 @@ public class BiomeCommands {
descFooter = "By default, uses all the blocks in your selection"
)
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
@CommandPermissions("worldedit.biome.set")
public void setBiome(
Player player, LocalSession session, EditSession editSession,
@ -184,9 +188,7 @@ public class BiomeCommands {
if (mask != null) {
replace = new RegionMaskingFilter(editSession, mask, replace);
}
//FAWE start > add extent to RegionVisitor to allow chunk preloading
RegionVisitor visitor = new RegionVisitor(region, replace, editSession);
//FAWE end
RegionVisitor visitor = new RegionVisitor(region, replace);
Operations.completeLegacy(visitor);
player.print(Caption.of(

View File

@ -45,6 +45,7 @@ import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.Logging;
import com.sk89q.worldedit.command.util.annotation.Confirm;
import com.sk89q.worldedit.command.util.annotation.Preload;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
@ -112,6 +113,7 @@ public class ClipboardCommands {
desc = "Copy the selection to the clipboard"
)
@CommandPermissions("worldedit.clipboard.copy")
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public void copy(
Actor actor, LocalSession session, EditSession editSession,
@ -242,6 +244,7 @@ public class ClipboardCommands {
)
@CommandPermissions("worldedit.clipboard.cut")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public void cut(
Actor actor, LocalSession session, EditSession editSession,

View File

@ -33,6 +33,7 @@ import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.Logging;
import com.sk89q.worldedit.command.util.annotation.Confirm;
import com.sk89q.worldedit.command.util.annotation.Preload;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
@ -435,6 +436,7 @@ public class GenerationCommands {
)
@CommandPermissions("worldedit.generation.shape.biome")
@Logging(ALL)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int generateBiome(
Actor actor, LocalSession session, EditSession editSession,
@ -511,6 +513,7 @@ public class GenerationCommands {
)
@CommandPermissions("worldedit.generation.caves")
@Logging(PLACEMENT)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public void caves(
Actor actor, LocalSession session, EditSession editSession, @Selection Region region,
@ -548,6 +551,7 @@ public class GenerationCommands {
)
@CommandPermissions("worldedit.generation.ore")
@Logging(PLACEMENT)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public void ores(
Actor actor,
@ -619,6 +623,7 @@ public class GenerationCommands {
@Command(name = "/ore", desc = "Generates ores")
@CommandPermissions("worldedit.generation.ore")
@Logging(PLACEMENT)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public void ore(
Actor actor,

View File

@ -32,6 +32,7 @@ import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.Logging;
import com.sk89q.worldedit.command.util.annotation.Confirm;
import com.sk89q.worldedit.command.util.annotation.Preload;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.function.GroundFunction;
@ -105,6 +106,7 @@ public class RegionCommands {
@CommandPermissions("worldedit.region.set")
@Logging(REGION)
@Confirm(Confirm.Processor.REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
public int set(
Actor actor, EditSession editSession,
@Selection Region region,
@ -125,6 +127,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.set")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
public void air(Actor actor, EditSession editSession, @Selection Region region) throws WorldEditException {
set(actor, editSession, region, BlockTypes.AIR);
}
@ -305,6 +308,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.replace")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int replace(
Actor actor, EditSession editSession, @Selection Region region,
@ -351,6 +355,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.overlay")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public void lay(
Player player,
@ -429,6 +434,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.faces")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int faces(
Actor actor, EditSession editSession, @Selection Region region,
@ -447,6 +453,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.smooth")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int smooth(
Actor actor, EditSession editSession, @Selection Region region,
@ -522,6 +529,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.move")
@Logging(ORIENTATION_REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int move(
Actor actor, World world, EditSession editSession, LocalSession session,
@ -586,6 +594,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.fall")
@Logging(ORIENTATION_REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public void fall(
Player player, EditSession editSession, LocalSession session,
@ -604,6 +613,7 @@ public class RegionCommands {
desc = "Repeat the contents of the selection"
)
@CommandPermissions("worldedit.region.stack")
@Preload(Preload.PreloadCheck.PRELOAD)
@Logging(ORIENTATION_REGION)
public int stack(
Actor actor, World world, EditSession editSession, LocalSession session,
@ -713,6 +723,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.deform")
@Logging(ALL)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int deform(
Actor actor, LocalSession session, EditSession editSession,
@ -788,6 +799,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.hollow")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int hollow(
Actor actor, EditSession editSession,
@ -823,6 +835,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.forest")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int forest(
Actor actor, EditSession editSession, @Selection Region region,
@ -853,7 +866,9 @@ public class RegionCommands {
density = density / 100;
FloraGenerator generator = new FloraGenerator(editSession);
GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator);
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
//FAWE start - provide extent for preloading
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground, editSession);
//FAWE end
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
Operations.completeLegacy(visitor);

View File

@ -87,7 +87,7 @@ public class FloodFillTool implements BlockTool {
//FAWE start - Respect masks
Mask mask = initialType.toMask(editSession);
BlockReplace function = new BlockReplace(editSession, pattern);
RecursiveVisitor visitor = new RecursiveVisitor(mask, function, range);
RecursiveVisitor visitor = new RecursiveVisitor(mask, function, range, editSession);
visitor.visit(origin);
Operations.completeLegacy(visitor);
//FAWE end

View File

@ -86,7 +86,7 @@ public class RecursivePickaxe implements BlockTool {
final int radius = (int) range;
final BlockReplace replace = new BlockReplace(editSession, (BlockTypes.AIR.getDefaultState()));
editSession.setMask(null);
RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius);
RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius, editSession);
//TODO: Fix below
//visitor.visit(pos);
//Operations.completeBlindly(visitor);

View File

@ -0,0 +1,46 @@
package com.sk89q.worldedit.command.util.annotation;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.queue.implementation.preloader.Preloader;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.world.World;
import org.enginehub.piston.inject.InjectAnnotation;
import org.enginehub.piston.inject.InjectedValueAccess;
import org.enginehub.piston.inject.Key;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates how the affected blocks should be hinted at in the log.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.PARAMETER,
ElementType.METHOD
})
@InjectAnnotation
public @interface Preload {
PreloadCheck value() default PreloadCheck.NEVER;
enum PreloadCheck {
PRELOAD {
@Override
public void preload(Actor actor, InjectedValueAccess context) {
World world = context.injectedValue(Key.of(EditSession.class)).get().getWorld();
Preloader preloader = Fawe.imp().getPreloader(true);
preloader.update(actor, world);
}
},
NEVER {};
public void preload(Actor actor, InjectedValueAccess context) {
}
}
}

View File

@ -0,0 +1,38 @@
package com.sk89q.worldedit.command.util.annotation;
import com.fastasyncworldedit.core.configuration.Settings;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extension.platform.Actor;
import org.enginehub.piston.CommandParameters;
import org.enginehub.piston.gen.CommandCallListener;
import org.enginehub.piston.inject.Key;
import java.lang.reflect.Method;
import java.util.Optional;
/**
* Logs called commands to a logger.
*/
public class PreloadHandler implements CommandCallListener {
@Override
public void beforeCall(Method method, CommandParameters parameters) {
Preload preloadAnnotation = method.getAnnotation(Preload.class);
if (preloadAnnotation == null) {
return;
}
Optional<Actor> actorOpt = parameters.injectedValue(Key.of(Actor.class));
Optional<EditSession> editSessionOpt = parameters.injectedValue(Key.of(EditSession.class));
if (actorOpt.isEmpty() || editSessionOpt.isEmpty()) {
return;
}
Actor actor = actorOpt.get();
// Don't attempt to preload if effectively disabled
if (Settings.IMP.QUEUE.PRELOAD_CHUNK_COUNT <= 1) {
return;
}
preloadAnnotation.value().preload(actor, parameters);
}
}

View File

@ -6,6 +6,8 @@
* @see com.sk89q.worldedit.command.util.annotation.ConfirmHandler
* @see com.sk89q.worldedit.command.util.annotation.Link
* @see com.sk89q.worldedit.command.util.annotation.PatternList
* @see com.sk89q.worldedit.command.util.annotation.Preload
* @see com.sk89q.worldedit.command.util.annotation.PreloadHandler
* @see com.sk89q.worldedit.command.util.annotation.Step
* @see com.sk89q.worldedit.command.util.annotation.Time
*/

View File

@ -106,6 +106,7 @@ import com.sk89q.worldedit.command.util.PermissionCondition;
import com.sk89q.worldedit.command.util.PrintCommandHelp;
import com.sk89q.worldedit.command.util.SubCommandPermissionCondition;
import com.sk89q.worldedit.command.util.annotation.ConfirmHandler;
import com.sk89q.worldedit.command.util.annotation.PreloadHandler;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.Event;
@ -219,7 +220,10 @@ public final class PlatformCommandManager {
ImmutableList.of(
new CommandLoggingHandler(worldEdit, COMMAND_LOG),
new MethodInjector(),
new ConfirmHandler()
//FAWE start
new ConfirmHandler(),
new PreloadHandler()
//FAWE end
));
// setup separate from main constructor

View File

@ -55,7 +55,10 @@ public class ApplyLayer implements Contextual<Operation> {
localRegion,
localRegion.getMinimumPoint().getY(),
localRegion.getMaximumPoint().getY(),
function.createFromContext(context)
function.createFromContext(context),
//FAWE start - provide extent for preloading
context.getDestination()
//FAWE end
);
}

View File

@ -70,7 +70,9 @@ public class Paint implements Contextual<Operation> {
Extent destination = firstNonNull(context.getDestination(), this.destination);
Region region = firstNonNull(context.getRegion(), this.region);
GroundFunction ground = new GroundFunction(new ExistingBlockMask(destination), function.createFromContext(context));
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
//FAWE start - provide extent for preloading
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground, destination);
//FAWE end
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
return visitor;
}

View File

@ -27,6 +27,9 @@ import com.fastasyncworldedit.core.function.block.BiomeCopy;
import com.fastasyncworldedit.core.function.block.CombinedBlockCopy;
import com.fastasyncworldedit.core.function.block.SimpleBlockCopy;
import com.fastasyncworldedit.core.function.visitor.IntersectRegionFunction;
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.util.MaskTraverser;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@ -398,7 +401,9 @@ public class ForwardExtentCopy implements Operation {
if (copyingBiomes && (source.isWorld() || region instanceof FlatRegion)) {
copy = CombinedRegionFunction.combine(copy, new BiomeCopy(source, finalDest));
}
blockCopy = new RegionVisitor(region, copy);
ExtentTraverser<ParallelQueueExtent> queueTraverser = new ExtentTraverser<>(finalDest).find(ParallelQueueExtent.class);
Extent preloader = queueTraverser != null ? queueTraverser.get() : source;
blockCopy = new RegionVisitor(region, copy, preloader);
}
List<? extends Entity> entities;

View File

@ -20,11 +20,16 @@
package com.sk89q.worldedit.function.visitor;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.BlockVectorSet;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
@ -84,7 +89,8 @@ public abstract class BreadthFirstSearch implements Operation {
//FAWE end
private final RegionFunction function;
//FAWE Start - BVS > Queue<BV3>, Set<BV3>, List<BV3>
//FAWE start - allow chunk preloading and BVS > Queue<BV3>, Set<BV3>, List<BV3>
private final SingleThreadQueueExtent singleQueue;
private BlockVectorSet queue = new BlockVectorSet();
private BlockVectorSet visited = new BlockVectorSet();
private BlockVector3[] directions;
@ -108,11 +114,34 @@ public abstract class BreadthFirstSearch implements Operation {
}
//FAWE start
/**
* Create a new instance.
*
* @param function the function to apply to visited blocks
* @param maxDepth the maximum number of iterations
*/
public BreadthFirstSearch(RegionFunction function, int maxDepth) {
this(function, maxDepth, null);
}
/**
* Create a new instance.
*
* @param function the function to apply to visited blocks
* @param maxDepth the maximum number of iterations
* @param extent extent to use for preloading
*/
public BreadthFirstSearch(RegionFunction function, int maxDepth, Extent extent) {
checkNotNull(function);
this.function = function;
this.directions = DEFAULT_DIRECTIONS;
this.maxDepth = maxDepth;
if (extent != null) {
ExtentTraverser<ParallelQueueExtent> queueTraverser = new ExtentTraverser<>(extent).find(ParallelQueueExtent.class);
this.singleQueue = queueTraverser != null ? (SingleThreadQueueExtent) queueTraverser.get().getExtent() : null;
} else {
this.singleQueue = null;
}
}
public void setDirections(BlockVector3... directions) {
@ -245,11 +274,39 @@ public abstract class BreadthFirstSearch implements Operation {
@Override
public Operation resume(RunContext run) throws WorldEditException {
//FAWE start - directions & visited
//FAWE start - directions, visited and preloading
MutableBlockVector3 mutable = new MutableBlockVector3();
BlockVector3[] dirs = directions;
BlockVectorSet tempQueue = new BlockVectorSet();
BlockVectorSet chunkLoadSet = new BlockVectorSet();
for (currentDepth = 0; !queue.isEmpty() && currentDepth <= maxDepth; currentDepth++) {
int loadCount = 0;
if (singleQueue != null && Settings.IMP.QUEUE.PRELOAD_CHUNK_COUNT > 1) {
int cx = Integer.MIN_VALUE;
int cz = Integer.MIN_VALUE;
outer: for (BlockVector3 from : queue) {
for (BlockVector3 direction : dirs) {
if (loadCount > Settings.IMP.QUEUE.PRELOAD_CHUNK_COUNT) {
break outer;
}
int x = from.getBlockX() + direction.getBlockX();
int z = from.getBlockZ() + direction.getBlockX();
if (cx != (cx = x >> 4) || cz != (cz = z >> 4)) {
int y = from.getBlockY() + direction.getBlockY();
if (y < singleQueue.getMinY() || y > singleQueue.getMaxY()) {
continue;
}
if (!visited.contains(x, y, z)) {
loadCount++;
chunkLoadSet.add(cx, 0, cz);
}
}
}
}
for (BlockVector3 chunk : chunkLoadSet) {
singleQueue.addChunkLoad(chunk.getBlockX(), chunk.getBlockZ());
}
}
for (BlockVector3 from : queue) {
if (function.apply(from)) {
affected++;

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.function.visitor;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.BlockVector3;
@ -48,10 +49,30 @@ public class DownwardVisitor extends RecursiveVisitor {
public DownwardVisitor(Mask mask, RegionFunction function, int baseY) {
this(mask, function, baseY, Integer.MAX_VALUE);
}
//FAWE end
/**
* Create a new visitor.
*
* @param mask the mask
* @param function the function
* @param baseY the base Y
* @param depth maximum number of iterations
*/
public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth) {
super(mask, function, depth);
this (mask, function, baseY, depth, null);
}
/**
* Create a new visitor.
*
* @param mask the mask
* @param function the function
* @param baseY the base Y
* @param depth maximum number of iterations
* @param extent extent for preloading
*/
public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, Extent extent) {
super(mask, function, depth, extent);
checkNotNull(mask);
this.baseY = baseY;
@ -64,6 +85,7 @@ public class DownwardVisitor extends RecursiveVisitor {
BlockVector3.UNIT_MINUS_Y
);
}
//FAWE end
@Override
protected boolean isVisitable(BlockVector3 from, BlockVector3 to) {

View File

@ -20,8 +20,12 @@
package com.sk89q.worldedit.function.visitor;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.FlatRegionFunction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
@ -37,10 +41,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public class FlatRegionVisitor implements Operation {
//FAWE start - chunk preloading
private final SingleThreadQueueExtent singleQueue;
private final FlatRegion flatRegion;
//FAWE end
private final FlatRegionFunction function;
private int affected = 0;
private final Iterable<BlockVector2> iterator;
//FAWE start - chunk preloading
/**
* Create a new visitor.
*
@ -48,12 +56,30 @@ public class FlatRegionVisitor implements Operation {
* @param function a function to apply to columns
*/
public FlatRegionVisitor(FlatRegion flatRegion, FlatRegionFunction function) {
this(flatRegion, function, null);
}
/**
* Create a new visitor.
*
* @param flatRegion a flat region
* @param function a function to apply to columns
* @param extent the extent for preloading
*/
public FlatRegionVisitor(FlatRegion flatRegion, FlatRegionFunction function, Extent extent) {
checkNotNull(flatRegion);
checkNotNull(function);
this.function = function;
this.iterator = flatRegion.asFlatRegion();
this.flatRegion = flatRegion;
if (extent != null) {
ExtentTraverser<ParallelQueueExtent> queueTraverser = new ExtentTraverser<>(extent).find(ParallelQueueExtent.class);
this.singleQueue = queueTraverser != null ? (SingleThreadQueueExtent) queueTraverser.get().getExtent() : null;
} else {
this.singleQueue = null;
}
}
//FAWE end
/**
* Get the number of affected objects.
@ -66,7 +92,12 @@ public class FlatRegionVisitor implements Operation {
@Override
public Operation resume(RunContext run) throws WorldEditException {
for (BlockVector2 pt : this.iterator) {
//FAWE start - chunk preloading
if (singleQueue != null) {
singleQueue.preload(flatRegion);
}
for (BlockVector2 pt : this.flatRegion.asFlatRegion()) {
//FAWE end
if (function.apply(pt)) {
affected++;
}

View File

@ -19,7 +19,13 @@
package com.sk89q.worldedit.function.visitor;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.BlockVectorSet;
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.LayerFunction;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.mask.Masks;
@ -29,6 +35,8 @@ import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.FlatRegion;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@ -44,19 +52,38 @@ public class LayerVisitor implements Operation {
private final FlatRegion flatRegion;
private final LayerFunction function;
//FAWE start - chunk preloading
private final SingleThreadQueueExtent singleQueue;
//FAWE end
private Mask2D mask = Masks.alwaysTrue2D();
private final int minY;
private final int maxY;
//FAWE start - chunk preloading
/**
* Create a new visitor.
*
* @param flatRegion the flat region to visit
* @param minY the minimum Y to stop the search at
* @param maxY the maximum Y to begin the search at
* @param function the layer function to apply to blocks
*/
public LayerVisitor(FlatRegion flatRegion, int minY, int maxY, LayerFunction function) {
this(flatRegion, minY, maxY, function, null);
}
/**
* Create a new visitor.
*
* @param flatRegion the flat region to visit
* @param minY the minimum Y to stop the search at
* @param maxY the maximum Y to begin the search at
* @param function the layer function to apply t blocks
* @param function the layer function to apply to blocks
* @param extent the extent for preloading
*/
public LayerVisitor(FlatRegion flatRegion, int minY, int maxY, LayerFunction function) {
public LayerVisitor(FlatRegion flatRegion, int minY, int maxY, LayerFunction function, Extent extent) {
//FAWE end
checkNotNull(flatRegion);
checkArgument(minY <= maxY, "minY <= maxY required");
checkNotNull(function);
@ -65,6 +92,14 @@ public class LayerVisitor implements Operation {
this.minY = minY;
this.maxY = maxY;
this.function = function;
//FAWE start - chunk preloading
if (extent != null) {
ExtentTraverser<ParallelQueueExtent> queueTraverser = new ExtentTraverser<>(extent).find(ParallelQueueExtent.class);
this.singleQueue = queueTraverser != null ? (SingleThreadQueueExtent) queueTraverser.get().getExtent() : null;
} else {
this.singleQueue = null;
}
//FAWE end
}
/**
@ -90,6 +125,11 @@ public class LayerVisitor implements Operation {
@Override
public Operation resume(RunContext run) throws WorldEditException {
//FAWE start - chunk preloading
if (singleQueue != null) {
singleQueue.preload(flatRegion);
}
//FAWE end
for (BlockVector2 column : flatRegion.asFlatRegion()) {
if (!mask.test(column)) {
continue;

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.function.visitor;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.BlockVector3;
@ -41,9 +42,28 @@ public class NonRisingVisitor extends RecursiveVisitor {
}
//FAWE end
//FAWE start - int depth
//FAWE start - int depth, preloading
/**
* Create a new recursive visitor.
*
* @param mask the mask
* @param function the function
* @param depth the maximum number of iterations
*/
public NonRisingVisitor(Mask mask, RegionFunction function, int depth) {
super(mask, function, depth);
this(mask, function, depth, null);
}
/**
* Create a new recursive visitor.
*
* @param mask the mask
* @param function the function
* @param depth the maximum number of iterations
* @param extent the extent for preloading
*/
public NonRisingVisitor(Mask mask, RegionFunction function, int depth, Extent extent) {
super(mask, function, depth, extent);
setDirections(
BlockVector3.UNIT_X,
BlockVector3.UNIT_MINUS_X,

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.function.visitor;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.BlockVector3;
@ -34,22 +35,41 @@ public class RecursiveVisitor extends BreadthFirstSearch {
private final Mask mask;
//FAWE start
public RecursiveVisitor(Mask mask, RegionFunction function) {
this(mask, function, Integer.MAX_VALUE);
}
//FAWE end
/**
* Create a new recursive visitor.
*
* @param mask the mask
* @param function the function
*/
public RecursiveVisitor(Mask mask, RegionFunction function) {
this(mask, function, Integer.MAX_VALUE);
}
/**
* Create a new recursive visitor.
*
* @param mask the mask
* @param function the function
* @param maxDepth the maximum number of iterations
*/
public RecursiveVisitor(Mask mask, RegionFunction function, int maxDepth) {
super(function, maxDepth);
this(mask, function, maxDepth, null);
}
/**
* Create a new recursive visitor.
*
* @param mask the mask
* @param function the function
* @param maxDepth the maximum number of iterations
* @param extent the extent for preloading
*/
public RecursiveVisitor(Mask mask, RegionFunction function, int maxDepth, Extent extent) {
super(function, maxDepth, extent);
checkNotNull(mask);
this.mask = mask;
}
//FAWE end
@Override
protected boolean isVisitable(BlockVector3 from, BlockVector3 to) {

View File

@ -19,16 +19,12 @@
package com.sk89q.worldedit.function.visitor;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkHolder;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.util.MemUtil;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
@ -49,10 +45,12 @@ import java.util.Iterator;
@Deprecated public class RegionVisitor implements Operation {
public final Iterable<? extends BlockVector3> iterable;
//FAWE start - allow chunk preloading
private final SingleThreadQueueExtent singleQueue;
//FAWE end
private final Region region;
private final RegionFunction function;
private int affected = 0;
private SingleThreadQueueExtent singleQueue;
/**
* @deprecated Use other constructors which will preload chunks during iteration
@ -91,7 +89,7 @@ import java.util.Iterator;
@Override public Operation resume(RunContext run) throws WorldEditException {
//FAWE start > allow chunk preloading
if (singleQueue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS > 1) {
if (singleQueue != null && Settings.IMP.QUEUE.PRELOAD_CHUNK_COUNT > 1) {
/*
* The following is done to reduce iteration cost
* - Preload chunks just in time
@ -105,7 +103,7 @@ import java.util.Iterator;
int lastTrailChunkZ = Integer.MIN_VALUE;
int lastLeadChunkX = Integer.MIN_VALUE;
int lastLeadChunkZ = Integer.MIN_VALUE;
int loadingTarget = Settings.IMP.QUEUE.PRELOAD_CHUNKS;
int loadingTarget = Settings.IMP.QUEUE.PRELOAD_CHUNK_COUNT;
try {
for (; ; ) {
BlockVector3 pt = trailIter.next();
@ -130,7 +128,7 @@ import java.util.Iterator;
if (vcx != lastLeadChunkX || vcz != lastLeadChunkZ) {
lastLeadChunkX = vcx;
lastLeadChunkZ = vcz;
queueChunkLoad(vcx, vcz);
singleQueue.addChunkLoad(vcx, vcz);
count++;
}
// Skip the next 15 blocks
@ -196,18 +194,6 @@ import java.util.Iterator;
affected++;
}
}
private void queueChunkLoad(int cx, int cz) {
TaskManager.IMP.sync(() -> {
boolean lowMem = MemUtil.isMemoryLimited();
if (!singleQueue.isQueueEnabled() || (!(lowMem && singleQueue.size() > Settings.IMP.QUEUE.PARALLEL_THREADS + 8)
&& singleQueue.size() < Settings.IMP.QUEUE.TARGET_SIZE && Fawe.get().getQueueHandler().isUnderutilized())) {
//The GET chunk is what will take longest.
((ChunkHolder)singleQueue.getOrCreateChunk(cx, cz)).getOrCreateGet();
}
return null;
});
}
//FAWE end
@Override public void cancel() {