From f5944fbcaf4fe197542b198d8f6bf8ffc97bb243 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Fri, 3 May 2019 00:45:03 +1000 Subject: [PATCH] filter set --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 6 + .../adapter/v1_13_1/Spigot_v1_13_R2.java | 10 +- .../fawe/bukkit/beta/BukkitChunkHolder.java | 484 +++++++++--------- .../fawe/bukkit/beta/BukkitGetBlocks.java | 42 +- .../com/boydti/fawe/beta/CharFilterBlock.java | 167 ++++-- .../com/boydti/fawe/beta/ChunkFuture.java | 6 +- .../java/com/boydti/fawe/beta/Filter.java | 2 +- .../com/boydti/fawe/beta/FilterBlock.java | 15 +- .../com/boydti/fawe/beta/IDelegateChunk.java | 6 +- .../fawe/beta/IDelegateQueueExtent.java | 12 +- .../java/com/boydti/fawe/beta/IGetBlocks.java | 2 - .../com/boydti/fawe/beta/IQueueExtent.java | 2 +- .../beta/{test => filters}/CountFilter.java | 22 +- .../boydti/fawe/beta/filters/SetFilter.java | 18 + .../beta/implementation/QueueHandler.java | 87 ++-- .../implementation/SimpleCharQueueExtent.java | 4 +- .../SingleThreadQueueExtent.java | 41 +- .../implementation/blocks/CharBlocks.java | 42 +- .../implementation/blocks/CharGetBlocks.java | 8 +- .../implementation/blocks/CharSetBlocks.java | 12 +- .../implementation/blocks/FullCharBlocks.java | 4 +- .../implementation/holder/ChunkHolder.java | 19 +- .../implementation/holder/FinalizedChunk.java | 2 +- .../implementation/holder/ReferenceChunk.java | 2 +- .../fawe/object/brush/InspectBrush.java | 1 - .../worldedit/command/RegionCommands.java | 81 ++- 26 files changed, 617 insertions(+), 480 deletions(-) rename worldedit-core/src/main/java/com/boydti/fawe/beta/{test => filters}/CountFilter.java (66%) create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/filters/SetFilter.java 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 eb872c73a..913d0f0bb 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 @@ -56,6 +56,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -147,6 +148,11 @@ public class FaweBukkit implements IFawe, Listener { }); } + @EventHandler + public static void onChunkUnload(ChunkUnloadEvent event) { + event.setCancelled(true); + } + @Override public QueueHandler getQueueHandler() { return new BukkitQueueHandler(); 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 9c9f341d5..2adc9d3ad 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 @@ -94,7 +94,7 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit public char[] idbToStateOrdinal; - private boolean init() { + private synchronized boolean init() { if (idbToStateOrdinal != null) return false; idbToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size for (int i = 0; i < idbToStateOrdinal.length; i++) { @@ -527,8 +527,8 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit int id = Block.REGISTRY_ID.getId(ibd); return idbToStateOrdinal[id]; } catch (NullPointerException e) { - if (init()) return adaptToInt(ibd); - throw e; + init(); + return adaptToInt(ibd); } } @@ -537,8 +537,8 @@ public final class Spigot_v1_13_R2 extends CachedBukkitAdapter implements Bukkit int id = Block.REGISTRY_ID.getId(ibd); return idbToStateOrdinal[id]; } catch (NullPointerException e) { - if (init()) return adaptToChar(ibd); - throw e; + init(); + return adaptToChar(ibd); } } 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 95d67c85b..0eec7fbf7 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 @@ -20,6 +20,7 @@ import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockTypes; import net.jpountz.util.UnsafeUtils; import net.minecraft.server.v1_13_R2.BiomeBase; import net.minecraft.server.v1_13_R2.BlockPosition; @@ -69,7 +70,7 @@ public class BukkitChunkHolder> extends ChunkHolder { get.reset(); } if (get.sections == null) { - get.sections = sections; + get.sections = sections.clone(); } if (get.sections[layer] != section) { get.sections[layer] = section; @@ -79,271 +80,274 @@ public class BukkitChunkHolder> extends ChunkHolder { } @Override - public T call() { - BukkitQueue extent = (BukkitQueue) getExtent(); - BukkitGetBlocks get = (BukkitGetBlocks) getOrCreateGet(); - CharSetBlocks set = (CharSetBlocks) getOrCreateSet(); - int X = getX(); - int Z = getZ(); + public synchronized T call() { + try { + int X = getX(); + int Z = getZ(); + BukkitQueue extent = (BukkitQueue) getExtent(); + BukkitGetBlocks get = (BukkitGetBlocks) getOrCreateGet(); + CharSetBlocks set = (CharSetBlocks) getOrCreateSet(); - Chunk nmsChunk = extent.ensureLoaded(X, Z); + Chunk nmsChunk = extent.ensureLoaded(X, Z); - // Remove existing tiles - { - Map tiles = nmsChunk.getTileEntities(); - if (!tiles.isEmpty()) { - final Iterator> iterator = tiles.entrySet().iterator(); - while (iterator.hasNext()) { - final Map.Entry entry = iterator.next(); - final BlockPosition pos = entry.getKey(); - final int lx = pos.getX() & 15; - final int ly = pos.getY(); - final int lz = pos.getZ() & 15; - final int layer = ly >> 4; - final char[] array = set.blocks[layer]; - if (array == null) { - continue; - } - final int index = (((ly & 0xF) << 8) | (lz << 4) | lx); - if (array[index] != 0) { - TileEntity tile = entry.getValue(); - tile.z(); - tile.invalidateBlockCache(); + // Remove existing tiles + { + Map tiles = nmsChunk.getTileEntities(); + if (!tiles.isEmpty()) { + final Iterator> iterator = tiles.entrySet().iterator(); + while (iterator.hasNext()) { + final Map.Entry entry = iterator.next(); + final BlockPosition pos = entry.getKey(); + final int lx = pos.getX() & 15; + final int ly = pos.getY(); + final int lz = pos.getZ() & 15; + final int layer = ly >> 4; + final char[] array = set.blocks[layer]; + if (array == null) { + continue; + } + final int index = (((ly & 0xF) << 8) | (lz << 4) | lx); + if (array[index] != 0) { + TileEntity tile = entry.getValue(); + tile.z(); + tile.invalidateBlockCache(); + } } } } - } - int bitMask = 0; - synchronized (nmsChunk) { - ChunkSection[] sections = nmsChunk.getSections(); - World world = extent.getBukkitWorld(); - boolean hasSky = world.getEnvironment() == World.Environment.NORMAL; + int bitMask = 0; + synchronized (nmsChunk) { + ChunkSection[] sections = nmsChunk.getSections(); + World world = extent.getBukkitWorld(); + boolean hasSky = world.getEnvironment() == World.Environment.NORMAL; - for (int layer = 0; layer < 16; layer++) { - if (!set.hasSection(layer)) continue; + for (int layer = 0; layer < 16; layer++) { + if (!set.hasSection(layer)) continue; - bitMask |= 1 << layer; + bitMask |= 1 << layer; - char[] setArr = set.blocks[layer]; - ChunkSection newSection; - ChunkSection existingSection = sections[layer]; - if (existingSection == null) { - newSection = extent.newChunkSection(layer, hasSky, setArr); - if (BukkitQueue.setSectionAtomic(sections, null, newSection, layer)) { - updateGet(get, nmsChunk, sections, newSection, setArr, layer); - continue; - } else { - existingSection = sections[layer]; - if (existingSection == null) { - System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); - continue; - } - } - } - DelegateLock lock = BukkitQueue.applyLock(existingSection); - synchronized (lock) { - lock.untilFree(); - synchronized (get) { - ChunkSection getSection; - if (get.nmsChunk != nmsChunk) { - if (get.nmsChunk != null) System.out.println("chunk doesn't match"); - get.nmsChunk = nmsChunk; - get.sections = null; - get.reset(); - } else { - getSection = get.getSections()[layer]; - if (getSection != existingSection) { - get.sections[layer] = existingSection; - get.reset(); - System.out.println("Section doesn't match"); - } else if (lock.isModified()) { - System.out.println("lock is outdated"); - get.reset(layer); - } - } - char[] getArr = get.load(layer); - for (int i = 0; i < 4096; i++) { - char value = setArr[i]; - if (value != 0) { - getArr[i] = value; - } - } - newSection = extent.newChunkSection(layer, hasSky, getArr); - if (!BukkitQueue.setSectionAtomic(sections, existingSection, newSection, layer)) { - System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); - continue; - } else { + char[] setArr = set.blocks[layer]; + ChunkSection newSection; + ChunkSection existingSection = sections[layer]; + if (existingSection == null) { + newSection = extent.newChunkSection(layer, hasSky, setArr); + if (BukkitQueue.setSectionAtomic(sections, null, newSection, layer)) { updateGet(get, nmsChunk, sections, newSection, setArr, layer); - } - } - } - } - - // Biomes - if (set.biomes != null) { - // set biomes - final BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex(); - for (int i = 0; i < set.biomes.length; i++) { - final BiomeType biome = set.biomes[i]; - if (biome != null) { - final Biome craftBiome = BukkitAdapter.adapt(biome); - currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome); - } - } - } - - Runnable[] syncTasks = null; - - net.minecraft.server.v1_13_R2.World nmsWorld = nmsChunk.getWorld(); - int bx = X << 4; - int bz = Z << 4; - - if (set.entityRemoves != null && !set.entityRemoves.isEmpty()) { - if (syncTasks == null) syncTasks = new Runnable[3]; - - HashSet entsToRemove = set.entityRemoves; - - syncTasks[2] = new Runnable() { - @Override - public void run() { - final List[] entities = nmsChunk.getEntitySlices(); - - for (int i = 0; i < entities.length; i++) { - final Collection ents = entities[i]; - if (!ents.isEmpty()) { - final Iterator iter = ents.iterator(); - while (iter.hasNext()) { - final Entity entity = iter.next(); - if (entsToRemove.contains(entity.getUniqueID())) { - iter.remove(); - entity.b(false); - entity.die(); - entity.valid = false; - } - } - } - } - } - }; - } - - if (set.entities != null && !set.entities.isEmpty()) { - if (syncTasks == null) syncTasks = new Runnable[2]; - - HashSet entitiesToSpawn = set.entities; - - syncTasks[1] = new Runnable() { - @Override - public void run() { - for (final CompoundTag nativeTag : entitiesToSpawn) { - final Map entityTagMap = ReflectionUtils.getMap(nativeTag.getValue()); - final StringTag idTag = (StringTag) entityTagMap.get("Id"); - final ListTag posTag = (ListTag) entityTagMap.get("Pos"); - final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); - if (idTag == null || posTag == null || rotTag == null) { - Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } else { + existingSection = sections[layer]; + if (existingSection == null) { + System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); continue; } - final double x = posTag.getDouble(0); - final double y = posTag.getDouble(1); - final double z = posTag.getDouble(2); - final float yaw = rotTag.getFloat(0); - final float pitch = rotTag.getFloat(1); - final String id = idTag.getValue(); - final Entity entity = EntityTypes.a(nmsWorld, new MinecraftKey(id)); - if (entity != null) { - final UUID uuid = entity.getUniqueID(); - entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); - entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); - if (nativeTag != null) { - final NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_13.fromNative(nativeTag); - for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - entity.f(tag); + } + } + DelegateLock lock = BukkitQueue.applyLock(existingSection); + synchronized (get) { + synchronized (lock) { + lock.untilFree(); + + ChunkSection getSection; + if (get.nmsChunk != nmsChunk) { + get.nmsChunk = nmsChunk; + get.sections = null; + get.reset(); + } else { + getSection = get.getSections()[layer]; + if (getSection != existingSection) { + get.sections[layer] = existingSection; + get.reset(); + } else if (lock.isModified()) { + get.reset(layer); } - entity.setLocation(x, y, z, yaw, pitch); - nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + } + char[] getArr = get.load(layer); + for (int i = 0; i < 4096; i++) { + char value = setArr[i]; + if (value != 0) { + getArr[i] = value; + } + } + newSection = extent.newChunkSection(layer, hasSky, getArr); + if (!BukkitQueue.setSectionAtomic(sections, existingSection, newSection, layer)) { + System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + continue; + } else { + updateGet(get, nmsChunk, sections, newSection, setArr, layer); } } } - }; - - } - - // set tiles - if (set.tiles != null && !set.tiles.isEmpty()) { - if (syncTasks == null) syncTasks = new Runnable[1]; - - HashMap tiles = set.tiles; - syncTasks[0] = new Runnable() { - @Override - public void run() { - for (final Map.Entry entry : tiles.entrySet()) { - final CompoundTag nativeTag = entry.getValue(); - final short blockHash = entry.getKey(); - final int x = (blockHash >> 12 & 0xF) + bx; - final int y = (blockHash & 0xFF); - final int z = (blockHash >> 8 & 0xF) + bz; - final BlockPosition pos = new BlockPosition(x, y, z); - synchronized (BukkitQueue_0.class) { - TileEntity tileEntity = nmsWorld.getTileEntity(pos); - if (tileEntity == null || tileEntity.x()) { - nmsWorld.n(pos); - tileEntity = nmsWorld.getTileEntity(pos); - } - if (tileEntity != null) { - final NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_13.fromNative(nativeTag); - tag.set("x", new NBTTagInt(x)); - tag.set("y", new NBTTagInt(y)); - tag.set("z", new NBTTagInt(z)); - tileEntity.load(tag); - } - } - } - } - }; - } - - int finalMask = bitMask; - Runnable callback = () -> { - if (finalMask != 0) { - // Set Modified - nmsChunk.f(true); - nmsChunk.mustSave = true; - nmsChunk.markDirty(); - // send to player - extent.sendChunk(X, Z, finalMask); } - extent.returnToPool(BukkitChunkHolder.this); - }; - if (syncTasks != null) { - QueueHandler queueHandler = Fawe.get().getQueueHandler(); - Runnable[] finalSyncTasks = syncTasks; + // Biomes + if (set.biomes != null) { + // set biomes + final BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex(); + for (int i = 0; i < set.biomes.length; i++) { + final BiomeType biome = set.biomes[i]; + if (biome != null) { + final Biome craftBiome = BukkitAdapter.adapt(biome); + currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome); + } + } + } - // Chain the sync tasks and the callback - Callable chain = new Callable() { - @Override - public Future call() { - // Run the sync tasks - for (int i = 1; i < finalSyncTasks.length; i++) { - Runnable task = finalSyncTasks[i]; - if (task != null) { - task.run(); + Runnable[] syncTasks = null; + + net.minecraft.server.v1_13_R2.World nmsWorld = nmsChunk.getWorld(); + int bx = X << 4; + int bz = Z << 4; + + if (set.entityRemoves != null && !set.entityRemoves.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[3]; + + HashSet entsToRemove = set.entityRemoves; + + syncTasks[2] = new Runnable() { + @Override + public void run() { + final List[] entities = nmsChunk.getEntitySlices(); + + for (int i = 0; i < entities.length; i++) { + final Collection ents = entities[i]; + if (!ents.isEmpty()) { + final Iterator iter = ents.iterator(); + while (iter.hasNext()) { + final Entity entity = iter.next(); + if (entsToRemove.contains(entity.getUniqueID())) { + iter.remove(); + entity.b(false); + entity.die(); + entity.valid = false; + } + } + } } } - return queueHandler.async(callback, null); + }; + } + + if (set.entities != null && !set.entities.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[2]; + + HashSet entitiesToSpawn = set.entities; + + syncTasks[1] = new Runnable() { + @Override + public void run() { + for (final CompoundTag nativeTag : entitiesToSpawn) { + final Map entityTagMap = ReflectionUtils.getMap(nativeTag.getValue()); + final StringTag idTag = (StringTag) entityTagMap.get("Id"); + final ListTag posTag = (ListTag) entityTagMap.get("Pos"); + final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } + final double x = posTag.getDouble(0); + final double y = posTag.getDouble(1); + final double z = posTag.getDouble(2); + final float yaw = rotTag.getFloat(0); + final float pitch = rotTag.getFloat(1); + final String id = idTag.getValue(); + final Entity entity = EntityTypes.a(nmsWorld, new MinecraftKey(id)); + if (entity != null) { + final UUID uuid = entity.getUniqueID(); + entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); + entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); + if (nativeTag != null) { + final NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_13.fromNative(nativeTag); + for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.f(tag); + } + entity.setLocation(x, y, z, yaw, pitch); + nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + } + } + } + }; + + } + + // set tiles + if (set.tiles != null && !set.tiles.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[1]; + + HashMap tiles = set.tiles; + syncTasks[0] = new Runnable() { + @Override + public void run() { + for (final Map.Entry entry : tiles.entrySet()) { + final CompoundTag nativeTag = entry.getValue(); + final short blockHash = entry.getKey(); + final int x = (blockHash >> 12 & 0xF) + bx; + final int y = (blockHash & 0xFF); + final int z = (blockHash >> 8 & 0xF) + bz; + final BlockPosition pos = new BlockPosition(x, y, z); + synchronized (BukkitQueue_0.class) { + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity == null || tileEntity.x()) { + nmsWorld.n(pos); + tileEntity = nmsWorld.getTileEntity(pos); + } + if (tileEntity != null) { + final NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_13.fromNative(nativeTag); + tag.set("x", new NBTTagInt(x)); + tag.set("y", new NBTTagInt(y)); + tag.set("z", new NBTTagInt(z)); + tileEntity.load(tag); + } + } + } + } + }; + } + + int finalMask = bitMask; + Runnable callback = () -> { + if (finalMask != 0) { + // Set Modified + nmsChunk.f(true); + nmsChunk.mustSave = true; + nmsChunk.markDirty(); + // send to player + extent.sendChunk(X, Z, finalMask); } + + extent.returnToPool(BukkitChunkHolder.this); }; - return (T) queueHandler.sync(chain); - } else { - callback.run(); + if (syncTasks != null) { + QueueHandler queueHandler = Fawe.get().getQueueHandler(); + Runnable[] finalSyncTasks = syncTasks; + + // Chain the sync tasks and the callback + Callable chain = new Callable() { + @Override + public Future call() { + // Run the sync tasks + for (int i = 1; i < finalSyncTasks.length; i++) { + Runnable task = finalSyncTasks[i]; + if (task != null) { + task.run(); + } + } + return queueHandler.async(callback, null); + } + }; + return (T) (Future) queueHandler.sync(chain); + } else { + callback.run(); + } } + return null; + } catch (Throwable e) { + e.printStackTrace(); + return null; } - return null; } @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 2f114780c..1fdd4ce85 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 @@ -21,6 +21,8 @@ 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 java.util.Arrays; + import static com.boydti.fawe.bukkit.v0.BukkitQueue_0.getAdapter; public class BukkitGetBlocks extends CharGetBlocks { @@ -29,7 +31,7 @@ public class BukkitGetBlocks extends CharGetBlocks { public World nmsWorld; public int X, Z; - public BukkitGetBlocks(World nmsWorld, int X, int Z) {/*d*/ + public BukkitGetBlocks(World nmsWorld, int X, int Z) { this.nmsWorld = nmsWorld; this.X = X; this.Z = Z; @@ -71,9 +73,10 @@ public class BukkitGetBlocks extends CharGetBlocks { 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 int bitsPerEntry = bits.c(); final long[] blockStates = bits.a(); + new BitArray4096(blockStates, bitsPerEntry).toRaw(data); int num_palette; @@ -112,23 +115,20 @@ public class BukkitGetBlocks extends CharGetBlocks { 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); + final int size = num_palette; + if (size != 1) { + for (int i = 0; i < size; i++) { + char ordinal = ordinal(palette.a(i)); + paletteToBlockChars[i] = ordinal; } - paletteToBlockChars[i] = ordinal; - } - for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; - data[i] = paletteToBlockChars[paletteVal]; - if (data[i] == Character.MAX_VALUE) { - System.out.println("Invalid " + paletteVal + " | " + num_palette); + for (int i = 0; i < 4096; i++) { + char paletteVal = data[i]; + char val = paletteToBlockChars[paletteVal]; + data[i] = val; } + } else { + char ordinal = ordinal(palette.a(0)); + Arrays.fill(data, ordinal); } } finally { for (int i = 0; i < num_palette; i++) { @@ -143,6 +143,14 @@ public class BukkitGetBlocks extends CharGetBlocks { } } + private final char ordinal(IBlockData ibd) { + if (ibd == null) { + return BlockTypes.AIR.getDefaultState().getOrdinalChar(); + } else { + return ((Spigot_v1_13_R2) getAdapter()).adaptToChar(ibd); + } + } + public ChunkSection[] getSections() { ChunkSection[] tmp = sections; if (tmp == null) { 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 index 1d9809d42..6db6b7c7d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java @@ -1,40 +1,58 @@ package com.boydti.fawe.beta; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; +import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; 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 javax.annotation.Nullable; + import static com.sk89q.worldedit.world.block.BlockTypes.states; public class CharFilterBlock implements FilterBlock { private IQueueExtent queue; - private CharGetBlocks chunk; - private char[] section; + private CharGetBlocks get; + private CharSetBlocks set; - @Override - public final void init(IQueueExtent queue) { - this.queue = queue; - } - - @Override - public final 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; - } + private char[] getArr; + private @Nullable char[] setArr; + private SetDelegate delegate; // local private int layer, index, x, y, z, xx, yy, zz, X, Z; - public final void filter(CharGetBlocks blocks, Filter filter) { - for (int layer = 0; layer < 16; layer++) { - if (!blocks.hasSection(layer)) continue; - char[] arr = blocks.sections[layer].get(blocks, layer); + @Override + public final FilterBlock init(final IQueueExtent queue) { + this.queue = queue; + return this; + } - this.section = arr; + @Override + public final FilterBlock init(final int X, final int Z, final IGetBlocks chunk) { + this.get = (CharGetBlocks) chunk; + this.X = X; + this.Z = Z; + this.xx = X << 4; + this.zz = Z << 4; + return this; + } + + public final void filter(final IGetBlocks iget, final ISetBlocks iset, final Filter filter) { + final CharSetBlocks set = (CharSetBlocks) iset; + final CharGetBlocks get = (CharGetBlocks) iget; + for (int layer = 0; layer < 16; layer++) { + if (!get.hasSection(layer)) continue; + this.set = set; + getArr = get.sections[layer].get(get, layer); + if (set.hasSection(layer)) { + setArr = set.blocks[layer]; + delegate = FULL; + } else { + delegate = NULL; + setArr = null; + } this.layer = layer; this.yy = layer << 4; @@ -48,6 +66,25 @@ public class CharFilterBlock implements FilterBlock { } } + @Override + public void setOrdinal(final int ordinal) { + delegate.set(this, (char) ordinal); + } + + @Override + public void setState(final BlockState state) { + delegate.set(this, state.getOrdinalChar()); + } + + @Override + public void setFullBlock(final BaseBlock block) { + delegate.set(this, block.getOrdinalChar()); + final CompoundTag nbt = block.getNbtData(); + if (nbt != null) { // TODO optimize check via ImmutableBaseBlock + set.setTile(x, yy + y, z, nbt); + } + } + @Override public final int getX() { return xx + x; @@ -89,26 +126,26 @@ public class CharFilterBlock implements FilterBlock { } public final char getOrdinalChar() { - return section[index]; + return getArr[index]; } @Override public final int getOrdinal() { - return section[index]; + return getArr[index]; } @Override public final BlockState getState() { - int ordinal = section[index]; + final int ordinal = getArr[index]; return BlockTypes.states[ordinal]; } @Override public final BaseBlock getBaseBlock() { - BlockState state = getState(); - BlockMaterial material = state.getMaterial(); + final BlockState state = getState(); + final BlockMaterial material = state.getMaterial(); if (material.hasContainer()) { - CompoundTag tag = chunk.getTag(x, y + (layer << 4), z); + final CompoundTag tag = get.getTag(x, y + (layer << 4), z); return state.toBaseBlock(tag); } return state.toBaseBlock(); @@ -121,11 +158,11 @@ public class CharFilterBlock implements FilterBlock { public final BlockState getOrdinalBelow() { if (y > 0) { - return states[section[index - 256]]; + return states[getArr[index - 256]]; } if (layer > 0) { final int newLayer = layer - 1; - final CharGetBlocks chunk = this.chunk; + final CharGetBlocks chunk = this.get; return states[chunk.sections[newLayer].get(chunk, newLayer, index + 3840)]; } return BlockTypes.__RESERVED__.getDefaultState(); @@ -133,22 +170,22 @@ public class CharFilterBlock implements FilterBlock { public final BlockState getStateAbove() { if (y < 16) { - return states[section[index + 256]]; + return states[getArr[index + 256]]; } if (layer < 16) { final int newLayer = layer + 1; - final CharGetBlocks chunk = this.chunk; + final CharGetBlocks chunk = this.get; return states[chunk.sections[newLayer].get(chunk, newLayer, index - 3840)]; } return BlockTypes.__RESERVED__.getDefaultState(); } - public final BlockState getStateRelativeY(int y) { - int newY = this.y + y; - int layerAdd = newY >> 4; + public final BlockState getStateRelativeY(final int y) { + final int newY = this.y + y; + final int layerAdd = newY >> 4; switch (layerAdd) { case 0: - return states[section[this.index + (y << 8)]]; + return states[getArr[this.index + (y << 8)]]; case 1: case 2: case 3: @@ -164,10 +201,10 @@ public class CharFilterBlock implements FilterBlock { case 13: case 14: case 15: { - int newLayer = layer + layerAdd; + final int newLayer = layer + layerAdd; if (newLayer < 16) { - int index = this.index + ((y & 15) << 8); - return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + final int index = this.index + ((y & 15) << 8); + return states[get.sections[newLayer].get(get, newLayer, index)]; } break; } @@ -186,10 +223,10 @@ public class CharFilterBlock implements FilterBlock { case -13: case -14: case -15: { - int newLayer = layer + layerAdd; + final int newLayer = layer + layerAdd; if (newLayer >= 0) { - int index = this.index + ((y & 15) << 8); - return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + final int index = this.index + ((y & 15) << 8); + return states[get.sections[newLayer].get(get, newLayer, index)]; } break; } @@ -198,15 +235,15 @@ public class CharFilterBlock implements FilterBlock { } public final BlockState getStateRelative(final int x, final int y, final int z) { - int newX = this.x + x; + final int newX = this.x + x; if (newX >> 4 == 0) { - int newZ = this.z + z; + final int newZ = this.z + z; if (newZ >> 4 == 0) { - int newY = this.y + y; - int layerAdd = newY >> 4; + final int newY = this.y + y; + final int layerAdd = newY >> 4; switch (layerAdd) { case 0: - return states[section[this.index + ((y << 8) + (z << 4) + x)]]; + return states[getArr[this.index + ((y << 8) + (z << 4) + x)]]; case 1: case 2: case 3: @@ -222,10 +259,10 @@ public class CharFilterBlock implements FilterBlock { case 13: case 14: case 15: { - int newLayer = layer + layerAdd; + final int newLayer = layer + layerAdd; if (newLayer < 16) { - int index = ((newY & 15) << 8) + (newZ << 4) + newX; - return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + final int index = ((newY & 15) << 8) + (newZ << 4) + newX; + return states[get.sections[newLayer].get(get, newLayer, index)]; } break; } @@ -244,10 +281,10 @@ public class CharFilterBlock implements FilterBlock { case -13: case -14: case -15: { - int newLayer = layer + layerAdd; + final int newLayer = layer + layerAdd; if (newLayer >= 0) { - int index = ((newY & 15) << 8) + (newZ << 4) + newX; - return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; + final int index = ((newY & 15) << 8) + (newZ << 4) + newX; + return states[get.sections[newLayer].get(get, newLayer, index)]; } break; } @@ -257,10 +294,36 @@ public class CharFilterBlock implements FilterBlock { } // queue.get // TODO return normal get block - int newY = this.y + y + yy; + final 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(); } + + /* + Set delegate + */ + private SetDelegate initSet() { + setArr = set.sections[layer].get(set, layer); + return delegate = FULL; + } + + private interface SetDelegate { + void set(CharFilterBlock block, char value); + } + + private static final SetDelegate NULL = new SetDelegate() { + @Override + public void set(final CharFilterBlock block, final char value) { + block.initSet().set(block, value); + } + }; + + private static final SetDelegate FULL = new SetDelegate() { + @Override + public final void set(final CharFilterBlock block, final char value) { + block.setArr[block.index] = value; + } + }; } 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 index 68721cd82..264eb4509 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java @@ -10,7 +10,7 @@ public class ChunkFuture implements Future { private volatile boolean cancelled; private volatile boolean done; - public ChunkFuture(IChunk chunk) { + public ChunkFuture(final IChunk chunk) { this.chunk = chunk; } @@ -19,7 +19,7 @@ public class ChunkFuture implements Future { } @Override - public boolean cancel(boolean mayInterruptIfRunning) { + public boolean cancel(final boolean mayInterruptIfRunning) { cancelled = true; if (done) return false; return true; @@ -46,7 +46,7 @@ public class ChunkFuture implements Future { } @Override - public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + public Void get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { synchronized (chunk) { if (!done) { this.wait(unit.toMillis(timeout)); 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 fe3adaea9..01a49bdc7 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 @@ -57,7 +57,7 @@ public interface Filter { return this; } - default void join(Filter parent) { + default void join(final 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 index 79fb85799..ac6cd3312 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java @@ -1,14 +1,23 @@ package com.boydti.fawe.beta; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; +import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; 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); + FilterBlock init(IQueueExtent queue); - void init(int X, int Z, IGetBlocks chunk); + FilterBlock init(int X, int Z, IGetBlocks chunk); + + void filter(IGetBlocks get, ISetBlocks set, Filter filter); + + void setOrdinal(int ordinal); + + void setState(BlockState state); + + void setFullBlock(BaseBlock block); int getOrdinal(); @@ -26,7 +35,7 @@ public interface FilterBlock { return getStateRelative(0, 1, 0); } - default BlockState getStateRelativeY(int y) { + default BlockState getStateRelativeY(final int y) { return getStateRelative(0, y, 0); } 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 1cb14e2af..8d57af1be 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 @@ -65,7 +65,7 @@ public interface IDelegateChunk extends IChunk { @Override - default boolean trim(boolean aggressive) { + default boolean trim(final boolean aggressive) { return getParent().trim(aggressive); } @@ -80,7 +80,7 @@ public interface IDelegateChunk extends IChunk { } @Override - default void filter(Filter filter, FilterBlock mutable) { + default void filter(final Filter filter, final FilterBlock mutable) { getParent().filter(filter, mutable); } @@ -100,7 +100,7 @@ public interface IDelegateChunk extends IChunk { } @Override - default void set(Filter filter) { + default void set(final Filter filter) { getParent().set(filter); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java index c11146ce3..f05a1b2e5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java @@ -11,27 +11,27 @@ public interface IDelegateQueueExtent extends IQueueExtent { IQueueExtent getParent(); @Override - default void init(WorldChunkCache cache) { + default void init(final WorldChunkCache cache) { getParent().init(cache); } @Override - default IChunk getCachedChunk(int X, int Z) { + default IChunk getCachedChunk(final int X, final int Z) { return getParent().getCachedChunk(X, Z); } @Override - default Future submit(IChunk chunk) { + default Future submit(final IChunk chunk) { return getParent().submit(chunk); } @Override - default IChunk create(boolean full) { + default IChunk create(final boolean full) { return getParent().create(full); } @Override - default IChunk wrap(IChunk root) { + default IChunk wrap(final IChunk root) { return getParent().wrap(root); } @@ -41,7 +41,7 @@ public interface IDelegateQueueExtent extends IQueueExtent { } @Override - default boolean trim(boolean aggressive) { + default boolean trim(final boolean aggressive) { return getParent().trim(aggressive); } } 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 b49a8bb79..b865e1f91 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 @@ -20,8 +20,6 @@ public interface IGetBlocks extends IBlocks, Trimable { @Override boolean trim(boolean aggressive); - void filter(Filter filter, FilterBlock block); - default void optimize() { } 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 7725810e5..a19e7ea8c 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 @@ -70,7 +70,7 @@ public interface IQueueExtent extends Flushable, Trimable { * @param root * @return wrapped chunk */ - default IChunk wrap(IChunk root) { + default IChunk wrap(final IChunk root) { return root; } 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/filters/CountFilter.java similarity index 66% rename from worldedit-core/src/main/java/com/boydti/fawe/beta/test/CountFilter.java rename to worldedit-core/src/main/java/com/boydti/fawe/beta/filters/CountFilter.java index 9117391f2..052519cd7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/test/CountFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/CountFilter.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.beta.test; +package com.boydti.fawe.beta.filters; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.FilterBlock; @@ -13,17 +13,17 @@ import java.util.Collections; import java.util.List; public class CountFilter implements Filter { - private int[] counter = new int[BlockTypes.states.length]; + private final int[] counter = new int[BlockTypes.states.length]; @Override - public void applyBlock(FilterBlock block) { + public void applyBlock(final FilterBlock block) { counter[block.getOrdinal()]++; } public List> getDistribution() { - List> distribution = new ArrayList<>(); + final List> distribution = new ArrayList<>(); for (int i = 0; i < counter.length; i++) { - int count = counter[i]; + final int count = counter[i]; if (count != 0) { distribution.add(new Countable<>(BlockTypes.states[i], count)); } @@ -32,10 +32,10 @@ public class CountFilter implements Filter { 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", + public void print(final Actor actor, final long size) { + for (final Countable c : getDistribution()) { + final String name = c.getID().toString(); + final String str = String.format("%-7s (%.3f%%) %s", String.valueOf(c.getAmount()), c.getAmount() / (double) size * 100, name); @@ -49,8 +49,8 @@ public class CountFilter implements Filter { } @Override - public void join(Filter parent) { - CountFilter other = (CountFilter) parent; + public void join(final Filter parent) { + final 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/beta/filters/SetFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/SetFilter.java new file mode 100644 index 000000000..04048d04d --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/filters/SetFilter.java @@ -0,0 +1,18 @@ +package com.boydti.fawe.beta.filters; + +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlock; +import com.sk89q.worldedit.world.block.BlockState; + +public class SetFilter implements Filter { + private final BlockState state; + + public SetFilter(final BlockState state) { + this.state = state; + } + + @Override + public void applyBlock(final FilterBlock block) { + block.setState(state); + } +} 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 a6e589f88..4cfe41121 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,5 +1,6 @@ package com.boydti.fawe.beta.implementation; +import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.FilterBlock; @@ -9,6 +10,7 @@ import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.util.MemUtil; +import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.wrappers.WorldWrapper; import com.google.common.util.concurrent.Futures; import com.sk89q.worldedit.math.BlockVector2; @@ -32,7 +34,7 @@ import java.util.concurrent.ThreadPoolExecutor; /** * Class which handles all the queues {@link IQueueExtent} */ -public abstract class QueueHandler implements Trimable { +public abstract class QueueHandler implements Trimable, Runnable { private ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool(); private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool(); private ThreadPoolExecutor blockingExecutor = FaweCache.newBlockingExecutor(); @@ -46,38 +48,52 @@ public abstract class QueueHandler implements Trimable { } }; - public Future async(Runnable run, T value) { + public QueueHandler() { + TaskManager.IMP.repeat(this, 1); + } + + @Override + public void run() { + if (!Fawe.isMainThread()) { + throw new IllegalStateException("Not main thread"); + } + while (!syncTasks.isEmpty()) { + final FutureTask task = syncTasks.poll(); + if (task != null) task.run(); + } + } + + public Future async(final Runnable run, final T value) { return forkJoinPoolSecondary.submit(run, value); } - public Future async(Callable call) { + public Future async(final Callable call) { return forkJoinPoolSecondary.submit(call); } - public Future sync(Runnable run, T value) { - FutureTask result = new FutureTask<>(run, value); + public Future sync(final Runnable run, final T value) { + final FutureTask result = new FutureTask<>(run, value); syncTasks.add(result); return result; } - public Future sync(Runnable run) { - FutureTask result = new FutureTask<>(run, null); + public Future sync(final Runnable run) { + final FutureTask result = new FutureTask<>(run, null); syncTasks.add(result); return result; } - public Future sync(Callable call) { - FutureTask result = new FutureTask<>(call); + public Future sync(final Callable call) { + final FutureTask result = new FutureTask<>(call); syncTasks.add(result); return result; } - public > T submit(IChunk chunk) { + public > T submit(final IChunk chunk) { if (MemUtil.isMemoryFree()) { // return (T) forkJoinPoolSecondary.submit(chunk); } return (T) blockingExecutor.submit(chunk); - } /** @@ -104,8 +120,8 @@ public abstract class QueueHandler implements Trimable { public abstract IQueueExtent create(); - public IQueueExtent getQueue(World world) { - IQueueExtent queue = queuePool.get(); + public IQueueExtent getQueue(final World world) { + final IQueueExtent queue = queuePool.get(); queue.init(getOrCreate(world)); return queue; } @@ -136,30 +152,31 @@ public abstract class QueueHandler implements Trimable { // Get a pool, to operate on the chunks in parallel final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS); - ForkJoinTask[] tasks = new ForkJoinTask[size]; + final ForkJoinTask[] tasks = new ForkJoinTask[size]; for (int i = 0; i < size; i++) { tasks[i] = forkJoinPoolPrimary.submit(new Runnable() { @Override public void run() { - Filter newFilter = filter.fork(); + final Filter newFilter = filter.fork(); // Create a chunk that we will reuse/reset for each operation - IQueueExtent queue = getQueue(world); + final IQueueExtent queue = getQueue(world); synchronized (queue) { FilterBlock block = null; - while (true) { - // Get the next chunk pos - final BlockVector2 pos; - synchronized (chunksIter) { - if (!chunksIter.hasNext()) break; - pos = chunksIter.next(); - } - final int X = pos.getX(); - final int Z = pos.getZ(); - IChunk chunk = queue.getCachedChunk(X, Z); - // Initialize - chunk.init(queue, X, Z); - try { + try { + while (true) { + // Get the next chunk pos + final int X, Z; + synchronized (chunksIter) { + if (!chunksIter.hasNext()) break; + final BlockVector2 pos = chunksIter.next(); + X = pos.getX(); + Z = pos.getZ(); + } + IChunk chunk = queue.getCachedChunk(X, Z); + // Initialize + chunk.init(queue, X, Z); + if (!newFilter.appliesChunk(X, Z)) { continue; } @@ -173,11 +190,11 @@ public abstract class QueueHandler implements Trimable { newFilter.finishChunk(chunk); queue.submit(chunk); - } finally { - if (filter != newFilter) { - synchronized (filter) { - newFilter.join(filter); - } + } + } finally { + if (filter != newFilter) { + synchronized (filter) { + newFilter.join(filter); } } } @@ -188,7 +205,7 @@ public abstract class QueueHandler implements Trimable { } // Join filters for (int i = 0; i < tasks.length; i++) { - ForkJoinTask task = tasks[i]; + final ForkJoinTask task = tasks[i]; if (task != null) { task.quietlyJoin(); } 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 index f6419164e..af1bce4b1 100644 --- 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 @@ -6,8 +6,8 @@ import com.boydti.fawe.beta.FilterBlock; public abstract class SimpleCharQueueExtent extends SingleThreadQueueExtent { @Override public FilterBlock initFilterBlock() { - CharFilterBlock filter = new CharFilterBlock(); - filter.init(this); + FilterBlock filter = new CharFilterBlock(); + filter = 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 4f57656be..661868e8f 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 @@ -5,6 +5,7 @@ import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.holder.ReferenceChunk; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MemUtil; import com.google.common.util.concurrent.Futures; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; @@ -78,12 +79,28 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { // Pool discarded chunks for reuse (can safely be cleared by another thread) private static final ConcurrentLinkedQueue CHUNK_POOL = new ConcurrentLinkedQueue<>(); - public void returnToPool(IChunk chunk) { + public void returnToPool(final IChunk chunk) { CHUNK_POOL.add(chunk); } @Override public > T submit(final IChunk chunk) { + if (lastChunk == chunk) { + lastPair = Long.MAX_VALUE; + lastChunk = null; + } + final long index = MathMan.pairInt(chunk.getX(), chunk.getZ()); + chunks.remove(index, chunk); + return submitUnchecked(chunk); + } + + /** + * Submit without first checking that it has been removed from the chunk map + * @param chunk + * @param + * @return + */ + private > T submitUnchecked(final IChunk chunk) { if (chunk.isEmpty()) { CHUNK_POOL.add(chunk); return (T) (Future) Futures.immediateFuture(null); @@ -97,7 +114,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { } @Override - public synchronized boolean trim(boolean aggressive) { + public synchronized boolean trim(final boolean aggressive) { // TODO trim individial chunk sections CHUNK_POOL.clear(); if (Thread.currentThread() == currentThread) { @@ -152,12 +169,12 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { checkThread(); final int size = chunks.size(); - boolean lowMem = MemUtil.isMemoryLimited(); + final boolean lowMem = MemUtil.isMemoryLimited(); if (lowMem || size > Settings.IMP.QUEUE.TARGET_SIZE) { chunk = chunks.removeFirst(); - Future future = submit(chunk); + final Future future = submitUnchecked(chunk); if (future != null && !future.isDone()) { - int targetSize; + final int targetSize; if (lowMem) { targetSize = Settings.IMP.QUEUE.PARALLEL_THREADS; } else { @@ -177,14 +194,14 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { return chunk; } - private void pollSubmissions(int targetSize, boolean aggressive) { - int overflow = submissions.size() - targetSize; + private void pollSubmissions(final int targetSize, final boolean aggressive) { + final int overflow = submissions.size() - targetSize; if (aggressive) { for (int i = 0; i < overflow; i++) { Future first = submissions.poll(); try { while ((first = (Future) first.get()) != null) ; - } catch (InterruptedException | ExecutionException e) { + } catch (final InterruptedException | ExecutionException e) { e.printStackTrace(); } } @@ -195,7 +212,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { if (next.isDone()) { try { next = (Future) next.get(); - } catch (InterruptedException | ExecutionException e) { + } catch (final InterruptedException | ExecutionException e) { e.printStackTrace(); } } else { @@ -212,8 +229,8 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { checkThread(); if (!chunks.isEmpty()) { if (MemUtil.isMemoryLimited()) { - for (IChunk chunk : chunks.values()) { - Future future = submit(chunk); + for (final IChunk chunk : chunks.values()) { + final Future future = submitUnchecked(chunk); if (future != null && !future.isDone()) { pollSubmissions(Settings.IMP.QUEUE.PARALLEL_THREADS, true); submissions.add(future); @@ -221,7 +238,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { } } else { for (final IChunk chunk : chunks.values()) { - Future future = submit(chunk); + final Future future = submitUnchecked(chunk); if (future != null && !future.isDone()) { submissions.add(future); } 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 7005b5ed1..66cb7b74a 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 @@ -13,7 +13,7 @@ public class CharBlocks implements IBlocks { } @Override - public boolean trim(boolean aggressive) { + public boolean trim(final boolean aggressive) { boolean result = true; for (int i = 0; i < 16; i++) { if (sections[i] == NULL) { @@ -30,34 +30,42 @@ public class CharBlocks implements IBlocks { for (int i = 0; i < 16; i++) sections[i] = NULL; } - public void reset(int layer) { + public void reset(final int layer) { sections[layer] = NULL; } - protected char[] load(int layer) { + public char[] load(final int layer) { return new char[4096]; } - protected char[] load(int layer, char[] data) { + public char[] load(final int layer, final char[] data) { for (int i = 0; i < 4096; i++) data[i] = 0; return data; } @Override - public boolean hasSection(int layer) { + public boolean hasSection(final 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); + public char get(final int x, final int y, final int z) { + final int layer = y >> 4; + final 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); + public void set(final int x, final int y, final int z, final char value) { + final int layer = y >> 4; + final int index = ((y & 15) << 8) | (z << 4) | (x & 15); + set(layer, index, value); + } + + public final char get(final int layer, final int index) { + return sections[layer].get(this, layer, index); + } + + public final void set(final int layer, final int index, final char value) { + sections[layer].set(this, layer, index, value); } /* @@ -67,18 +75,18 @@ public class CharBlocks implements IBlocks { public static abstract class Section { public abstract char[] get(CharBlocks blocks, int layer); - public final char get(CharBlocks blocks, int layer, int index) { + public final char get(final CharBlocks blocks, final int layer, final 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 final void set(final CharBlocks blocks, final int layer, final int index, final char value) { + get(blocks, layer)[index] = value; } } public static final Section NULL = new Section() { @Override - public final char[] get(CharBlocks blocks, int layer) { + public final char[] get(final CharBlocks blocks, final int layer) { blocks.sections[layer] = FULL; char[] arr = blocks.blocks[layer]; if (arr == null) { @@ -92,7 +100,7 @@ public class CharBlocks implements IBlocks { public static final Section FULL = new Section() { @Override - public final char[] get(CharBlocks blocks, int layer) { + public final char[] get(final CharBlocks blocks, final 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 fede5719c..fe5a0286e 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 @@ -20,13 +20,7 @@ public abstract class CharGetBlocks extends CharBlocks implements IGetBlocks { } @Override - public void filter(Filter filter, FilterBlock block) { - CharFilterBlock b = (CharFilterBlock) block; - b.filter(this, filter); - } - - @Override - public boolean trim(boolean aggressive) { + public boolean trim(final boolean aggressive) { for (int i = 0; i < 16; i++) { sections[i] = NULL; blocks[i] = null; 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 d962d5f86..3fc218961 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 @@ -17,7 +17,7 @@ public class CharSetBlocks extends CharBlocks implements ISetBlocks { public HashSet entityRemoves; @Override - public boolean setBiome(int x, int y, int z, BiomeType biome) { + public boolean setBiome(final int x, final int y, final int z, final BiomeType biome) { if (biomes == null) { biomes = new BiomeType[256]; } @@ -26,22 +26,22 @@ public class CharSetBlocks extends CharBlocks implements ISetBlocks { } @Override - public boolean setBlock(int x, int y, int z, BlockStateHolder holder) { + public boolean setBlock(final int x, final int y, final int z, final BlockStateHolder holder) { set(x, y, z, holder.getOrdinalChar()); return true; } @Override - public void setTile(int x, int y, int z, CompoundTag tile) { + public void setTile(final int x, final int y, final int z, final CompoundTag tile) { if (tiles == null) { tiles = new HashMap<>(); } - short pair = MathMan.tripleBlockCoord(x, y, z); + final short pair = MathMan.tripleBlockCoord(x, y, z); tiles.put(pair, tile); } @Override - public void setEntity(CompoundTag tag) { + public void setEntity(final CompoundTag tag) { if (entities == null) { entities = new HashSet<>(); } @@ -49,7 +49,7 @@ public class CharSetBlocks extends CharBlocks implements ISetBlocks { } @Override - public void removeEntity(UUID uuid) { + public void removeEntity(final UUID uuid) { if (entityRemoves == null) { entityRemoves = new HashSet<>(); } 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 9d0732d11..bbe4bfec2 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 @@ -8,7 +8,7 @@ public class FullCharBlocks implements IBlocks { public final char[] blocks = new char[65536]; @Override - public boolean hasSection(int layer) { + public boolean hasSection(final int layer) { return false; } @@ -18,7 +18,7 @@ public class FullCharBlocks implements IBlocks { } @Override - public boolean trim(boolean aggressive) { + public boolean trim(final 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 7ff757113..153e162bd 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 @@ -31,21 +31,22 @@ public abstract class ChunkHolder implements IChunk, Supplier { this.delegate = NULL; } - public ChunkHolder(IBlockDelegate delegate) { + public ChunkHolder(final IBlockDelegate delegate) { this.delegate = delegate; } @Override - public void filter(Filter filter, FilterBlock block) { - block.init(X, Z, get); - IGetBlocks get = getOrCreateGet(); - get.filter(filter, block); + public void filter(final Filter filter, FilterBlock block) { + final IGetBlocks get = getOrCreateGet(); + final ISetBlocks set = getOrCreateSet(); + block = block.init(X, Z, get); + block.filter(get, set, filter); } @Override - public boolean trim(boolean aggressive) { + public boolean trim(final boolean aggressive) { if (set != null) { - boolean result = set.trim(aggressive); + final boolean result = set.trim(aggressive); if (result) { delegate = NULL; get = null; @@ -87,7 +88,7 @@ public abstract class ChunkHolder implements IChunk, Supplier { private IGetBlocks newGet() { if (extent instanceof SingleThreadQueueExtent) { - WorldChunkCache cache = ((SingleThreadQueueExtent) extent).getCache(); + final WorldChunkCache cache = ((SingleThreadQueueExtent) extent).getCache(); return cache.get(MathMan.pairInt(X, Z), this); } return get(); @@ -101,7 +102,7 @@ public abstract class ChunkHolder implements IChunk, Supplier { } @Override - public void init(IQueueExtent extent, final int X, final int Z) { + public void init(final IQueueExtent extent, final int X, final int Z) { this.extent = extent; this.X = X; this.Z = Z; 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 527bcf5ce..ca599ae94 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 @@ -10,7 +10,7 @@ import com.boydti.fawe.beta.IChunk; public class FinalizedChunk extends DelegateChunk { private final IQueueExtent queueExtent; - public FinalizedChunk(final IChunk parent, IQueueExtent queueExtent) { + public FinalizedChunk(final IChunk parent, final IQueueExtent queueExtent) { super(parent); this.queueExtent = queueExtent; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ReferenceChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ReferenceChunk.java index 6ef9b01a1..cb88a839e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ReferenceChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/ReferenceChunk.java @@ -14,7 +14,7 @@ import java.lang.ref.Reference; public abstract class ReferenceChunk implements IDelegateChunk { private final Reference ref; - public ReferenceChunk(final IChunk parent, IQueueExtent queueExtent) { + public ReferenceChunk(final IChunk parent, final IQueueExtent queueExtent) { this.ref = toRef(new FinalizedChunk(parent, queueExtent)); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java index 785ab43af..ed34c3e60 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java @@ -49,7 +49,6 @@ public class InspectBrush extends BrushTool implements DoubleActionTraceTool { } public Vector3 getTarget(Player player, boolean adjacent) { - Location target = null; int range = this.range > -1 ? getRange() : MAX_RANGE; if (adjacent) { Location face = player.getBlockTraceFace(range, true); 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 8a5da394a..9b9939ede 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 @@ -22,9 +22,9 @@ 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.filters.SetFilter; 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.beta.filters.CountFilter; import com.boydti.fawe.config.BBC; import com.boydti.fawe.example.NMSMappedFaweQueue; import com.boydti.fawe.object.FaweLimit; @@ -48,7 +48,6 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.generator.FloraGenerator; -import com.sk89q.worldedit.function.generator.ForestGenerator; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.NoiseFilter2D; @@ -66,7 +65,6 @@ 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; @@ -79,12 +77,12 @@ 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.BlockType; 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; @@ -116,6 +114,41 @@ public class RegionCommands extends MethodCommands { this.worldEdit = worldEdit; } + + @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(); + long start = System.currentTimeMillis(); + queueHandler.apply(world, region, filter); + long diff = System.currentTimeMillis() - start; + System.out.println(diff); + } + + @Command( + aliases = {"db2"}, + usage = "", + desc = "db2", + help = "db2" + ) + public void db2(Player player, @Selection Region region, String blockStr) throws WorldEditException { + QueueHandler queueHandler = Fawe.get().getQueueHandler(); + World world = player.getWorld(); + BlockState block = BlockState.get(blockStr); + SetFilter filter = new SetFilter(block); + long start = System.currentTimeMillis(); + queueHandler.apply(world, region, filter); + long diff = System.currentTimeMillis() - start; + System.out.println(diff); + } + + @Command( aliases = {"/fixlighting"}, desc = "Get the light at a position", @@ -271,44 +304,6 @@ 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(); - long start = System.currentTimeMillis(); - queueHandler.apply(world, region, filter); - long diff = System.currentTimeMillis() - start; - System.out.println(diff); - } - - @Command( - aliases = {"db2"}, - usage = "", - desc = "db2", - help = "db2" - ) - public void db2(Player player, @Selection Region region) throws WorldEditException { - QueueHandler queueHandler = Fawe.get().getQueueHandler(); - World world = player.getWorld(); - IQueueExtent queue = queueHandler.getQueue(world); - BlockState block = BlockTypes.STONE.getDefaultState(); - long start = System.currentTimeMillis(); - for (BlockVector3 p : region) { - queue.setBlock(p.getX(), p.getY(), p.getZ(), block); - } - long start2 = System.currentTimeMillis(); - queue.flush(); - long diff = System.currentTimeMillis() - start; - long diff2 = System.currentTimeMillis() - start2; - System.out.println(diff + " | " + diff2); - } - @Command( aliases = {"/curve", "/spline"}, usage = " [thickness]",