document some methods

This commit is contained in:
Jesse Boyd 2019-04-29 03:36:23 +10:00
parent 57b5be84f4
commit 6692a2eb92
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
19 changed files with 237 additions and 44 deletions

View File

@ -38,7 +38,7 @@ public class BukkitChunkHolder extends ChunkHolder<Boolean, BukkitQueue> {
} }
@Override @Override
public void filter(final Filter filter) { public void set(final Filter filter) {
// for each block // for each block
// filter.applyBlock(block) // filter.applyBlock(block)
throw new UnsupportedOperationException("Not implemented"); throw new UnsupportedOperationException("Not implemented");

View File

@ -21,7 +21,7 @@ public class BukkitFullChunk extends ChunkHolder {
} }
@Override @Override
public void filter(Filter filter) { public void set(Filter filter) {
} }

View File

@ -2,7 +2,10 @@ package com.boydti.fawe.beta;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
public interface Filter { /**
* A filter is an interface used for setting blocks
*/
public interface Filter {
/** /**
* Check whether a chunk should be read * Check whether a chunk should be read
* *
@ -47,4 +50,13 @@ public interface Filter {
*/ */
default void finishChunk(final IChunk chunk) { default void finishChunk(final IChunk chunk) {
} }
/**
* Fork this for use by another thread
* - Typically filters are simple and don't need to create another copy to be thread safe here
* @return this
*/
default Filter fork() {
return this;
}
} }

View File

@ -1,5 +1,8 @@
package com.boydti.fawe.beta; package com.boydti.fawe.beta;
/**
* Shared interface for IGetBlocks and ISetBlocks
*/
public interface IBlocks { public interface IBlocks {
} }

View File

@ -5,30 +5,59 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
public interface IChunk<T, V extends IQueueExtent> { /**
/* set */ * Represents a chunk in the queue {@link IQueueExtent}
boolean setBiome(int x, int y, int z, BiomeType biome); * Used for getting and setting blocks / biomes / entities
* @param <T> The result type (typically returns true when the chunk is applied)
boolean setBlock(int x, int y, int z, BlockStateHolder block); * @param <V> The IQueue class
*/
/* get */ public interface IChunk<T, V extends IQueueExtent> extends Trimable {
BiomeType getBiome(int x, int z); /**
* Initialize at the location
BlockState getBlock(int x, int y, int z); * @param extent
* @param X
BaseBlock getFullBlock(int x, int y, int z); * @param Z
*/
void init(V extent, int X, int Z); void init(V extent, int X, int Z);
T apply();
int getX(); int getX();
int getZ(); int getZ();
/**
* If the chunk is a delegate, returns it's paren'ts root
* @return root IChunk
*/
default IChunk getRoot() { default IChunk getRoot() {
return this; return this;
} }
void filter(Filter filter); /**
* @return true if no changes are queued for this chunk
*/
boolean isEmpty();
/**
* Apply the queued changes to the world
* @return
*/
T apply();
/* set - queues a change */
boolean setBiome(int x, int y, int z, BiomeType biome);
boolean setBlock(int x, int y, int z, BlockStateHolder block);
/**
* Set using the filter
* @param filter
*/
void set(Filter filter);
/* get - from the world */
BiomeType getBiome(int x, int z);
BlockState getBlock(int x, int y, int z);
BaseBlock getFullBlock(int x, int y, int z);
} }

View File

@ -1,11 +1,16 @@
package com.boydti.fawe.beta; package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.SingleThreadQueueExtent;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
/**
* Delegate for IChunk
* @param <T> The result type (typically returns true when the chunk is applied)
* @param <V> The IQueue class
* @param <U> parent class
*/
public interface IDelegateChunk<T, V extends IQueueExtent, U extends IChunk<T, V>> extends IChunk<T, V> { public interface IDelegateChunk<T, V extends IQueueExtent, U extends IChunk<T, V>> extends IChunk<T, V> {
U getParent(); U getParent();
@ -57,11 +62,26 @@ public interface IDelegateChunk<T, V extends IQueueExtent, U extends IChunk<T, V
return getParent().getZ(); return getParent().getZ();
} }
@Override
default boolean trim(boolean aggressive) {
return getParent().trim(aggressive);
}
/**
* Apply this chunk to the world
* @return result T (typically a boolean)
*/
@Override @Override
default T apply() { default T apply() {
return getParent().apply(); return getParent().apply();
} }
@Override
default boolean isEmpty() {
return getParent().isEmpty();
}
default <T extends IChunk> T findParent(final Class<T> clazz) { default <T extends IChunk> T findParent(final Class<T> clazz) {
IChunk root = getParent(); IChunk root = getParent();
if (clazz.isAssignableFrom(root.getClass())) return (T) root; if (clazz.isAssignableFrom(root.getClass())) return (T) root;
@ -73,7 +93,7 @@ public interface IDelegateChunk<T, V extends IQueueExtent, U extends IChunk<T, V
} }
@Override @Override
default void filter(Filter filter) { default void set(Filter filter) {
getParent().filter(filter); getParent().set(filter);
} }
} }

