diff --git a/worldedit-bukkit/build.gradle b/worldedit-bukkit/build.gradle index 42df6e565..205c447d8 100644 --- a/worldedit-bukkit/build.gradle +++ b/worldedit-bukkit/build.gradle @@ -10,7 +10,7 @@ repositories { dependencies { compile project(':worldedit-core') - compile 'org.bukkit:craftbukkit-1.14:pre5' +// compile 'org.bukkit:craftbukkit-1.14:pre5' compile 'net.milkbowl.vault:VaultAPI:1.7' compile 'com.sk89q:dummypermscompat:1.10' compile 'com.destroystokyo.paper:paper-api:1.13.2-R0.1-SNAPSHOT' diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 793f2ef1e..eb872c73a 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -2,6 +2,9 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; +import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.bukkit.beta.BukkitQueue; +import com.boydti.fawe.bukkit.beta.BukkitQueueHandler; import com.boydti.fawe.bukkit.chat.BukkitChatManager; import com.boydti.fawe.bukkit.listener.AsyncTabCompleteListener; import com.boydti.fawe.bukkit.listener.BrushListener; @@ -144,6 +147,11 @@ public class FaweBukkit implements IFawe, Listener { }); } + @Override + public QueueHandler getQueueHandler() { + return new BukkitQueueHandler(); + } + @Override public CUI getCUI(FawePlayer player) { if (Settings.IMP.EXPERIMENTAL.VANILLA_CUI) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/v1_13_1/Spigot_v1_13_R2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/v1_13_1/Spigot_v1_13_R2.java index 1d3b5fb53..9c9f341d5 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/v1_13_1/Spigot_v1_13_R2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/v1_13_1/Spigot_v1_13_R2.java @@ -92,16 +92,16 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit nbtCreateTagMethod.setAccessible(true); } - public int[] idbToStateOrdinal; + public char[] idbToStateOrdinal; private boolean init() { if (idbToStateOrdinal != null) return false; - idbToStateOrdinal = new int[Block.REGISTRY_ID.a()]; // size + idbToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size for (int i = 0; i < idbToStateOrdinal.length; i++) { BlockState state = BlockTypes.states[i]; BlockMaterial_1_13 material = (BlockMaterial_1_13) state.getMaterial(); int id = Block.REGISTRY_ID.getId(material.getState()); - idbToStateOrdinal[id] = state.getOrdinal(); + idbToStateOrdinal[id] = state.getOrdinalChar(); } return true; } @@ -532,6 +532,16 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit } } + public char adaptToChar(IBlockData ibd) { + try { + int id = Block.REGISTRY_ID.getId(ibd); + return idbToStateOrdinal[id]; + } catch (NullPointerException e) { + if (init()) return adaptToChar(ibd); + throw e; + } + } + @Override public BlockData adapt(BlockStateHolder state) { BlockMaterial_1_13 material = (BlockMaterial_1_13) state.getMaterial(); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitChunkHolder.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitChunkHolder.java index 3faba2ebf..e36c29777 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitChunkHolder.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitChunkHolder.java @@ -2,23 +2,24 @@ package com.boydti.fawe.bukkit.beta; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IGetBlocks; +import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; import com.boydti.fawe.beta.implementation.holder.ChunkHolder; -public class BukkitChunkHolder extends ChunkHolder { +public class BukkitChunkHolder extends ChunkHolder { @Override - public void init(final BukkitQueue extent, final int X, final int Z) { + public void init(final IQueueExtent extent, final int X, final int Z) { super.init(extent, X, Z); } @Override public IGetBlocks get() { - BukkitQueue extent = getExtent(); - return null; + BukkitQueue extent = (BukkitQueue) getExtent(); + return new BukkitGetBlocks(extent.getNmsWorld(), getX(), getZ()); } @Override - public Boolean apply() { + public boolean applyAsync() { BukkitGetBlocks get = (BukkitGetBlocks) cachedGet(); CharSetBlocks set = (CharSetBlocks) cachedSet(); // - getBlocks @@ -37,10 +38,15 @@ public class BukkitChunkHolder extends ChunkHolder { // return true; } + @Override + public boolean applySync() { + return true; + } + @Override public void set(final Filter filter) { // for each block // filter.applyBlock(block) throw new UnsupportedOperationException("Not implemented"); } -} +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitFullChunk.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitFullChunk.java index 61306140e..c69c48bdb 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitFullChunk.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitFullChunk.java @@ -16,8 +16,13 @@ public class BukkitFullChunk extends ChunkHolder { } @Override - public Object apply() { - return null; + public boolean applyAsync() { + return false; + } + + @Override + public boolean applySync() { + return false; } @Override diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitGetBlocks.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitGetBlocks.java index 85fa55a11..d3d4f478d 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitGetBlocks.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitGetBlocks.java @@ -1,8 +1,175 @@ package com.boydti.fawe.bukkit.beta; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; +import com.boydti.fawe.bukkit.adapter.v1_13_1.Spigot_v1_13_R2; +import com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13; +import com.boydti.fawe.jnbt.anvil.BitArray4096; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockTypes; +import net.minecraft.server.v1_13_R2.Chunk; +import net.minecraft.server.v1_13_R2.ChunkCoordIntPair; +import net.minecraft.server.v1_13_R2.ChunkProviderServer; import net.minecraft.server.v1_13_R2.ChunkSection; +import net.minecraft.server.v1_13_R2.DataBits; +import net.minecraft.server.v1_13_R2.DataPalette; +import net.minecraft.server.v1_13_R2.DataPaletteBlock; +import net.minecraft.server.v1_13_R2.DataPaletteHash; +import net.minecraft.server.v1_13_R2.DataPaletteLinear; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.World; + +import static com.boydti.fawe.bukkit.v0.BukkitQueue_0.getAdapter; public class BukkitGetBlocks extends CharGetBlocks { private ChunkSection[] sections; + private Chunk nmsChunk; + private World nmsWorld; + private int X, Z; + + public BukkitGetBlocks(World nmsWorld, int X, int Z) {/*d*/ + this.nmsWorld = nmsWorld; + this.X = X; + this.Z = Z; + } + + @Override + public BiomeType getBiome(int x, int z) { + // TODO + return null; + } + + @Override + public CompoundTag getTag(int x, int y, int z) { + // TODO + return null; + } + + @Override + protected char[] load(int layer) { + return load(layer, null); + } + + @Override + protected char[] load(int layer, char[] data) { + ChunkSection section = getSections()[layer]; + // Section is null, return empty array + if (section == null) { + return FaweCache.EMPTY_CHAR_4096; + } + if (data == null || data == FaweCache.EMPTY_CHAR_4096) { + data = new char[4096]; + } + + // Efficiently convert ChunkSection to raw data + try { + final DataPaletteBlock blocks = section.getBlocks(); + final DataBits bits = (DataBits) BukkitQueue_1_13.fieldBits.get(blocks); + final DataPalette palette = (DataPalette) BukkitQueue_1_13.fieldPalette.get(blocks); + final int bitsPerEntry = bits.c(); + + final long[] blockStates = bits.a(); + new BitArray4096(blockStates, bitsPerEntry).toRaw(data); + + int num_palette; + if (palette instanceof DataPaletteLinear) { + num_palette = ((DataPaletteLinear) palette).b(); + } else if (palette instanceof DataPaletteHash) { + num_palette = ((DataPaletteHash) palette).b(); + } else { + num_palette = 0; + int[] paletteToBlockInts = FaweCache.PALETTE_TO_BLOCK.get(); + char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get(); + try { + for (int i = 0; i < 4096; i++) { + char paletteVal = data[i]; + char ordinal = paletteToBlockChars[paletteVal]; + if (ordinal == Character.MAX_VALUE) { + paletteToBlockInts[num_palette++] = paletteVal; + IBlockData ibd = palette.a(data[i]); + if (ibd == null) { + ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar(); + } else { + ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToChar(ibd); + } + paletteToBlockChars[paletteVal] = ordinal; + } + data[i] = ordinal; + } + } finally { + for (int i = 0; i < num_palette; i++) { + int paletteVal = paletteToBlockInts[i]; + paletteToBlockChars[paletteVal] = Character.MAX_VALUE; + } + } + return data; + } + + char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get(); + try { + for (int i = 0; i < num_palette; i++) { + IBlockData ibd = palette.a(i); + char ordinal; + if (ibd == null) { + ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar(); + System.out.println("Invalid palette"); + } else { + ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToChar(ibd); + } + paletteToBlockChars[i] = ordinal; + } + for (int i = 0; i < 4096; i++) { + char paletteVal = data[i]; + data[i] = paletteToBlockChars[paletteVal]; + } + } finally { + for (int i = 0; i < num_palette; i++) { + paletteToBlockChars[i] = Character.MAX_VALUE; + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return data; + } + + private ChunkSection[] getSections() { + ChunkSection[] tmp = sections; + if (tmp == null) { + Chunk chunk = getChunk(); + sections = tmp = chunk.getSections(); + } + return tmp; + } + + private Chunk getChunk() { + Chunk tmp = nmsChunk; + if (tmp == null) { + ChunkProviderServer provider = (ChunkProviderServer) nmsWorld.getChunkProvider(); + nmsChunk = tmp = provider.chunks.get(ChunkCoordIntPair.a(X, Z)); + if (tmp == null) { + System.out.println("SYNC"); + // TOOD load with paper + nmsChunk = tmp = TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z)); + } + } + return tmp; + } + + @Override + public boolean hasSection(int layer) { + return getSections()[layer] != null; + } + + @Override + public boolean trim(boolean aggressive) { + if (aggressive) { + sections = null; + nmsChunk = null; + } + return super.trim(aggressive); + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitQueue.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitQueue.java index b8fab4466..616f84c99 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitQueue.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitQueue.java @@ -1,19 +1,50 @@ package com.boydti.fawe.bukkit.beta; import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.implementation.SimpleCharQueueExtent; import com.boydti.fawe.beta.implementation.SingleThreadQueueExtent; import com.boydti.fawe.beta.implementation.WorldChunkCache; import com.boydti.fawe.object.collection.IterableThreadLocal; +import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.world.World; +import net.minecraft.server.v1_13_R2.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; -public class BukkitQueue extends SingleThreadQueueExtent { +import static com.google.common.base.Preconditions.checkNotNull; + +public class BukkitQueue extends SimpleCharQueueExtent { + + private org.bukkit.World bukkitWorld; + private WorldServer nmsWorld; @Override public synchronized void init(WorldChunkCache cache) { World world = cache.getWorld(); + if (world instanceof BukkitWorld) { + this.bukkitWorld = ((BukkitWorld) world).getWorld(); + } else { + this.bukkitWorld = Bukkit.getWorld(world.getName()); + } + checkNotNull(this.bukkitWorld); + CraftWorld craftWorld = ((CraftWorld) bukkitWorld); + this.nmsWorld = craftWorld.getHandle(); super.init(cache); } + public WorldServer getNmsWorld() { + return nmsWorld; + } + + public org.bukkit.World getBukkitWorld() { + return bukkitWorld; + } + + @Override + protected synchronized void reset() { + super.reset(); + } + private static final IterableThreadLocal FULL_CHUNKS = new IterableThreadLocal() { @Override public BukkitFullChunk init() { @@ -24,9 +55,9 @@ public class BukkitQueue extends SingleThreadQueueExtent { @Override public IChunk create(boolean full) { if (full) { - return FULL_CHUNKS.get(); - } else { - return new BukkitChunkHolder(); + // TODO implement +// return FULL_CHUNKS.get(); } + return new BukkitChunkHolder(); } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitQueueHandler.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitQueueHandler.java new file mode 100644 index 000000000..00528d29a --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/beta/BukkitQueueHandler.java @@ -0,0 +1,11 @@ +package com.boydti.fawe.bukkit.beta; + +import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.implementation.QueueHandler; + +public class BukkitQueueHandler extends QueueHandler { + @Override + public IQueueExtent create() { + return new BukkitQueue(); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_13/BukkitQueue_1_13.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_13/BukkitQueue_1_13.java index f72b821ec..8f0f188b6 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_13/BukkitQueue_1_13.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_13/BukkitQueue_1_13.java @@ -68,8 +68,9 @@ import java.util.function.Supplier; public class BukkitQueue_1_13 extends BukkitQueue_0 { - protected final static Field fieldBits; - protected final static Field fieldPalette; + public final static Field fieldBits; + public final static Field fieldPalette; + protected final static Field fieldSize; protected final static Field fieldHashBlocks; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java index a7bdb8334..38838b309 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java @@ -1,5 +1,6 @@ package com.boydti.fawe; +import com.boydti.fawe.beta.implementation.QueueHandler; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Commands; import com.boydti.fawe.config.Settings; @@ -82,6 +83,8 @@ public class Fawe { private DefaultTransformParser transformParser; private ChatManager chatManager = new PlainChatManager(); + private QueueHandler queueHandler; + /** * Get the implementation specific class * @@ -199,6 +202,17 @@ public class Fawe { public void onDisable() { } + public QueueHandler getQueueHandler() { + if (queueHandler == null) { + synchronized (this) { + if (queueHandler == null) { + queueHandler = IMP.getQueueHandler(); + } + } + } + return queueHandler; + } + public CUI getCUI(Actor actor) { FawePlayer fp = FawePlayer.wrap(actor); CUI cui = fp.getMeta("CUI"); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java index ab34b9fc5..6b4820e0c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java @@ -13,6 +13,7 @@ import java.lang.reflect.Field; import java.util.*; public class FaweCache implements Trimable { + public static final char[] EMPTY_CHAR_4096 = new char[4096]; /* Palette buffers / cache @@ -40,7 +41,16 @@ public class FaweCache implements Trimable { public static final IterableThreadLocal PALETTE_TO_BLOCK = new IterableThreadLocal() { @Override public int[] init() { - return new int[Character.MAX_VALUE]; + return new int[Character.MAX_VALUE + 1]; + } + }; + + public static final IterableThreadLocal PALETTE_TO_BLOCK_CHAR = new IterableThreadLocal() { + @Override + public char[] init() { + char[] result = new char[Character.MAX_VALUE + 1]; + Arrays.fill(result, Character.MAX_VALUE); + return result; } }; @@ -58,17 +68,10 @@ public class FaweCache implements Trimable { } }; - public static Map asMap(Object... pairs) { - HashMap map = new HashMap<>(pairs.length >> 1); - for (int i = 0; i < pairs.length; i += 2) { - String key = (String) pairs[i]; - Object value = pairs[i + 1]; - map.put(key, value); - } - return map; - } - - private static final class Palette { + /** + * Holds data for a palette used in a chunk section + */ + public static final class Palette { public int paletteToBlockLength; /** * Reusable buffer array, MUST check paletteToBlockLength for actual length @@ -91,31 +94,31 @@ public class FaweCache implements Trimable { /** * Convert raw char array to palette - * @param layer + * @param layerOffset * @param blocks * @return palette */ - public static Palette toPalette(int layer, char[] blocks) { - return toPalette(layer, null, blocks); + public static Palette toPalette(int layerOffset, char[] blocks) { + return toPalette(layerOffset, null, blocks); } /** * Convert raw int array to palette - * @param layer + * @param layerOffset * @param blocks * @return palette */ - public static Palette toPalette(int layer, int[] blocks) { - return toPalette(layer, blocks, null); + public static Palette toPalette(int layerOffset, int[] blocks) { + return toPalette(layerOffset, blocks, null); } - private static Palette toPalette(int layer, int[] blocksInts, char[] blocksChars) { + private static Palette toPalette(int layerOffset, int[] blocksInts, char[] blocksChars) { int[] blockToPalette = BLOCK_TO_PALETTE.get(); int[] paletteToBlock = PALETTE_TO_BLOCK.get(); long[] blockstates = BLOCK_STATES.get(); int[] blocksCopy = SECTION_BLOCKS.get(); - int blockIndexStart = layer << 12; + int blockIndexStart = layerOffset << 12; int blockIndexEnd = blockIndexStart + 4096; int num_palette = 0; try { @@ -124,7 +127,7 @@ public class FaweCache implements Trimable { int ordinal = blocksChars[i]; int palette = blockToPalette[ordinal]; if (palette == Integer.MAX_VALUE) { - BlockState state = BlockTypes.states[ordinal]; +// BlockState state = BlockTypes.states[ordinal]; blockToPalette[ordinal] = palette = num_palette; paletteToBlock[num_palette] = ordinal; num_palette++; @@ -183,6 +186,16 @@ public class FaweCache implements Trimable { Conversion methods between JNBT tags and raw values */ + public static Map asMap(Object... pairs) { + HashMap map = new HashMap<>(pairs.length >> 1); + for (int i = 0; i < pairs.length; i += 2) { + String key = (String) pairs[i]; + Object value = pairs[i + 1]; + map.put(key, value); + } + return map; + } + public static ShortTag asTag(short value) { return new ShortTag(value); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java index ecba46a2f..f41dca35c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -1,5 +1,6 @@ package com.boydti.fawe; +import com.boydti.fawe.beta.implementation.QueueHandler; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; @@ -62,4 +63,6 @@ public interface IFawe { return ""; } + QueueHandler getQueueHandler(); + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java new file mode 100644 index 000000000..b0f8ac231 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java @@ -0,0 +1,249 @@ +package com.boydti.fawe.beta; + +import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import static com.sk89q.worldedit.world.block.BlockTypes.states; +public class CharFilterBlock implements FilterBlock { + private IQueueExtent queue; + private CharGetBlocks chunk; + private char[] section; + + @Override + public void init(IQueueExtent queue) { + this.queue = queue; + } + + @Override + public void init(int X, int Z, IGetBlocks chunk) { + this.chunk = (CharGetBlocks) chunk; + this.X = X; + this.Z = Z; + this.xx = X << 4; + this.zz = Z << 4; + } + + public void init(char[] section, int layer) { + this.section = section; + this.layer = layer; + this.yy = layer << 4; + } + + // local + public int layer, index, x, y, z, xx, yy, zz, X, Z; + + @Override + public int getX() { + return xx + x; + } + + @Override + public int getY() { + return yy + y; + } + + @Override + public int getZ() { + return zz + z; + } + + @Override + public int getLocalX() { + return x; + } + + @Override + public int getLocalY() { + return y; + } + + @Override + public int getLocalZ() { + return z; + } + + @Override + public int getChunkX() { + return X; + } + + @Override + public int getChunkZ() { + return Z; + } + + @Override + public int getOrdinal() { + return section[index]; + } + + @Override + public BlockState getState() { + int ordinal = section[index]; + return BlockTypes.states[ordinal]; + } + + @Override + public BaseBlock getBaseBlock() { + BlockState state = getState(); + BlockMaterial material = state.getMaterial(); + if (material.hasContainer()) { + CompoundTag tag = chunk.getTag(x, y + (layer << 4), z); + return state.toBaseBlock(tag); + } + return state.toBaseBlock(); + } + + @Override + public CompoundTag getTag() { + return null; + } + + public BlockState getOrdinalBelow() { + if (y > 0) { + return states[section[index - 256]]; + } + if (layer > 0) { + final int newLayer = layer - 1; + final CharGetBlocks chunk = this.chunk; + return states[chunk.sections[newLayer].get(chunk, newLayer, index + 3840)]; + } + return BlockTypes.__RESERVED__.getDefaultState(); + } + + public BlockState getStateAbove() { + if (y < 16) { + return states[section[index + 256]]; + } + if (layer < 16) { + final int newLayer = layer + 1; + final CharGetBlocks chunk = this.chunk; + return states[chunk.sections[newLayer].get(chunk, newLayer, index - 3840)]; + } + return BlockTypes.__RESERVED__.getDefaultState(); + } + + public BlockState getStateRelativeY(int y) { + int newY = this.y + y; + int layerAdd = newY >> 4; + switch (layerAdd) { + case 0: + return states[section[this.index + (y << 8)]]; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: { + int newLayer = layer + layerAdd; + if (newLayer < 16) { + int index = this.index + ((y & 15) << 8); + return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + } + break; + } + case -1: + case -2: + case -3: + case -4: + case -5: + case -6: + case -7: + case -8: + case -9: + case -10: + case -11: + case -12: + case -13: + case -14: + case -15: { + int newLayer = layer + layerAdd; + if (newLayer >= 0) { + int index = this.index + ((y & 15) << 8); + return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + } + break; + } + } + return BlockTypes.__RESERVED__.getDefaultState(); + } + + public BlockState getStateRelative(final int x, final int y, final int z) { + int newX = this.x + x; + if (newX >> 4 == 0) { + int newZ = this.z + z; + if (newZ >> 4 == 0) { + int newY = this.y + y; + int layerAdd = newY >> 4; + switch (layerAdd) { + case 0: + return states[section[this.index + ((y << 8) | (z << 4) | x)]]; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: { + int newLayer = layer + layerAdd; + if (newLayer < 16) { + int index = this.index + (((y & 15) << 8) | (z << 4) | x); + return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + } + break; + } + case -1: + case -2: + case -3: + case -4: + case -5: + case -6: + case -7: + case -8: + case -9: + case -10: + case -11: + case -12: + case -13: + case -14: + case -15: { + int newLayer = layer + layerAdd; + if (newLayer >= 0) { + int index = this.index + (((y & 15) << 8) | (z << 4) | x); + return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + } + break; + } + } + return BlockTypes.__RESERVED__.getDefaultState(); + } + } +// queue.get + // TODO return normal get block + int newY = this.y + y + yy; + if (newY >= 0 && newY <= 256) { + return queue.getBlock(xx + newX, newY, this.zz + this.z + z); + } + return BlockTypes.__RESERVED__.getDefaultState(); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java new file mode 100644 index 000000000..68721cd82 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java @@ -0,0 +1,60 @@ +package com.boydti.fawe.beta; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class ChunkFuture implements Future { + private final IChunk chunk; + private volatile boolean cancelled; + private volatile boolean done; + + public ChunkFuture(IChunk chunk) { + this.chunk = chunk; + } + + public IChunk getChunk() { + return chunk; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + cancelled = true; + if (done) return false; + return true; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public boolean isDone() { + return done; + } + + @Override + public Void get() throws InterruptedException, ExecutionException { + synchronized (chunk) { + if (!done) { + this.wait(); + } + } + return null; + } + + @Override + public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + synchronized (chunk) { + if (!done) { + this.wait(unit.toMillis(timeout)); + if (!done) { + throw new TimeoutException(); + } + } + } + return null; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java index 0d1450fe9..fe3adaea9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/Filter.java @@ -34,12 +34,9 @@ public interface Filter { * - e.g. block.setId(...)
* - Note: Performance is critical here
* - * @param x - * @param y - * @param z * @param block */ - default void applyBlock(final int x, final int y, final int z, final BaseBlock block) { + default void applyBlock(final FilterBlock block) { } /** @@ -59,4 +56,8 @@ public interface Filter { default Filter fork() { return this; } + + default void join(Filter parent) { + + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java new file mode 100644 index 000000000..79fb85799 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java @@ -0,0 +1,60 @@ +package com.boydti.fawe.beta; + +import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; + +public interface FilterBlock { + void init(IQueueExtent queue); + + void init(int X, int Z, IGetBlocks chunk); + + int getOrdinal(); + + BlockState getState(); + + BaseBlock getBaseBlock(); + + CompoundTag getTag(); + + default BlockState getOrdinalBelow() { + return getStateRelative(0, -1, 0); + } + + default BlockState getStateAbove() { + return getStateRelative(0, 1, 0); + } + + default BlockState getStateRelativeY(int y) { + return getStateRelative(0, y, 0); + } + + int getX(); + + int getY(); + + int getZ(); + + default int getLocalX() { + return getX() & 15; + } + + default int getLocalY() { + return getY() & 15; + } + + default int getLocalZ() { + return getZ() & 15; + } + + default int getChunkX() { + return getX() >> 4; + } + + default int getChunkZ() { + return getZ() >> 4; + } + + BlockState getStateRelative(final int x, final int y, final int z); +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index 064510830..0a63725e1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -3,6 +3,8 @@ package com.boydti.fawe.beta; /** * Shared interface for IGetBlocks and ISetBlocks */ -public interface IBlocks { +public interface IBlocks extends Trimable { + boolean hasSection(int layer); + void reset(); } \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java index 32206ef75..dcd9fbf6c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java @@ -8,17 +8,15 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; /** * Represents a chunk in the queue {@link IQueueExtent} * Used for getting and setting blocks / biomes / entities - * @param The result type (typically returns true when the chunk is applied) - * @param The IQueue class */ -public interface IChunk extends Trimable { +public interface IChunk extends Trimable { /** * Initialize at the location * @param extent * @param X * @param Z */ - void init(V extent, int X, int Z); + void init(IQueueExtent extent, int X, int Z); int getX(); @@ -38,10 +36,16 @@ public interface IChunk extends Trimable { boolean isEmpty(); /** - * Apply the queued changes to the world - * @return + * Apply the queued async changes to the world + * @return false if applySync needs to run */ - T apply(); + boolean applyAsync(); + + /** + * Apply the queued sync changes to the world + * @return true + */ + boolean applySync(); /* set - queues a change */ boolean setBiome(int x, int y, int z, BiomeType biome); @@ -60,4 +64,6 @@ public interface IChunk extends Trimable { BlockState getBlock(int x, int y, int z); BaseBlock getFullBlock(int x, int y, int z); + + void filter(Filter filter, FilterBlock mutable); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java index e919fb90e..62786336f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java @@ -7,11 +7,9 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; /** * Delegate for IChunk - * @param The result type (typically returns true when the chunk is applied) - * @param The IQueue class * @param parent class */ -public interface IDelegateChunk> extends IChunk { +public interface IDelegateChunk extends IChunk { U getParent(); default IChunk getRoot() { @@ -48,7 +46,7 @@ public interface IDelegateChunk Future submit(IChunk chunk) { + default Future submit(IChunk chunk) { return getParent().submit(chunk); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IGetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IGetBlocks.java index 97f47af8e..bdddaff22 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IGetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IGetBlocks.java @@ -1,5 +1,6 @@ package com.boydti.fawe.beta; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -14,6 +15,10 @@ public interface IGetBlocks extends IBlocks, Trimable { BlockState getBlock(int x, int y, int z); + CompoundTag getTag(int x, int y, int z); + @Override boolean trim(boolean aggressive); + + void filter(Filter filter, FilterBlock block); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java index 3e3604275..4e6c35a01 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java @@ -15,6 +15,12 @@ import java.util.concurrent.Future; public interface IQueueExtent extends Flushable, Trimable { void init(WorldChunkCache world); + /** + * Get the {@link WorldChunkCache} + * @return + */ + WorldChunkCache getCache(); + /** * Get the IChunk at a position (and cache it if it's not already) * @param X @@ -26,10 +32,9 @@ public interface IQueueExtent extends Flushable, Trimable { /** * Submit the chunk so that it's changes are applied to the world * @param chunk - * @param result type * @return result */ - Future submit(IChunk chunk); + Future submit(IChunk chunk); default boolean setBlock(final int x, final int y, final int z, final BlockStateHolder state) { final IChunk chunk = getCachedChunk(x >> 4, z >> 4); @@ -75,4 +80,6 @@ public interface IQueueExtent extends Flushable, Trimable { */ @Override void flush(); + + FilterBlock initFilterBlock(); } \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java index 213c903d4..a78bfddc9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java @@ -1,10 +1,17 @@ package com.boydti.fawe.beta.implementation; +import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.Trimable; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.IterableThreadLocal; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.wrappers.WorldWrapper; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; @@ -12,6 +19,10 @@ import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.Future; /** * Class which handles all the queues {@link IQueueExtent} @@ -26,6 +37,17 @@ public abstract class QueueHandler implements Trimable { } }; + public Future submit(IChunk chunk) { + if (Fawe.isMainThread()) { + if (!chunk.applyAsync()) { + chunk.applySync(); + } + return null; + } + // TODO return future + return null; + } + /** * Get or create the WorldChunkCache for a world * @param world @@ -50,6 +72,12 @@ public abstract class QueueHandler implements Trimable { public abstract IQueueExtent create(); + public IQueueExtent getQueue(World world) { + IQueueExtent queue = pool.get(); + queue.init(getOrCreate(world)); + return queue; + } + @Override public boolean trim(final boolean aggressive) { boolean result = true; @@ -69,67 +97,68 @@ public abstract class QueueHandler implements Trimable { return result; } - public static void apply(final Region region, final Filter filter) { // TODO not MCAFilter, but another similar class -// // The chunks positions to iterate over -// final Set chunks = region.getChunks(); -// final Iterator chunksIter = chunks.iterator(); -// -// // Get a pool, to operate on the chunks in parallel -// final ForkJoinPool pool = TaskManager.IMP.getPublicForkJoinPool(); -// final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS); -// final ForkJoinTask[] tasks = new ForkJoinTask[size]; -// -// for (int i = 0; i < size; i++) { -// tasks[i] = pool.submit(new Runnable() { -// @Override -// public void run() { -// // Create a chunk that we will reuse/reset for each operation -// IChunk chunk = create(true); -// -// while (true) { -// // Get the next chunk pos -// final BlockVector2 pos; -// synchronized (chunksIter) { -// if (!chunksIter.hasNext()) return; -// pos = chunksIter.next(); -// } -// final int X = pos.getX(); -// final int Z = pos.getZ(); -// final long pair = MathMan.pairInt(X, Z); -// -// // Initialize -// chunk.init(SingleThreadQueueExtent.this, X, Z); -// -// { // Start set -// lastPair = pair; -// lastChunk = chunk; -// } -// try { -// if (!filter.appliesChunk(X, Z)) { -// continue; -// } -// chunk = filter.applyChunk(chunk); -// -// if (chunk == null) continue; -// -// chunk.filter(filter); -// -// filter.finishChunk(chunk); -// -// chunk.apply(); -// } finally -// { // End set -// lastPair = Long.MAX_VALUE; -// lastChunk = null; -// } -// } -// } -// }); -// } -// -// // Join the tasks -// for (final ForkJoinTask task : tasks) { -// task.join(); -// } + public void apply(final World world, final Region region, final Filter filter) { + // The chunks positions to iterate over + final Set chunks = region.getChunks(); + final Iterator chunksIter = chunks.iterator(); + + // Get a pool, to operate on the chunks in parallel + final ForkJoinPool pool = TaskManager.IMP.getPublicForkJoinPool(); + final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS); + final ForkJoinTask[] tasks = new ForkJoinTask[size]; + + for (int i = 0; i < size; i++) { + tasks[i] = pool.submit(new Runnable() { + @Override + public void run() { + Filter newFilter = filter.fork(); + // Create a chunk that we will reuse/reset for each operation + IQueueExtent queue = getQueue(world); + FilterBlock block = null; + + while (true) { + // Get the next chunk pos + final BlockVector2 pos; + synchronized (chunksIter) { + if (!chunksIter.hasNext()) return; + pos = chunksIter.next(); + } + final int X = pos.getX(); + final int Z = pos.getZ(); + // TODO create full + IChunk chunk = queue.getCachedChunk(X, Z); + // Initialize + chunk.init(queue, X, Z); + try { + if (!newFilter.appliesChunk(X, Z)) { + continue; + } + chunk = newFilter.applyChunk(chunk); + + if (chunk == null) continue; + + if (block == null) block = queue.initFilterBlock(); + chunk.filter(newFilter, block); + + newFilter.finishChunk(chunk); + + queue.submit(chunk); + } finally + { + if (filter != newFilter) { + synchronized (filter) { + newFilter.join(filter); + } + } + } + } + } + }); + } + + // Join the tasks + for (final ForkJoinTask task : tasks) { + task.join(); + } } } \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SimpleCharQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SimpleCharQueueExtent.java new file mode 100644 index 000000000..f6419164e --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SimpleCharQueueExtent.java @@ -0,0 +1,13 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.CharFilterBlock; +import com.boydti.fawe.beta.FilterBlock; + +public abstract class SimpleCharQueueExtent extends SingleThreadQueueExtent { + @Override + public FilterBlock initFilterBlock() { + CharFilterBlock filter = new CharFilterBlock(); + filter.init(this); + return filter; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SingleThreadQueueExtent.java index bab38ff70..1e0809a75 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/SingleThreadQueueExtent.java @@ -1,5 +1,6 @@ package com.boydti.fawe.beta.implementation; +import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.holder.ReferenceChunk; @@ -12,8 +13,10 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.Future; import static com.google.common.base.Preconditions.checkNotNull; @@ -37,10 +40,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { } } - /** - * Get the {@link WorldChunkCache} - * @return - */ + @Override public WorldChunkCache getCache() { return cache; } @@ -52,12 +52,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { checkThread(); cache = null; if (!chunks.isEmpty()) { - for (IChunk chunk : chunks.values()) { - chunk = chunk.getRoot(); - if (chunk != null) { - CHUNK_POOL.add(chunk); - } - } + CHUNK_POOL.addAll(chunks.values()); chunks.clear(); } lastChunk = null; @@ -88,27 +83,19 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { private static final ConcurrentLinkedQueue CHUNK_POOL = new ConcurrentLinkedQueue<>(); @Override - public ForkJoinTask submit(final IChunk chunk) { + public Future submit(final IChunk chunk) { if (chunk.isEmpty()) { CHUNK_POOL.add(chunk); return null; } - // TODO use SetQueue to run in parallel - final ForkJoinPool pool = TaskManager.IMP.getPublicForkJoinPool(); - return pool.submit(new Callable() { - @Override - public T call() { - IChunk tmp = chunk; - - T result = tmp.apply(); - - tmp = tmp.getRoot(); - if (tmp != null) { - CHUNK_POOL.add(tmp); - } - return result; + if (Fawe.isMainThread()) { + if (!chunk.applyAsync()) { + chunk.applySync(); } - }); + return null; + } + QueueHandler handler = Fawe.get().getQueueHandler(); + return handler.submit(chunk); } @Override @@ -141,7 +128,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { @Override public final IChunk getCachedChunk(final int X, final int Z) { - final long pair = MathMan.pairInt(X, Z); + final long pair = (((long) X) << 32) | (Z & 0xffffffffL); if (pair == lastPair) { return lastChunk; } @@ -178,14 +165,21 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { public synchronized void flush() { checkThread(); if (!chunks.isEmpty()) { - final ForkJoinTask[] tasks = new ForkJoinTask[chunks.size()]; + final Future[] tasks = new ForkJoinTask[chunks.size()]; int i = 0; for (final IChunk chunk : chunks.values()) { tasks[i++] = submit(chunk); } chunks.clear(); - for (final ForkJoinTask task : tasks) { - if (task != null) task.join(); + for (final Future task : tasks) { + if (task != null) { + try { + task.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } } } reset(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java index f07b8b44b..c348f8d18 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java @@ -3,5 +3,93 @@ package com.boydti.fawe.beta.implementation.blocks; import com.boydti.fawe.beta.IBlocks; public class CharBlocks implements IBlocks { - protected char[][] blocks; + public final char[][] blocks; + public final Section[] sections; + + public CharBlocks() { + blocks = new char[16][]; + sections = new Section[16]; + for (int i = 0; i < 16; i++) sections[i] = NULL; + } + + @Override + public boolean trim(boolean aggressive) { + boolean result = true; + for (int i = 0; i < 16; i++) { + if (sections[i] == NULL) { + blocks[i] = null; + } else { + result = false; + } + } + return result; + } + + @Override + public void reset() { + for (int i = 0; i < 16; i++) sections[i] = NULL; + } + + protected char[] load(int layer) { + return new char[4096]; + } + + protected char[] load(int layer, char[] data) { + for (int i = 0; i < 4096; i++) data[i] = 0; + return data; + } + + @Override + public boolean hasSection(int layer) { + return sections[layer] == FULL; + } + + public char get(int x, int y, int z) { + int layer = y >> 4; + int index = ((y & 15) << 8) | (z << 4) | (x & 15); + return sections[layer].get(this, layer, index); + } + + public char set(int x, int y, int z, char value) { + int layer = y >> 4; + int index = ((y & 15) << 8) | (z << 4) | (x & 15); + return sections[layer].set(this, layer, index, value); + } + + /* + Section + */ + + public static abstract class Section { + public abstract char[] get(CharBlocks blocks, int layer); + + public final char get(CharBlocks blocks, int layer, int index) { + return get(blocks, layer)[index]; + } + + public final char set(CharBlocks blocks, int layer, int index, char value) { + return get(blocks, layer)[index] = value; + } + } + + public static final Section NULL = new Section() { + @Override + public final char[] get(CharBlocks blocks, int layer) { + blocks.sections[layer] = FULL; + char[] arr = blocks.blocks[layer]; + if (arr == null) { + arr = blocks.blocks[layer] = blocks.load(layer); + } else { + blocks.blocks[layer] = blocks.load(layer, arr); + } + return arr; + } + }; + + public static final Section FULL = new Section() { + @Override + public final char[] get(CharBlocks blocks, int layer) { + return blocks.blocks[layer]; + } + }; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java index fe3287145..1bed28997 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharGetBlocks.java @@ -1,23 +1,52 @@ package com.boydti.fawe.beta.implementation.blocks; +import com.boydti.fawe.beta.CharFilterBlock; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlock; import com.boydti.fawe.beta.IGetBlocks; -import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; -public class CharGetBlocks extends CharBlocks implements IGetBlocks { - +public abstract class CharGetBlocks extends CharBlocks implements IGetBlocks { @Override public BaseBlock getFullBlock(final int x, final int y, final int z) { - return null; - } - - @Override - public BiomeType getBiome(final int x, final int z) { + return BlockTypes.states[get(x, y, z)].toBaseBlock(); } @Override public BlockState getBlock(final int x, final int y, final int z) { - return null; + return BlockTypes.states[get(x, y, z)]; } -} + + @Override + public void filter(Filter filter, FilterBlock block) { + CharFilterBlock b = (CharFilterBlock) block; + for (int layer = 0; layer < 16; layer++) { + if (!hasSection(layer)) continue; + char[] arr = sections[layer].get(this, layer); + b.init(arr, layer); + for (b.y = 0, b.index = 0; b.y < 16; b.y++) { + for (b.z = 0; b.z < 16; b.z++) { + for (b.x = 0; b.x < 16; b.x++, b.index++) { + filter.applyBlock(b); + } + } + } + } + } + + @Override + public boolean trim(boolean aggressive) { + for (int i = 0; i < 16; i++) { + sections[i] = NULL; + blocks[i] = null; + } + return true; + } + + @Override + public void reset() { + super.reset(); + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java index f7395d847..9be7c4391 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java @@ -2,14 +2,48 @@ package com.boydti.fawe.beta.implementation.blocks; import com.boydti.fawe.beta.ISetBlocks; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockStateHolder; import java.util.HashMap; import java.util.HashSet; import java.util.UUID; public class CharSetBlocks extends CharBlocks implements ISetBlocks { - private byte[] biomes; + private BiomeType[] biomes; private HashMap tiles; private HashSet entities; private HashSet entityRemoves; -} + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { + if (biomes == null) { + biomes = new BiomeType[256]; + } + biomes[x + (z << 4)] = biome; + return true; + } + + @Override + public boolean setBlock(int x, int y, int z, BlockStateHolder holder) { + set(x, y, z, holder.getOrdinalChar()); + return true; + } + + @Override + public boolean isEmpty() { + if (biomes != null) return false; + for (int i = 0; i < 16; i++) { + if (hasSection(i)) { + return false; + } + } + return true; + } + + @Override + public void reset() { + biomes = null; + super.reset(); + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FullCharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FullCharBlocks.java index 695387bca..9d0732d11 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FullCharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FullCharBlocks.java @@ -2,7 +2,23 @@ package com.boydti.fawe.beta.implementation.blocks; import com.boydti.fawe.beta.IBlocks; +// TODO implement public class FullCharBlocks implements IBlocks { public final boolean[] hasSections = new boolean[16]; public final char[] blocks = new char[65536]; -} + + @Override + public boolean hasSection(int layer) { + return false; + } + + @Override + public void reset() { + + } + + @Override + public boolean trim(boolean aggressive) { + return false; + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ChunkHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ChunkHolder.java index 024da75bf..efed7128a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ChunkHolder.java @@ -1,5 +1,9 @@ package com.boydti.fawe.beta.implementation.holder; +import com.boydti.fawe.beta.CharFilterBlock; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; import com.boydti.fawe.beta.IChunk; @@ -18,11 +22,11 @@ import java.util.function.Supplier; /** * Abstract IChunk class that implements basic get/set blocks */ -public abstract class ChunkHolder implements IChunk, Supplier { +public abstract class ChunkHolder implements IChunk, Supplier { private IGetBlocks get; private ISetBlocks set; private IBlockDelegate delegate; - private SingleThreadQueueExtent extent; + private IQueueExtent extent; private int X,Z; public ChunkHolder() { @@ -33,6 +37,37 @@ public abstract class ChunkHolder implemen this.delegate = delegate; } + @Override + public void filter(Filter filter, FilterBlock block) { + block.init(X, Z, get); + IGetBlocks get = cachedGet(); + get.filter(filter, block); + } + + @Override + public boolean trim(boolean aggressive) { + if (set != null) { + boolean result = set.trim(aggressive); + if (result) { + delegate = NULL; + get = null; + set = null; + return true; + } + } + if (aggressive) { + get = null; + if (delegate == BOTH) { + delegate = SET; + } else if (delegate == GET) { + delegate = NULL; + } + } else { + get.trim(false); + } + return false; + } + @Override public boolean isEmpty() { return set == null || set.isEmpty(); @@ -53,25 +88,29 @@ public abstract class ChunkHolder implemen } private IGetBlocks newGet() { - WorldChunkCache cache = extent.getCache(); - cache.get(MathMan.pairInt(X, Z), this); - return new CharGetBlocks(); + if (extent instanceof SingleThreadQueueExtent) { + WorldChunkCache cache = ((SingleThreadQueueExtent) extent).getCache(); + return cache.get(MathMan.pairInt(X, Z), this); + } + return get(); } - public void init(final SingleThreadQueueExtent extent, final int X, final int Z) { + @Override + public void init(IQueueExtent extent, final int X, final int Z) { this.extent = extent; this.X = X; this.Z = Z; - set = null; - if (delegate == BOTH) { - delegate = GET; - } else if (delegate == SET) { + if (set != null) { + set.reset(); + delegate = SET; + } else { delegate = NULL; } + get = null; } - public V getExtent() { - return (V) extent; + public IQueueExtent getExtent() { + return extent; } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/FinalizedChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/FinalizedChunk.java index 18d6e2d6a..527bcf5ce 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/FinalizedChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/FinalizedChunk.java @@ -22,9 +22,10 @@ public class FinalizedChunk extends DelegateChunk { @Override protected void finalize() throws Throwable { if (getParent() != null) { - apply(); + // TODO apply safely +// apply(); setParent(null); } super.finalize(); } -} +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/test/CountFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/test/CountFilter.java new file mode 100644 index 000000000..9117391f2 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/test/CountFilter.java @@ -0,0 +1,58 @@ +package com.boydti.fawe.beta.test; + +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlock; +import com.boydti.fawe.config.BBC; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.util.Countable; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CountFilter implements Filter { + private int[] counter = new int[BlockTypes.states.length]; + + @Override + public void applyBlock(FilterBlock block) { + counter[block.getOrdinal()]++; + } + + public List> getDistribution() { + List> distribution = new ArrayList<>(); + for (int i = 0; i < counter.length; i++) { + int count = counter[i]; + if (count != 0) { + distribution.add(new Countable<>(BlockTypes.states[i], count)); + } + } + Collections.sort(distribution); + return distribution; + } + + public void print(Actor actor, long size) { + for (Countable c : getDistribution()) { + String name = c.getID().toString(); + String str = String.format("%-7s (%.3f%%) %s", + String.valueOf(c.getAmount()), + c.getAmount() / (double) size * 100, + name); + actor.print(BBC.getPrefix() + str); + } + } + + @Override + public Filter fork() { + return new CountFilter(); + } + + @Override + public void join(Filter parent) { + CountFilter other = (CountFilter) parent; + for (int i = 0; i < counter.length; i++) { + other.counter[i] += this.counter[i]; + } + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java index d0a93bcc8..497059c2f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/BitArray4096.java @@ -159,4 +159,38 @@ public final class BitArray4096 { } return buffer; } + + public final char[] toRaw(char[] buffer) { + final long[] data = this.data; + final int dataLength = longLen; + final int bitsPerEntry = this.bitsPerEntry; + final int maxEntryValue = this.maxEntryValue; + final int maxSeqLocIndex = this.maxSeqLocIndex; + + int localStart = 0; + char lastVal; + int arrI = 0; + long l; + for (int i = 0; i < dataLength; i++) { + l = data[i]; + for (; localStart <= maxSeqLocIndex; localStart += bitsPerEntry) { + lastVal = (char) (l >>> localStart & maxEntryValue); + buffer[arrI++] = lastVal; + } + if (localStart < 64) { + if (i != dataLength - 1) { + lastVal = (char) (l >>> localStart); + localStart -= maxSeqLocIndex; + l = data[i + 1]; + int localShift = bitsPerEntry - localStart; + lastVal |= l << localShift; + lastVal &= maxEntryValue; + buffer[arrI++] = lastVal; + } + } else { + localStart = 0; + } + } + return buffer; + } } \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index bb528fc1e..3fefa6ab9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -19,7 +19,12 @@ package com.sk89q.worldedit.command; +import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.WorldChunkCache; +import com.boydti.fawe.beta.test.CountFilter; import com.boydti.fawe.config.BBC; import com.boydti.fawe.example.NMSMappedFaweQueue; import com.boydti.fawe.object.FaweLimit; @@ -61,20 +66,25 @@ import com.sk89q.worldedit.math.convolution.HeightMap; import com.sk89q.worldedit.math.convolution.HeightMapFilter; import com.sk89q.worldedit.math.noise.RandomNoise; import com.sk89q.worldedit.regions.*; +import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.Biomes; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.registry.BiomeRegistry; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -261,6 +271,19 @@ public class RegionCommands extends MethodCommands { BBC.VISITOR_BLOCK.send(player, blocksChanged); } + @Command( + aliases = {"debugtest"}, + usage = "", + desc = "debugtest", + help = "debugtest" + ) + public void debugtest(Player player, @Selection Region region) throws WorldEditException { + QueueHandler queueHandler = Fawe.get().getQueueHandler(); + World world = player.getWorld(); + CountFilter filter = new CountFilter(); + queueHandler.apply(world, region, filter); + } + @Command( aliases = {"/curve", "/spline"}, usage = " [thickness]", diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index 840cb6d7c..27d4d33a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -710,7 +710,7 @@ public class SelectionCommands { distributionData = (List) editSession.getBlockDistributionWithData(region); else distributionData = (List) editSession.getBlockDistribution(region); - size = session.getSelection(player.getWorld()).getArea(); + size = region.getArea(); if (distributionData.size() <= 0) { player.printError(BBC.getPrefix() + "No blocks counted."); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 437e6db74..31ada8944 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -21,6 +21,8 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.implementation.WorldChunkCache; import com.boydti.fawe.command.FaweParser; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Commands; @@ -70,6 +72,7 @@ import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java index 38285926c..886dee7cc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java @@ -19,25 +19,23 @@ package com.sk89q.worldedit.world.block; -import static com.google.common.base.Preconditions.checkNotNull; - import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.world.registry.BlockMaterial; -import com.sk89q.worldedit.world.registry.LegacyMapper; -import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.PropertyKey; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.world.registry.LegacyMapper; import javax.annotation.Nullable; - import java.util.Map; import java.util.Objects; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Represents a "snapshot" of a block with NBT Data. * @@ -84,7 +82,6 @@ public class BaseBlock implements BlockStateHolder { */ public BaseBlock(BlockState blockState) { -// this(blockState, blockState.getNbtData()); this.blockState = blockState; } @@ -210,7 +207,12 @@ public class BaseBlock implements BlockStateHolder { } @Override - public BaseBlock toBaseBlock() { + public final char getOrdinalChar() { + return blockState.getOrdinalChar(); + } + + @Override + public final BaseBlock toBaseBlock() { return this; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index 71c6b7ad0..3aef40e27 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -52,6 +52,7 @@ import java.util.stream.Stream; public class BlockState implements BlockStateHolder, FawePattern { private final int internalId; private final int ordinal; + private final char ordinalChar; private final BlockType blockType; private BlockMaterial material; private BaseBlock emptyBaseBlock; @@ -60,6 +61,7 @@ public class BlockState implements BlockStateHolder, FawePattern { this.blockType = blockType; this.internalId = internalId; this.ordinal = ordinal; + this.ordinalChar = (char) ordinal; this.emptyBaseBlock = new BaseBlock(this); } @@ -285,7 +287,7 @@ public class BlockState implements BlockStateHolder, FawePattern { } @Override - public BaseBlock toBaseBlock() { + public final BaseBlock toBaseBlock() { return this.emptyBaseBlock; } @@ -330,10 +332,15 @@ public class BlockState implements BlockStateHolder, FawePattern { return material; } - @Override - public int getOrdinal() { - return this.ordinal; - } + @Override + public int getOrdinal() { + return this.ordinal; + } + + @Override + public final char getOrdinalChar() { + return this.ordinalChar; + } @Override public String toString() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java index 6ab42e12c..6747f670a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java @@ -63,6 +63,9 @@ public interface BlockStateHolder> extends FawePat @Deprecated int getOrdinal(); + @Deprecated + char getOrdinalChar(); + BlockMaterial getMaterial(); /** * Get type id (legacy uses)