From 82bcc0e9a5decad08c78c6756015569519fd308c Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Mon, 28 Sep 2020 11:13:02 +0100 Subject: [PATCH] Allow "post processing" of chunks (#658) * begin allowing "post processing" of chunks - restores legacy capability to continue saving edits in the background after sending the chunks - speeds up the edit clientside - nail in the coffin of the terrible and staticly coded coreedit - We should totally make IronGolem work so Core* is no longer used by anyone * begin allowing background history saving * Handle post processors in queues properly * Use futures for postprocessing so we're not waiting for them needlessly * better use of closed boolean * Reword --- .../mc1_15_2/BukkitAdapter_1_15_2.java | 16 +++ .../mc1_15_2/BukkitGetBlocks_1_15_2.java | 31 +++++ .../mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java | 129 +++++++++++++++++ .../mc1_16_1/BukkitAdapter_1_16_1.java | 14 ++ .../mc1_16_1/BukkitGetBlocks_1_16_1.java | 32 ++++- .../mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java | 130 ++++++++++++++++++ .../mc1_16_2/BukkitAdapter_1_16_2.java | 20 ++- .../mc1_16_2/BukkitGetBlocks_1_16_2.java | 41 +++++- .../mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java | 130 ++++++++++++++++++ .../com/boydti/fawe/beta/IBatchProcessor.java | 7 + .../java/com/boydti/fawe/beta/IChunkGet.java | 10 ++ .../blocks/FallbackChunkGet.java | 6 + .../implementation/blocks/NullChunkGet.java | 6 + .../implementation/chunk/ChunkHolder.java | 20 ++- .../beta/implementation/chunk/NullChunk.java | 6 + .../processors/BatchProcessorHolder.java | 18 +++ .../processors/ChunkSendProcessor.java | 10 +- .../processors/EmptyBatchProcessor.java | 15 ++ .../ExtentBatchProcessorHolder.java | 13 +- .../processors/IBatchProcessorHolder.java | 17 +++ .../processors/MultiBatchProcessor.java | 31 +++++ .../processors/NullProcessor.java | 7 + .../queue/ParallelQueueExtent.java | 8 +- .../implementation/queue/QueueHandler.java | 7 +- .../queue/SingleThreadQueueExtent.java | 2 + .../java/com/boydti/fawe/config/Settings.java | 6 + .../com/boydti/fawe/jnbt/anvil/MCAChunk.java | 10 ++ .../object/changeset/AbstractChangeSet.java | 8 +- .../fawe/object/extent/HeightBoundExtent.java | 7 + .../fawe/object/extent/MultiRegionExtent.java | 6 + .../boydti/fawe/object/extent/NullExtent.java | 6 + .../object/extent/SingleRegionExtent.java | 6 + .../extent/AbstractDelegateExtent.java | 18 +++ .../com/sk89q/worldedit/extent/Extent.java | 11 +- .../sk89q/worldedit/extent/MaskingExtent.java | 9 ++ .../com/sk89q/worldedit/regions/Region.java | 8 ++ .../worldedit/regions/RegionIntersection.java | 8 ++ 37 files changed, 811 insertions(+), 18 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java index ff9af59c7..d7544aa80 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java @@ -14,6 +14,8 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; import net.jpountz.util.UnsafeUtils; +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.BiomeStorage; import net.minecraft.server.v1_15_R1.Block; import net.minecraft.server.v1_15_R1.Chunk; import net.minecraft.server.v1_15_R1.ChunkCoordIntPair; @@ -61,6 +63,8 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter { private final static Field fieldDirtyCount; private final static Field fieldDirtyBits; + private static final Field fieldBiomeArray; + private final static MethodHandle methodGetVisibleChunk; public final static MethodHandle methodSetLightNibbleArray; @@ -91,6 +95,9 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter { fieldDirtyBits = PlayerChunk.class.getDeclaredField("r"); fieldDirtyBits.setAccessible(true); + fieldBiomeArray = BiomeStorage.class.getDeclaredField("h"); + fieldBiomeArray.setAccessible(true); + Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class); declaredGetVisibleChunk.setAccessible(true); methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk); @@ -307,4 +314,13 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter { fieldTickingBlockCount.setShort(section, (short) tickingBlockCount); fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount); } + + public static BiomeBase[] getBiomeArray(BiomeStorage storage) { + try { + return (BiomeBase[]) fieldBiomeArray.get(storage); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java index 5d986758c..504697b57 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java @@ -2,6 +2,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_15_2; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.blocks.CharBlocks; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; @@ -86,6 +87,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { public int X, Z; public NibbleArray[] blockLight = new NibbleArray[16]; public NibbleArray[] skyLight = new NibbleArray[16]; + private boolean createCopy = false; + private BukkitGetBlocks_1_15_2_Copy copy = null; public BukkitGetBlocks_1_15_2(World world, int X, int Z) { this(((CraftWorld) world).getHandle(), X, Z); @@ -97,6 +100,21 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { this.Z = Z; } + @Override + public void setCreateCopy(boolean createCopy) { + this.createCopy = createCopy; + } + + @Override + public boolean isCreateCopy() { + return createCopy; + } + + @Override + public IChunkGet getCopy() { + return copy; + } + public int getX() { return X; } @@ -292,6 +310,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { @Override public > T call(IChunkSet set, Runnable finalizer) { + copy = createCopy ? new BukkitGetBlocks_1_15_2_Copy(world, getX(), getZ()) : null; try { WorldServer nmsWorld = world; Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); @@ -316,6 +335,9 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { if (ordinal != 0) { TileEntity tile = entry.getValue(); nmsChunk.removeTileEntity(tile.getPosition()); + if (createCopy) { + copy.storeTile(tile); + } } } } @@ -329,6 +351,9 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { if (!set.hasSection(layer)){ continue; } + if (createCopy) { + copy.storeSection(layer); + } bitMask |= 1 << layer; @@ -384,6 +409,9 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { if (biomes != null) { // set biomes BiomeStorage currentBiomes = nmsChunk.getBiomeIndex(); + if (createCopy) { + copy.storeBiomes(currentBiomes); + } for (int z = 0, i = 0; z < 16; z++) { for (int x = 0; x < 16; x++, i++) { final BiomeType biome = biomes[i]; @@ -446,6 +474,9 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { while (iter.hasNext()) { final Entity entity = iter.next(); if (entityRemoves.contains(entity.getUniqueID())) { + if (createCopy) { + copy.storeEntity(entity); + } iter.remove(); removeEntity(entity); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java new file mode 100644 index 000000000..07b06075c --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java @@ -0,0 +1,129 @@ +package com.boydti.fawe.bukkit.adapter.mc1_15_2; + + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2; +import com.google.common.base.Suppliers; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.math.BlockVector3; +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.BlockTypesCache; +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.BiomeStorage; +import net.minecraft.server.v1_15_R1.Entity; +import net.minecraft.server.v1_15_R1.NBTTagCompound; +import net.minecraft.server.v1_15_R1.TileEntity; +import net.minecraft.server.v1_15_R1.WorldServer; +import org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class BukkitGetBlocks_1_15_2_Copy extends BukkitGetBlocks_1_15_2 { + + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); + private BiomeStorage biomeStorage; + private final char[][] blocks = new char[16][4096]; + + protected BukkitGetBlocks_1_15_2_Copy(WorldServer world, int X, int Z) { + super(world, X, Z); + } + + protected void storeTile(TileEntity tile) { + tiles.put(BlockVector3.at(tile.getPosition().getX(), tile.getPosition().getY(), tile.getPosition().getZ()), + new LazyCompoundTag_1_15_2(Suppliers.memoize(() -> tile.save(new NBTTagCompound())))); + } + + @Override + public Map getTiles() { + return tiles; + } + + @Override + @Nullable + public CompoundTag getTile(int x, int y, int z) { + return tiles.get(BlockVector3.at(x, y, z)); + } + + protected void storeEntity(Entity entity) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + NBTTagCompound tag = new NBTTagCompound(); + entities.add((CompoundTag) adapter.toNative(entity.save(tag))); + } + + @Override + public Set getEntities() { + return this.entities; + } + + @Override + public CompoundTag getEntity(UUID uuid) { + for (CompoundTag tag : entities) { + UUID tagUUID; + if (tag.containsKey("UUID")) { + int[] arr = tag.getIntArray("UUID"); + tagUUID = new UUID((long) arr[0] << 32 | (arr[1] & 0xFFFFFFFFL), (long) arr[2] << 32 | (arr[3] & 0xFFFFFFFFL)); + } else if (tag.containsKey("UUIDMost")) { + tagUUID = new UUID(tag.getLong("UUIDMost"), tag.getLong("UUIDLeast")); + } else if (tag.containsKey("PersistentIDMSB")) { + tagUUID = new UUID(tag.getLong("PersistentIDMSB"), tag.getLong("PersistentIDLSB")); + } else { + return null; + } + if (uuid.equals(tagUUID)) { + return tag; + } + } + return null; + } + + protected void storeBiomes(BiomeStorage biomeStorage) { + this.biomeStorage = new BiomeStorage(BukkitAdapter_1_15_2.getBiomeArray(biomeStorage).clone()); + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + BiomeBase base = null; + if (y == -1) { + for (y = 0; y < FaweCache.IMP.WORLD_HEIGHT; y++) { + base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2); + if (base != null) break; + } + } else { + base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2); + } + return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null; + } + + protected void storeSection(int layer) { + update(layer, blocks[layer]); + } + + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + BlockState state = BlockTypesCache.states[get(x, y, z)]; + return state.toBaseBlock(this, x, y, z); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypesCache.states[get(x, y, z)]; + } + + @Override + public char get(int x, int y, int z) { + final int layer = y >> 4; + final int index = (y & 15) << 8 | z << 4 | x; + return blocks[layer][index]; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java index aa8e20d18..eb22f5743 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitAdapter_1_16_1.java @@ -47,6 +47,8 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter { private final static Field fieldDirtyCount; private final static Field fieldDirtyBits; + private static final Field fieldBiomeArray; + private final static MethodHandle methodGetVisibleChunk; private static final int CHUNKSECTION_BASE; @@ -78,6 +80,9 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter { fieldDirtyBits = PlayerChunk.class.getDeclaredField("r"); fieldDirtyBits.setAccessible(true); + fieldBiomeArray = BiomeStorage.class.getDeclaredField("h"); + fieldBiomeArray.setAccessible(true); + Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class); declaredGetVisibleChunk.setAccessible(true); methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk); @@ -291,4 +296,13 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter { fieldTickingBlockCount.setShort(section, (short) tickingBlockCount); fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount); } + + public static BiomeBase[] getBiomeArray(BiomeStorage storage) { + try { + return (BiomeBase[]) fieldBiomeArray.get(storage); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index 95c7f312a..5bd3fb709 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -2,6 +2,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_16_1; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.blocks.CharBlocks; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; @@ -11,7 +12,6 @@ import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.AdaptedMap; -import com.boydti.fawe.object.collection.BitArray; import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.google.common.base.Suppliers; import com.google.common.collect.Iterables; @@ -55,6 +55,8 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { public int X, Z; public NibbleArray[] blockLight = new NibbleArray[16]; public NibbleArray[] skyLight = new NibbleArray[16]; + private boolean createCopy = false; + private BukkitGetBlocks_1_16_1_Copy copy = null; public BukkitGetBlocks_1_16_1(World world, int X, int Z) { this(((CraftWorld) world).getHandle(), X, Z); @@ -66,6 +68,21 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { this.Z = Z; } + @Override + public void setCreateCopy(boolean createCopy) { + this.createCopy = createCopy; + } + + @Override + public boolean isCreateCopy() { + return createCopy; + } + + @Override + public IChunkGet getCopy() { + return copy; + } + public int getX() { return X; } @@ -259,6 +276,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { @Override public > T call(IChunkSet set, Runnable finalizer) { + copy = createCopy ? new BukkitGetBlocks_1_16_1_Copy(world, getX(), getZ()) : null; try { WorldServer nmsWorld = world; Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); @@ -283,6 +301,9 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { if (ordinal != 0) { TileEntity tile = entry.getValue(); nmsChunk.removeTileEntity(tile.getPosition()); + if (createCopy) { + copy.storeTile(tile); + } } } } @@ -296,6 +317,9 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { if (!set.hasSection(layer)){ continue; } + if (createCopy) { + copy.storeSection(layer); + } bitMask |= 1 << layer; @@ -353,6 +377,9 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { if (biomes != null) { // set biomes BiomeStorage currentBiomes = nmsChunk.getBiomeIndex(); + if (createCopy) { + copy.storeBiomes(currentBiomes); + } for (int z = 0, i = 0; z < 16; z++) { for (int x = 0; x < 16; x++, i++) { final BiomeType biome = biomes[i]; @@ -415,6 +442,9 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { while (iter.hasNext()) { final Entity entity = iter.next(); if (entityRemoves.contains(entity.getUniqueID())) { + if (createCopy) { + copy.storeEntity(entity); + } iter.remove(); removeEntity(entity); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java new file mode 100644 index 000000000..42364ed89 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java @@ -0,0 +1,130 @@ +package com.boydti.fawe.bukkit.adapter.mc1_16_1; + + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; +import com.google.common.base.Suppliers; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.math.BlockVector3; +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.BlockTypesCache; +import net.minecraft.server.v1_16_R1.BiomeBase; +import net.minecraft.server.v1_16_R1.BiomeStorage; +import net.minecraft.server.v1_16_R1.Entity; +import net.minecraft.server.v1_16_R1.IRegistry; +import net.minecraft.server.v1_16_R1.NBTTagCompound; +import net.minecraft.server.v1_16_R1.TileEntity; +import net.minecraft.server.v1_16_R1.WorldServer; +import org.bukkit.craftbukkit.v1_16_R1.block.CraftBlock; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class BukkitGetBlocks_1_16_1_Copy extends BukkitGetBlocks_1_16_1 { + + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); + private BiomeStorage biomeStorage; + private final char[][] blocks = new char[16][4096]; + + protected BukkitGetBlocks_1_16_1_Copy(WorldServer world, int X, int Z) { + super(world, X, Z); + } + + protected void storeTile(TileEntity tile) { + tiles.put(BlockVector3.at(tile.getPosition().getX(), tile.getPosition().getY(), tile.getPosition().getZ()), + new LazyCompoundTag_1_16_1(Suppliers.memoize(() -> tile.save(new NBTTagCompound())))); + } + + @Override + public Map getTiles() { + return tiles; + } + + @Override + @Nullable + public CompoundTag getTile(int x, int y, int z) { + return tiles.get(BlockVector3.at(x, y, z)); + } + + protected void storeEntity(Entity entity) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + NBTTagCompound tag = new NBTTagCompound(); + entities.add((CompoundTag) adapter.toNative(entity.save(tag))); + } + + @Override + public Set getEntities() { + return this.entities; + } + + @Override + public CompoundTag getEntity(UUID uuid) { + for (CompoundTag tag : entities) { + UUID tagUUID; + if (tag.containsKey("UUID")) { + int[] arr = tag.getIntArray("UUID"); + tagUUID = new UUID((long) arr[0] << 32 | (arr[1] & 0xFFFFFFFFL), (long) arr[2] << 32 | (arr[3] & 0xFFFFFFFFL)); + } else if (tag.containsKey("UUIDMost")) { + tagUUID = new UUID(tag.getLong("UUIDMost"), tag.getLong("UUIDLeast")); + } else if (tag.containsKey("PersistentIDMSB")) { + tagUUID = new UUID(tag.getLong("PersistentIDMSB"), tag.getLong("PersistentIDLSB")); + } else { + return null; + } + if (uuid.equals(tagUUID)) { + return tag; + } + } + return null; + } + + protected void storeBiomes(BiomeStorage biomeStorage) { + this.biomeStorage = new BiomeStorage(BukkitAdapter_1_16_1.getBiomeArray(biomeStorage).clone()); + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + BiomeBase base = null; + if (y == -1) { + for (y = 0; y < FaweCache.IMP.WORLD_HEIGHT; y++) { + base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2); + if (base != null) break; + } + } else { + base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2); + } + return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null; + } + + protected void storeSection(int layer) { + update(layer, blocks[layer]); + } + + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + BlockState state = BlockTypesCache.states[get(x, y, z)]; + return state.toBaseBlock(this, x, y, z); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypesCache.states[get(x, y, z)]; + } + + @Override + public char get(int x, int y, int z) { + final int layer = y >> 4; + final int index = (y & 15) << 8 | z << 4 | x; + return blocks[layer][index]; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitAdapter_1_16_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitAdapter_1_16_2.java index 7363160bc..9ce2e25f1 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitAdapter_1_16_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitAdapter_1_16_2.java @@ -15,6 +15,8 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; import net.jpountz.util.UnsafeUtils; +import net.minecraft.server.v1_16_R2.BiomeBase; +import net.minecraft.server.v1_16_R2.BiomeStorage; import net.minecraft.server.v1_16_R2.Block; import net.minecraft.server.v1_16_R2.Chunk; import net.minecraft.server.v1_16_R2.ChunkCoordIntPair; @@ -64,6 +66,8 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter { private static final Field fieldDirty; private static final Field fieldDirtyBlocks; + private static final Field fieldBiomeArray; + private static final MethodHandle methodGetVisibleChunk; private static final int CHUNKSECTION_BASE; @@ -95,6 +99,9 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter { fieldDirtyBlocks = PlayerChunk.class.getDeclaredField("dirtyBlocks"); fieldDirtyBlocks.setAccessible(true); + fieldBiomeArray = BiomeStorage.class.getDeclaredField("h"); + fieldBiomeArray.setAccessible(true); + Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class); declaredGetVisibleChunk.setAccessible(true); methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk); @@ -286,7 +293,7 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter { .setType(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), Block.getByCombinedId(ordinal))); } - } catch (final IllegalAccessException | NoSuchFieldException e) { + } catch (final IllegalAccessException e) { throw new RuntimeException(e); } @@ -301,9 +308,18 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter { return new ChunkSection(layer << 4); } - public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException { + public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws IllegalAccessException { fieldFluidCount.setShort(section, (short) 0); // TODO FIXME fieldTickingBlockCount.setShort(section, (short) tickingBlockCount); fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount); } + + public static BiomeBase[] getBiomeArray(BiomeStorage storage) { + try { + return (BiomeBase[]) fieldBiomeArray.get(storage); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java index 32ea97d44..1965b2830 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java @@ -2,6 +2,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_16_2; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.blocks.CharBlocks; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; @@ -11,7 +12,6 @@ import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_16_2.nbt.LazyCompoundTag_1_16_2; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.AdaptedMap; -import com.boydti.fawe.object.collection.BitArray; import com.boydti.fawe.object.collection.BitArrayUnstretched; import com.google.common.base.Suppliers; import com.google.common.collect.Iterables; @@ -55,6 +55,8 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { public int X, Z; public NibbleArray[] blockLight = new NibbleArray[16]; public NibbleArray[] skyLight = new NibbleArray[16]; + private boolean createCopy = false; + private BukkitGetBlocks_1_16_2_Copy copy = null; public BukkitGetBlocks_1_16_2(World world, int X, int Z) { this(((CraftWorld) world).getHandle(), X, Z); @@ -66,6 +68,21 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { this.Z = Z; } + @Override + public void setCreateCopy(boolean createCopy) { + this.createCopy = createCopy; + } + + @Override + public boolean isCreateCopy() { + return createCopy; + } + + @Override + public IChunkGet getCopy() { + return copy; + } + public int getX() { return X; } @@ -107,7 +124,8 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); } - @Override public int getSkyLight(int x, int y, int z) { + @Override + public int getSkyLight(int x, int y, int z) { int layer = y >> 4; if (skyLight[layer] == null) { SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); @@ -126,7 +144,8 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); } - @Override public int getEmmittedLight(int x, int y, int z) { + @Override + public int getEmmittedLight(int x, int y, int z) { int layer = y >> 4; if (blockLight[layer] == null) { SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); @@ -145,7 +164,8 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l))); } - @Override public int[] getHeightMap(HeightMapType type) { + @Override + public int[] getHeightMap(HeightMapType type) { long[] longArray = getChunk().heightMap.get(HeightMap.Type.valueOf(type.name())).a(); BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256, longArray); return bitArray.toRaw(new int[256]); @@ -259,6 +279,7 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { @Override public > T call(IChunkSet set, Runnable finalizer) { + copy = createCopy ? new BukkitGetBlocks_1_16_2_Copy(world, getX(), getZ()) : null; try { WorldServer nmsWorld = world; Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); @@ -283,6 +304,9 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { if (ordinal != 0) { TileEntity tile = entry.getValue(); nmsChunk.removeTileEntity(tile.getPosition()); + if (createCopy) { + copy.storeTile(tile); + } } } } @@ -296,6 +320,9 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { if (!set.hasSection(layer)){ continue; } + if (createCopy) { + copy.storeSection(layer); + } bitMask |= 1 << layer; @@ -353,6 +380,9 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { if (biomes != null) { // set biomes BiomeStorage currentBiomes = nmsChunk.getBiomeIndex(); + if (createCopy) { + copy.storeBiomes(currentBiomes); + } for (int z = 0, i = 0; z < 16; z++) { for (int x = 0; x < 16; x++, i++) { final BiomeType biome = biomes[i]; @@ -415,6 +445,9 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { while (iter.hasNext()) { final Entity entity = iter.next(); if (entityRemoves.contains(entity.getUniqueID())) { + if (createCopy) { + copy.storeEntity(entity); + } iter.remove(); removeEntity(entity); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java new file mode 100644 index 000000000..6981d63a7 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java @@ -0,0 +1,130 @@ +package com.boydti.fawe.bukkit.adapter.mc1_16_2; + + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.adapter.mc1_16_2.nbt.LazyCompoundTag_1_16_2; +import com.google.common.base.Suppliers; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.math.BlockVector3; +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.BlockTypesCache; +import net.minecraft.server.v1_16_R2.BiomeBase; +import net.minecraft.server.v1_16_R2.BiomeStorage; +import net.minecraft.server.v1_16_R2.Entity; +import net.minecraft.server.v1_16_R2.IRegistry; +import net.minecraft.server.v1_16_R2.NBTTagCompound; +import net.minecraft.server.v1_16_R2.TileEntity; +import net.minecraft.server.v1_16_R2.WorldServer; +import org.bukkit.craftbukkit.v1_16_R2.block.CraftBlock; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class BukkitGetBlocks_1_16_2_Copy extends BukkitGetBlocks_1_16_2 { + + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); + private BiomeStorage biomeStorage; + private final char[][] blocks = new char[16][4096]; + + protected BukkitGetBlocks_1_16_2_Copy(WorldServer world, int X, int Z) { + super(world, X, Z); + } + + protected void storeTile(TileEntity tile) { + tiles.put(BlockVector3.at(tile.getPosition().getX(), tile.getPosition().getY(), tile.getPosition().getZ()), + new LazyCompoundTag_1_16_2(Suppliers.memoize(() -> tile.save(new NBTTagCompound())))); + } + + @Override + public Map getTiles() { + return tiles; + } + + @Override + @Nullable + public CompoundTag getTile(int x, int y, int z) { + return tiles.get(BlockVector3.at(x, y, z)); + } + + protected void storeEntity(Entity entity) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + NBTTagCompound tag = new NBTTagCompound(); + entities.add((CompoundTag) adapter.toNative(entity.save(tag))); + } + + @Override + public Set getEntities() { + return this.entities; + } + + @Override + public CompoundTag getEntity(UUID uuid) { + for (CompoundTag tag : entities) { + UUID tagUUID; + if (tag.containsKey("UUID")) { + int[] arr = tag.getIntArray("UUID"); + tagUUID = new UUID((long) arr[0] << 32 | (arr[1] & 0xFFFFFFFFL), (long) arr[2] << 32 | (arr[3] & 0xFFFFFFFFL)); + } else if (tag.containsKey("UUIDMost")) { + tagUUID = new UUID(tag.getLong("UUIDMost"), tag.getLong("UUIDLeast")); + } else if (tag.containsKey("PersistentIDMSB")) { + tagUUID = new UUID(tag.getLong("PersistentIDMSB"), tag.getLong("PersistentIDLSB")); + } else { + return null; + } + if (uuid.equals(tagUUID)) { + return tag; + } + } + return null; + } + + protected void storeBiomes(BiomeStorage biomeStorage) { + this.biomeStorage = new BiomeStorage(biomeStorage.g, BukkitAdapter_1_16_2.getBiomeArray(biomeStorage).clone()); + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + BiomeBase base = null; + if (y == -1) { + for (y = 0; y < FaweCache.IMP.WORLD_HEIGHT; y++) { + base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2); + if (base != null) break; + } + } else { + base = biomeStorage.getBiome(x >> 2, y >> 2, z >> 2); + } + return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null; + } + + protected void storeSection(int layer) { + update(layer, blocks[layer]); + } + + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + BlockState state = BlockTypesCache.states[get(x, y, z)]; + return state.toBaseBlock(this, x, y, z); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypesCache.states[get(x, y, z)]; + } + + @Override + public char get(int x, int y, int z) { + final int layer = y >> 4; + final int index = (y & 15) << 8 | z << 4 | x; + return blocks[layer][index]; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java index 477420967..33d8afd5c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Map; import java.util.Set; +import java.util.concurrent.Future; import java.util.function.Function; public interface IBatchProcessor { @@ -23,6 +24,8 @@ public interface IBatchProcessor { */ IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set); + Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set); + default boolean processGet(int chunkX, int chunkZ) { return true; } @@ -115,6 +118,10 @@ public interface IBatchProcessor { return MultiBatchProcessor.of(this, other); } + default IBatchProcessor joinPost(IBatchProcessor other) { + return MultiBatchProcessor.of(this, other); + } + default void flush() {} /** 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 76e9dc607..887006799 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 @@ -7,6 +7,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; +import org.jetbrains.annotations.Nullable; import java.util.UUID; import java.util.concurrent.Future; @@ -46,4 +47,13 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { > T call(IChunkSet set, Runnable finalize); CompoundTag getEntity(UUID uuid); + + void setCreateCopy(boolean createCopy); + + boolean isCreateCopy(); + + @Nullable + default IChunkGet getCopy() { + return null; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java index d407f14c1..1a6ab8612 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java @@ -98,6 +98,12 @@ public class FallbackChunkGet implements IChunkGet { return null; } + @Override public void setCreateCopy(boolean createCopy) {} + + @Override public boolean isCreateCopy() { + return false; + } + @Override public boolean trim(boolean aggressive) { return true; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java index c8efade2d..a59b31b40 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java @@ -63,6 +63,12 @@ public final class NullChunkGet implements IChunkGet { return null; } + @Override public void setCreateCopy(boolean createCopy) {} + + @Override public boolean isCreateCopy() { + return false; + } + public boolean trim(boolean aggressive) { return true; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java index 6c311b0e9..d0cde6de2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java @@ -9,6 +9,7 @@ import com.boydti.fawe.beta.IQueueChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.implementation.lighting.HeightMapType; +import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor; import com.boydti.fawe.beta.implementation.queue.Pool; import com.boydti.fawe.config.Settings; import com.sk89q.jnbt.CompoundTag; @@ -20,11 +21,11 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import org.jetbrains.annotations.Range; +import javax.annotation.Nullable; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; -import javax.annotation.Nullable; /** * An abstract {@link IChunk} class that implements basic get/set blocks @@ -47,6 +48,7 @@ public class ChunkHolder> implements IQueueChunk { private boolean fastmode; private int bitMask = -1; // Allow forceful setting of bitmask (for lighting) private boolean isInit = false; // Lighting handles queue differently. It relies on the chunk cache and not doing init. + private boolean createCopy = false; private ChunkHolder() { this.delegate = NULL; @@ -140,6 +142,14 @@ public class ChunkHolder> implements IQueueChunk { return delegate.get(this).getEntity(uuid); } + @Override public void setCreateCopy(boolean createCopy) { + this.createCopy = createCopy; + } + + @Override public boolean isCreateCopy() { + return createCopy; + } + private static final IBlockDelegate BOTH = new IBlockDelegate() { @Override public IChunkGet get(ChunkHolder chunk) { @@ -778,9 +788,15 @@ public class ChunkHolder> implements IQueueChunk { if (set != null) { IChunkGet get = getOrCreateGet(); get.trim(false); + boolean postProcess = !(getExtent().getPostProcessor() instanceof EmptyBatchProcessor); + get.setCreateCopy(postProcess); set = getExtent().processSet(this, get, set); - if (set != null) { + try { return get.call(set, finalize); + } finally { + if (postProcess) { + getExtent().postProcessSet(this, get.getCopy(), set); + } } } return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java index cd356bb1a..35c99c17f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java @@ -171,6 +171,12 @@ public final class NullChunk implements IQueueChunk { return null; } + @Override public void setCreateCopy(boolean createCopy) {} + + @Override public boolean isCreateCopy() { + return false; + } + @Nullable public > T call(@Nullable IChunkSet set, @Nullable Runnable finalize) { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java index e3d08fbf1..17a700621 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java @@ -5,19 +5,32 @@ import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; +import java.util.concurrent.Future; + public class BatchProcessorHolder implements IBatchProcessorHolder { private IBatchProcessor processor = EmptyBatchProcessor.getInstance(); + private IBatchProcessor postProcessor = EmptyBatchProcessor.getInstance(); @Override public IBatchProcessor getProcessor() { return processor; } + @Override + public IBatchProcessor getPostProcessor() { + return postProcessor; + } + @Override public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { return getProcessor().processSet(chunk, get, set); } + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return getPostProcessor().postProcessSet(chunk, get, set); + } + @Override public void flush() { getProcessor().flush(); @@ -28,6 +41,11 @@ public class BatchProcessorHolder implements IBatchProcessorHolder { this.processor = set; } + @Override + public void setPostProcessor(IBatchProcessor set) { + this.postProcessor = set; + } + @Override public String toString() { IBatchProcessor tmp = getProcessor(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java index 00951a626..8767430b6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java @@ -12,6 +12,8 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.World; import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.function.Supplier; public class ChunkSendProcessor implements IBatchProcessor { @@ -65,6 +67,12 @@ public class ChunkSendProcessor implements IBatchProcessor { return set; } + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + // Doesn't need to do anything + return CompletableFuture.completedFuture(set); + } + public IBlocks combine(IChunk chunk, IChunkGet get, IChunkSet set) { return new CombinedBlocks(get, set, 0); } @@ -73,4 +81,4 @@ public class ChunkSendProcessor implements IBatchProcessor { public Extent construct(Extent child) { throw new UnsupportedOperationException("Processing only"); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java index 58af2692c..6708d9d35 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java @@ -9,6 +9,9 @@ import com.sk89q.worldedit.extent.Extent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + public final class EmptyBatchProcessor implements IBatchProcessor { private static final EmptyBatchProcessor instance = new EmptyBatchProcessor(); @@ -26,11 +29,23 @@ public final class EmptyBatchProcessor implements IBatchProcessor { return set; } + @Override + @NotNull + public Future postProcessSet(@Nullable IChunk chunk, @Nullable IChunkGet get, @Nullable IChunkSet set) { + // Doesn't need to do anything + return CompletableFuture.completedFuture(set); + } + @NotNull public IBatchProcessor join(@Nullable IBatchProcessor other) { return other; } + @NotNull + public IBatchProcessor joinPost(@Nullable IBatchProcessor other) { + return other; + } + private EmptyBatchProcessor() { } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ExtentBatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ExtentBatchProcessorHolder.java index 870035c2c..8272ec74c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ExtentBatchProcessorHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ExtentBatchProcessorHolder.java @@ -1,6 +1,7 @@ package com.boydti.fawe.beta.implementation.processors; import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.changeset.AbstractChangeSet; import com.sk89q.worldedit.extent.Extent; @@ -11,9 +12,19 @@ public abstract class ExtentBatchProcessorHolder extends BatchProcessorHolder im return this; } + @Override + public Extent addPostProcessor(IBatchProcessor processor) { + joinPost(processor); + return this; + } + @Override public Extent enableHistory(AbstractChangeSet changeSet) { - return this.addProcessor(changeSet); + if (Settings.IMP.EXPERIMENTAL.SEND_BEFORE_HISTORY) { + return this.addPostProcessor(changeSet); + } else { + return this.addProcessor(changeSet); + } } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/IBatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/IBatchProcessorHolder.java index 1a2770cd4..0caeffd91 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/IBatchProcessorHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/IBatchProcessorHolder.java @@ -6,6 +6,8 @@ import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.sk89q.worldedit.extent.Extent; +import java.util.concurrent.Future; + /** * Holds a batch processor * (Join and remove operations affect the held processor) @@ -13,17 +15,26 @@ import com.sk89q.worldedit.extent.Extent; public interface IBatchProcessorHolder extends IBatchProcessor { IBatchProcessor getProcessor(); + IBatchProcessor getPostProcessor(); + /** * set the held processor * @param set */ void setProcessor(IBatchProcessor set); + void setPostProcessor(IBatchProcessor set); + @Override default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { return getProcessor().processSet(chunk, get, set); } + @Override + default Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return getPostProcessor().postProcessSet(chunk, get, set); + } + @Override default boolean processGet(int chunkX, int chunkZ) { return getProcessor().processGet(chunkX, chunkZ); @@ -40,6 +51,12 @@ public interface IBatchProcessorHolder extends IBatchProcessor { return this; } + @Override + default IBatchProcessor joinPost(IBatchProcessor other) { + setPostProcessor(getPostProcessor().joinPost(other)); + return this; + } + @Override default IBatchProcessor remove(Class clazz) { setProcessor(getProcessor().remove(clazz)); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java index 8a5ef7fa7..f93fc9865 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.Map; import java.util.function.Supplier; @@ -84,6 +86,23 @@ public class MultiBatchProcessor implements IBatchProcessor { } } + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + try { + for (int i = processors.length - 1 ; i >= 0; i--) { + IBatchProcessor processor = processors[i]; + set = processor.postProcessSet(chunk, get, set).get(); + if (set == null) { + return null; + } + } + return CompletableFuture.completedFuture(set); + } catch (Throwable e) { + e.printStackTrace(); + return null; + } + } + @Override public boolean processGet(int chunkX, int chunkZ) { for (IBatchProcessor processor : this.processors) { @@ -121,6 +140,18 @@ public class MultiBatchProcessor implements IBatchProcessor { return this; } + @Override + public IBatchProcessor joinPost(IBatchProcessor other) { + if (other instanceof MultiBatchProcessor) { + for (IBatchProcessor processor : ((MultiBatchProcessor) other).processors) { + addBatchProcessor(processor); + } + } else { + addBatchProcessor(other); + } + return this; + } + @Override public void flush() { for (IBatchProcessor processor : this.processors) processor.flush(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java index b9f327987..dbabe51f0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java @@ -9,6 +9,8 @@ import com.sk89q.worldedit.extent.NullExtent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.Future; + public final class NullProcessor implements IBatchProcessor { private static final NullProcessor instance = new NullProcessor(); @@ -21,6 +23,11 @@ public final class NullProcessor implements IBatchProcessor { return null; } + @Nullable + public Future postProcessSet(@NotNull IChunk chunk, @NotNull IChunkGet get, @NotNull IChunkSet set) { + return null; + } + @NotNull public Extent construct(@NotNull Extent child) { return new NullExtent(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java index bab16d39a..953ee7be3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java @@ -43,14 +43,16 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap private final World world; private final QueueHandler handler; private final BatchProcessorHolder processor; + private final BatchProcessorHolder postProcessor; private int changes; private final boolean fastmode; public ParallelQueueExtent(QueueHandler handler, World world, boolean fastmode) { - super(handler.getQueue(world, new BatchProcessorHolder())); + super(handler.getQueue(world, new BatchProcessorHolder(), new BatchProcessorHolder())); this.world = world; this.handler = handler; this.processor = (BatchProcessorHolder) getExtent().getProcessor(); + this.postProcessor = (BatchProcessorHolder) getExtent().getPostProcessor(); this.fastmode = fastmode; } @@ -63,19 +65,21 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap public boolean cancel() { if (super.cancel()) { processor.setProcessor(new NullExtent(this, FaweCache.MANUAL)); + postProcessor.setPostProcessor(new NullExtent(this, FaweCache.MANUAL)); return true; } return false; } private IQueueExtent getNewQueue() { - return wrapQueue(handler.getQueue(this.world, this.processor)); + return wrapQueue(handler.getQueue(this.world, this.processor, this.postProcessor)); } @Override public IQueueExtent wrapQueue(IQueueExtent queue) { // TODO wrap queue.setProcessor(this.processor); + queue.setPostProcessor(this.postProcessor); return queue; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/QueueHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/QueueHandler.java index aaa5ca89d..f70db2e0d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/QueueHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/QueueHandler.java @@ -280,10 +280,10 @@ public abstract class QueueHandler implements Trimable, Runnable { public abstract void endSet(boolean parallel); public IQueueExtent getQueue(World world) { - return getQueue(world, null); + return getQueue(world, null, null); } - public IQueueExtent getQueue(World world, IBatchProcessor processor) { + public IQueueExtent getQueue(World world, IBatchProcessor processor, IBatchProcessor postProcessor) { final IQueueExtent queue = pool(); IChunkCache cacheGet = getOrCreateWorldCache(world); IChunkCache set = null; // TODO cache? @@ -291,6 +291,9 @@ public abstract class QueueHandler implements Trimable, Runnable { if (processor != null) { queue.setProcessor(processor); } + if (postProcessor != null) { + queue.setPostProcessor(postProcessor); + } return queue; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java index 8195fd826..f8056de24 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java @@ -113,6 +113,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen this.currentThread = null; this.initialized = false; this.setProcessor(EmptyBatchProcessor.getInstance()); + this.setPostProcessor(EmptyBatchProcessor.getInstance()); } /** @@ -132,6 +133,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen this.cacheGet = get; this.cacheSet = set; this.setProcessor(EmptyBatchProcessor.getInstance()); + this.setPostProcessor(EmptyBatchProcessor.getInstance()); initialized = true; } 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 7bb4bf232..7ad0bcd45 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 @@ -393,6 +393,12 @@ public class Settings extends Config { "This will increase time taken slightly." }) public boolean ALLOW_TICK_EXISTING = true; + @Comment({ + "Do not wait for a chunk's history to save before sending it", + " - Undo/redo commands will wait until the history has been written to disk before executing", + " - Requires combine_stages = true" + }) + public boolean SEND_BEFORE_HISTORY = true; } public static class PLOTSQUARED_INTEGRATION { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java index 5ad437a09..7b0b7973e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java @@ -62,6 +62,8 @@ public class MCAChunk implements IChunk { public int chunkX; public int chunkZ; + private boolean createCopy = false; + public MCAChunk() {} private boolean readLayer(Section section) { @@ -602,6 +604,14 @@ public class MCAChunk implements IChunk { return this.entities.get(uuid); } + @Override public void setCreateCopy(boolean createCopy) { + this.createCopy = createCopy; + } + + @Override public boolean isCreateCopy() { + return createCopy; + } + @Override public Future call(IChunkSet set, Runnable finalize) { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java index 89827082f..e0c4b84c5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java @@ -59,7 +59,6 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { if (closed) { return; } - closed = true; waitingAsync.incrementAndGet(); TaskManager.IMP.async(() -> { waitingAsync.decrementAndGet(); @@ -97,8 +96,8 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { @Override public void close() throws IOException { if (!closed) { - closed = true; flush(); + closed = true; } } @@ -204,6 +203,11 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { return set; } + @Override + public Future postProcessSet(final IChunk chunk, final IChunkGet get,final IChunkSet set) { + return (Future) addWriteTask(() -> processSet(chunk, get, set)); + } + public abstract void addTileCreate(CompoundTag tag); public abstract void addTileRemove(CompoundTag tag); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/HeightBoundExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/HeightBoundExtent.java index 624113cdd..86aac2e94 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/HeightBoundExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/HeightBoundExtent.java @@ -10,6 +10,8 @@ import com.sk89q.worldedit.regions.Region; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; public class HeightBoundExtent extends FaweRegionExtent { @@ -51,4 +53,9 @@ public class HeightBoundExtent extends FaweRegionExtent { } return null; } + + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return CompletableFuture.completedFuture(set); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiRegionExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiRegionExtent.java index 21851ff8b..4b0bbc5d5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiRegionExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiRegionExtent.java @@ -10,6 +10,7 @@ import com.sk89q.worldedit.regions.RegionIntersection; import java.util.Arrays; import java.util.Collection; +import java.util.concurrent.Future; public class MultiRegionExtent extends FaweRegionExtent { @@ -86,4 +87,9 @@ public class MultiRegionExtent extends FaweRegionExtent { public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { return intersection.processSet(chunk, get, set); } + + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return intersection.postProcessSet(chunk, get, set); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java index fab71f244..433ba7ff3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java @@ -37,6 +37,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.Future; import javax.annotation.Nullable; //todo This should be removed in favor of com.sk89q.worldedit.extent.NullExtent @@ -340,6 +341,11 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor { throw reason; } + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + throw reason; + } + @Override public boolean processGet(int chunkX, int chunkZ) { throw reason; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/SingleRegionExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/SingleRegionExtent.java index eeeb9699d..d5d61d7c3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/SingleRegionExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/SingleRegionExtent.java @@ -9,6 +9,7 @@ import com.sk89q.worldedit.regions.Region; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.Future; public class SingleRegionExtent extends FaweRegionExtent { @@ -44,6 +45,11 @@ public class SingleRegionExtent extends FaweRegionExtent { return region.processSet(chunk, get, set); } + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return region.postProcessSet(chunk, get, set); + } + @Override public boolean processGet(int chunkX, int chunkZ) { return region.containsChunk(chunkX, chunkZ); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 3f89f926d..31c42a8ba 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -317,6 +317,24 @@ public class AbstractDelegateExtent implements Extent { return this; } + @Override + public Extent addPostProcessor(IBatchProcessor processor) { + if (Settings.IMP.EXPERIMENTAL.OTHER) { + logger.info("addPostProcessor Info: \t " + processor.getClass().getName()); + logger.info("The following is not an error or a crash:"); + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for (StackTraceElement stackTraceElement : stackTrace) { + logger.info(stackTraceElement.toString()); + } + + } + Extent result = this.extent.addPostProcessor(processor); + if (result != this.extent) { + new ExtentTraverser(this).setNext(result); + } + return this; + } + @Override public Extent disableHistory() { Extent result = this.extent.disableHistory(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index d74c32475..c0e061791 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -23,6 +23,7 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.changeset.AbstractChangeSet; import com.boydti.fawe.object.clipboard.WorldCopyClipboard; import com.boydti.fawe.object.exception.FaweException; @@ -671,8 +672,16 @@ public interface Extent extends InputExtent, OutputExtent { return processor.construct(this); } + default Extent addPostProcessor(IBatchProcessor processor) { + return processor.construct(this); + } + default Extent enableHistory(AbstractChangeSet changeSet) { - return addProcessor(changeSet); + if (Settings.IMP.EXPERIMENTAL.SEND_BEFORE_HISTORY) { + return addPostProcessor(changeSet); + } else { + return addProcessor(changeSet); + } } default Extent disableHistory() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java index 86f9bcd1c..b178c26fc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java @@ -36,6 +36,9 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -106,6 +109,12 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce return filter.filter(chunk, get, set, MaskingExtent.this); } + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + // This should not do anything otherwise dangerous... + return CompletableFuture.completedFuture(set); + } + @Override public void applyBlock(final FilterBlock block) { if (!this.mask.test(block)) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 326ee1d65..6f75ea8f6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -37,6 +37,8 @@ import com.sk89q.worldedit.world.World; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import javax.annotation.Nullable; /** @@ -350,6 +352,12 @@ public interface Region extends Iterable, Cloneable, IBatchProcess } } + @Override + default Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + // Doesn't need to do anything + return CompletableFuture.completedFuture(set); + } + @Override default Extent construct(Extent child) { if (isGlobal()) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionIntersection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionIntersection.java index c9f1056e1..03ddff3f4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionIntersection.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionIntersection.java @@ -34,6 +34,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -170,6 +172,12 @@ public class RegionIntersection extends AbstractRegion { return null; } + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + // Doesn't need to do anything + return CompletableFuture.completedFuture(set); + } + public List getRegions() { return regions; }