View File

@ -4,6 +4,9 @@ import com.boydti.fawe.beta.implementation.WorldChunkCache;
import java.util.concurrent.Future; import java.util.concurrent.Future;
/**
* Delegate for IQueueExtent
*/
public interface IDelegateQueueExtent extends IQueueExtent { public interface IDelegateQueueExtent extends IQueueExtent {
IQueueExtent getParent(); IQueueExtent getParent();

View File

@ -4,12 +4,16 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
public interface IGetBlocks extends IBlocks { /**
* Interface for getting blocks
*/
public interface IGetBlocks extends IBlocks, Trimable {
BaseBlock getFullBlock(int x, int y, int z); BaseBlock getFullBlock(int x, int y, int z);
BiomeType getBiome(int x, int z); BiomeType getBiome(int x, int z);
BlockState getBlock(int x, int y, int z); BlockState getBlock(int x, int y, int z);
void trim(); @Override
boolean trim(boolean aggressive);
} }

View File

@ -8,11 +8,27 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.io.Flushable; import java.io.Flushable;
import java.util.concurrent.Future; import java.util.concurrent.Future;
/**
* TODO: implement Extent (need to refactor Extent first)
* Interface for a queue based extent which uses chunks
*/
public interface IQueueExtent extends Flushable, Trimable { public interface IQueueExtent extends Flushable, Trimable {
void init(WorldChunkCache world); void init(WorldChunkCache world);
/**
* Get the IChunk at a position (and cache it if it's not already)
* @param X
* @param Z
* @return IChunk
*/
IChunk getCachedChunk(int X, int Z); IChunk getCachedChunk(int X, int Z);
/**
* Submit the chunk so that it's changes are applied to the world
* @param chunk
* @param <T> result type
* @return result
*/
<T> Future<T> submit(IChunk<T, ?> chunk); <T> Future<T> submit(IChunk<T, ?> chunk);
default boolean setBlock(final int x, final int y, final int z, final BlockStateHolder state) { default boolean setBlock(final int x, final int y, final int z, final BlockStateHolder state) {
@ -36,14 +52,16 @@ public interface IQueueExtent extends Flushable, Trimable {
} }
/** /**
* Return the IChunk * Create a new root IChunk object<br>
* - Full chunks will be reused, so a more optimized chunk can be returned in that case<br>
* - Don't wrap the chunk, that should be done in {@link #wrap(IChunk)}
* @param full * @param full
* @return * @return
*/ */
IChunk create(boolean full); IChunk create(boolean full);
/** /**
* Wrap the chunk object (i.e. for region restrictions etc.) * Wrap the chunk object (i.e. for region restrictions / limits etc.)
* @param root * @param root
* @return wrapped chunk * @return wrapped chunk
*/ */
@ -51,6 +69,10 @@ public interface IQueueExtent extends Flushable, Trimable {
return root; return root;
} }
/**
* Flush all changes to the world
* - Best to call this async so it doesn't hang the server
*/
@Override @Override
void flush(); void flush();
} }

View File

@ -3,8 +3,13 @@ package com.boydti.fawe.beta;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
/**
* Interface for setting blocks
*/
public interface ISetBlocks extends IBlocks { public interface ISetBlocks extends IBlocks {
boolean setBiome(int x, int y, int z, BiomeType biome); boolean setBiome(int x, int y, int z, BiomeType biome);
boolean setBlock(int x, int y, int z, BlockStateHolder holder); boolean setBlock(int x, int y, int z, BlockStateHolder holder);
boolean isEmpty();
} }

View File

@ -1,5 +1,14 @@
package com.boydti.fawe.beta; package com.boydti.fawe.beta;
/**
* Interface for objects that can be trimmed (memory related)<br>
* - Trimming will reduce it's memory footprint
*/
public interface Trimable { public interface Trimable {
/**
* Trim the object, reducing it's memory footprint
* @param aggressive if trimming should be aggressive e.g. Not return early when the first element cannot be trimmed
* @return if this object is empty at the end of the trim, and can therefore be deleted
*/
boolean trim(boolean aggressive); boolean trim(boolean aggressive);
} }

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -12,8 +13,12 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
/**
* Class which handles all the queues {@link IQueueExtent}
*/
public abstract class QueueHandler implements Trimable { public abstract class QueueHandler implements Trimable {
private Map<World, WeakReference<WorldChunkCache>> chunkCache = new HashMap<>(); private Map<World, WeakReference<WorldChunkCache>> chunkCache = new HashMap<>();
private IterableThreadLocal<IQueueExtent> pool = new IterableThreadLocal<IQueueExtent>() { private IterableThreadLocal<IQueueExtent> pool = new IterableThreadLocal<IQueueExtent>() {
@Override @Override
public IQueueExtent init() { public IQueueExtent init() {
@ -21,7 +26,14 @@ public abstract class QueueHandler implements Trimable {
} }
}; };
public WorldChunkCache getOrCreate(final World world) { /**
* Get or create the WorldChunkCache for a world
* @param world
* @return
*/
public WorldChunkCache getOrCreate(World world) {
world = WorldWrapper.unwrap(world);
synchronized (chunkCache) { synchronized (chunkCache) {
final WeakReference<WorldChunkCache> ref = chunkCache.get(world); final WeakReference<WorldChunkCache> ref = chunkCache.get(world);
if (ref != null) { if (ref != null) {
@ -38,6 +50,7 @@ public abstract class QueueHandler implements Trimable {
public abstract IQueueExtent create(); public abstract IQueueExtent create();
@Override
public boolean trim(final boolean aggressive) { public boolean trim(final boolean aggressive) {
boolean result = true; boolean result = true;
synchronized (chunkCache) { synchronized (chunkCache) {

View File

@ -6,6 +6,7 @@ import com.boydti.fawe.beta.implementation.holder.ReferenceChunk;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
@ -16,20 +17,37 @@ import java.util.concurrent.ForkJoinTask;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
/**
* Single threaded implementation for IQueueExtent (still abstract)
* - Does not implement creation of chunks (that has to implemented by the platform e.g. Bukkit)
*
* This queue is reusable {@link #init(WorldChunkCache)}
*/
public abstract class SingleThreadQueueExtent implements IQueueExtent { public abstract class SingleThreadQueueExtent implements IQueueExtent {
private WorldChunkCache cache; private WorldChunkCache cache;
private Thread currentThread; private Thread currentThread;
/**
* Safety check to ensure that the thread being used matches the one being initialized on
* - Can be removed later
*/
private void checkThread() { private void checkThread() {
if (Thread.currentThread() != currentThread && currentThread != null) { if (Thread.currentThread() != currentThread && currentThread != null) {
throw new UnsupportedOperationException("This class must be used from a single thread. Use multiple queues for concurrent operations"); throw new UnsupportedOperationException("This class must be used from a single thread. Use multiple queues for concurrent operations");
} }
} }
/**
* Get the {@link WorldChunkCache}
* @return
*/
public WorldChunkCache getCache() { public WorldChunkCache getCache() {
return cache; return cache;
} }
/**
* Reset the queue
*/
protected synchronized void reset() { protected synchronized void reset() {
checkThread(); checkThread();
cache = null; cache = null;
@ -37,7 +55,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
for (IChunk chunk : chunks.values()) { for (IChunk chunk : chunks.values()) {
chunk = chunk.getRoot(); chunk = chunk.getRoot();
if (chunk != null) { if (chunk != null) {
chunkPool.add(chunk); CHUNK_POOL.add(chunk);
} }
} }
chunks.clear(); chunks.clear();
@ -47,6 +65,10 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
currentThread = null; currentThread = null;
} }
/**
* Initialize the queue
* @param cache
*/
@Override @Override
public synchronized void init(final WorldChunkCache cache) { public synchronized void init(final WorldChunkCache cache) {
if (cache != null) { if (cache != null) {
@ -57,24 +79,32 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
this.cache = cache; this.cache = cache;
} }
// Last access pointers
private IChunk lastChunk; private IChunk lastChunk;
private long lastPair = Long.MAX_VALUE; private long lastPair = Long.MAX_VALUE;
// Chunks currently being queued / worked on
private final Long2ObjectLinkedOpenHashMap<IChunk> chunks = new Long2ObjectLinkedOpenHashMap<>(); private final Long2ObjectLinkedOpenHashMap<IChunk> chunks = new Long2ObjectLinkedOpenHashMap<>();
private final ConcurrentLinkedQueue<IChunk> chunkPool = new ConcurrentLinkedQueue<>(); // Pool discarded chunks for reuse (can safely be cleared by another thread)
private static final ConcurrentLinkedQueue<IChunk> CHUNK_POOL = new ConcurrentLinkedQueue<>();
@Override @Override
public <T> ForkJoinTask<T> submit(final IChunk<T, ?> tmp) { public <T> ForkJoinTask<T> submit(final IChunk<T, ?> chunk) {
if (chunk.isEmpty()) {
CHUNK_POOL.add(chunk);
return null;
}
// TODO use SetQueue to run in parallel
final ForkJoinPool pool = TaskManager.IMP.getPublicForkJoinPool(); final ForkJoinPool pool = TaskManager.IMP.getPublicForkJoinPool();
return pool.submit(new Callable<T>() { return pool.submit(new Callable<T>() {
@Override @Override
public T call() { public T call() {
IChunk<T, ?> chunk = tmp; IChunk<T, ?> tmp = chunk;
T result = chunk.apply(); T result = tmp.apply();
chunk = chunk.getRoot(); tmp = tmp.getRoot();
if (chunk != null) { if (tmp != null) {
chunkPool.add(chunk); CHUNK_POOL.add(tmp);
} }
return result; return result;
} }
@ -83,7 +113,8 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
@Override @Override
public synchronized boolean trim(boolean aggressive) { public synchronized boolean trim(boolean aggressive) {
chunkPool.clear(); // TODO trim individial chunk sections
CHUNK_POOL.clear();
if (Thread.currentThread() == currentThread) { if (Thread.currentThread() == currentThread) {
lastChunk = null; lastChunk = null;
lastPair = Long.MAX_VALUE; lastPair = Long.MAX_VALUE;
@ -94,13 +125,21 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
} }
} }
private IChunk pool(final int X, final int Z) { /**
IChunk next = chunkPool.poll(); * Get a new IChunk from either the pool, or create a new one<br>
* + Initialize it at the coordinates
* @param X
* @param Z
* @return IChunk
*/
private IChunk poolOrCreate(final int X, final int Z) {
IChunk next = CHUNK_POOL.poll();
if (next == null) next = create(false); if (next == null) next = create(false);
next.init(this, X, Z); next.init(this, X, Z);
return next; return next;
} }
@Override
public final IChunk getCachedChunk(final int X, final int Z) { public final IChunk getCachedChunk(final int X, final int Z) {
final long pair = MathMan.pairInt(X, Z); final long pair = MathMan.pairInt(X, Z);
if (pair == lastPair) { if (pair == lastPair) {
@ -125,7 +164,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
submit(chunk); submit(chunk);
} }
} }
chunk = pool(X, Z); chunk = poolOrCreate(X, Z);
chunk = wrap(chunk); chunk = wrap(chunk);
chunks.put(pair, chunk); chunks.put(pair, chunk);

View File

@ -10,6 +10,10 @@ import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.function.Supplier; import java.util.function.Supplier;
/**
* IGetBlocks may be cached by the WorldChunkCache so that it can be used between multiple IQueueExtents
* - avoids conversion between palette and raw data on every block get
*/
public class WorldChunkCache implements Trimable { public class WorldChunkCache implements Trimable {
protected final Long2ObjectLinkedOpenHashMap<WeakReference<IGetBlocks>> getCache; protected final Long2ObjectLinkedOpenHashMap<WeakReference<IGetBlocks>> getCache;
private final World world; private final World world;
@ -27,6 +31,12 @@ public class WorldChunkCache implements Trimable {
return getCache.size(); return getCache.size();
} }
/**
* Get or create the IGetBlocks
* @param index chunk index {@link com.boydti.fawe.util.MathMan#pairInt(int, int)}
* @param provider used to create if it isn't already cached
* @return cached IGetBlocks
*/
public synchronized IGetBlocks get(final long index, final Supplier<IGetBlocks> provider) { public synchronized IGetBlocks get(final long index, final Supplier<IGetBlocks> provider) {
final WeakReference<IGetBlocks> ref = getCache.get(index); final WeakReference<IGetBlocks> ref = getCache.get(index);
if (ref != null) { if (ref != null) {
@ -52,7 +62,7 @@ public class WorldChunkCache implements Trimable {
result = false; result = false;
if (!aggressive) return result; if (!aggressive) return result;
synchronized (igb) { synchronized (igb) {
igb.trim(); igb.trim(aggressive);
} }
} }
} }

View File

@ -15,6 +15,9 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.function.Supplier; import java.util.function.Supplier;
/**
* Abstract IChunk class that implements basic get/set blocks
*/
public abstract class ChunkHolder<T, V extends SingleThreadQueueExtent> implements IChunk<T, V>, Supplier<IGetBlocks> { public abstract class ChunkHolder<T, V extends SingleThreadQueueExtent> implements IChunk<T, V>, Supplier<IGetBlocks> {
private IGetBlocks get; private IGetBlocks get;
private ISetBlocks set; private ISetBlocks set;
@ -30,6 +33,11 @@ public abstract class ChunkHolder<T, V extends SingleThreadQueueExtent> implemen
this.delegate = delegate; this.delegate = delegate;
} }
@Override
public boolean isEmpty() {
return set == null || set.isEmpty();
}
public final IGetBlocks cachedGet() { public final IGetBlocks cachedGet() {
if (get == null) get = newGet(); if (get == null) get = newGet();
return get; return get;

View File

@ -3,6 +3,10 @@ package com.boydti.fawe.beta.implementation.holder;
import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IDelegateChunk; import com.boydti.fawe.beta.IDelegateChunk;
/**
* Implementation of IDelegateChunk
* @param <T>
*/
public class DelegateChunk<T extends IChunk> implements IDelegateChunk { public class DelegateChunk<T extends IChunk> implements IDelegateChunk {
private T parent; private T parent;

View File

@ -6,6 +6,11 @@ import com.boydti.fawe.beta.IQueueExtent;
import java.lang.ref.Reference; import java.lang.ref.Reference;
/**
* An IChunk may be wrapped by a ReferenceChunk if there is low memory<br>
* A reference chunk stores a reference (for garbage collection purposes)<br>
* - If it is garbage collected, the {@link FinalizedChunk} logic is run
*/
public abstract class ReferenceChunk implements IDelegateChunk { public abstract class ReferenceChunk implements IDelegateChunk {
private final Reference<FinalizedChunk> ref; private final Reference<FinalizedChunk> ref;

View File

@ -6,9 +6,12 @@ import com.boydti.fawe.beta.IQueueExtent;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
/**
* Soft reference implementation of {@link ReferenceChunk}
*/
public class SoftChunk extends ReferenceChunk { public class SoftChunk extends ReferenceChunk {
public SoftChunk(final IChunk parent, IQueueExtent queueExtent) { public SoftChunk(final IChunk parent, final IQueueExtent queueExtent) {
super(parent, queueExtent); super(parent, queueExtent);
} }

View File

@ -1,13 +1,17 @@
package com.boydti.fawe.beta.implementation.holder; package com.boydti.fawe.beta.implementation.holder;
import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IQueueExtent;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
/**
* Weak reference implementation of {@link ReferenceChunk}
*/
public class WeakChunk extends ReferenceChunk { public class WeakChunk extends ReferenceChunk {
public WeakChunk(final IChunk parent) { public WeakChunk(final IChunk parent, final IQueueExtent queueExtent) {
super(parent); super(parent, queueExtent);
} }
@Override @Override