From d434dfcfdd22f07d562ad46996084f196bb6ac39 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sun, 18 Aug 2019 02:09:09 +0100 Subject: [PATCH] some minor refactoring --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 172 ++------- .../fawe/bukkit/beta/BukkitGetBlocks.java | 358 +++++++++++++++++- .../boydti/fawe/bukkit/beta/BukkitQueue.java | 74 ++-- .../fawe/bukkit/beta/BukkitQueueHandler.java | 69 +++- .../bukkit/listener/BukkitImageListener.java | 6 +- .../bukkit/listener/CFIPacketListener.java | 188 +++++---- .../fawe/bukkit/regions/FreeBuildRegion.java | 7 +- .../com/boydti/fawe/bukkit/util/ItemUtil.java | 15 +- .../fawe/bukkit/v0/FaweAdapter_All.java | 2 +- .../bukkit/v1_14/adapter/Spigot_v1_14_R1.java | 25 +- .../fawe/bukkit/wrapper/AsyncBlock.java | 51 ++- .../fawe/bukkit/wrapper/AsyncBlockState.java | 48 +-- .../fawe/bukkit/wrapper/AsyncChunk.java | 11 +- .../fawe/bukkit/wrapper/AsyncWorld.java | 121 +++--- .../wrapper/state/AsyncDataContainer.java | 2 +- .../fawe/bukkit/wrapper/state/AsyncSign.java | 6 +- .../sk89q/worldedit/bukkit/BukkitAdapter.java | 4 - .../sk89q/worldedit/bukkit/BukkitWorld.java | 14 + .../worldedit/bukkit/WorldEditPlugin.java | 4 +- .../bukkit/adapter/BukkitImplAdapter.java | 8 + .../bukkit/adapter/IBukkitAdapter.java | 4 - .../main/java/com/boydti/fawe/FaweCache.java | 233 ++++++++---- .../java/com/boydti/fawe/beta/IChunk.java | 4 +- .../java/com/boydti/fawe/beta/IChunkGet.java | 6 + .../com/boydti/fawe/beta/IDelegateChunk.java | 14 + .../fawe/beta/IDelegateQueueExtent.java | 351 ++++++++++++++++- .../com/boydti/fawe/beta/IQueueExtent.java | 22 +- .../java/com/boydti/fawe/beta/Trimable.java | 2 + .../fawe/beta/implementation/ChunkCache.java | 77 ++++ .../beta/implementation/FallbackChunkGet.java | 135 +++++++ .../fawe/beta/implementation/IChunkCache.java | 16 + .../beta/implementation/NullChunkGet.java | 63 +++ .../beta/implementation/QueueHandler.java | 37 +- .../SingleThreadQueueExtent.java | 90 +++-- .../implementation/blocks/BitSetBlocks.java | 2 +- .../implementation/blocks/CharGetBlocks.java | 2 + .../implementation/blocks/CharSetBlocks.java | 18 +- .../implementation/holder/ChunkHolder.java | 107 ++++-- .../implementation/holder/DelegateChunk.java | 16 +- .../boydti/fawe/command/AnvilCommands.java | 2 +- .../java/com/boydti/fawe/config/Settings.java | 8 +- .../boydti/fawe/logging/LoggingChangeSet.java | 8 +- .../cfi/HeightMapMCAGenerator.java | 28 +- .../visualization/cfi/WritableMCAChunk.java | 8 +- .../fawe/object/changeset/FaweChangeSet.java | 2 +- .../clipboard/remap/ClipboardRemapper.java | 4 +- .../collection/IterableThreadLocal.java | 22 +- .../fawe/object/collection/MemBlockSet.java | 8 +- .../fawe/object/mask/FaweBlockMatcher.java | 18 +- .../regions/selector/FuzzyRegionSelector.java | 2 +- .../object/schematic/MinecraftStructure.java | 10 +- .../general/plot/FaweLocalBlockQueue.java | 7 +- .../general/plot/FaweSchematicHandler.java | 2 +- .../java/com/boydti/fawe/util/BrushCache.java | 2 +- .../java/com/boydti/fawe/util/IOUtil.java | 31 ++ .../boydti/fawe/wrappers/WorldWrapper.java | 21 + .../java/com/sk89q/worldedit/EditSession.java | 90 +---- .../worldedit/command/SelectionCommands.java | 2 +- .../sk89q/worldedit/entity/BaseEntity.java | 19 + .../worldedit/math/MutableBlockVector3.java | 2 +- .../sk89q/worldedit/math/MutableVector3.java | 4 +- .../worldedit/util/task/LinkedFuture.java | 6 +- .../com/sk89q/worldedit/world/NullWorld.java | 7 + .../java/com/sk89q/worldedit/world/World.java | 7 +- 64 files changed, 1892 insertions(+), 812 deletions(-) create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/FallbackChunkGet.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkCache.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/NullChunkGet.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 061f04902..cbad81464 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 @@ -13,16 +13,11 @@ import com.boydti.fawe.bukkit.util.BukkitTaskMan; import com.boydti.fawe.bukkit.util.ItemUtil; import com.boydti.fawe.bukkit.util.VaultUtil; import com.boydti.fawe.bukkit.util.image.BukkitImageViewer; -import com.boydti.fawe.bukkit.v0.BukkitQueue_0; -import com.boydti.fawe.bukkit.v0.BukkitQueue_All; import com.boydti.fawe.bukkit.v0.ChunkListener_8; import com.boydti.fawe.bukkit.v0.ChunkListener_9; -import com.boydti.fawe.bukkit.v1_13.BukkitQueue_1_13; -import com.boydti.fawe.bukkit.v1_14.BukkitQueue_1_14; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.Jars; import com.boydti.fawe.util.MainUtil; @@ -40,9 +35,11 @@ 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.WorldLoadEvent; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.FileOutputStream; @@ -52,6 +49,9 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.function.Consumer; +import java.util.function.Supplier; public class FaweBukkit implements IFawe, Listener { @@ -279,102 +279,6 @@ public class FaweBukkit implements IFawe, Listener { return new BukkitTaskMan(plugin); } - private boolean hasNMS = true; - private boolean playerChunk = false; - - @Override - public FaweQueue getNewQueue(String world, boolean fast) { - if (playerChunk != (playerChunk = true)) { - try { - Field fieldDirtyCount = BukkitReflectionUtils.getRefClass("{nms}.PlayerChunk").getField("dirtyCount").getRealField(); - fieldDirtyCount.setAccessible(true); - int mod = fieldDirtyCount.getModifiers(); - if ((mod & Modifier.VOLATILE) == 0) { - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(fieldDirtyCount, mod + Modifier.VOLATILE); - } - } catch (Throwable ignore) {} - } - try { - return getQueue(world); - } catch (Throwable throwable) { - // Disable incompatible settings - Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level - Settings.IMP.HISTORY.COMBINE_STAGES = false; // Performing a chunk copy (if possible) wouldn't be faster using the BukkitAPI - if (hasNMS) { - - debug("====== NO NMS BLOCK PLACER FOUND ======"); - debug("FAWE couldn't find a fast block placer"); - debug("Bukkit version: " + Bukkit.getVersion()); - debug("NMS label: " + plugin.getClass().getSimpleName().split("_")[1]); - debug("Fallback placer: " + BukkitQueue_All.class); - debug("======================================="); - debug("Download the version of FAWE for your platform"); - debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target"); - debug("======================================="); - throwable.printStackTrace(); - debug("======================================="); - TaskManager.IMP.laterAsync( - () -> MainUtil.sendAdmin("&cNo NMS placer found, see console!"), 1); - hasNMS = false; - } - return new BukkitQueue_All(world); - } - } - - /** - * The FaweQueue is a core part of block placement
- * - The queue returned here is used in the SetQueue class (SetQueue handles the implementation specific queue)
- * - Block changes are grouped by chunk (as it's more efficient for lighting/packet sending)
- * - The FaweQueue returned here will provide the wrapper around the chunk object (FaweChunk)
- * - When a block change is requested, the SetQueue will first check if the chunk exists in the queue, or it will create and add it
- */ - @Override - public FaweQueue getNewQueue(World world, boolean fast) { - if (fast) { - if (playerChunk != (playerChunk = true)) { - try { - Field fieldDirtyCount = BukkitReflectionUtils.getRefClass("{nms}.PlayerChunk").getField("dirtyCount").getRealField(); - fieldDirtyCount.setAccessible(true); - int mod = fieldDirtyCount.getModifiers(); - if ((mod & Modifier.VOLATILE) == 0) { - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(fieldDirtyCount, mod + Modifier.VOLATILE); - } - } catch (Throwable ignore) { - } - } - Throwable error; - try { - return getQueue(world); - } catch (Throwable throwable) { - error = throwable; - } - // Disable incompatible settings - Settings.IMP.QUEUE.PARALLEL_THREADS = 1; // BukkitAPI placer is too slow to parallel thread at the chunk level - Settings.IMP.HISTORY.COMBINE_STAGES = false; // Performing a chunk copy (if possible) wouldn't be faster using the BukkitAPI - if (hasNMS) { - debug("====== NO NMS BLOCK PLACER FOUND ======"); - debug("FAWE couldn't find a fast block placer"); - debug("Bukkit version: " + Bukkit.getVersion()); - debug("NMS label: " + plugin.getClass().getSimpleName()); - debug("Fallback placer: " + BukkitQueue_All.class); - debug("======================================="); - debug("Download the version of FAWE for your platform"); - debug(" - http://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target"); - debug("======================================="); - error.printStackTrace(); - debug("======================================="); - TaskManager.IMP.laterAsync( - () -> MainUtil.sendAdmin("&cNo NMS placer found, see console!"), 1); - hasNMS = false; - } - } - return new BukkitQueue_All(world); - } - public Plugin getPlugin() { return plugin; } @@ -479,6 +383,25 @@ public class FaweBukkit implements IFawe, Listener { return managers; } + private volatile boolean keepUnloaded; + + @EventHandler(priority = EventPriority.MONITOR) + public void onWorldLoad(WorldLoadEvent event) { + if (keepUnloaded) { + org.bukkit.World world = event.getWorld(); + world.setKeepSpawnInMemory(false); + } + } + + public synchronized T createWorldUnloaded(Supplier task) { + keepUnloaded = true; + try { + return task.get(); + } finally { + keepUnloaded = false; + } + } + @EventHandler(priority = EventPriority.MONITOR) public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); @@ -520,51 +443,4 @@ public class FaweBukkit implements IFawe, Listener { return null; // return ((BlocksHubBukkit) blocksHubPlugin).getApi(); } - - private Version version = null; - - public Version getVersion() { - Version tmp = this.version; - if (tmp == null) { - tmp = Version.NONE; - for (Version v : Version.values()) { - try { - BukkitQueue_0.checkVersion(v.name()); - this.version = tmp = v; - break; - } catch (IllegalStateException ignored) {} - } - } - return tmp; - } - - public enum Version { - v1_14_R1, - v1_13_R2, - NONE, - } - - private FaweQueue getQueue(World world) { - switch (getVersion()) { - case v1_13_R2: - return new BukkitQueue_1_13(world); - case v1_14_R1: - return new BukkitQueue_1_14(world); - default: - case NONE: - return new BukkitQueue_All(world); - } - } - - private FaweQueue getQueue(String world) { - switch (getVersion()) { - case v1_13_R2: - return new BukkitQueue_1_13(world); - case v1_14_R1: - return new BukkitQueue_1_14(world); - default: - case NONE: - return new BukkitQueue_All(world); - } - } } 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 256268ec5..f17897891 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,54 +1,76 @@ package com.boydti.fawe.bukkit.beta; +import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.QueueHandler; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; -import com.boydti.fawe.bukkit.v1_14.BukkitQueue_1_14; import com.boydti.fawe.bukkit.v1_14.adapter.Spigot_v1_14_R1; -import com.boydti.fawe.jnbt.anvil.BitArray4096; -import com.boydti.fawe.util.MemUtil; -import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockTypes; import net.minecraft.server.v1_14_R1.BiomeBase; +import net.minecraft.server.v1_14_R1.BlockPosition; import net.minecraft.server.v1_14_R1.Chunk; -import net.minecraft.server.v1_14_R1.ChunkCoordIntPair; -import net.minecraft.server.v1_14_R1.ChunkProviderServer; import net.minecraft.server.v1_14_R1.ChunkSection; import net.minecraft.server.v1_14_R1.DataBits; import net.minecraft.server.v1_14_R1.DataPalette; import net.minecraft.server.v1_14_R1.DataPaletteBlock; import net.minecraft.server.v1_14_R1.DataPaletteHash; import net.minecraft.server.v1_14_R1.DataPaletteLinear; +import net.minecraft.server.v1_14_R1.Entity; +import net.minecraft.server.v1_14_R1.EntityTypes; import net.minecraft.server.v1_14_R1.IBlockData; -import net.minecraft.server.v1_14_R1.World; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.NBTTagInt; +import net.minecraft.server.v1_14_R1.TileEntity; import net.minecraft.server.v1_14_R1.WorldServer; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; import org.bukkit.craftbukkit.v1_14_R1.block.CraftBlock; +import org.bukkit.event.entity.CreatureSpawnEvent; import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; public class BukkitGetBlocks extends CharGetBlocks { public ChunkSection[] sections; public Chunk nmsChunk; - public World nmsWorld; + public CraftWorld world; public int X, Z; private boolean forceLoad; - public BukkitGetBlocks(World nmsWorld, int X, int Z, boolean forceLoad) { - this.nmsWorld = nmsWorld; + public BukkitGetBlocks(World world, int X, int Z, boolean forceLoad) { + this.world = (CraftWorld) world; this.X = X; this.Z = Z; if (forceLoad) { - ((WorldServer) nmsWorld).setForceLoaded(X, Z, this.forceLoad = true); + this.world.getHandle().setForceLoaded(X, Z, this.forceLoad = true); } } @Override protected void finalize() { if (forceLoad) { - ((WorldServer) nmsWorld).setForceLoaded(X, Z, forceLoad = false); + this.world.getHandle().setForceLoaded(X, Z, forceLoad = false); } } @@ -69,14 +91,310 @@ public class BukkitGetBlocks extends CharGetBlocks { return load(layer, null); } + private void updateGet(BukkitGetBlocks get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + synchronized (get) { + if (this.nmsChunk != nmsChunk) { + this.nmsChunk = nmsChunk; + this.sections = sections.clone(); + this.reset(); + } + if (this.sections == null) { + this.sections = sections.clone(); + } + if (this.sections[layer] != section) { + this.sections[layer] = section; + } + this.blocks[layer] = arr; + } + } + + private void removeEntity(Entity entity) { + entity.die(); + entity.valid = false; + } + + @Override + public > T call(IChunkSet set, Runnable finalizer) { + try { + WorldServer nmsWorld = world.getHandle(); + Chunk nmsChunk = BukkitQueue.ensureLoaded(nmsWorld, 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; + if (!set.hasSection(layer)) { + continue; + } + if (set.getBlock(lx, ly, lz).getOrdinal() != 0) { + TileEntity tile = entry.getValue(); + tile.n(); + tile.invalidateBlockCache(); + } + } + } + } + + int bitMask = 0; + synchronized (nmsChunk) { + ChunkSection[] sections = nmsChunk.getSections(); + + for (int layer = 0; layer < 16; layer++) { + if (!set.hasSection(layer)) continue; + + bitMask |= 1 << layer; + + char[] setArr = set.getArray(layer); + ChunkSection newSection; + ChunkSection existingSection = sections[layer]; + if (existingSection == null) { + newSection = BukkitQueue.newChunkSection(layer, setArr); + if (BukkitQueue.setSectionAtomic(sections, null, newSection, layer)) { + updateGet(this, 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 (this) { + synchronized (lock) { + lock.untilFree(); + ChunkSection getSection; + if (this.nmsChunk != nmsChunk) { + this.nmsChunk = nmsChunk; + this.sections = null; + this.reset(); + } else { + getSection = this.getSections()[layer]; + if (getSection != existingSection) { + this.sections[layer] = existingSection; + this.reset(); + } else if (lock.isModified()) { + this.reset(layer); + } + } + char[] getArr = this.load(layer); + for (int i = 0; i < 4096; i++) { + char value = setArr[i]; + if (value != 0) { + getArr[i] = value; + } + } + newSection = BukkitQueue.newChunkSection(layer, getArr); + if (!BukkitQueue.setSectionAtomic(sections, existingSection, newSection, layer)) { + System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + continue; + } else { + updateGet(this, nmsChunk, sections, newSection, setArr, layer); + } + } + } + } + + // Biomes + BiomeType[] biomes = set.getBiomes(); + if (biomes != null) { + // set biomes + final BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex(); + for (int i = 0; i < biomes.length; i++) { + final BiomeType biome = biomes[i]; + if (biome != null) { + final Biome craftBiome = BukkitAdapter.adapt(biome); + currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome); + } + } + } + + Runnable[] syncTasks = null; + + int bx = X << 4; + int bz = Z << 4; + + Set entityRemoves = set.getEntityRemoves(); + if (entityRemoves != null && !entityRemoves.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[3]; + + 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 (entityRemoves.contains(entity.getUniqueID())) { + iter.remove(); + removeEntity(entity); + } + } + } + } + } + }; + } + + Set entities = set.getEntities(); + if (entities != null && !entities.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[2]; + + syncTasks[1] = new Runnable() { + @Override + public void run() { + for (final CompoundTag nativeTag : entities) { + 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(); + + EntityTypes type = EntityTypes.a(id).orElse(null); + if (type != null) { + Entity entity = type.a(nmsWorld); + if (entity != null) { + UUID uuid = entity.getUniqueID(); + entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); + entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); + if (nativeTag != null) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + final NBTTagCompound tag = (NBTTagCompound) adapter.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 + Map tiles = set.getTiles(); + if (tiles != null && !tiles.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[1]; + + 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 (nmsWorld) { + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeTileEntity(pos); + tileEntity = nmsWorld.getTileEntity(pos); + } + if (tileEntity != null) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + final NBTTagCompound tag = (NBTTagCompound) adapter.fromNative(nativeTag); + tag.set("x", new NBTTagInt(x)); + tag.set("y", new NBTTagInt(y)); + tag.set("z", new NBTTagInt(z)); + tileEntity.load(tag); + } + } + } + } + }; + } + + Runnable callback; + if (bitMask == 0) { + callback = null; + } else { + int finalMask = bitMask; + callback = () -> { + // Set Modified + nmsChunk.d(true); // Set Modified + nmsChunk.mustNotSave = false; + nmsChunk.markDirty(); + // send to player + BukkitQueue.sendChunk(nmsWorld, X, Z, finalMask); + if (finalizer != null) finalizer.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(); + } + } + if (callback == null) { + if (finalizer != null) finalizer.run(); + return null; + } else { + return queueHandler.async(callback, null); + } + } + }; + return (T) (Future) queueHandler.sync(chain); + } else { + if (callback == null) { + if (finalizer != null) finalizer.run(); + } else { + callback.run(); + } + } + } + return null; + } catch (Throwable e) { + e.printStackTrace(); + return null; + } + } + @Override public synchronized char[] load(int layer, char[] data) { ChunkSection section = getSections()[layer]; // Section is null, return empty array if (section == null) { - return FaweCache.EMPTY_CHAR_4096; + return FaweCache.IMP.EMPTY_CHAR_4096; } - if (data == null || data == FaweCache.EMPTY_CHAR_4096) { + if (data == null || data == FaweCache.IMP.EMPTY_CHAR_4096) { data = new char[4096]; } DelegateLock lock = BukkitQueue.applyLock(section); @@ -88,8 +406,8 @@ public class BukkitGetBlocks extends CharGetBlocks { Spigot_v1_14_R1 adapter = ((Spigot_v1_14_R1) WorldEditPlugin.getInstance().getBukkitImplAdapter()); final DataPaletteBlock blocks = section.getBlocks(); - final DataBits bits = (DataBits) BukkitQueue_1_14.fieldBits.get(blocks); - final DataPalette palette = (DataPalette) BukkitQueue_1_14.fieldPalette.get(blocks); + final DataBits bits = (DataBits) BukkitQueue.fieldBits.get(blocks); + final DataPalette palette = (DataPalette) BukkitQueue.fieldPalette.get(blocks); final int bitsPerEntry = bits.c(); final long[] blockStates = bits.a(); @@ -103,8 +421,8 @@ public class BukkitGetBlocks extends CharGetBlocks { num_palette = ((DataPaletteHash) palette).b(); } else { num_palette = 0; - int[] paletteToBlockInts = FaweCache.PALETTE_TO_BLOCK.get(); - char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get(); + int[] paletteToBlockInts = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + char[] paletteToBlockChars = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get(); try { for (int i = 0; i < 4096; i++) { char paletteVal = data[i]; @@ -130,7 +448,7 @@ public class BukkitGetBlocks extends CharGetBlocks { return data; } - char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get(); + char[] paletteToBlockChars = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get(); try { final int size = num_palette; if (size != 1) { @@ -188,7 +506,7 @@ public class BukkitGetBlocks extends CharGetBlocks { synchronized (this) { tmp = nmsChunk; if (tmp == null) { - nmsChunk = tmp = BukkitQueue.ensureLoaded(nmsWorld, X, Z); + nmsChunk = tmp = BukkitQueue.ensureLoaded(this.world.getHandle(), X, Z); } } } 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 a3530e1df..c5f701d28 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 @@ -2,15 +2,18 @@ package com.boydti.fawe.bukkit.beta; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.SimpleCharQueueExtent; -import com.boydti.fawe.beta.implementation.WorldChunkCache; +import com.boydti.fawe.beta.implementation.IChunkCache; import com.boydti.fawe.bukkit.v1_14.adapter.BlockMaterial_1_14; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BitArray4096; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.wrappers.WorldWrapper; import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; @@ -42,30 +45,19 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.Lock; import java.util.function.Supplier; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull; public class BukkitQueue extends SimpleCharQueueExtent { - private org.bukkit.World bukkitWorld; private WorldServer nmsWorld; - @Override - public void enableQueue() { - + public BukkitQueue() { } @Override - public void disableQueue() { - - } - - @Override - public synchronized void init(WorldChunkCache cache) { - World world = cache.getWorld(); + public synchronized void init(Extent extent, IChunkCache get, IChunkCache set) { + World world = WorldWrapper.unwrap(extent); + if (world == null) throw new IllegalArgumentException("Get must be a world."); if (world instanceof BukkitWorld) { this.bukkitWorld = ((BukkitWorld) world).getWorld(); } else { @@ -74,15 +66,7 @@ public class BukkitQueue extends SimpleCharQueueExtent { 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; + super.init(extent, get, set); } @Override @@ -90,22 +74,6 @@ public class BukkitQueue extends SimpleCharQueueExtent { super.reset(); } -// private static final IterableThreadLocal FULL_CHUNKS = new IterableThreadLocal() { -// @Override -// public BukkitFullChunk init() { -// return new BukkitFullChunk(); -// } -// }; - - @Override - public IChunk create(boolean isFull) { -// if (full) { -// //TODO implement -// return FULL_CHUNKS.get(); -// } - return new BukkitChunkHolder(); - } - /* NMS fields */ @@ -235,7 +203,7 @@ public class BukkitQueue extends SimpleCharQueueExtent { return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z)); } - private PlayerChunk getPlayerChunk(final int cx, final int cz) { + private static PlayerChunk getPlayerChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, final int cx, final int cz) { PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap; PlayerChunk playerChunk = chunkMap.visibleChunks.get(ChunkCoordIntPair.pair(cx, cz)); if (playerChunk == null) { @@ -244,9 +212,8 @@ public class BukkitQueue extends SimpleCharQueueExtent { return playerChunk; } - @Override - public void sendChunk(final int X, final int Z, final int mask) { - PlayerChunk playerChunk = getPlayerChunk(X, Z); + public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask) { + PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z); if (playerChunk == null) { return; } @@ -256,7 +223,7 @@ public class BukkitQueue extends SimpleCharQueueExtent { // sections[layer] = new ChunkSection(layer << 4); // } // } - if (playerChunk.k()) { + if (playerChunk.hasBeenLoaded()) { TaskManager.IMP.sync(new Supplier() { @Override public Object get() { @@ -284,6 +251,11 @@ public class BukkitQueue extends SimpleCharQueueExtent { return; } + @Override + public void sendChunk(final int X, final int Z, final int mask) { + sendChunk(nmsWorld, X, Z, mask); + } + /* NMS conversion */ @@ -293,10 +265,10 @@ public class BukkitQueue extends SimpleCharQueueExtent { if (blocks == null) { return section; } - final int[] blockToPalette = FaweCache.BLOCK_TO_PALETTE.get(); - final int[] paletteToBlock = FaweCache.PALETTE_TO_BLOCK.get(); - final long[] blockStates = FaweCache.BLOCK_STATES.get(); - final int[] blocksCopy = FaweCache.SECTION_BLOCKS.get(); + final int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get(); + final int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + final long[] blockStates = FaweCache.IMP.BLOCK_STATES.get(); + final int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get(); try { int num_palette = 0; int air = 0; 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 index 00528d29a..fbace1507 100644 --- 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 @@ -1,11 +1,78 @@ package com.boydti.fawe.bukkit.beta; +import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.bukkit.v0.ChunkListener; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; public class BukkitQueueHandler extends QueueHandler { @Override public IQueueExtent create() { return new BukkitQueue(); } -} + + private volatile boolean timingsEnabled; + private static boolean alertTimingsChange = true; + + private static Field fieldTimingsEnabled; + private static Field fieldAsyncCatcherEnabled; + private static Method methodCheck; + static { + try { + fieldAsyncCatcherEnabled = Class.forName("org.spigotmc.AsyncCatcher").getField("enabled"); + fieldAsyncCatcherEnabled.setAccessible(true); + } catch (Throwable ignore) {} + try { + fieldTimingsEnabled = Class.forName("co.aikar.timings.Timings").getDeclaredField("timingsEnabled"); + fieldTimingsEnabled.setAccessible(true); + methodCheck = Class.forName("co.aikar.timings.TimingsManager").getDeclaredMethod("recheckEnabled"); + methodCheck.setAccessible(true); + } catch (Throwable ignore){} + } + + @Override + public void startSet(boolean parallel) { + ChunkListener.physicsFreeze = true; + if (parallel) { + try { + if (fieldAsyncCatcherEnabled != null) { + fieldAsyncCatcherEnabled.set(null, false); + } + if (fieldTimingsEnabled != null) { + timingsEnabled = (boolean) fieldTimingsEnabled.get(null); + if (timingsEnabled) { + if (alertTimingsChange) { + alertTimingsChange = false; + Fawe.debug("Having `parallel-threads` > 1 interferes with the timings."); + } + fieldTimingsEnabled.set(null, false); + methodCheck.invoke(null); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void endSet(boolean parallel) { + ChunkListener.physicsFreeze = false; + if (parallel) { + try { + if (fieldAsyncCatcherEnabled != null) { + fieldAsyncCatcherEnabled.set(null, true); + } + if (fieldTimingsEnabled != null && timingsEnabled) { + fieldTimingsEnabled.set(null, true); + methodCheck.invoke(null); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java index 44df8facb..7051dd1a8 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java @@ -1,11 +1,11 @@ package com.boydti.fawe.bukkit.listener; +import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.bukkit.util.image.BukkitImageViewer; import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.brush.BrushSettings; -import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.TaskManager; @@ -16,6 +16,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.InvalidToolBindException; import com.sk89q.worldedit.command.tool.brush.Brush; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -286,7 +287,8 @@ public class BukkitImageListener implements Listener { .combineStages(false).autoQueue(false).blockBag(null).limitUnlimited() .build(); ExtentTraverser last = new ExtentTraverser(es.getExtent()).last(); - if (last.get() instanceof FastWorldEditExtent) { + Extent extent = last.get(); + if (extent instanceof IQueueExtent) { last = last.previous(); } last.setNext(generator); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java index e1a1b36b9..1fc42dde9 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/CFIPacketListener.java @@ -1,12 +1,9 @@ package com.boydti.fawe.bukkit.listener; import com.boydti.fawe.command.CFICommands; -import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.RunnableVal3; import com.boydti.fawe.object.brush.visualization.VirtualWorld; -import com.boydti.fawe.util.SetQueue; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolManager; @@ -58,98 +55,99 @@ public class CFIPacketListener implements Listener { this.plugin = plugin; this.protocolmanager = ProtocolLibrary.getProtocolManager(); - // Direct digging to the virtual world - registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3() { - @Override - public void run(Builder event, URI gen, String pt) { - try { - Player plr = event.getPlayer(); - BlockVector3 realPos = pt.add(gen.getOrigin().toBlockPoint()); - if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) { - gen.setBlock(pt, BlockTypes.AIR.getDefaultState()); - } - } catch (WorldEditException e) { - e.printStackTrace(); - } - } - }); - - // Direct placing to the virtual world - RunnableVal3 placeTask = new RunnableVal3() { - @Override - public void run(Builder event, URI gen, String pt) { - try { - Player plr = event.getPlayer(); - List hands = event.getPacket().getHands().getValues(); - - EnumWrappers.Hand enumHand = hands.isEmpty() ? EnumWrappers.Hand.MAIN_HAND : hands.get(0); - PlayerInventory inv = plr.getInventory(); - ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand(); - if (hand.getType().isBlock()) { - Material type = hand.getType(); - switch (type) { - case AIR: - case CAVE_AIR: - case VOID_AIR: - break; - default: { - BlockStateHolder block = BukkitAdapter.asBlockState(hand); - if (block != null) { - gen.setBlock(pt, block); - return; - } - } - } - } - pt = getRelPos(event, gen); - sendBlockChange(plr, gen, pt, Interaction.OPEN); - } catch (WorldEditException e) { - e.printStackTrace(); - } - } - }; - registerBlockEvent(PacketType.Play.Client.BLOCK_PLACE, true, placeTask); - registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask); - - // Cancel block change packets where the real world overlaps with the virtual one - registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3() { - @Override - public void run(Builder event, URI gen, String pt) { - // Do nothing - } - }); - - // Modify chunk packets where the real world overlaps with the virtual one - protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) { - @Override - public void onPacketSending(PacketEvent event) { - if (!event.isServerPacket()) return; - - VirtualWorld gen = getGenerator(event); - if (gen != null) { - BlockVector3 origin = gen.getOrigin().toBlockPoint(); - PacketContainer packet = event.getPacket(); - StructureModifier ints = packet.getIntegers(); - int cx = ints.read(0); - int cz = ints.read(1); - - int ocx = origin.getBlockX() >> 4; - int ocz = origin.getBlockZ() >> 4; - - if (gen.contains(BlockVector3.at((cx - ocx) << 4, 0, (cz - ocz) << 4))) { - event.setCancelled(true); - - Player plr = event.getPlayer(); - - FaweQueue queue = SetQueue.IMP.getNewQueue(plr.getWorld().getName(), true, false); - - FaweChunk toSend = gen.getSnapshot(cx - ocx, cz - ocz); - toSend.setLoc(gen, cx, cz); - queue.sendChunkUpdate(toSend, FawePlayer.wrap(plr)); - } - } - } - }); + // TODO NOT IMPLEMENTED +// // Direct digging to the virtual world +// registerBlockEvent(PacketType.Play.Client.BLOCK_DIG, false, new RunnableVal3() { +// @Override +// public void run(Builder event, URI gen, String pt) { +// try { +// Player plr = event.getPlayer(); +// BlockVector3 realPos = pt.add(gen.getOrigin().toBlockPoint()); +// if (!sendBlockChange(plr, gen, pt, Interaction.HIT)) { +// gen.setBlock(pt, BlockTypes.AIR.getDefaultState()); +// } +// } catch (WorldEditException e) { +// e.printStackTrace(); +// } +// } +// }); +// +// // Direct placing to the virtual world +// RunnableVal3 placeTask = new RunnableVal3() { +// @Override +// public void run(Builder event, URI gen, String pt) { +// try { +// Player plr = event.getPlayer(); +// List hands = event.getPacket().getHands().getValues(); +// +// EnumWrappers.Hand enumHand = hands.isEmpty() ? EnumWrappers.Hand.MAIN_HAND : hands.get(0); +// PlayerInventory inv = plr.getInventory(); +// ItemStack hand = enumHand == EnumWrappers.Hand.MAIN_HAND ? inv.getItemInMainHand() : inv.getItemInOffHand(); +// if (hand.getType().isBlock()) { +// Material type = hand.getType(); +// switch (type) { +// case AIR: +// case CAVE_AIR: +// case VOID_AIR: +// break; +// default: { +// BlockStateHolder block = BukkitAdapter.asBlockState(hand); +// if (block != null) { +// gen.setBlock(pt, block); +// return; +// } +// } +// } +// } +// pt = getRelPos(event, gen); +// sendBlockChange(plr, gen, pt, Interaction.OPEN); +// } catch (WorldEditException e) { +// e.printStackTrace(); +// } +// } +// }; +// registerBlockEvent(PacketType.Play.Client.BLOCK_PLACE, true, placeTask); +// registerBlockEvent(PacketType.Play.Client.USE_ITEM, true, placeTask); +// +// // Cancel block change packets where the real world overlaps with the virtual one +// registerBlockEvent(PacketType.Play.Server.BLOCK_CHANGE, false, new RunnableVal3() { +// @Override +// public void run(Builder event, URI gen, String pt) { +// // Do nothing +// } +// }); +// +// // Modify chunk packets where the real world overlaps with the virtual one +// protocolmanager.addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.MAP_CHUNK) { +// @Override +// public void onPacketSending(PacketEvent event) { +// if (!event.isServerPacket()) return; +// +// VirtualWorld gen = getGenerator(event); +// if (gen != null) { +// BlockVector3 origin = gen.getOrigin().toBlockPoint(); +// PacketContainer packet = event.getPacket(); +// StructureModifier ints = packet.getIntegers(); +// int cx = ints.read(0); +// int cz = ints.read(1); +// +// int ocx = origin.getBlockX() >> 4; +// int ocz = origin.getBlockZ() >> 4; +// +// if (gen.contains(BlockVector3.at((cx - ocx) << 4, 0, (cz - ocz) << 4))) { +// event.setCancelled(true); +// +// Player plr = event.getPlayer(); +// +// FaweQueue queue = SetQueue.IMP.getNewQueue(plr.getWorld().getName(), true, false); +// +// FaweChunk toSend = gen.getSnapshot(cx - ocx, cz - ocz); +// toSend.setLoc(gen, cx, cz); +// queue.sendChunkUpdate(toSend, FawePlayer.wrap(plr)); +// } +// } +// } +// }); // The following few listeners are to ignore block collisions where the virtual and real world overlap diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/FreeBuildRegion.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/FreeBuildRegion.java index 3a4c1268d..0e07ae3f8 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/FreeBuildRegion.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/FreeBuildRegion.java @@ -3,16 +3,12 @@ package com.boydti.fawe.bukkit.regions; import com.boydti.fawe.bukkit.wrapper.AsyncBlock; import com.boydti.fawe.bukkit.wrapper.AsyncWorld; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.queue.NullFaweQueue; import com.boydti.fawe.regions.FaweMask; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.world.block.BlockTypes; -import jdk.nashorn.internal.ir.Block; import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.EventException; @@ -21,7 +17,6 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.plugin.RegisteredListener; import java.util.ArrayList; -import org.bukkit.util.BlockVector; public class FreeBuildRegion extends BukkitMaskManager { private final ArrayList listeners; @@ -60,7 +55,7 @@ public class FreeBuildRegion extends BukkitMaskManager { BlockVector3 pos1 = BlockVector3.ZERO; BlockVector3 pos2 = BlockVector3.ZERO; - AsyncBlock block = new AsyncBlock(asyncWorld, new NullFaweQueue(asyncWorld.getWorldName(), BlockTypes.STONE.getDefaultState()), 0, 0, 0); + AsyncBlock block = new AsyncBlock(asyncWorld, 0, 0, 0); BlockBreakEvent event = new BlockBreakEvent(block, BukkitAdapter.adapt(player.toWorldEditPlayer())); return new FaweMask(pos1, pos2) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/ItemUtil.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/ItemUtil.java index c06d53497..e2542ae8a 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/ItemUtil.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/ItemUtil.java @@ -1,10 +1,14 @@ package com.boydti.fawe.bukkit.util; -import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.bukkit.beta.BukkitQueue; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.bukkit.inventory.ItemStack; @@ -13,6 +17,8 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.Method; +import static com.google.gson.internal.$Gson$Preconditions.checkNotNull; + public class ItemUtil { private final Method methodAsNMSCopy; @@ -21,10 +27,13 @@ public class ItemUtil { private final Method methodSetTag; private final Method methodAsBukkitCopy; private final Field fieldHandle; + private final BukkitImplAdapter adapter; private SoftReference>> hashToNMSTag = new SoftReference(new Int2ObjectOpenHashMap<>()); public ItemUtil() throws Exception { + this.adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + checkNotNull(adapter); Class classCraftItemStack = BukkitReflectionUtils.getCbClass("inventory.CraftItemStack"); Class classNMSItem = BukkitReflectionUtils.getNmsClass("ItemStack"); this.methodAsNMSCopy = ReflectionUtils.setAccessible(classCraftItemStack.getDeclaredMethod("asNMSCopy", ItemStack.class)); @@ -68,7 +77,7 @@ public class ItemUtil { Tag nativeTag = nativeTagRef.get(); if (nativeTag != null) return (CompoundTag) nativeTag; } - Tag nativeTag = BukkitQueue_0.toNative(nmsTag); + Tag nativeTag = adapter.toNative(nmsTag); map.put(nmsTag.hashCode(), new WeakReference<>(nativeTag)); return null; } @@ -86,7 +95,7 @@ public class ItemUtil { copy = true; nmsItem = methodAsNMSCopy.invoke(null, item); } - Object nmsTag = BukkitQueue_0.fromNative(tag); + Object nmsTag = adapter.fromNative(tag); methodSetTag.invoke(nmsItem, nmsTag); if (copy) return (ItemStack) methodAsBukkitCopy.invoke(null, nmsItem); return item; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/FaweAdapter_All.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/FaweAdapter_All.java index 05b1ad04c..d4aa283d1 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/FaweAdapter_All.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v0/FaweAdapter_All.java @@ -382,7 +382,7 @@ // int z = location.getBlockZ(); // // org.bukkit.block.Block bukkitBlock = location.getBlock(); -// BaseBlock block = FaweCache.getBlock(bukkitBlock.getTypeId(), bukkitBlock.getData()); +// BaseBlock block = FaweCache.IMP.getBlock(bukkitBlock.getTypeId(), bukkitBlock.getData()); // // // Read the NBT data // Object nmsWorld = getHandleWorld.invoke(craftWorld); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_14/adapter/Spigot_v1_14_R1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_14/adapter/Spigot_v1_14_R1.java index a636b78b1..6fb99229e 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_14/adapter/Spigot_v1_14_R1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/v1_14/adapter/Spigot_v1_14_R1.java @@ -51,6 +51,7 @@ import com.sk89q.worldedit.registry.state.EnumProperty; import com.sk89q.worldedit.registry.state.IntegerProperty; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -104,6 +105,7 @@ import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_14_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.util.CraftMagicNumbers; import org.bukkit.entity.Player; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.slf4j.Logger; @@ -250,6 +252,21 @@ public final class Spigot_v1_14_R1 extends CachedBukkitAdapter implements Bukkit // Code that is less likely to break // ------------------------------------------------------------------------ + @Override + public int getDataVersion() { + return CraftMagicNumbers.INSTANCE.getDataVersion(); + } + + @Override + public DataFixer getDataFixer() { + try { + Class converter = Class.forName("com.sk89q.worldedit.bukkit.adapter.impl.DataConverters_1_14_R4"); + return (DataFixer) converter.getDeclaredField("INSTANCE").get(null); + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + @SuppressWarnings("deprecation") @Override public BaseBlock getBlock(Location location) { @@ -409,15 +426,15 @@ public final class Spigot_v1_14_R1 extends CachedBukkitAdapter implements Bukkit for (IBlockState state : blockStateList.d()) { Property property; if (state instanceof BlockStateBoolean) { - property = new BooleanProperty(state.a(), ImmutableList.copyOf(state.d())); + property = new BooleanProperty(state.a(), ImmutableList.copyOf(state.getValues())); } else if (state instanceof BlockStateDirection) { property = new DirectionalProperty(state.a(), - (List) state.d().stream().map(e -> Direction.valueOf(((INamable) e).getName().toUpperCase())).collect(Collectors.toList())); + (List) state.getValues().stream().map(e -> Direction.valueOf(((INamable) e).getName().toUpperCase())).collect(Collectors.toList())); } else if (state instanceof BlockStateEnum) { property = new EnumProperty(state.a(), - (List) state.d().stream().map(e -> ((INamable) e).getName()).collect(Collectors.toList())); + (List) state.getValues().stream().map(e -> ((INamable) e).getName()).collect(Collectors.toList())); } else if (state instanceof BlockStateInteger) { - property = new IntegerProperty(state.a(), ImmutableList.copyOf(state.d())); + property = new IntegerProperty(state.a(), ImmutableList.copyOf(state.getValues())); } else { throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName()); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java index 61ec93e2d..ab8802f98 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java @@ -1,13 +1,14 @@ package com.boydti.fawe.bukkit.wrapper; import com.boydti.fawe.bukkit.wrapper.state.AsyncSign; -import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.util.TaskManager; import com.destroystokyo.paper.block.BlockSoundGroup; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockID; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import org.bukkit.FluidCollisionMode; @@ -34,12 +35,10 @@ public class AsyncBlock implements Block { public int z; public int y; public int x; - public final FaweQueue queue; public final AsyncWorld world; - public AsyncBlock(AsyncWorld world, FaweQueue queue, int x, int y, int z) { + public AsyncBlock(AsyncWorld world, int x, int y, int z) { this.world = world; - this.queue = queue; this.x = x; this.y = Math.max(0, Math.min(255, y)); this.z = z; @@ -54,25 +53,24 @@ public class AsyncBlock implements Block { @Override @Deprecated public byte getData() { - return (byte) (queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId()) & 0xF); + return (byte) getPropertyId(); } public int getPropertyId() { - return (queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId()) >> BlockTypes.BIT_OFFSET); + return world.getBlock(x, y, z).getInternalId() >> BlockTypes.BIT_OFFSET; } public int getCombinedId() { - return queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId()); + return world.getBlock(x, y, z).getInternalId(); } public int getTypeId() { - int id = (queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId())); - return BlockTypes.getFromStateId(id).getInternalId(); + return world.getBlock(x, y, z).getBlockType().getInternalId(); } @NotNull @Override public AsyncBlock getRelative(int modX, int modY, int modZ) { - return new AsyncBlock(world, queue, x + modX, y + modY, z + modZ); + return new AsyncBlock(world, x + modX, y + modY, z + modZ); } @NotNull @Override @@ -92,7 +90,7 @@ public class AsyncBlock implements Block { @NotNull @Override public BlockData getBlockData() { - return BukkitAdapter.getBlockData(queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId())); + return BukkitAdapter.adapt(world.getBlock(x, y, z)); } @Deprecated @@ -102,7 +100,7 @@ public class AsyncBlock implements Block { @Deprecated public boolean setCombinedId(int combinedId) { - return queue.setBlock(x, y, z, combinedId); + return world.setBlock(x, y, z, BlockState.getFromInternalId(combinedId)); } @Deprecated @@ -112,7 +110,7 @@ public class AsyncBlock implements Block { @Deprecated public boolean setTypeId(int typeId) { - return queue.setBlock(x, y, z, BlockTypes.get(typeId).getDefaultState()); + return world.setBlock(x, y, z, BlockTypes.get(typeId).getDefaultState()); } @Deprecated @@ -122,17 +120,17 @@ public class AsyncBlock implements Block { @Override public byte getLightLevel() { - return (byte) queue.getLight(x, y, z); + return (byte) world.getLight(x, y, z); } @Override public byte getLightFromSky() { - return (byte) queue.getSkyLight(x, y, z); + return (byte) world.getSkyLight(x, y, z); } @Override public byte getLightFromBlocks() { - return (byte) queue.getEmmittedLight(x, y, z); + return (byte) world.getBlockLight(x, y, z); } @NotNull @Override @@ -179,7 +177,7 @@ public class AsyncBlock implements Block { @Override public void setBlockData(@NotNull BlockData blockData) { try { - queue.setBlock(x, y, z, BukkitAdapter.adapt(blockData)); + world.setBlock(x, y, z, BukkitAdapter.adapt(blockData)); } catch (WorldEditException e) { throw new RuntimeException(e); } @@ -193,7 +191,7 @@ public class AsyncBlock implements Block { @Override public void setType(@NotNull Material type) { try { - queue.setBlock(x, y, z, BukkitAdapter.adapt(type).getDefaultState()); + world.setBlock(x, y, z, BukkitAdapter.adapt(type).getDefaultState()); } catch (WorldEditException e) { throw new RuntimeException(e); } @@ -219,9 +217,8 @@ public class AsyncBlock implements Block { @NotNull @Override public AsyncBlockState getState() { - int combined = queue.getCombinedId4Data(x, y, z, 0); - BlockType type = BlockTypes.getFromStateId(combined); - switch (type.getInternalId()) { + BaseBlock state = world.getFullBlock(x, y, z); + switch (state.getBlockType().getInternalId()) { case BlockID.ACACIA_SIGN: case BlockID.SPRUCE_SIGN: case BlockID.ACACIA_WALL_SIGN: @@ -234,9 +231,9 @@ public class AsyncBlock implements Block { case BlockID.JUNGLE_WALL_SIGN: case BlockID.OAK_SIGN: case BlockID.OAK_WALL_SIGN: - return new AsyncSign(this, combined); + return new AsyncSign(this, state); default: - return new AsyncBlockState(this, combined); + return new AsyncBlockState(this, state); } } @@ -248,13 +245,13 @@ public class AsyncBlock implements Block { @NotNull @Override public Biome getBiome() { - return world.getAdapter().adapt(queue.getBiomeType(x, z)); + return world.getAdapter().adapt(world.getBiomeType(x, z)); } @Override public void setBiome(@NotNull Biome bio) { BiomeType biome = world.getAdapter().adapt(bio); - queue.setBiome(x, z, biome); + world.setBiome(x, 0, z, biome); } @Override @@ -301,9 +298,7 @@ public class AsyncBlock implements Block { @Override public boolean isLiquid() { - int combined = queue.getCombinedId4Data(x, y, z, 0); - BlockType type = BlockTypes.getFromStateId(combined); - return type.getMaterial().isLiquid(); + return world.getBlock(x, y, z).getMaterial().isLiquid(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java index 39b469c29..a70360373 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlockState.java @@ -6,6 +6,8 @@ import java.util.List; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import org.bukkit.Chunk; import org.bukkit.Location; @@ -20,30 +22,26 @@ import org.bukkit.plugin.Plugin; public class AsyncBlockState implements BlockState { - private int combinedId; + private BaseBlock state; private BlockData blockData; - private CompoundTag nbt; private final AsyncBlock block; public AsyncBlockState(AsyncBlock block) { - this(block, block.queue.getCombinedId4Data(block.x, block.y, block.z, 0)); + this(block, block.world.getFullBlock(block.x, block.y, block.z)); } - public AsyncBlockState(AsyncBlock block, int combined) { - this.combinedId = combined; + public AsyncBlockState(AsyncBlock block, BaseBlock state) { + this.state = state; this.block = block; - this.blockData = BukkitAdapter.getBlockData(combined); - if (BlockTypes.getFromStateId(combined).getMaterial().hasContainer()) { - this.nbt = block.queue.getTileEntity(block.x, block.y, block.z); - } + this.blockData = BukkitAdapter.adapt(state); } public int getTypeId() { - return BlockTypes.getFromStateId(combinedId).getInternalId(); + return state.getBlockType().getInternalId(); } public int getPropertyId() { - return combinedId >> BlockTypes.BIT_OFFSET; + return state.getInternalId() >> BlockTypes.BIT_OFFSET; } @Override @@ -68,7 +66,7 @@ public class AsyncBlockState implements BlockState { @Override public byte getLightLevel() { - return (byte) BlockTypes.getFromStateId(combinedId).getMaterial().getLightValue(); + return (byte) state.getMaterial().getLightValue(); } @Override @@ -114,7 +112,14 @@ public class AsyncBlockState implements BlockState { @Override public void setBlockData(BlockData blockData) { this.blockData = blockData; - this.combinedId = BukkitAdapter.adapt(blockData).getInternalId(); + CompoundTag nbt = state.getNbtData(); + BlockType oldType = state.getBlockType(); + com.sk89q.worldedit.world.block.BlockState newState = BukkitAdapter.adapt(blockData); + if (nbt != null && newState.getBlockType() == oldType) { + state = newState.toBaseBlock(nbt); + } else { + state = newState.toBaseBlock(); + } } @Override @@ -135,33 +140,30 @@ public class AsyncBlockState implements BlockState { @Override public boolean update(boolean force, boolean applyPhysics) { try { - boolean result = block.queue.setBlock(block.x, block.y, block.z, BukkitAdapter.adapt(blockData)); - if (nbt != null) { - block.queue.setTile(block.x, block.y, block.z, nbt); - } - return result; + return block.world.setBlock(block.x, block.y, block.z, state); } catch (WorldEditException e) { throw new RuntimeException(e); } } public CompoundTag getNbtData() { - return nbt; + return state.getNbtData(); } public void setNbtData(CompoundTag nbt) { - this.nbt = nbt; + state = this.state.toBaseBlock(nbt); } @Override public byte getRawData() { - return (byte) (combinedId >> BlockTypes.BIT_OFFSET); + return (byte) (state.getInternalId() >> BlockTypes.BIT_OFFSET); } @Override public void setRawData(byte data) { - this.combinedId = getTypeId() + (data << BlockTypes.BIT_OFFSET); - this.blockData = BukkitAdapter.getBlockData(this.combinedId); + int combinedId = getTypeId() + (data << BlockTypes.BIT_OFFSET); + state = com.sk89q.worldedit.world.block.BlockState.getFromInternalId(combinedId).toBaseBlock(state.getNbtData()); + this.blockData = BukkitAdapter.adapt(state); } @Override diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncChunk.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncChunk.java index 1e1baed2c..c7bcbfc1a 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncChunk.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncChunk.java @@ -1,8 +1,6 @@ package com.boydti.fawe.bukkit.wrapper; import com.boydti.fawe.Fawe; -import com.boydti.fawe.bukkit.v0.BukkitQueue_0; -import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.TaskManager; @@ -21,11 +19,9 @@ public class AsyncChunk implements Chunk { private final AsyncWorld world; private final int z; private final int x; - private final FaweQueue queue; - public AsyncChunk(World world, FaweQueue queue, int x, int z) { + public AsyncChunk(World world, int x, int z) { this.world = world instanceof AsyncWorld ? (AsyncWorld) world : new AsyncWorld(world, true); - this.queue = queue; this.x = x; this.z = z; } @@ -61,7 +57,7 @@ public class AsyncChunk implements Chunk { @Override public AsyncBlock getBlock(int x, int y, int z) { - return new AsyncBlock(world, queue, (this.x << 4) + x, y, (this.z << 4) + z); + return new AsyncBlock(world, (this.x << 4) + x, y, (this.z << 4) + z); } @Override @@ -87,8 +83,7 @@ public class AsyncChunk implements Chunk { task.run(); return task.value; } - if (queue instanceof BukkitQueue_0) { - BukkitQueue_0 bq = (BukkitQueue_0) queue; + if (world.isWorld()) { if (world.isChunkLoaded(x, z)) { if (world.isChunkLoaded(x, z)) { task.run(); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 51562304a..0a0e9d617 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -1,32 +1,36 @@ package com.boydti.fawe.bukkit.wrapper; -import com.bekvon.bukkit.residence.commands.material; import com.boydti.fawe.FaweAPI; -import com.boydti.fawe.bukkit.v0.BukkitQueue_0; -import com.boydti.fawe.object.FaweQueue; -import com.boydti.fawe.object.HasFaweQueue; import com.boydti.fawe.object.RunnableVal; -import com.boydti.fawe.object.queue.DelegateFaweQueue; -import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.PassthroughExtent; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.world.biome.BiomeType; -import java.io.File; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; -import org.bukkit.*; +import com.sk89q.worldedit.world.block.BlockState; +import org.bukkit.BlockChangeDelegate; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Difficulty; +import org.bukkit.Effect; +import org.bukkit.FluidCollisionMode; +import org.bukkit.GameRule; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.StructureType; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.WorldBorder; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; @@ -50,6 +54,17 @@ import org.bukkit.util.Consumer; import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.function.Supplier; /** * Modify the world from an async thread
@@ -60,10 +75,9 @@ import org.jetbrains.annotations.NotNull; * @see #wrap(World) * @see #create(WorldCreator) */ -public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue { +public class AsyncWorld extends PassthroughExtent implements World { private World parent; - private FaweQueue queue; private BukkitImplAdapter adapter; @Override @@ -78,7 +92,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue */ @Deprecated public AsyncWorld(World parent, boolean autoQueue) { - this(parent, FaweAPI.createQueue(parent.getName(), autoQueue)); + this(parent, FaweAPI.createQueue(new BukkitWorld(parent), autoQueue)); } public AsyncWorld(String world, boolean autoQueue) { @@ -91,19 +105,10 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue * @param queue */ @Deprecated - public AsyncWorld(World parent, FaweQueue queue) { - super(queue); + public AsyncWorld(World parent, Extent extent) { + super(extent); this.parent = parent; - this.queue = queue; - if (queue instanceof BukkitQueue_0) { - this.adapter = BukkitQueue_0.getAdapter(); - } else { - try { - this.adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - } catch (Throwable e) { - e.printStackTrace(); - } - } + this.adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); } /** @@ -118,32 +123,15 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue return new AsyncWorld(world, false); } - public void changeWorld(World world, FaweQueue queue) { - this.parent = world; - if (queue != this.queue) { - if (this.queue != null) { - final FaweQueue oldQueue = this.queue; - TaskManager.IMP.async(oldQueue::flush); - } - this.queue = queue; - } - setParent(queue); - } - @Override public String toString() { - return super.toString() + ":" + queue.toString(); + return getName(); } public World getBukkitWorld() { return parent; } - @Override - public FaweQueue getQueue() { - return queue; - } - /** * Create a world async (untested) * - Only optimized for 1.10 @@ -151,8 +139,8 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue * @return */ public synchronized static AsyncWorld create(final WorldCreator creator) { - BukkitQueue_0 queue = (BukkitQueue_0) SetQueue.IMP.getNewQueue(creator.name(), true, false); - World world = queue.createWorld(creator); + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + @Nullable World world = adapter.createWorld(creator); return wrap(world); } @@ -163,9 +151,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue } public void flush() { - if (queue != null) { - queue.flush(); - } + getExtent().commit(); } @Override @@ -240,7 +226,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue @Override public AsyncBlock getBlockAt(final int x, final int y, final int z) { - return new AsyncBlock(this, queue, x, y, z); + return new AsyncBlock(this, x, y, z); } @Override @@ -251,9 +237,8 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue @Override public int getHighestBlockYAt(int x, int z) { for (int y = getMaxHeight() - 1; y >= 0; y--) { - int stateId = queue.getCachedCombinedId4Data(x, y, z, BlockTypes.AIR.getInternalId()); - BlockType type = BlockTypes.getFromStateId(stateId); - if (!type.getMaterial().isAir()) return y; + BlockState state = this.getBlock(x, y, z); + if (!state.getMaterial().isAir()) return y; } return 0; } @@ -276,7 +261,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue @Override public AsyncChunk getChunkAt(int x, int z) { - return new AsyncChunk(this, queue, x, z); + return new AsyncChunk(this, x, z); } @Override @@ -422,8 +407,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue @Override @Deprecated public boolean refreshChunk(int x, int z) { - queue.sendChunk(queue.getFaweChunk(x, z)); - return true; + return parent.refreshChunk(x, z); } @Override @@ -825,13 +809,13 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue @Override public Biome getBiome(int x, int z) { - return adapter.adapt(queue.getBiomeType(x, z)); + return adapter.adapt(getExtent().getBiomeType(x, z)); } @Override public void setBiome(int x, int z, Biome bio) { BiomeType biome = adapter.adapt(bio); - queue.setBiome(x, z, biome); + getExtent().setBiome(x, 0, z, biome); } @Override @@ -1114,6 +1098,11 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue return parent.locateNearestStructure(arg0, arg1, arg2, arg3); } + @Override + public int getViewDistance() { + return parent.getViewDistance(); + } + @Override public RayTraceResult rayTrace(Location arg0, Vector arg1, double arg2, FluidCollisionMode arg3, boolean arg4, double arg5, Predicate arg6) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java index f5807c7f4..e2bc8ce64 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncDataContainer.java @@ -54,7 +54,7 @@ public final class AsyncDataContainer implements PersistentDataContainer { Validate.notNull(key, "The provided key for the custom value was null"); Validate.notNull(type, "The provided type for the custom value was null"); Validate.notNull(value, "The provided value for the custom value was null"); - get().put(key.toString(), FaweCache.asTag(type.toPrimitive(value, null))); + get().put(key.toString(), FaweCache.IMP.asTag(type.toPrimitive(value, null))); } public boolean has(NamespacedKey key, PersistentDataType type) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java index 3e7b72fd3..b6e073bee 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/state/AsyncSign.java @@ -10,6 +10,8 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer; import com.sk89q.worldedit.util.formatting.text.serializer.legacy.LegacyComponentSerializer; import java.util.Map; + +import com.sk89q.worldedit.world.block.BaseBlock; import org.bukkit.DyeColor; import org.bukkit.block.Sign; import org.bukkit.persistence.PersistentDataContainer; @@ -17,8 +19,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class AsyncSign extends AsyncBlockState implements Sign { - public AsyncSign(AsyncBlock block, int combined) { - super(block, combined); + public AsyncSign(AsyncBlock block, BaseBlock state) { + super(block, state); } private boolean isEditable = false; diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java index 034f01bcd..ec1d484f9 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java @@ -330,10 +330,6 @@ public enum BukkitAdapter { return getAdapter().adapt(block); } - public static BlockData getBlockData(int combinedId) { - return getAdapter().getBlockData(combinedId); - } - /** * Create a WorldEdit BlockState from a Bukkit ItemStack * diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 12f922f54..84e32fd07 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -21,6 +21,10 @@ package com.sk89q.worldedit.bukkit; import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.bukkit.beta.BukkitGetBlocks; +import com.boydti.fawe.config.Settings; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; @@ -494,9 +498,19 @@ public class BukkitWorld extends AbstractWorld { return BukkitAdapter.adapt(getWorld().getBiome(position.getBlockX(), position.getBlockZ())); } + @Override + public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + return false; + } + @Override public boolean setBiome(BlockVector2 position, BiomeType biome) { getWorld().setBiome(position.getBlockX(), position.getBlockZ(), BukkitAdapter.adapt(biome)); return true; } + + @Override + public IChunkGet get(int chunkX, int chunkZ) { + return new BukkitGetBlocks(getWorldChecked(), chunkX, chunkZ, Settings.IMP.QUEUE.POOL); + } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index eb2b96707..59b0217bf 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -41,18 +41,18 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.internal.command.CommandUtil; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockCategory; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; -import com.sk89q.worldedit.world.registry.LegacyMapper; import io.papermc.lib.PaperLib; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Biome; +import org.bukkit.command.BlockCommandSender; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index 8e5bde860..e48b847b9 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.bukkit.adapter; +import com.boydti.fawe.Fawe; +import com.boydti.fawe.bukkit.FaweBukkit; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.BaseEntity; @@ -33,6 +35,8 @@ import com.sk89q.worldedit.world.registry.BlockMaterial; import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldCreator; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -149,4 +153,8 @@ public interface BukkitImplAdapter extends IBukkitAdapter { * @param player The player */ void sendFakeOP(Player player); + + default @org.jetbrains.annotations.Nullable World createWorld(WorldCreator creator) { + return ((FaweBukkit) Fawe.imp()).createWorldUnloaded(creator::createWorld); + } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IBukkitAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IBukkitAdapter.java index 7c9d823d9..bf630d76f 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IBukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/IBukkitAdapter.java @@ -303,10 +303,6 @@ public interface IBukkitAdapter { */ BlockData adapt(BlockStateHolder block); - default BlockData getBlockData(int combinedId) { - return adapt(BlockState.getFromInternalId(combinedId)); - } - /** * Create a WorldEdit BlockStateHolder from a Bukkit ItemStack * 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 0e2606fe9..89ee6e998 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java @@ -4,6 +4,7 @@ import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BitArray4096; import com.boydti.fawe.object.collection.IterableThreadLocal; +import com.boydti.fawe.util.IOUtil; import com.boydti.fawe.util.MathMan; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.ByteTag; @@ -29,92 +30,182 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; -public final class FaweCache implements Trimable { - public final static int BLOCKS_PER_LAYER = 4096; - public final static int CHUNK_LAYERS = 16; - public final static int WORLD_HEIGHT = CHUNK_LAYERS << 4; - public final static int WORLD_MAX_Y = WORLD_HEIGHT - 1; +import static com.google.common.base.Preconditions.checkNotNull; +public enum FaweCache implements Trimable { + IMP + ; // singleton - public static final char[] EMPTY_CHAR_4096 = new char[4096]; + public final int BLOCKS_PER_LAYER = 4096; + public final int CHUNK_LAYERS = 16; + public final int WORLD_HEIGHT = CHUNK_LAYERS << 4; + public final int WORLD_MAX_Y = WORLD_HEIGHT - 1; + + public final char[] EMPTY_CHAR_4096 = new char[4096]; + + private final IdentityHashMap REGISTERED_SINGLETONS = new IdentityHashMap<>(); + private final IdentityHashMap REGISTERED_POOLS = new IdentityHashMap<>(); + + public interface Pool { + T poll(); + default boolean offer(T recycle) { + return false; + } + default void clear() {} + } + + public class QueuePool extends ConcurrentLinkedQueue implements Pool { + private final Supplier supplier; + + public QueuePool(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public boolean offer(T t) { + return super.offer(t); + } + + @Override + public T poll() { + T result = super.poll(); + if (result == null) { + return supplier.get(); + } + return result; + } + + @Override + public void clear() { + if (!isEmpty()) super.clear(); + } + } /* Palette buffers / cache */ @Override - public boolean trim(boolean aggressive) { + public synchronized boolean trim(boolean aggressive) { BLOCK_TO_PALETTE.clean(); PALETTE_TO_BLOCK.clean(); BLOCK_STATES.clean(); SECTION_BLOCKS.clean(); PALETTE_CACHE.clean(); PALETTE_TO_BLOCK_CHAR.clean(); + INDEX_STORE.clean(); MUTABLE_VECTOR3.clean(); MUTABLE_BLOCKVECTOR3.clean(); SECTION_BITS_TO_CHAR.clean(); + for (Map.Entry entry : REGISTERED_SINGLETONS.entrySet()) { + entry.getValue().clean(); + } + for (Map.Entry entry : REGISTERED_POOLS.entrySet()) { + Pool pool = entry.getValue(); + pool.clear(); + } + return false; } - public static final IterableThreadLocal BLOCK_TO_PALETTE = new IterableThreadLocal() { - @Override - public int[] init() { - int[] result = new int[BlockTypes.states.length]; - Arrays.fill(result, Integer.MAX_VALUE); - return result; + public final Pool getPool(Class clazz) { + Pool pool = REGISTERED_POOLS.get(clazz); + if (pool == null) { + synchronized (this) { + pool = REGISTERED_POOLS.get(clazz); + if (pool == null) { + Fawe.debug("Not registered " + clazz); + Supplier supplier = IOUtil.supplier(clazz::newInstance); + pool = supplier::get; + REGISTERED_POOLS.put(clazz, pool); + } + } } - }; + return pool; + } - public static final IterableThreadLocal SECTION_BITS_TO_CHAR = new IterableThreadLocal() { - @Override - public char[] init() { - char[] result = new char[4096]; - return result; - } - }; + public final T getFromPool(Class clazz) { + Pool pool = getPool(clazz); + return pool.poll(); + } - public static final IterableThreadLocal PALETTE_TO_BLOCK = new IterableThreadLocal() { - @Override - public int[] init() { - return new int[Character.MAX_VALUE + 1]; + public final T getSingleton(Class clazz) { + IterableThreadLocal cache = REGISTERED_SINGLETONS.get(clazz); + if (cache == null) { + synchronized (this) { + cache = REGISTERED_SINGLETONS.get(clazz); + if (cache == null) { + Fawe.debug("Not registered " + clazz); + cache = new IterableThreadLocal<>(IOUtil.supplier(clazz::newInstance)); + REGISTERED_SINGLETONS.put(clazz, cache); + } + } } - }; + return cache.get(); + } - 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; + public synchronized IterableThreadLocal registerSingleton(Class clazz, Supplier cache) { + checkNotNull(cache); + IterableThreadLocal local = new IterableThreadLocal<>(cache); + IterableThreadLocal previous = REGISTERED_SINGLETONS.putIfAbsent(clazz, local); + if (previous != null) { + throw new IllegalStateException("Previous key"); } - }; + return local; + } - public static final IterableThreadLocal BLOCK_STATES = new IterableThreadLocal() { - @Override - public long[] init() { - return new long[2048]; + public synchronized Pool registerPool(Class clazz, Supplier cache, boolean buffer) { + checkNotNull(cache); + Pool pool; + if (buffer) { + pool = new QueuePool<>(cache); + } else { + pool = cache::get; } - }; + Pool previous = REGISTERED_POOLS.putIfAbsent(clazz, pool); + if (previous != null) { + throw new IllegalStateException("Previous key"); + } + return pool; + } - public static final IterableThreadLocal SECTION_BLOCKS = new IterableThreadLocal() { - @Override - public int[] init() { - return new int[4096]; + public final IterableThreadLocal BLOCK_TO_PALETTE = new IterableThreadLocal<>(() -> { + int[] result = new int[BlockTypes.states.length]; + Arrays.fill(result, Integer.MAX_VALUE); + return result; + }); + + public final IterableThreadLocal SECTION_BITS_TO_CHAR = new IterableThreadLocal<>(() -> new char[4096]); + + public final IterableThreadLocal PALETTE_TO_BLOCK = new IterableThreadLocal<>(() -> new int[Character.MAX_VALUE + 1]); + + public final IterableThreadLocal PALETTE_TO_BLOCK_CHAR = new IterableThreadLocal<>( + () -> new char[Character.MAX_VALUE + 1], a -> { + Arrays.fill(a, Character.MAX_VALUE); } - }; + ); + + public final IterableThreadLocal BLOCK_STATES = new IterableThreadLocal<>(() -> new long[2048]); + + public final IterableThreadLocal SECTION_BLOCKS = new IterableThreadLocal<>(() -> new int[4096]); + + public final IterableThreadLocal INDEX_STORE = new IterableThreadLocal<>(() -> new int[256]); /** * Holds data for a palette used in a chunk section */ - public static final class Palette { + public final class Palette { public int paletteToBlockLength; /** * Reusable buffer array, MUST check paletteToBlockLength for actual length @@ -128,12 +219,7 @@ public final class FaweCache implements Trimable { public long[] blockStates; } - private static final IterableThreadLocal PALETTE_CACHE = new IterableThreadLocal() { - @Override - public Palette init() { - return new Palette(); - } - }; + private final IterableThreadLocal PALETTE_CACHE = new IterableThreadLocal<>(Palette::new); /** * Convert raw char array to palette @@ -141,7 +227,7 @@ public final class FaweCache implements Trimable { * @param blocks * @return palette */ - public static Palette toPalette(int layerOffset, char[] blocks) { + public Palette toPalette(int layerOffset, char[] blocks) { return toPalette(layerOffset, null, blocks); } @@ -151,11 +237,11 @@ public final class FaweCache implements Trimable { * @param blocks * @return palette */ - public static Palette toPalette(int layerOffset, int[] blocks) { + public Palette toPalette(int layerOffset, int[] blocks) { return toPalette(layerOffset, blocks, null); } - private static Palette toPalette(int layerOffset, int[] blocksInts, char[] blocksChars) { + private 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(); @@ -229,14 +315,9 @@ public final class FaweCache implements Trimable { * Vector cache */ - public static IterableThreadLocal MUTABLE_BLOCKVECTOR3 = new IterableThreadLocal() { - @Override - public MutableBlockVector3 init() { - return new MutableBlockVector3(); - } - }; + public IterableThreadLocal MUTABLE_BLOCKVECTOR3 = new IterableThreadLocal<>(MutableBlockVector3::new); - public static IterableThreadLocal MUTABLE_VECTOR3 = new IterableThreadLocal() { + public IterableThreadLocal MUTABLE_VECTOR3 = new IterableThreadLocal(MutableVector3::new) { @Override public MutableVector3 init() { return new MutableVector3(); @@ -246,7 +327,7 @@ public final class FaweCache implements Trimable { /* Conversion methods between JNBT tags and raw values */ - public static Map asMap(Object... pairs) { + public 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]; @@ -256,47 +337,47 @@ public final class FaweCache implements Trimable { return map; } - public static ShortTag asTag(short value) { + public ShortTag asTag(short value) { return new ShortTag(value); } - public static IntTag asTag(int value) { + public IntTag asTag(int value) { return new IntTag(value); } - public static DoubleTag asTag(double value) { + public DoubleTag asTag(double value) { return new DoubleTag(value); } - public static ByteTag asTag(byte value) { + public ByteTag asTag(byte value) { return new ByteTag(value); } - public static FloatTag asTag(float value) { + public FloatTag asTag(float value) { return new FloatTag(value); } - public static LongTag asTag(long value) { + public LongTag asTag(long value) { return new LongTag(value); } - public static ByteArrayTag asTag(byte[] value) { + public ByteArrayTag asTag(byte[] value) { return new ByteArrayTag(value); } - public static IntArrayTag asTag(int[] value) { + public IntArrayTag asTag(int[] value) { return new IntArrayTag(value); } - public static LongArrayTag asTag(long[] value) { + public LongArrayTag asTag(long[] value) { return new LongArrayTag(value); } - public static StringTag asTag(String value) { + public StringTag asTag(String value) { return new StringTag(value); } - public static CompoundTag asTag(Map value) { + public CompoundTag asTag(Map value) { HashMap map = new HashMap<>(); for (Map.Entry entry : value.entrySet()) { Object child = entry.getValue(); @@ -306,7 +387,7 @@ public final class FaweCache implements Trimable { return new CompoundTag(map); } - public static Tag asTag(Object value) { + public Tag asTag(Object value) { if (value instanceof Integer) { return asTag((int) value); } else if (value instanceof Short) { @@ -359,7 +440,7 @@ public final class FaweCache implements Trimable { } } - public static ListTag asTag(Object... values) { + public ListTag asTag(Object... values) { Class clazz = null; List list = new ArrayList<>(values.length); for (Object value : values) { @@ -373,7 +454,7 @@ public final class FaweCache implements Trimable { return new ListTag(clazz, list); } - public static ListTag asTag(Collection values) { + public ListTag asTag(Collection values) { Class clazz = null; List list = new ArrayList<>(values.size()); for (Object value : values) { @@ -390,7 +471,7 @@ public final class FaweCache implements Trimable { /* Thread stuff */ - public static ThreadPoolExecutor newBlockingExecutor() { + public ThreadPoolExecutor newBlockingExecutor() { int nThreads = Settings.IMP.QUEUE.PARALLEL_THREADS; ArrayBlockingQueue queue = new ArrayBlockingQueue<>(nThreads); return new ThreadPoolExecutor(nThreads, nThreads, 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 f2af58deb..fe360a387 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 @@ -106,6 +106,8 @@ public interface IChunk> extends Trimable, Callable, IChu /* set - queues a change */ boolean setBiome(int x, int y, int z, BiomeType biome); + boolean setTile(int x, int y, int z, CompoundTag tag); + boolean setBlock(int x, int y, int z, BlockStateHolder block); @Override @@ -126,7 +128,7 @@ public interface IChunk> extends Trimable, Callable, IChu */ @Override default IBlocks reset() { - init(getQueue(), getX(), getZ()); + init(null, getX(), getZ()); return this; } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java index c59d4d09d..a90abe831 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java @@ -6,6 +6,8 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; +import java.util.concurrent.Future; + /** * An interface for getting blocks. */ @@ -28,4 +30,8 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent { default void optimize() { } + + > T call(IChunkSet set, Runnable finalize); + + char[] load(int layer); } 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 1750c9e42..6c39316c9 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 @@ -28,6 +28,10 @@ public interface IDelegateChunk extends IChunk { return root; } + @Override + default > T call(IChunkSet set, Runnable finalize) { + return getParent().call(set, finalize); + } @Override default IQueueExtent getQueue() { @@ -49,6 +53,11 @@ public interface IDelegateChunk extends IChunk { getParent().flood(flood, mask, block); } + @Override + default boolean setTile(int x, int y, int z, CompoundTag tag) { + return getParent().setTile(x, y, z, tag); + } + @Override default boolean setBiome(int x, int y, int z, BiomeType biome) { return getParent().setBiome(x, y, z, biome); @@ -74,6 +83,11 @@ public interface IDelegateChunk extends IChunk { return getParent().getFullBlock(x, y, z); } + @Override + default char[] load(int layer) { + return getParent().load(layer); + } + @Override default void init(IQueueExtent extent, int chunkX, int chunkZ) { getParent().init(extent, chunkX, chunkZ); 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 240ab76b9..03219aded 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 @@ -1,6 +1,35 @@ package com.boydti.fawe.beta; -import com.boydti.fawe.beta.implementation.WorldChunkCache; +import com.boydti.fawe.beta.implementation.IChunkCache; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.function.generator.GenBase; +import com.sk89q.worldedit.function.generator.Resource; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.Countable; +import com.sk89q.worldedit.util.Location; +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.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.Future; /** @@ -11,8 +40,43 @@ public interface IDelegateQueueExtent extends IQueueExtent { IQueueExtent getParent(); @Override - default void init(WorldChunkCache cache) { - getParent().init(cache); + default boolean isQueueEnabled() { + return getParent().isQueueEnabled(); + } + + @Override + default void clearBlockUpdates(Player... players) { + getParent().clearBlockUpdates(players); + } + + @Override + default void sendBlockUpdates(Player... players) { + getParent().sendBlockUpdates(players); + } + + @Override + default void enableQueue() { + getParent().enableQueue(); + } + + @Override + default void disableQueue() { + getParent().disableQueue(); + } + + @Override + default void init(Extent extent, IChunkCache get, IChunkCache set) { + getParent().init(extent, get, set); + } + + @Override + default IChunkGet getCachedGet(int x, int z) { + return getParent().getCachedGet(x, z); + } + + @Override + default IChunkSet getCachedSet(int x, int z) { + return getParent().getCachedSet(x, z); } @Override @@ -21,10 +85,50 @@ public interface IDelegateQueueExtent extends IQueueExtent { } @Override - default Future submit(IChunk chunk) { + default > T submit(IChunk chunk) { return getParent().submit(chunk); } + @Override + default boolean setBlock(int x, int y, int z, BlockStateHolder state) { + return getParent().setBlock(x, y, z, state); + } + + @Override + default boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + return getParent().setTile(x, y, z, tile); + } + + @Override + default boolean setBiome(int x, int y, int z, BiomeType biome) { + return getParent().setBiome(x, y, z, biome); + } + + @Override + default BlockState getBlock(int x, int y, int z) { + return getParent().getBlock(x, y, z); + } + + @Override + default BaseBlock getFullBlock(int x, int y, int z) { + return getParent().getFullBlock(x, y, z); + } + + @Override + default BiomeType getBiome(int x, int z) { + return getParent().getBiome(x, z); + } + + @Override + default BlockVector3 getMinimumPoint() { + return getParent().getMinimumPoint(); + } + + @Override + default BlockVector3 getMaximumPoint() { + return getParent().getMaximumPoint(); + } + @Override default IChunk create(boolean isFull) { return getParent().create(isFull); @@ -40,8 +144,247 @@ public interface IDelegateQueueExtent extends IQueueExtent { getParent().flush(); } + @Override + default ChunkFilterBlock initFilterBlock() { + return getParent().initFilterBlock(); + } + + @Override + default int size() { + return getParent().size(); + } + + @Override + default boolean isEmpty() { + return getParent().isEmpty(); + } + + @Override + default void sendChunk(int chunkX, int chunkZ, int bitMask) { + getParent().sendChunk(chunkX, chunkZ, bitMask); + } + @Override default boolean trim(boolean aggressive) { return getParent().trim(aggressive); } + + @Override + default void recycle() { + getParent().recycle(); + } + + @Override + default List getEntities(Region region) { + return getParent().getEntities(region); + } + + @Override + default List getEntities() { + return getParent().getEntities(); + } + + @Override + @Nullable + default Entity createEntity(Location location, BaseEntity entity) { + return getParent().createEntity(location, entity); + } + + @Override + @Nullable + default void removeEntity(int x, int y, int z, UUID uuid) { + getParent().removeEntity(x, y, z, uuid); + } + + @Override + default boolean isWorld() { + return getParent().isWorld(); + } + + @Override + default boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) { + return getParent().regenerateChunk(x, z, type, seed); + } + + @Override + default int getHighestTerrainBlock(int x, int z, int minY, int maxY) { + return getParent().getHighestTerrainBlock(x, z, minY, maxY); + } + + @Override + default int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) { + return getParent().getHighestTerrainBlock(x, z, minY, maxY, filter); + } + + @Override + default int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { + return getParent().getNearestSurfaceLayer(x, z, y, minY, maxY); + } + + @Override + default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) { + return getParent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir); + } + + @Override + default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { + return getParent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); + } + + @Override + default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) { + return getParent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); + } + + @Override + default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) { + return getParent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask); + } + + @Override + default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, boolean ignoreAir) { + return getParent().getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir); + } + + @Override + default void addCaves(Region region) throws WorldEditException { + getParent().addCaves(region); + } + + @Override + default void generate(Region region, GenBase gen) throws WorldEditException { + getParent().generate(region, gen); + } + + @Override + default void addSchems(Region region, Mask mask, List clipboards, int rarity, boolean rotate) throws WorldEditException { + getParent().addSchems(region, mask, clipboards, rarity, rotate); + } + + @Override + default void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException { + getParent().spawnResource(region, gen, rarity, frequency); + } + + @Override + default boolean contains(BlockVector3 pt) { + return getParent().contains(pt); + } + + @Override + default void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { + getParent().addOre(region, mask, material, size, frequency, rarity, minY, maxY); + } + + @Override + default void addOres(Region region, Mask mask) throws WorldEditException { + getParent().addOres(region, mask); + } + + @Override + default List> getBlockDistribution(Region region) { + return getParent().getBlockDistribution(region); + } + + @Override + default List> getBlockDistributionWithData(Region region) { + return getParent().getBlockDistributionWithData(region); + } + + @Override + @Nullable + default Operation commit() { + return getParent().commit(); + } + + @Override + default boolean cancel() { + return getParent().cancel(); + } + + @Override + default int getMaxY() { + return getParent().getMaxY(); + } + + @Override + default BlockArrayClipboard lazyCopy(Region region) { + return getParent().lazyCopy(region); + } + + @Override + default int countBlocks(Region region, Set searchBlocks) { + return getParent().countBlocks(region, searchBlocks); + } + + @Override + default int countBlocks(Region region, Mask searchMask) { + return getParent().countBlocks(region, searchMask); + } + + @Override + default > int setBlocks(Region region, B block) throws MaxChangedBlocksException { + return getParent().setBlocks(region, block); + } + + @Override + default int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { + return getParent().setBlocks(region, pattern); + } + + @Override + default > int replaceBlocks(Region region, Set filter, B replacement) throws MaxChangedBlocksException { + return getParent().replaceBlocks(region, filter, replacement); + } + + @Override + default int replaceBlocks(Region region, Set filter, Pattern pattern) throws MaxChangedBlocksException { + return getParent().replaceBlocks(region, filter, pattern); + } + + @Override + default int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException { + return getParent().replaceBlocks(region, mask, pattern); + } + + @Override + default int center(Region region, Pattern pattern) throws MaxChangedBlocksException { + return getParent().center(region, pattern); + } + + @Override + default int setBlocks(Set vset, Pattern pattern) { + return getParent().setBlocks(vset, pattern); + } + + @Override + default BlockState getBlock(BlockVector3 position) { + return getParent().getBlock(position); + } + + @Override + default BaseBlock getFullBlock(BlockVector3 position) { + return getParent().getFullBlock(position); + } + + @Override + default BiomeType getBiome(BlockVector2 position) { + return getParent().getBiome(position); + } + + @Override + default BiomeType getBiomeType(int x, int z) { + return getParent().getBiomeType(x, z); + } + + @Override + @Deprecated + default > boolean setBlock(BlockVector3 position, T block) throws WorldEditException { + return getParent().setBlock(position, block); + } + + @Override + default boolean setBiome(BlockVector2 position, BiomeType biome) { + return getParent().setBiome(position, biome); + } } 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 0c78ff4c5..3dd0fe6a9 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 @@ -1,24 +1,24 @@ package com.boydti.fawe.beta; import com.boydti.fawe.FaweCache; -import com.boydti.fawe.beta.implementation.WorldChunkCache; +import com.boydti.fawe.beta.implementation.IChunkCache; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.registry.Keyed; 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.BlockStateHolder; import java.io.Flushable; import java.util.concurrent.Future; -import java.util.function.Supplier; /** * TODO: implement Extent (need to refactor Extent first) Interface for a queue based extent which * uses chunks */ -public interface IQueueExtent extends Flushable, Trimable, Extent, Keyed { +public interface IQueueExtent extends Flushable, Trimable, Extent { @Override default boolean isQueueEnabled() { @@ -54,7 +54,7 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, Keyed { void disableQueue(); - void init(WorldChunkCache world); // TODO NOT IMPLEMENTED replace with supplier + void init(Extent extent, IChunkCache get, IChunkCache set); /** * Get the cached get object @@ -64,7 +64,7 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, Keyed { * @param supplier * @return */ - IChunkGet getCachedGet(int x, int z, Supplier supplier); + IChunkGet getCachedGet(int x, int z); /** * Get the cached chunk set object @@ -73,7 +73,7 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, Keyed { * @param supplier * @return */ - IChunkSet getCachedSet(int x, int z, Supplier supplier); + IChunkSet getCachedSet(int x, int z); /** * Get the IChunk at a position (and cache it if it's not already) @@ -100,6 +100,12 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, Keyed { return chunk.setBlock(x & 15, y, z & 15, state); } + @Override + default boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + final IChunk chunk = getCachedChunk(x >> 4, z >> 4); + return chunk.setTile(x & 15, y, z & 15, tile); + } + @Override default boolean setBiome(int x, int y, int z, BiomeType biome) { final IChunk chunk = getCachedChunk(x >> 4, z >> 4); @@ -130,7 +136,7 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, Keyed { @Override default BlockVector3 getMaximumPoint() { - return BlockVector3.at(30000000, FaweCache.WORLD_MAX_Y, 30000000); + return BlockVector3.at(30000000, FaweCache.IMP.WORLD_MAX_Y, 30000000); } /** diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/Trimable.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/Trimable.java index cfbc8480b..c40a4836f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/Trimable.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/Trimable.java @@ -14,4 +14,6 @@ public interface Trimable { * @return if this object is empty at the end of the trim, and can therefore be deleted */ boolean trim(boolean aggressive); + + default void recycle() {} } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java new file mode 100644 index 000000000..00b5ff35b --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkCache.java @@ -0,0 +1,77 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.Trimable; +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.world.World; +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; + +import java.lang.ref.WeakReference; + +public class ChunkCache implements IChunkCache { + + protected final Long2ObjectLinkedOpenHashMap> getCache; + private final IChunkCache delegate; + + protected ChunkCache(IChunkCache delegate) { + this.getCache = new Long2ObjectLinkedOpenHashMap<>(); + this.delegate = delegate; + } + + /** + * Get or create the IGetBlocks + * + * @param index chunk index {@link com.boydti.fawe.util.MathMan#pairInt(int, int)} + * @param provider used to create if it isn't already cached + * @return cached IGetBlocks + */ + @Override + public synchronized T get(int x, int z) { + long pair = MathMan.pairInt(x, z); + final WeakReference ref = getCache.get(pair); + if (ref != null) { + final T blocks = ref.get(); + if (blocks != null) { + return blocks; + } + } + final T blocks = newChunk(x, z); + getCache.put(pair, new WeakReference<>(blocks)); + return blocks; + } + + public T newChunk(int chunkX, int chunkZ) { + return delegate.get(chunkX, chunkZ); + } + + @Override + public synchronized boolean trim(boolean aggressive) { + if (getCache.size() == 0) { + return true; + } + boolean result = true; + if (!getCache.isEmpty()) { + final ObjectIterator>> iter = getCache + .long2ObjectEntrySet().fastIterator(); + while (iter.hasNext()) { + final Long2ObjectMap.Entry> entry = iter.next(); + final WeakReference value = entry.getValue(); + final T igb = value.get(); + if (igb == null) { + iter.remove(); + } else { + result = false; + if (!aggressive) { + return false; + } + synchronized (igb) { + igb.trim(true); + } + } + } + } + return result; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/FallbackChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/FallbackChunkGet.java new file mode 100644 index 000000000..9b9061d57 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/FallbackChunkGet.java @@ -0,0 +1,135 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBlocks; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.util.MathMan; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Future; + +public class FallbackChunkGet implements IChunkGet { + private final int bx, bz; + private final Extent extent; + + public FallbackChunkGet(Extent extent, int chunkX, int chunkZ) { + this.extent = extent; + this.bx = chunkX << 4; + this.bz = chunkZ << 4; + } + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + return extent.getFullBlock(bx + x, y, bz + z); + } + + @Override + public BiomeType getBiomeType(int x, int z) { + return extent.getBiomeType(bx + x, bz + z); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return extent.getBlock(bx + x, y, bz + z); + } + + @Override + public CompoundTag getTag(int x, int y, int z) { + return extent.getFullBlock(bx + x, y, bz + z).getNbtData(); + } + + @Override + public boolean trim(boolean aggressive) { + return true; + } + + @Override + public > T call(IChunkSet set, Runnable finalize) { + for (int layer = 0; layer < 16; layer++) { + if (set.hasSection(layer)) { + char[] arr = set.getArray(layer); + int by = layer << 4; + for (int y = 0, i = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, i++) { + char ordinal = arr[i]; + if (ordinal != 0) { + BlockState block = BlockState.getFromOrdinal(ordinal); + extent.setBlock(bx + x, by + y, bz + z, block); + } + } + } + } + + } + } + Map tiles = set.getTiles(); + if (!tiles.isEmpty()) { + for (Map.Entry entry : tiles.entrySet()) { + short blockHash = entry.getKey(); + final int x = (blockHash >> 12 & 0xF) + bx; + final int y = (blockHash & 0xFF); + final int z = (blockHash >> 8 & 0xF) + bz; + extent.setTile(bx + x, y, bz + z, entry.getValue()); + } + + } + Set spawns = set.getEntities(); + if (!spawns.isEmpty()) { + for (CompoundTag spawn : spawns) { + BaseEntity ent = new BaseEntity(spawn); + extent.createEntity(ent.getLocation(extent), ent); + } + } + Set kills = set.getEntityRemoves(); + if (!kills.isEmpty()) { + for (UUID kill : kills) { + extent.removeEntity(0, 0, 0, kill); + } + } + BiomeType[] biomes = set.getBiomes(); + if (biomes != null) { + for (int z = 0, i = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, i++) { + BiomeType biome = biomes[i]; + if (biome != null) { + extent.setBiome(bx + x, 0, bz + z, biome); + } + } + } + } + return null; + } + + @Override + public char[] load(int layer) { + char[] arr = FaweCache.IMP.SECTION_BITS_TO_CHAR.get(); + int by = layer << 4; + for (int y = 0, i = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, i++) { + arr[i] = getBlock(bx + x, by + y, bz + z).getOrdinalChar(); + } + } + } + return arr; + } + + @Override + public boolean hasSection(int layer) { + return true; + } + + @Override + public IBlocks reset() { + return null; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkCache.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkCache.java new file mode 100644 index 000000000..7724e74ef --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkCache.java @@ -0,0 +1,16 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.Trimable; + +/** + * IGetBlocks may be cached by the WorldChunkCache so that it can be used between multiple + * IQueueExtents - avoids conversion between palette and raw data on every block get + */ +public interface IChunkCache extends Trimable { + T get(int chunkX, int chunkZ); + + @Override + default boolean trim(boolean aggressive) { + return false; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/NullChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/NullChunkGet.java new file mode 100644 index 000000000..4aff8c605 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/NullChunkGet.java @@ -0,0 +1,63 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBlocks; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.concurrent.Future; + +public enum NullChunkGet implements IChunkGet { + INSTANCE + ; + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + return BlockTypes.AIR.getDefaultState().toBaseBlock(); + } + + @Override + public BiomeType getBiomeType(int x, int z) { + return BiomeTypes.FOREST; + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypes.AIR.getDefaultState(); + } + + @Override + public CompoundTag getTag(int x, int y, int z) { + return null; + } + + @Override + public boolean trim(boolean aggressive) { + return true; + } + + @Override + public > T call(IChunkSet set, Runnable finalize) { + return null; + } + + @Override + public char[] load(int layer) { + return FaweCache.IMP.EMPTY_CHAR_4096; + } + + @Override + public boolean hasSection(int layer) { + return false; + } + + @Override + public IBlocks reset() { + return null; + } +} 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 b4da68e89..70b859358 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 @@ -3,6 +3,8 @@ package com.boydti.fawe.beta.implementation; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.config.Settings; @@ -11,6 +13,7 @@ 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.extent.Extent; import com.sk89q.worldedit.world.World; import java.lang.ref.WeakReference; import java.util.HashMap; @@ -33,16 +36,11 @@ public abstract class QueueHandler implements Trimable, Runnable { private ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool(); private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool(); - private ThreadPoolExecutor blockingExecutor = FaweCache.newBlockingExecutor(); + private ThreadPoolExecutor blockingExecutor = FaweCache.IMP.newBlockingExecutor(); private ConcurrentLinkedQueue syncTasks = new ConcurrentLinkedQueue<>(); - private Map> chunkCache = new HashMap<>(); - private IterableThreadLocal queuePool = new IterableThreadLocal() { - @Override - public IQueueExtent init() { - return create(); - } - }; + private Map>> chunkCache = new HashMap<>(); + private IterableThreadLocal queuePool = new IterableThreadLocal<>(QueueHandler.this::create); /** * Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the * server @@ -50,6 +48,7 @@ public abstract class QueueHandler implements Trimable, Runnable { private long last; private long allocate = 50; private double targetTPS = 18; + public QueueHandler() { TaskManager.IMP.repeat(this, 1); } @@ -197,18 +196,18 @@ public abstract class QueueHandler implements Trimable, Runnable { * @param world * @return */ - public WorldChunkCache getOrCreate(World world) { + public IChunkCache getOrCreateWorldCache(World world) { world = WorldWrapper.unwrap(world); synchronized (chunkCache) { - final WeakReference ref = chunkCache.get(world); + final WeakReference> ref = chunkCache.get(world); if (ref != null) { - final WorldChunkCache cached = ref.get(); + final IChunkCache cached = ref.get(); if (cached != null) { return cached; } } - final WorldChunkCache created = new WorldChunkCache(world); + final IChunkCache created = new ChunkCache<>(world); chunkCache.put(world, new WeakReference<>(created)); return created; } @@ -222,7 +221,9 @@ public abstract class QueueHandler implements Trimable, Runnable { public IQueueExtent getQueue(World world) { final IQueueExtent queue = queuePool.get(); - queue.init(getOrCreate(world)); + IChunkCache cacheGet = getOrCreateWorldCache(world); + IChunkCache set = null; // TODO cache? + queue.init(world, cacheGet, set); return queue; } @@ -230,13 +231,13 @@ public abstract class QueueHandler implements Trimable, Runnable { public boolean trim(boolean aggressive) { boolean result = true; synchronized (chunkCache) { - final Iterator>> iter = chunkCache + final Iterator>>> iter = chunkCache .entrySet().iterator(); while (iter.hasNext()) { - final Map.Entry> entry = iter.next(); - final WeakReference value = entry.getValue(); - final WorldChunkCache cache = value.get(); - if (cache == null || cache.size() == 0 || cache.trim(aggressive)) { + final Map.Entry>> entry = iter.next(); + final WeakReference> value = entry.getValue(); + final IChunkCache cache = value.get(); + if (cache.trim(aggressive)) { iter.remove(); continue; } 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 31d2946b4..0622f26c9 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,41 +1,49 @@ package com.boydti.fawe.beta.implementation; -import static com.google.common.base.Preconditions.checkNotNull; - import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; +import com.boydti.fawe.beta.implementation.holder.ChunkHolder; 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 com.sk89q.worldedit.extent.Extent; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; + import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.function.Supplier; /** * Single threaded implementation for IQueueExtent (still abstract) - Does not implement creation of * chunks (that has to implemented by the platform e.g. Bukkit) *

- * This queue is reusable {@link #init(WorldChunkCache)} + * This queue is reusable {@link #init(IChunkCache)} */ 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<>(); +// // Pool discarded chunks for reuse (can safely be cleared by another thread) +// private static final ConcurrentLinkedQueue CHUNK_POOL = new ConcurrentLinkedQueue<>(); // Chunks currently being queued / worked on private final Long2ObjectLinkedOpenHashMap chunks = new Long2ObjectLinkedOpenHashMap<>(); - private WorldChunkCache cache; + + private IChunkCache cacheGet; + private IChunkCache cacheSet; + private boolean initialized; + private Thread currentThread; private ConcurrentLinkedQueue submissions = new ConcurrentLinkedQueue<>(); // Last access pointers private IChunk lastChunk; private long lastPair = Long.MAX_VALUE; + private boolean enabledQueue = true; + /** * Safety check to ensure that the thread being used matches the one being initialized on. - Can * be removed later @@ -48,23 +56,42 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { } @Override - public IChunkGet getCachedGet(int x, int z, Supplier supplier) { - return cache.get(MathMan.pairInt(x, z), supplier); + public void enableQueue() { + enabledQueue = true; + } + + @Override + public void disableQueue() { + enabledQueue = false; + } + + @Override + public IChunkGet getCachedGet(int x, int z) { + return cacheGet.get(x, z); + } + + @Override + public IChunkSet getCachedSet(int x, int z) { + return cacheSet.get(x, z); } /** * Resets the queue. */ protected synchronized void reset() { + if (!initialized) return; checkThread(); - cache = null; if (!chunks.isEmpty()) { - CHUNK_POOL.addAll(chunks.values()); + for (IChunk chunk : chunks.values()) { + chunk.recycle(); + } chunks.clear(); } + enabledQueue = true; lastChunk = null; lastPair = Long.MAX_VALUE; currentThread = null; + initialized = false; } /** @@ -73,17 +100,18 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { * @param cache */ @Override - public synchronized void init(WorldChunkCache cache) { - if (this.cache != null) { - reset(); - } + public synchronized void init(Extent extent, IChunkCache get, IChunkCache set) { + reset(); currentThread = Thread.currentThread(); - checkNotNull(cache); - this.cache = cache; - } - - public void returnToPool(IChunk chunk) { - CHUNK_POOL.add(chunk); + if (get == null) { + get = (x, z) -> { throw new UnsupportedOperationException(); }; + } + if (set == null) { + set = (x, z) -> CharSetBlocks.newInstance(); + } + this.cacheGet = get; + this.cacheSet = set; + initialized = true; } @Override @@ -116,8 +144,9 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { */ private > T submitUnchecked(IChunk chunk) { if (chunk.isEmpty()) { - CHUNK_POOL.add(chunk); - return (T) (Future) Futures.immediateFuture(null); + chunk.recycle(); + Future result = Futures.immediateFuture(null); + return (T) result; } if (Fawe.isMainThread()) { @@ -130,7 +159,8 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { @Override public synchronized boolean trim(boolean aggressive) { // TODO trim individial chunk sections - CHUNK_POOL.clear(); + cacheGet.trim(aggressive); + cacheSet.trim(aggressive); if (Thread.currentThread() == currentThread) { lastChunk = null; lastPair = Long.MAX_VALUE; @@ -157,10 +187,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { * @return IChunk */ private IChunk poolOrCreate(int X, int Z) { - IChunk next = CHUNK_POOL.poll(); - if (next == null) { - next = create(false); - } + IChunk next = create(false); next.init(this, X, Z); return next; } @@ -187,7 +214,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { checkThread(); final int size = chunks.size(); final boolean lowMem = MemUtil.isMemoryLimited(); - if (lowMem || size > Settings.IMP.QUEUE.TARGET_SIZE) { + if (enabledQueue && (lowMem || size > Settings.IMP.QUEUE.TARGET_SIZE)) { chunk = chunks.removeFirst(); final Future future = submitUnchecked(chunk); if (future != null && !future.isDone()) { @@ -211,6 +238,11 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent { return chunk; } + @Override + public IChunk create(boolean isFull) { + return ChunkHolder.newInstance(); + } + private void pollSubmissions(int targetSize, boolean aggressive) { final int overflow = submissions.size() - targetSize; if (aggressive) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java index 74498610a..02642433f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java @@ -63,7 +63,7 @@ public class BitSetBlocks implements IChunkSet { @Override public char[] getArray(int layer) { - char[] arr = FaweCache.SECTION_BITS_TO_CHAR.get(); + char[] arr = FaweCache.IMP.SECTION_BITS_TO_CHAR.get(); MemBlockSet.IRow nullRowY = row.getRow(layer); if (nullRowY instanceof MemBlockSet.RowY) { char value = blockState.getOrdinalChar(); 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 84b857470..2f3dfd368 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 @@ -6,6 +6,8 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import java.util.concurrent.Future; + public abstract class CharGetBlocks extends CharBlocks implements IChunkGet { @Override 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 aff10f536..ee71ccf56 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 @@ -1,6 +1,8 @@ package com.boydti.fawe.beta.implementation.blocks; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.util.MathMan; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.world.biome.BiomeType; @@ -14,21 +16,21 @@ import java.util.Set; import java.util.UUID; public class CharSetBlocks extends CharBlocks implements IChunkSet { + private static FaweCache.Pool POOL = FaweCache.IMP.registerPool(CharSetBlocks.class, CharSetBlocks::new, Settings.IMP.QUEUE.POOL); + public static CharSetBlocks newInstance() { + return POOL.poll(); + } public BiomeType[] biomes; public HashMap tiles; public HashSet entities; public HashSet entityRemoves; - public CharSetBlocks(CharBlocks other) { - super(other); - if (other instanceof CharSetBlocks) { - - } - } - - public CharSetBlocks() { + private CharSetBlocks() {} + @Override + public void recycle() { + POOL.offer(this); } @Override 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 a8a08c5d4..d47808dd2 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,6 @@ package com.boydti.fawe.beta.implementation.holder; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.FilterBlockMask; @@ -10,19 +11,67 @@ import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.SingleThreadQueueExtent; import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; +import com.boydti.fawe.config.Settings; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.regions.Region; 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.BlockStateHolder; + +import java.util.concurrent.Future; import java.util.function.Supplier; import javax.annotation.Nullable; /** * An abstract {@link IChunk} class that implements basic get/set blocks */ -public abstract class ChunkHolder implements IChunk { +public class ChunkHolder> implements IChunk { + + private static FaweCache.Pool POOL = FaweCache.IMP.registerPool(ChunkHolder.class, ChunkHolder::new, Settings.IMP.QUEUE.POOL); + + public static ChunkHolder newInstance() { + return POOL.poll(); + } + + private IChunkGet get; + private IChunkSet set; + private IBlockDelegate delegate; + private IQueueExtent extent; + private int chunkX; + private int chunkZ; + + public ChunkHolder() { + this.delegate = NULL; + } + + public void init(IBlockDelegate delegate) { + this.delegate = delegate; + } + + @Override + public void recycle() { + delegate = NULL; + } + + public IBlockDelegate getDelegate() { + return delegate; + } + + @Override + public IQueueExtent getQueue() { + return extent; + } + + @Override + public boolean setTile(int x, int y, int z, CompoundTag tag) { + return false; + } + + @Override + public char[] load(int layer) { + return getOrCreateGet().load(layer); + } public static final IBlockDelegate BOTH = new IBlockDelegate() { @Override @@ -160,20 +209,6 @@ public abstract class ChunkHolder implements IChunk { return chunk.getFullBlock(x, y, z); } }; - private IChunkGet get; - private IChunkSet set; - private IBlockDelegate delegate; - private IQueueExtent extent; - private int chunkX; - private int chunkZ; - - public ChunkHolder() { - this.delegate = NULL; - } - - public ChunkHolder(IBlockDelegate delegate) { - this.delegate = delegate; - } @Override public void flood(Flood flood, FilterBlockMask mask, ChunkFilterBlock block) { @@ -264,14 +299,6 @@ public abstract class ChunkHolder implements IChunk { return set; } - /** - * Create the settable part of this chunk (defaults to a char array) - * @return - */ - public IChunkSet createSet() { - return new CharSetBlocks(); - } - /** * Create a wrapped set object * - The purpose of wrapping is to allow different extents to intercept / alter behavior @@ -279,13 +306,7 @@ public abstract class ChunkHolder implements IChunk { * @return */ private IChunkSet newWrappedSet() { - if (extent instanceof SingleThreadQueueExtent) { - IChunkSet newSet = extent.getCachedSet(chunkX, chunkZ, this::createSet); - if (newSet != null) { - return newSet; - } - } - return createSet(); + return extent.getCachedSet(chunkX, chunkZ); } /** @@ -295,17 +316,9 @@ public abstract class ChunkHolder implements IChunk { * @return */ private IChunkGet newWrappedGet() { - if (extent instanceof SingleThreadQueueExtent) { - IChunkGet newGet = extent.getCachedGet(chunkX, chunkZ, this::get); - if (newGet != null) { - return newGet; - } - } - return get(); + return extent.getCachedGet(chunkX, chunkZ); } - public abstract IChunkGet get(); - @Override public void init(IQueueExtent extent, int chunkX, int chunkZ) { this.extent = extent; @@ -320,6 +333,22 @@ public abstract class ChunkHolder implements IChunk { get = null; } + @Override + public synchronized T call() { + if (get != null && set != null) { + return getOrCreateGet().call(getOrCreateSet(), this::recycle); + } + return null; + } + + @Override + public T call(IChunkSet set, Runnable finalize) { + if (get != null && set != null) { + return getOrCreateGet().call(set, finalize); + } + return null; + } + public IQueueExtent getExtent() { return extent; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/DelegateChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/DelegateChunk.java index 6c285b320..7a284df67 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/DelegateChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/DelegateChunk.java @@ -1,27 +1,29 @@ package com.boydti.fawe.beta.implementation.holder; import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IDelegateChunk; +import com.sk89q.jnbt.CompoundTag; + +import java.util.concurrent.Future; /** * Implementation of IDelegateChunk - * - * @param */ -public class DelegateChunk implements IDelegateChunk { +public class DelegateChunk implements IDelegateChunk { - private T parent; + private U parent; - public DelegateChunk(T parent) { + public DelegateChunk(U parent) { this.parent = parent; } @Override - public final T getParent() { + public final U getParent() { return parent; } - public final void setParent(T parent) { + public final void setParent(U parent) { this.parent = parent; } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java b/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java index 4b9fb7110..0401419d5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java @@ -475,7 +475,7 @@ public class AnvilCommands { // }); // if (useData) { // for (long[] c : map) { -// BaseBlock block = FaweCache.CACHE_BLOCK[(int) c[0]]; +// BaseBlock block = FaweCache.IMP.CACHE_BLOCK[(int) c[0]]; // String name = BlockType.fromID(block.getId()).getName(); // String str = String.format("%-7s (%.3f%%) %s #%d:%d", // String.valueOf(c[1]), diff --git a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java index 82229b7bd..18a9b5e59 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java @@ -293,7 +293,13 @@ public class Settings extends Config { " - Low values may result in FAWE waiting on requests to the main thread", " - Higher values use more memory and isn't noticeably faster", }) - public int PRELOAD_CHUNKS = 32; + public int PRELOAD_CHUNKS = 100000; + + @Comment({ + "If pooling is enabled (reduces GC, higher memory usage)", + " - Enable to improve performance at the expense of memory", + }) + public boolean POOL = true; @Comment({ "Discard edits which have been idle for a certain amount of time (ms)", diff --git a/worldedit-core/src/main/java/com/boydti/fawe/logging/LoggingChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/logging/LoggingChangeSet.java index 8ac50bbde..76809abb3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/logging/LoggingChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/logging/LoggingChangeSet.java @@ -58,10 +58,10 @@ public class LoggingChangeSet extends AbstractDelegateChangeSet { // loc.x = x; // loc.y = y; // loc.z = z; -// oldBlock.id = FaweCache.getId(combinedId4DataFrom); -// oldBlock.data = FaweCache.getData(combinedId4DataFrom); -// newBlock.id = FaweCache.getId(combinedId4DataTo); -// newBlock.data = FaweCache.getData(combinedId4DataTo); +// oldBlock.id = FaweCache.IMP.getId(combinedId4DataFrom); +// oldBlock.data = FaweCache.IMP.getData(combinedId4DataFrom); +// newBlock.id = FaweCache.IMP.getId(combinedId4DataTo); +// newBlock.data = FaweCache.IMP.getData(combinedId4DataTo); // // Log to BlocksHub and parent // api.logBlock(loc, player, world, oldBlock, newBlock); parent.add(x, y, z, combinedId4DataFrom, combinedId4DataTo); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java index 3d78a73fe..bb2c17763 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java @@ -1,7 +1,12 @@ package com.boydti.fawe.object.brush.visualization.cfi; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBlocks; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.implementation.FallbackChunkGet; import com.boydti.fawe.object.FaweInputStream; import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.FawePlayer; @@ -44,6 +49,7 @@ import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -58,6 +64,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.UUID; +import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nullable; @@ -65,13 +72,6 @@ import javax.annotation.Nullable; public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Drawable, VirtualWorld { private final MutableBlockVector3 mutable = new MutableBlockVector3(); - private final ThreadLocal indexStore = new ThreadLocal() { - @Override - protected int[] initialValue() { - return new int[256]; - } - }; - private final DifferentialBlockBuffer blocks; protected final DifferentialArray heights; protected final DifferentialArray biomes; @@ -1571,7 +1571,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int[] floor = this.floor.get(); int[] overlay = this.overlay != null ? this.overlay.get() : null; try { - int[] indexes = indexStore.get(); + int[] indexes = FaweCache.IMP.INDEX_STORE.get(); int index; int maxY = 0; @@ -1902,12 +1902,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr })); } - @Override - protected void finalize() throws Throwable { - IterableThreadLocal.clean(indexStore); - super.finalize(); - } - @Override public int getMaxY() { return 255; @@ -1973,4 +1967,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr // TODO Auto-generated method stub return null; } + + @Override + public IChunkGet get(int x, int z) { + Fawe.debug("Should not be using buffering with HMMG"); + return new FallbackChunkGet(this, x, z); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java index 6f681bd90..fbe06d76c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java @@ -79,10 +79,10 @@ public class WritableMCAChunk { } public void write(NBTOutputStream nbtOut) throws IOException { - int[] blockToPalette = FaweCache.BLOCK_TO_PALETTE.get(); - int[] paletteToBlock = FaweCache.PALETTE_TO_BLOCK.get(); - long[] blockstates = FaweCache.BLOCK_STATES.get(); - int[] blocksCopy = FaweCache.SECTION_BLOCKS.get(); + int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get(); + int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + long[] blockstates = FaweCache.IMP.BLOCK_STATES.get(); + int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get(); nbtOut.writeNamedTagName("", NBTConstants.TYPE_COMPOUND); nbtOut.writeNamedTag("DataVersion", 1631); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java index 6b4b3995f..dc0d27592 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -52,7 +52,7 @@ public abstract class FaweChangeSet implements ChangeSet { public FaweChangeSet(String world) { this.worldName = world; this.mainThread = Fawe.get() == null || Fawe.isMainThread(); - this.layers = FaweCache.CHUNK_LAYERS; + this.layers = FaweCache.IMP.CHUNK_LAYERS; } public FaweChangeSet(World world) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/remap/ClipboardRemapper.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/remap/ClipboardRemapper.java index df9d21db7..e2c00c8e5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/remap/ClipboardRemapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/remap/ClipboardRemapper.java @@ -223,7 +223,7 @@ public class ClipboardRemapper { // String name = entry.getKey(); // int id = value.get("id").getAsInt(); // int data = value.get("data").getAsInt(); -// int combined = FaweCache.getCombined(id, data); +// int combined = FaweCache.IMP.getCombined(id, data); // map.putIfAbsent(name, new ArrayList<>()); // map.get(name).add(combined); // } @@ -496,7 +496,7 @@ public class ClipboardRemapper { // int combined = block.getCombined(); // if (remap[combined]) { // char value = remapCombined[combined]; -// BaseBlock newBlock = FaweCache.CACHE_BLOCK[value]; +// BaseBlock newBlock = FaweCache.IMP.CACHE_BLOCK[value]; // newBlock.setNbtData(block.getNbtData()); // return newBlock; // } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java index 2aef7daf7..eaa4fe297 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/IterableThreadLocal.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.collection; +import com.boydti.fawe.util.IOUtil; import com.boydti.fawe.util.MainUtil; import java.lang.ref.Reference; import java.lang.reflect.Array; @@ -8,12 +9,27 @@ import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; -public abstract class IterableThreadLocal extends ThreadLocal implements Iterable { +public class IterableThreadLocal extends ThreadLocal implements Iterable { private final ConcurrentLinkedDeque allValues = new ConcurrentLinkedDeque<>(); + private final Supplier supplier; - public IterableThreadLocal() { + public IterableThreadLocal(Supplier supplier) { + this.supplier = supplier; + } + + public IterableThreadLocal(Supplier supplier, Function modifier) { + this.supplier = supplier; + } + + public IterableThreadLocal(Supplier supplier, Consumer modifier) { + this.supplier = supplier; } @Override @@ -33,7 +49,7 @@ public abstract class IterableThreadLocal extends ThreadLocal implements I } public T init() { - return null; + return supplier.get(); } public void clean() { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/MemBlockSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/MemBlockSet.java index bb7418ee0..b9ae7a46e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/MemBlockSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/MemBlockSet.java @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull; */ public final class MemBlockSet extends BlockSet { public final static int BITS_PER_WORD = 6; - public final static int WORDS = FaweCache.BLOCKS_PER_LAYER >> BITS_PER_WORD; + public final static int WORDS = FaweCache.IMP.BLOCKS_PER_LAYER >> BITS_PER_WORD; public final static IRow NULL_ROW_X = new NullRowX(); public final static IRow NULL_ROW_Z = new NullRowZ(); public final static IRow NULL_ROW_Y = new NullRowY(); @@ -354,7 +354,7 @@ public final class MemBlockSet extends BlockSet { maxy = y + 1; } by = (Y << 4) + y; - if (by == FaweCache.WORLD_MAX_Y) return FaweCache.WORLD_MAX_Y; + if (by == FaweCache.IMP.WORLD_MAX_Y) return FaweCache.IMP.WORLD_MAX_Y; break outer; } } @@ -823,7 +823,7 @@ public final class MemBlockSet extends BlockSet { private final IRow[] rows; public RowZ() { - this.rows = new IRow[FaweCache.CHUNK_LAYERS]; + this.rows = new IRow[FaweCache.IMP.CHUNK_LAYERS]; reset(); } @@ -866,7 +866,7 @@ public final class MemBlockSet extends BlockSet { } public void reset() { - for (int i = 0; i < FaweCache.CHUNK_LAYERS; i++) rows[i] = NULL_ROW_Y; + for (int i = 0; i < FaweCache.IMP.CHUNK_LAYERS; i++) rows[i] = NULL_ROW_Y; } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java index 421e3c133..84f3e7aca 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/FaweBlockMatcher.java @@ -31,10 +31,10 @@ public abstract class FaweBlockMatcher { // public boolean apply(BaseBlock oldBlock) { // int currentId = oldBlock.getId(); // oldBlock.setId(id); -// if (FaweCache.hasData(currentId)) { +// if (FaweCache.IMP.hasData(currentId)) { // oldBlock.setData(0); // } -// if (FaweCache.hasNBT(currentId)) { +// if (FaweCache.IMP.hasNBT(currentId)) { // oldBlock.setNbtData(null); // } // return true; @@ -47,7 +47,7 @@ public abstract class FaweBlockMatcher { // int currentId = oldBlock.getId(); // oldBlock.setId(id); // oldBlock.setData(data); -// if (FaweCache.hasNBT(currentId)) { +// if (FaweCache.IMP.hasNBT(currentId)) { // oldBlock.setNbtData(null); // } // return true; @@ -67,10 +67,10 @@ public abstract class FaweBlockMatcher { // BaseBlock replace = array[random.random(size)]; // int currentId = block.getId(); // block.setId(replace.getId()); -// if (FaweCache.hasNBT(currentId)) { +// if (FaweCache.IMP.hasNBT(currentId)) { // block.setNbtData(null); // } -// if (FaweCache.hasData(currentId) || replace.getData() != 0) { +// if (FaweCache.IMP.hasData(currentId) || replace.getData() != 0) { // block.setData(replace.getData()); // } // return true; @@ -82,7 +82,7 @@ public abstract class FaweBlockMatcher { public static FaweBlockMatcher fromBlock(BaseBlock block, boolean checkData) { // final int id = block.getId(); // final int data = block.getData(); -// if (checkData && FaweCache.hasData(id)) { +// if (checkData && FaweCache.IMP.hasData(id)) { // return new FaweBlockMatcher() { // @Override // public boolean apply(BaseBlock block) { @@ -104,13 +104,13 @@ public abstract class FaweBlockMatcher { // if (searchBlocks.size() == 1) { // return fromBlock(searchBlocks.iterator().next(), checkData); // } -// final boolean[] allowedId = new boolean[FaweCache.getId(Character.MAX_VALUE)]; +// final boolean[] allowedId = new boolean[FaweCache.IMP.getId(Character.MAX_VALUE)]; // for (BaseBlock block : searchBlocks) { // allowedId[block.getId()] = true; // } // final boolean[] allowed = new boolean[Character.MAX_VALUE]; // for (BaseBlock block : searchBlocks) { -// allowed[FaweCache.getCombined(block)] = true; +// allowed[FaweCache.IMP.getCombined(block)] = true; // } // if (checkData) { // return new FaweBlockMatcher() { @@ -118,7 +118,7 @@ public abstract class FaweBlockMatcher { // public boolean apply(BaseBlock block) { // int id = block.getId(); // if (allowedId[id]) { -// if (FaweCache.hasData(id)) { +// if (FaweCache.IMP.hasData(id)) { // return allowed[(id << 4) + block.getData()]; // } // return true; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java index c373441b4..5b1847e26 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java @@ -36,7 +36,7 @@ public class FuzzyRegionSelector extends PassthroughExtent implements RegionSele .player(FawePlayer.wrap(player)) .changeSetNull() .checkMemory(false) - .autoQueue(true) + .autoQueue(false) .build()); this.player = player; this.region = new FuzzyRegion(world, getExtent(), mask); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/MinecraftStructure.java b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/MinecraftStructure.java index 70cdc7498..9bd1f5bc4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/MinecraftStructure.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/MinecraftStructure.java @@ -161,7 +161,7 @@ public class MinecraftStructure implements ClipboardReader, ClipboardWriter { if (width > WARN_SIZE || height > WARN_SIZE || length > WARN_SIZE) { Fawe.debug("A structure longer than 32 is unsupported by minecraft (but probably still works)"); } - Map structure = FaweCache.asMap("version", 1, "author", owner); + Map structure = FaweCache.IMP.asMap("version", 1, "author", owner); // ignored: version / owner MutableBlockVector3 mutable = new MutableBlockVector3(0, 0, 0); Int2ObjectArrayMap indexes = new Int2ObjectArrayMap<>(); @@ -211,10 +211,10 @@ public class MinecraftStructure implements ClipboardReader, ClipboardWriter { List pos = Arrays.asList(point.getX() - min.getX(), point.getY() - min.getY(), point.getZ() - min.getZ()); if (!block.hasNbtData()) { - blocks.add(FaweCache.asMap("state", index, "pos", pos)); + blocks.add(FaweCache.IMP.asMap("state", index, "pos", pos)); } else { blocks.add( - FaweCache.asMap("state", index, "pos", pos, "nbt", block.getNbtData())); + FaweCache.IMP.asMap("state", index, "pos", pos, "nbt", block.getNbtData())); } } } @@ -234,14 +234,14 @@ public class MinecraftStructure implements ClipboardReader, ClipboardWriter { // Replace rotation data nbtMap.put("Rotation", writeRotation(entity.getLocation())); nbtMap.put("id", new StringTag(state.getType().getId())); - Map entityMap = FaweCache.asMap("pos", pos, "blockPos", blockPos, "nbt", nbt); + Map entityMap = FaweCache.IMP.asMap("pos", pos, "blockPos", blockPos, "nbt", nbt); entities.add(entityMap); } } if (!entities.isEmpty()) { structure.put("entities", entities); } - out.writeNamedTag("", FaweCache.asTag(structure)); + out.writeNamedTag("", FaweCache.IMP.asTag(structure)); close(); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweLocalBlockQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweLocalBlockQueue.java index ea5cb6080..e248035d3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweLocalBlockQueue.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweLocalBlockQueue.java @@ -25,10 +25,11 @@ public class FaweLocalBlockQueue extends LocalBlockQueue { public final IQueueExtent IMP; private final LegacyMapper legacyMapper; + private final World world; public FaweLocalBlockQueue(String worldName) { super(worldName); - World world = FaweAPI.getWorld(worldName); + this.world = FaweAPI.getWorld(worldName); IMP = Fawe.get().getQueueHandler().getQueue(world); legacyMapper = LegacyMapper.getInstance(); } @@ -104,7 +105,7 @@ public class FaweLocalBlockQueue extends LocalBlockQueue { @Override public String getWorld() { - return IMP.getId(); + return world.getId(); } @Override @@ -134,7 +135,7 @@ public class FaweLocalBlockQueue extends LocalBlockQueue { @Override public boolean setTile(int x, int y, int z, CompoundTag tag) { - IMP.setTile(x, y, z, (com.sk89q.jnbt.CompoundTag) FaweCache.asTag(tag)); + IMP.setTile(x, y, z, (com.sk89q.jnbt.CompoundTag) FaweCache.IMP.asTag(tag)); return true; } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java index ac39c26ea..5fff70a1e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java @@ -116,7 +116,7 @@ public class FaweSchematicHandler extends SchematicHandler { public void run(OutputStream output) { try { try (PGZIPOutputStream gzip = new PGZIPOutputStream(output)) { - CompoundTag weTag = (CompoundTag) FaweCache.asTag(tag); + CompoundTag weTag = (CompoundTag) FaweCache.IMP.asTag(tag); try (NBTOutputStream nos = new NBTOutputStream(gzip)) { Map map = weTag.getValue(); nos.writeNamedTag("Schematic", map.getOrDefault("Schematic", weTag)); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java b/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java index 7a75d4876..4d5e7ffba 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java @@ -93,7 +93,7 @@ public final class BrushCache { } else { displayMap = ReflectionUtils.getMap(display.getValue()); } - displayMap.put("Lore", FaweCache.asTag(json.split("\\r?\\n"))); + displayMap.put("Lore", FaweCache.IMP.asTag(json.split("\\r?\\n"))); String primary = (String) tool.getPrimary().getSettings().get(BrushSettings.SettingType.BRUSH); String secondary = (String) tool.getSecondary().getSettings().get(BrushSettings.SettingType.BRUSH); if (primary == null) primary = secondary; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java index 7317b703d..8430a9720 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java @@ -5,6 +5,11 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.Callable; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; public final class IOUtil { @@ -79,4 +84,30 @@ public final class IOUtil { out.write(buf, 0, r); } } + + public static Supplier supplier(IntFunction funx, int size) { + return () -> funx.apply(size); + } + + public static Supplier supplier(Supplier supplier, Function modifier) { + return () -> modifier.apply(supplier.get()); + } + + public static Supplier supplier(Supplier supplier, Consumer modifier) { + return () -> { + T instance = supplier.get(); + modifier.accept(instance); + return instance; + }; + } + + public static Supplier supplier(Callable callable) { + return () -> { + try { + return callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java index 965e68ee9..839205736 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java @@ -1,6 +1,8 @@ package com.boydti.fawe.wrappers; +import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.TaskManager; import com.sk89q.jnbt.CompoundTag; @@ -12,6 +14,8 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; @@ -57,6 +61,18 @@ public class WorldWrapper extends AbstractWorld { return world; } + public static World unwrap(Extent extent) { + if (extent.isWorld()) { + if (extent instanceof World) { + return unwrap((World) extent); + } + if (extent instanceof AbstractDelegateExtent) { + return unwrap(new ExtentTraverser<>(extent).find(World.class).get()); + } + } + return null; + } + private WorldWrapper(World parent) { this.parent = parent; } @@ -267,4 +283,9 @@ public class WorldWrapper extends AbstractWorld { public BlockVector3 getSpawnPosition() { return parent.getSpawnPosition(); } + + @Override + public IChunkGet get(int x, int z) { + return parent.get(x, z); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 033c629cf..1c002059a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -164,7 +164,7 @@ import org.slf4j.LoggerFactory; * using the {@link ChangeSetExtent}.

*/ @SuppressWarnings({"FieldCanBeLocal"}) -public class EditSession extends PassthroughExtent implements SimpleWorld, AutoCloseable { +public class EditSession extends PassthroughExtent implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(EditSession.class); @@ -3023,33 +3023,10 @@ public class EditSession extends PassthroughExtent implements SimpleWorld, AutoC Direction.DOWN.toBlockVector(), }; - - @Override - public String getName() { - return worldName; - } - - @Override public @org.jetbrains.annotations.Nullable Path getStoragePath() { - return null; - } - - @Override - public boolean clearContainerBlockContents(BlockVector3 pos) { - BaseBlock block = getFullBlock(pos); - CompoundTag nbt = block.getNbtData(); - if (nbt != null) { - if (nbt.containsKey("items")) { - return setBlock(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), block.toBlockState().toBaseBlock()); - } - } - return false; - } - public boolean regenerate(final Region region) { return regenerate(region, this); } - @Override public boolean regenerate(final Region region, final EditSession session) { return session.regenerate(region, null, null); } @@ -3164,69 +3141,4 @@ public class EditSession extends PassthroughExtent implements SimpleWorld, AutoC } return false; } - - @Override - public void simulateBlockMine(BlockVector3 position) { - TaskManager.IMP.sync((Supplier) () -> { - world.simulateBlockMine(position); - return null; - }); - } - - public boolean generateTree(TreeGenerator.TreeType type, BlockVector3 position) { - return generateTree(type, this, position); - } - - @Override - public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) { - if (getWorld() != null) { - try { - return getWorld().generateTree(type, editSession, position); - } catch (MaxChangedBlocksException e) { - throw new RuntimeException(e); - } - } - return false; - } - - @Override - public WeatherType getWeather() { - return world.getWeather(); - } - - @Override - public long getRemainingWeatherDuration() { - return world.getRemainingWeatherDuration(); - } - - @Override - public void setWeather(WeatherType weatherType) { - world.setWeather(weatherType); - } - - @Override - public void setWeather(WeatherType weatherType, long duration) { - world.setWeather(weatherType, duration); - } - - @Override - public void dropItem(Vector3 position, BaseItemStack item) { - world.dropItem(position, item); - } - - @Override - public boolean playEffect(Vector3 position, int type, int data) { - return world.playEffect(position, type, data); - } - - @Override - public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { - return world.notifyAndLightBlock(position, previousType); - } - - @Override - public BlockVector3 getSpawnPosition() { - return world.getSpawnPosition(); - } - } 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 837669aa9..947e60792 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 @@ -618,7 +618,7 @@ public class SelectionCommands { maskOpt = new IdMask(world); } //TODO Make FuzzyRegionSelector accept actors - newSelector = new FuzzyRegionSelector((Player) actor, editSession, maskOpt); + newSelector = new FuzzyRegionSelector((Player) actor, world, maskOpt); actor.print(BBC.SEL_FUZZY.s()); actor.print(BBC.SEL_LIST.s()); break; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java index 456acaa3d..c5e4b4dd3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java @@ -22,8 +22,12 @@ package com.sk89q.worldedit.entity; import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NbtValued; import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.entity.EntityTypes; import javax.annotation.Nullable; @@ -56,6 +60,10 @@ public class BaseEntity implements NbtValued { setNbtData(nbtData); } + public BaseEntity(CompoundTag tag) { + this(EntityTypes.parse(tag.getString("Id")), tag); + } + /** * Create a new base entity with no NBT data. * @@ -76,6 +84,17 @@ public class BaseEntity implements NbtValued { setNbtData(other.getNbtData()); } + public Location getLocation(Extent extent) { + ListTag posTag = nbtData.getListTag("Pos"); + ListTag rotTag = nbtData.getListTag("Rotation"); + double x = posTag.getDouble(0); + double y = posTag.getDouble(1); + double z = posTag.getDouble(2); + float yaw = rotTag.getFloat(0); + float pitch = rotTag.getFloat(1); + return new Location(extent, x, y, z, yaw, pitch); + } + @Override public boolean hasNbtData() { return true; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector3.java index 48296a3a6..cbee3feb9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector3.java @@ -13,7 +13,7 @@ public class MutableBlockVector3 extends BlockVector3 { } public static MutableBlockVector3 get(int x, int y, int z) { - return FaweCache.MUTABLE_BLOCKVECTOR3.get().setComponents(x, y, z); + return FaweCache.IMP.MUTABLE_BLOCKVECTOR3.get().setComponents(x, y, z); } public MutableBlockVector3() {} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java index 5ed311cfd..4c38e9ea1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java @@ -10,11 +10,11 @@ public class MutableVector3 extends Vector3 { } public static MutableVector3 get(int x, int y, int z) { - return FaweCache.MUTABLE_VECTOR3.get().setComponents(x, y, z); + return FaweCache.IMP.MUTABLE_VECTOR3.get().setComponents(x, y, z); } public static MutableVector3 get(double x, double y, double z) { - return FaweCache.MUTABLE_VECTOR3.get().setComponents(x, y, z); + return FaweCache.IMP.MUTABLE_VECTOR3.get().setComponents(x, y, z); } public MutableVector3(double x, double y, double z) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java index 9923e8c87..4db571854 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/task/LinkedFuture.java @@ -43,12 +43,10 @@ public class LinkedFuture> implements Future { @Override public synchronized T get(long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (task != null) { - T result = task.get(timeout, unit); - if (task != null || !task.isDone()) { + task = task.get(timeout, unit); + if (task != null) { return (T) this; } - task = null; - } return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java index 73e5ca207..5cc38068d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.world; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.implementation.NullChunkGet; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; @@ -132,6 +134,11 @@ public class NullWorld extends AbstractWorld { return BlockVector3.ZERO; } + @Override + public IChunkGet get(int x, int z) { + return NullChunkGet.INSTANCE; + } + @Override public BlockState getBlock(int x, int y, int z) { return BlockTypes.AIR.getDefaultState(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index 81342c6ef..4a705ff7b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.world; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.implementation.IChunkCache; import com.boydti.fawe.object.extent.LightingExtent; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; @@ -47,7 +49,7 @@ import java.util.Locale; /** * Represents a world (dimension). */ -public interface World extends Extent, Keyed { +public interface World extends Extent, Keyed, IChunkCache { /** * Get the name of the world. @@ -290,4 +292,7 @@ public interface World extends Extent, Keyed { default String getId() { return getName().replace(" ", "_").toLowerCase(Locale.ROOT); } + + @Override + IChunkGet get(int x, int z); }