preloading chunks is significantly faster
The RegionVisitor loads (default: 32) expected chunks just in time for the operation. TODO rewrite operations to use the new RegionVisitor
This commit is contained in:
parent
d5d5b47cdb
commit
85ac3dff41
@ -113,6 +113,15 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queueChunkLoad(int cx, int cz) {
|
||||
if (super.queueChunkLoad(cx, cz)) {
|
||||
keepLoaded.put(MathMan.pairInt(cx, cz), System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public World createWorld(final WorldCreator creator) {
|
||||
World world = TaskManager.IMP.sync(new RunnableVal<World>() {
|
||||
@Override
|
||||
|
@ -233,6 +233,13 @@ public class Settings extends Config {
|
||||
})
|
||||
public int EXTRA_TIME_MS = 0;
|
||||
|
||||
@Comment({
|
||||
"Loading the right amount of chunks beforehand can speed up operations",
|
||||
" - Low values will result in FAWE waiting on requests to the main thread",
|
||||
" - Higher values will use memory and is slower if the operation ends early",
|
||||
})
|
||||
public int PRELOAD_CHUNKS = 32;
|
||||
|
||||
@Comment({
|
||||
"Discard edits which have been idle for a certain amount of time (ms) (e.g. a plugin creates",
|
||||
"an EditSession but never does anything with it)."
|
||||
|
@ -276,26 +276,6 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, CHUNKSECTIONS, SECTION> exte
|
||||
|
||||
public abstract CompoundTag getTileEntity(CHUNK chunk, int x, int y, int z);
|
||||
|
||||
// public CHUNKSECTIONS ensureSectionsLoaded(int cx, int cz) throws FaweException.FaweChunkLoadException {
|
||||
// CHUNKSECTIONS sections = getCachedSections(getWorld(), cx, cz);
|
||||
// if (sections != null) {
|
||||
// return sections;
|
||||
// }
|
||||
// boolean sync = Thread.currentThread() == Fawe.get().getMainThread();
|
||||
// if (sync) {
|
||||
// CHUNK chunk = loadChunk(getWorld(), cx, cz, true);
|
||||
// return chunk != null ? getSections(chunk) : null;
|
||||
// } else if (Settings.IMP.HISTORY.CHUNK_WAIT_MS > 0) {
|
||||
// cachedLoadChunk = null;
|
||||
// loadChunk.value.x = cx;
|
||||
// loadChunk.value.z = cz;
|
||||
// TaskManager.IMP.syncWhenFree(loadChunk, Settings.IMP.HISTORY.CHUNK_WAIT_MS);
|
||||
// return cachedLoadChunk != null ? getSections(cachedLoadChunk) : null;
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
public CHUNK ensureChunkLoaded(int cx, int cz) throws FaweException.FaweChunkLoadException {
|
||||
CHUNK chunk = getCachedChunk(getWorld(), cx, cz);
|
||||
if (chunk != null) {
|
||||
@ -315,6 +295,20 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, CHUNKSECTIONS, SECTION> exte
|
||||
}
|
||||
}
|
||||
|
||||
public boolean queueChunkLoad(final int cx, final int cz) {
|
||||
CHUNK chunk = getCachedChunk(getWorld(), cx, cz);
|
||||
if (chunk == null) {
|
||||
SetQueue.IMP.addTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadChunk(getWorld(), cx, cz, true);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBlock(int x, int y, int z) throws FaweException.FaweChunkLoadException {
|
||||
int cx = x >> 4;
|
||||
|
@ -24,6 +24,7 @@ import com.boydti.fawe.FaweAPI;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.example.MappedFaweQueue;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAQueue;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAWorld;
|
||||
import com.boydti.fawe.logging.LoggingChangeSet;
|
||||
@ -1610,7 +1611,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
|
||||
return setBlocks(region, ((BlockPattern) pattern).getBlock());
|
||||
}
|
||||
final BlockReplace replace = new BlockReplace(EditSession.this, Patterns.wrap(pattern));
|
||||
final RegionVisitor visitor = new RegionVisitor(region, replace);
|
||||
final RegionVisitor visitor = new RegionVisitor(region, replace, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null);
|
||||
Operations.completeSmart(visitor, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -1652,9 +1653,9 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public int replaceBlocks(final Region region, final Set<BaseBlock> filter, final Pattern pattern) throws MaxChangedBlocksException {
|
||||
if (pattern instanceof BlockPattern) {
|
||||
return replaceBlocks(region, filter, ((BlockPattern) pattern).getBlock());
|
||||
}
|
||||
// if (pattern instanceof BlockPattern) {
|
||||
// return replaceBlocks(region, filter, ((BlockPattern) pattern).getBlock());
|
||||
// }
|
||||
final Mask mask = filter == null ? new ExistingBlockMask(this) : new FuzzyBlockMask(this, filter);
|
||||
return this.replaceBlocks(region, mask, pattern);
|
||||
}
|
||||
@ -1680,7 +1681,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
|
||||
checkNotNull(pattern);
|
||||
final BlockReplace replace = new BlockReplace(EditSession.this, Patterns.wrap(pattern));
|
||||
final RegionMaskingFilter filter = new RegionMaskingFilter(mask, replace);
|
||||
final RegionVisitor visitor = new RegionVisitor(region, filter);
|
||||
final RegionVisitor visitor = new RegionVisitor(region, filter, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null);
|
||||
Operations.completeSmart(visitor, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -359,7 +359,7 @@ public class BrushCommands {
|
||||
flags = "h",
|
||||
desc = "Height brush",
|
||||
help =
|
||||
"This brush raises land.\n",
|
||||
"This brush raises and lowers land.\n",
|
||||
min = 1,
|
||||
max = 4
|
||||
)
|
||||
@ -383,7 +383,6 @@ public class BrushCommands {
|
||||
}
|
||||
ReadableByteChannel rbc = Channels.newChannel(url.openStream());
|
||||
stream = Channels.newInputStream(rbc);
|
||||
System.out.println("Loaded " + url);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -19,9 +19,12 @@
|
||||
|
||||
package com.sk89q.worldedit.function.operation;
|
||||
|
||||
import com.boydti.fawe.example.MappedFaweQueue;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.boydti.fawe.object.extent.BlockTranslateExtent;
|
||||
import com.boydti.fawe.object.extent.PositionTransformExtent;
|
||||
import com.boydti.fawe.object.function.block.SimpleBlockCopy;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
@ -216,7 +219,14 @@ public class ForwardExtentCopy implements Operation {
|
||||
if (currentTransform == null) {
|
||||
currentTransform = transform;
|
||||
}
|
||||
|
||||
FaweQueue queue;
|
||||
if (source instanceof EditSession) {
|
||||
queue = ((EditSession) source).getQueue();
|
||||
} else if (destination instanceof EditSession) {
|
||||
queue = ((EditSession) destination).getQueue();
|
||||
} else {
|
||||
queue = null;
|
||||
}
|
||||
Extent finalDest = destination;
|
||||
Vector translation = to.subtract(from);
|
||||
if (!translation.equals(Vector.ZERO)) {
|
||||
@ -237,7 +247,7 @@ public class ForwardExtentCopy implements Operation {
|
||||
if (sourceFunction != null) {
|
||||
copy = new CombinedRegionFunction(copy, sourceFunction);
|
||||
}
|
||||
RegionVisitor blockVisitor = new RegionVisitor(region, copy);
|
||||
RegionVisitor blockVisitor = new RegionVisitor(region, copy, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null);
|
||||
|
||||
List<? extends Entity> entities = source.getEntities(region);
|
||||
|
||||
|
@ -20,6 +20,11 @@
|
||||
package com.sk89q.worldedit.function.visitor;
|
||||
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.example.MappedFaweQueue;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.function.RegionFunction;
|
||||
@ -27,6 +32,7 @@ import com.sk89q.worldedit.function.operation.Operation;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
import com.sk89q.worldedit.function.operation.RunContext;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -36,11 +42,27 @@ public class RegionVisitor implements Operation {
|
||||
|
||||
public final Region region;
|
||||
public final RegionFunction function;
|
||||
private final MappedFaweQueue queue;
|
||||
public int affected = 0;
|
||||
|
||||
/**
|
||||
* Deprecated in favor of the other constructors which will preload chunks during iteration
|
||||
* @param region
|
||||
* @param function
|
||||
*/
|
||||
@Deprecated
|
||||
public RegionVisitor(Region region, RegionFunction function) {
|
||||
this(region, function, (FaweQueue) null);
|
||||
}
|
||||
|
||||
public RegionVisitor(Region region, RegionFunction function, EditSession editSession) {
|
||||
this(region, function, editSession != null ? editSession.getQueue() : null);
|
||||
}
|
||||
|
||||
public RegionVisitor(Region region, RegionFunction function, FaweQueue queue) {
|
||||
this.region = region;
|
||||
this.function = function;
|
||||
this.queue = queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,9 +76,95 @@ public class RegionVisitor implements Operation {
|
||||
|
||||
@Override
|
||||
public Operation resume(final RunContext run) throws WorldEditException {
|
||||
for (Vector pt : region) {
|
||||
if (function.apply(pt)) {
|
||||
affected++;
|
||||
if (queue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) {
|
||||
/*
|
||||
* The following is done to reduce iteration cost
|
||||
* - Preload chunks just in time
|
||||
* - Only check every 16th block for potential chunk loads
|
||||
* - Stop iteration on exception instead of hasNext
|
||||
* - Do not calculate the stacktrace as it is expensive
|
||||
*/
|
||||
Iterator<BlockVector> trailIter = region.iterator();
|
||||
Iterator<BlockVector> leadIter = region.iterator();
|
||||
int lastTrailChunkX = Integer.MIN_VALUE;
|
||||
int lastTrailChunkZ = Integer.MIN_VALUE;
|
||||
int lastLeadChunkX = Integer.MIN_VALUE;
|
||||
int lastLeadChunkZ = Integer.MIN_VALUE;
|
||||
int loadingTarget = Settings.IMP.QUEUE.PRELOAD_CHUNKS;
|
||||
try {
|
||||
for (;;) {
|
||||
BlockVector pt = trailIter.next();
|
||||
function.apply(pt);
|
||||
int cx = pt.getBlockX() >> 4;
|
||||
int cz = pt.getBlockZ() >> 4;
|
||||
if (cx != lastTrailChunkX || cz != lastTrailChunkZ) {
|
||||
lastTrailChunkX = cx;
|
||||
lastTrailChunkZ = cz;
|
||||
int amount;
|
||||
if (lastLeadChunkX == Integer.MIN_VALUE) {
|
||||
lastLeadChunkX = cx;
|
||||
lastLeadChunkZ = cz;
|
||||
amount = loadingTarget;
|
||||
} else {
|
||||
amount = 1;
|
||||
}
|
||||
for (int count = 0; count < amount;) {
|
||||
BlockVector v = leadIter.next();
|
||||
int vcx = v.getBlockX() >> 4;
|
||||
int vcz = v.getBlockZ() >> 4;
|
||||
if (vcx != lastLeadChunkX || vcz != lastLeadChunkZ) {
|
||||
lastLeadChunkX = vcx;
|
||||
lastLeadChunkZ = vcz;
|
||||
queue.queueChunkLoad(vcx, vcz);
|
||||
count++;
|
||||
}
|
||||
// Skip the next 15 blocks
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
leadIter.next();
|
||||
}
|
||||
}
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
}
|
||||
} catch (Throwable ignore) {}
|
||||
try {
|
||||
for (;;) {
|
||||
function.apply(trailIter.next());
|
||||
function.apply(trailIter.next());
|
||||
}
|
||||
} catch (Throwable ignore) {}
|
||||
affected = region.getArea();
|
||||
} else {
|
||||
for (Vector pt : region) {
|
||||
if (function.apply(pt)) {
|
||||
affected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -73,5 +181,4 @@ public class RegionVisitor implements Operation {
|
||||
public static Class<?> inject() {
|
||||
return Operations.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.storage.ChunkStore;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@ -402,8 +403,8 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
|
||||
if (Settings.IMP.HISTORY.COMPRESSION_LEVEL >= 9) {
|
||||
return iterator_old();
|
||||
}
|
||||
final MutableBlockVector mutable = new MutableBlockVector(0,0,0);
|
||||
return new Iterator<BlockVector>() {
|
||||
final MutableBlockVector mutable = new MutableBlockVector(0,0,0);
|
||||
private Vector min = getMinimumPoint();
|
||||
private Vector max = getMaximumPoint();
|
||||
|
||||
@ -445,6 +446,14 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
|
||||
if (x > tx) {
|
||||
x = bx;
|
||||
if (z > tz) {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException("End of iterator") {
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
hasNext = false;
|
||||
return mutable;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user