diff --git a/buildSrc/src/main/kotlin/CommonConfig.kt b/buildSrc/src/main/kotlin/CommonConfig.kt index 23891514d..b342b7992 100644 --- a/buildSrc/src/main/kotlin/CommonConfig.kt +++ b/buildSrc/src/main/kotlin/CommonConfig.kt @@ -13,6 +13,7 @@ fun Project.applyCommonConfiguration() { maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } maven { url = uri("http://empcraft.com/maven2") } maven { url = uri("https://repo.destroystokyo.com/repository/maven-public") } + maven { url = uri("https://ci.athion.net/job/FAWE-Piston/ws/") } ivy { url = uri("https://ci.athion.net/job") patternLayout { artifact("/[organisation]/[revision]/artifact/[module].[ext]") @@ -23,4 +24,5 @@ fun Project.applyCommonConfiguration() { cacheChangingModulesFor(10, "minutes") } } + } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 6343c729d..0ec4b175d 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -1,7 +1,7 @@ object Versions { const val TEXT = "3.0.1" const val TEXT_EXTRAS = "3.0.2" - const val PISTON = "0.5.2" + const val PISTON = "0.5.3-SNAPSHOT" const val AUTO_VALUE = "1.6.5" const val JUNIT = "5.5.0" const val MOCKITO = "3.0.0" diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index d9895e7fa..a8d6d3c4f 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { "compile"("it.unimi.dsi:fastutil:8.2.1") "api"("com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT") { exclude("junit", "junit") + isTransitive = false } "compileOnly"("org.spigotmc:spigot:1.14.4-R0.1-SNAPSHOT") "implementation"("io.papermc:paperlib:1.0.2") @@ -58,9 +59,6 @@ dependencies { "implementation"("com.thevoxelbox.voxelsniper:voxelsniper:5.171.0") { isTransitive = false } "implementation"("com.comphenix.protocol:ProtocolLib-API:4.4.0-SNAPSHOT") { isTransitive = false } "implementation"("com.wasteofplastic:askyblock:3.0.8.2") { isTransitive = false } - "compile"("com.github.intellectualsites.plotsquared:PlotSquared-API:latest") { - isTransitive = false - } } tasks.named("processResources") { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitCommand.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitCommand.java index 13094b551..8044b4829 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitCommand.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitCommand.java @@ -2,6 +2,7 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweCommand; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitBlockCommandSender; import com.sk89q.worldedit.bukkit.BukkitCommandSender; import com.sk89q.worldedit.bukkit.BukkitPlayer; @@ -40,7 +41,7 @@ public class BukkitCommand implements CommandExecutor { * @return a wrapped player */ public com.sk89q.worldedit.bukkit.BukkitPlayer wrapPlayer(Player player) { - return new BukkitPlayer(WorldEditPlugin.getInstance(), player); + return BukkitAdapter.adapt(player); } public Actor wrapCommandSender(CommandSender sender) { 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 d140eaada..bad0d4270 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 @@ -18,7 +18,6 @@ import com.boydti.fawe.bukkit.regions.ResidenceFeature; import com.boydti.fawe.bukkit.regions.TownyFeature; import com.boydti.fawe.bukkit.regions.Worldguard; import com.boydti.fawe.bukkit.regions.WorldguardFlag; -import com.boydti.fawe.bukkit.regions.plotquared.PlotSquaredFeature; import com.boydti.fawe.bukkit.util.BukkitTaskMan; import com.boydti.fawe.bukkit.util.ItemUtil; import com.boydti.fawe.bukkit.util.VaultUtil; @@ -31,6 +30,7 @@ import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.image.ImageViewer; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitPlayer; import com.sk89q.worldedit.world.World; import io.papermc.lib.PaperLib; import java.io.File; @@ -84,13 +84,6 @@ public class FaweBukkit implements IFawe, Listener { if (PaperLib.isPaper() && Settings.IMP.EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING > 1) { new RenderListener(plugin); } - if (Bukkit.getPluginManager().getPlugin("PlotSquared") != null) { - try { - WEManager.IMP.managers.add(new PlotSquaredFeature()); - } catch (Exception ignored) { - //Not everyone uses or needs PlotSquared. - } - } } catch (final Throwable e) { e.printStackTrace(); Bukkit.getServer().shutdown(); @@ -159,11 +152,6 @@ public class FaweBukkit implements IFawe, Listener { return null; } - @Override - public int getPlayerCount() { - return plugin.getServer().getOnlinePlayers().size(); - } - @Override public boolean isOnlineMode() { return Bukkit.getOnlineMode(); @@ -203,25 +191,22 @@ public class FaweBukkit implements IFawe, Listener { @Override public com.sk89q.worldedit.entity.Player wrap(final Object obj) { - if (obj.getClass() == String.class) { + Player player = null; + if (obj.getClass() == Player.class) { + player = (Player) obj; + } + else if (obj.getClass() == String.class) { String name = (String) obj; - com.sk89q.worldedit.entity.Player existing = Fawe.get().getCachedPlayer(name); - if (existing != null) { - return existing; - } - Player player = Bukkit.getPlayer(name); - return player != null ? BukkitAdapter.adapt(player) : null; + player = Bukkit.getPlayer(name); } - if (obj.getClass() == UUID.class) { + else if (obj.getClass() == UUID.class) { UUID uuid = (UUID) obj; - com.sk89q.worldedit.entity.Player existing = Fawe.get().getCachedPlayer(uuid); - if (existing != null) { - return existing; - } - Player player = Bukkit.getPlayer(uuid); - return player != null ? BukkitAdapter.adapt(player) : null; + player = Bukkit.getPlayer(uuid); } - return null; + if (player == null) { + throw new IllegalArgumentException("Unknown player type: " + obj); + } + return BukkitAdapter.adapt(player); } @Override public void startMetrics() { @@ -379,12 +364,8 @@ public class FaweBukkit implements IFawe, Listener { @EventHandler(priority = EventPriority.MONITOR) public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); - String name = player.getName(); - com.sk89q.worldedit.entity.Player wePlayer = Fawe.get().getCachedPlayer(name); - if (wePlayer != null) { - wePlayer.unregister(); - Fawe.get().unregister(name); - } + BukkitPlayer wePlayer = BukkitAdapter.adapt(player); + wePlayer.unregister(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/DelegateLock.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/DelegateLock.java index de3c8bfe8..e8f86448f 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/DelegateLock.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/DelegateLock.java @@ -1,12 +1,14 @@ package com.boydti.fawe.bukkit.adapter; +import com.destroystokyo.paper.util.ReentrantLockWithGetOwner; + import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -public class DelegateLock extends ReentrantLock { +public class DelegateLock extends ReentrantLockWithGetOwner { private final Lock parent; private volatile boolean modified; private final AtomicInteger count; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java index 4a491ef8d..8e80c56f1 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java @@ -37,21 +37,6 @@ import java.util.function.Supplier; public class BukkitAdapter_1_14 { - /* - - 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 { - this.bukkitWorld = Bukkit.getWorld(world.getName()); - } - checkNotNull(this.bukkitWorld); - CraftWorld craftWorld = ((CraftWorld) bukkitWorld); - this.nmsWorld = craftWorld.getHandle(); - - */ - /* NMS fields */ @@ -89,15 +74,15 @@ public class BukkitAdapter_1_14 { fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount"); fieldDirtyCount.setAccessible(true); - fieldDirtyBits = PlayerChunk.class.getDeclaredField("h"); + fieldDirtyBits = PlayerChunk.class.getDeclaredField("r"); fieldDirtyBits.setAccessible(true); { Field tmp = null; try { - tmp = DataPaletteBlock.class.getDeclaredField("j"); - } catch (NoSuchFieldException paper) { tmp = DataPaletteBlock.class.getDeclaredField("writeLock"); + } catch (NoSuchFieldException paper) { + tmp = DataPaletteBlock.class.getDeclaredField("j"); } fieldLock = tmp; fieldLock.setAccessible(true); @@ -310,8 +295,8 @@ public class BukkitAdapter_1_14 { } public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException { - fieldFluidCount.set(section, 0); // TODO FIXME - fieldTickingBlockCount.set(section, tickingBlockCount); - fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount); + fieldFluidCount.setShort(section, (short) 0); // TODO FIXME + fieldTickingBlockCount.setShort(section, (short) tickingBlockCount); + fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount); } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java index a3828361f..79e21ed7d 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java @@ -6,8 +6,11 @@ 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.adapter.DelegateLock; +import com.boydti.fawe.object.collection.AdaptedMap; +import com.boydti.fawe.object.collection.AdaptedSetCollection; import com.boydti.fawe.object.collection.BitArray4096; import com.boydti.fawe.util.ReflectionUtils; +import com.google.common.collect.Iterables; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.LongTag; @@ -17,7 +20,9 @@ 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.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; import net.minecraft.server.v1_14_R1.BiomeBase; import net.minecraft.server.v1_14_R1.BlockPosition; @@ -40,16 +45,13 @@ 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 org.jetbrains.annotations.NotNull; -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 javax.annotation.Nullable; +import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.Future; +import java.util.function.Function; public class BukkitGetBlocks_1_14 extends CharGetBlocks { public ChunkSection[] sections; @@ -82,10 +84,114 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { @Override public CompoundTag getTag(int x, int y, int z) { - // TODO + TileEntity tile = getChunk().getTileEntity(new BlockPosition((x & 15) + (X << 4), y, (z & 15) + (Z << 4))); + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + return (CompoundTag) adapter.toNative(tile); + } + + private static final Function posNms2We = new Function() { + @Override + public BlockVector3 apply(BlockPosition v) { + return BlockVector3.at(v.getX(), v.getY(), v.getZ()); + } + }; + + private final static Function nmsTile2We = new Function() { + @Override + public CompoundTag apply(TileEntity tileEntity) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + return (CompoundTag) adapter.toNative(tileEntity.b()); + } + }; + + @Override + public Map getTiles() { + Map nmsTiles = getChunk().getTileEntities(); + if (nmsTiles.isEmpty()) { + return Collections.emptyMap(); + } + return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + } + + @Override + public CompoundTag getEntity(UUID uuid) { + org.bukkit.entity.Entity bukkitEnt = world.getEntity(uuid); + if (bukkitEnt != null) { + return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); + } + for (List entry : getChunk().getEntitySlices()) { + if (entry != null) { + for (Entity entity : entry) { + if (uuid.equals(entity.getUniqueID())) { + return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); + } + } + } + } return null; } + @Override + public Set getEntities() { + List[] slices = getChunk().getEntitySlices(); + int size = 0; + for (List slice : slices) { + if (slice != null) size += slice.size(); + } + if (slices.length == 0) { + return Collections.emptySet(); + } + int finalSize = size; + return new AbstractSet() { + @Override + public int size() { + return finalSize; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof CompoundTag)) { + return false; + } + CompoundTag getTag = (CompoundTag) get; + Map value = getTag.getValue(); + CompoundTag getParts = (CompoundTag) value.get("UUID"); + UUID getUUID = new UUID(getParts.getLong("Most"), getParts.getLong("Least")); + for (List slice : slices) { + if (slice != null) { + for (Entity entity : slice) { + UUID uuid = entity.getUniqueID(); + if (uuid.equals(getUUID)) { + return true; + } + } + } + } + return false; + } + + @NotNull + @Override + public Iterator iterator() { + Iterable result = Iterables.transform(Iterables.concat(slices), new com.google.common.base.Function() { + @Nullable + @Override + public CompoundTag apply(@Nullable Entity input) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + NBTTagCompound tag = new NBTTagCompound(); + return (CompoundTag) adapter.toNative(input.save(tag)); + } + }); + return result.iterator(); + } + }; + } + @Override public char[] load(int layer) { return load(layer, null); @@ -120,7 +226,6 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { Chunk nmsChunk = BukkitAdapter_1_14.ensureLoaded(nmsWorld, X, Z); // Remove existing tiles - { Map tiles = nmsChunk.getTileEntities(); if (!tiles.isEmpty()) { @@ -299,19 +404,19 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks { } // set tiles - Map tiles = set.getTiles(); + 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()) { + 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 BlockVector3 blockHash = entry.getKey(); + final int x = blockHash.getX()+ bx; + final int y = blockHash.getY(); + final int z = blockHash.getZ() + bz; final BlockPosition pos = new BlockPosition(x, y, z); synchronized (nmsWorld) { TileEntity tileEntity = nmsWorld.getTileEntity(pos); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/nbt/LazyCompoundTag_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/nbt/LazyCompoundTag_1_14.java new file mode 100644 index 000000000..9674013f0 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/nbt/LazyCompoundTag_1_14.java @@ -0,0 +1,138 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14.nbt; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import net.minecraft.server.v1_14_R1.NBTBase; +import net.minecraft.server.v1_14_R1.NBTNumber; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.NBTTagList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class LazyCompoundTag_1_14 extends CompoundTag { + private final NBTTagCompound nmsTag; + + public LazyCompoundTag_1_14(NBTTagCompound tag) { + super(null); + this.nmsTag = tag; + } + + @Override + public Map getValue() { + Map value = super.getValue(); + if (value == null) { + Tag tag = WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(nmsTag); + setValue(((CompoundTag) tag).getValue()); + } + return super.getValue(); + } + + public boolean containsKey(String key) { + return nmsTag.hasKey(key); + } + + public byte[] getByteArray(String key) { + return nmsTag.getByteArray(key); + } + + public byte getByte(String key) { + return nmsTag.getByte(key); + } + + public double getDouble(String key) { + return nmsTag.getDouble(key); + } + + public double asDouble(String key) { + NBTBase value = nmsTag.get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asDouble(); + } + return 0; + } + + public float getFloat(String key) { + return nmsTag.getFloat(key); + } + + public int[] getIntArray(String key) { + return nmsTag.getIntArray(key); + } + + public int getInt(String key) { + return nmsTag.getInt(key); + } + + public int asInt(String key) { + NBTBase value = nmsTag.get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asInt(); + } + return 0; + } + + public List getList(String key) { + NBTBase tag = nmsTag.get(key); + if (tag instanceof NBTTagList) { + ArrayList list = new ArrayList<>(); + NBTTagList nbtList = (NBTTagList) tag; + for (NBTBase elem : nbtList) { + if (elem instanceof NBTTagCompound) { + list.add(new LazyCompoundTag_1_14((NBTTagCompound) elem)); + } else { + list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem)); + } + } + return list; + } + return Collections.emptyList(); + } + + public ListTag getListTag(String key) { + NBTBase tag = nmsTag.get(key); + if (tag instanceof NBTTagList) { + return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag); + } + return new ListTag(StringTag.class, Collections.emptyList()); + } + + @SuppressWarnings("unchecked") + public List getList(String key, Class listType) { + ListTag listTag = getListTag(key); + if (listTag.getType().equals(listType)) { + return (List) listTag.getValue(); + } else { + return Collections.emptyList(); + } + } + + public long[] getLongArray(String key) { + return nmsTag.getLongArray(key); + } + + public long getLong(String key) { + return nmsTag.getLong(key); + } + + public long asLong(String key) { + NBTBase value = nmsTag.get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asLong(); + } + return 0; + } + + public short getShort(String key) { + return nmsTag.getShort(key); + } + + public String getString(String key) { + return nmsTag.getString(key); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/RenderListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/RenderListener.java index 6eae8de1e..e89b90745 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/RenderListener.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/RenderListener.java @@ -96,8 +96,7 @@ public class RenderListener implements Listener { } } } - throw new UnsupportedOperationException("TODO FIXME: PAPER 1.14"); -// player.setViewDistance(value); + player.setViewDistance(value); } private int getViewDistance(Player player) { diff --git a/worldedit-bukkit/src/main/java/com/destroystokyo/paper/util/ReentrantLockWithGetOwner.java b/worldedit-bukkit/src/main/java/com/destroystokyo/paper/util/ReentrantLockWithGetOwner.java new file mode 100644 index 000000000..8087583e6 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/destroystokyo/paper/util/ReentrantLockWithGetOwner.java @@ -0,0 +1,11 @@ +package com.destroystokyo.paper.util; + +import java.util.concurrent.locks.ReentrantLock; + +public class ReentrantLockWithGetOwner extends ReentrantLock { + + @Override + public Thread getOwner() { + return super.getOwner(); + } +} \ No newline at end of file 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 e462f8409..28dd35557 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 @@ -21,6 +21,8 @@ package com.sk89q.worldedit.bukkit; import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.Fawe; +import com.boydti.fawe.wrappers.PlayerWrapper; import com.sk89q.worldedit.NotABlockException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; @@ -29,6 +31,7 @@ import com.sk89q.worldedit.bukkit.adapter.IBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.SimpleBukkitAdapter; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.PlayerProxy; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; @@ -92,7 +95,7 @@ public enum BukkitAdapter { * @return If they are equal */ public static boolean equals(BlockType blockType, Material type) { - return Objects.equals(blockType.getId(), type.getKey().toString()); + return getAdapter().equals(blockType, type); } /** @@ -105,15 +108,7 @@ public enum BukkitAdapter { * @return a wrapped Bukkit world */ public static BukkitWorld asBukkitWorld(World world) { - if (world instanceof BukkitWorld) { - return (BukkitWorld) world; - } else { - BukkitWorld bukkitWorld = WorldEditPlugin.getInstance().getInternalPlatform().matchWorld(world); - if (bukkitWorld == null) { - throw new RuntimeException("World '" + world.getName() + "' has no matching version in Bukkit"); - } - return bukkitWorld; - } + return getAdapter().asBukkitWorld(world); } /** @@ -123,8 +118,7 @@ public enum BukkitAdapter { * @return a WorldEdit world */ public static World adapt(org.bukkit.World world) { - checkNotNull(world); - return new BukkitWorld(world); + return getAdapter().adapt(world); } /** @@ -144,6 +138,7 @@ public enum BukkitAdapter { * @return The Bukkit player */ public static Player adapt(com.sk89q.worldedit.entity.Player player) { + player = PlayerProxy.unwrap(player); return ((BukkitPlayer) player).getPlayer(); } @@ -154,17 +149,7 @@ public enum BukkitAdapter { * @return a Bukkit world */ public static org.bukkit.World adapt(World world) { - checkNotNull(world); - if (world instanceof BukkitWorld) { - return ((BukkitWorld) world).getWorld(); - } else { - org.bukkit.World match = Bukkit.getServer().getWorld(world.getName()); - if (match != null) { - return match; - } else { - throw new IllegalArgumentException("Can't find a Bukkit world for " + world.getName()); - } - } + return getAdapter().adapt(world); } /** @@ -275,8 +260,7 @@ public enum BukkitAdapter { * @return a WorldEdit entity */ public static Entity adapt(org.bukkit.entity.Entity entity) { - checkNotNull(entity); - return new BukkitEntity(entity); + return getAdapter().adapt(entity); } /** @@ -286,11 +270,7 @@ public enum BukkitAdapter { * @return The Bukkit Material */ public static Material adapt(ItemType itemType) { - checkNotNull(itemType); - if (!itemType.getId().startsWith("minecraft:")) { - throw new IllegalArgumentException("Bukkit only supports Minecraft items"); - } - return Material.getMaterial(itemType.getId().substring(10).toUpperCase(Locale.ROOT)); + return getAdapter().adapt(itemType); } /** @@ -300,11 +280,7 @@ public enum BukkitAdapter { * @return The Bukkit Material */ public static Material adapt(BlockType blockType) { - checkNotNull(blockType); - if (!blockType.getId().startsWith("minecraft:")) { - throw new IllegalArgumentException("Bukkit only supports Minecraft blocks"); - } - return Material.getMaterial(blockType.getId().substring(10).toUpperCase(Locale.ROOT)); + return getAdapter().adapt(blockType); } /** @@ -314,8 +290,7 @@ public enum BukkitAdapter { * @return WorldEdit GameMode */ public static GameMode adapt(org.bukkit.GameMode gameMode) { - checkNotNull(gameMode); - return GameModes.get(gameMode.name().toLowerCase(Locale.ROOT)); + return getAdapter().adapt(gameMode); } /** @@ -325,18 +300,11 @@ public enum BukkitAdapter { * @return WorldEdit BiomeType */ public static BiomeType adapt(Biome biome) { - return BiomeTypes.get(biome.name().toLowerCase(Locale.ROOT)); + return getAdapter().adapt(biome); } public static Biome adapt(BiomeType biomeType) { - if (!biomeType.getId().startsWith("minecraft:")) { - throw new IllegalArgumentException("Bukkit only supports vanilla biomes"); - } - try { - return Biome.valueOf(biomeType.getId().substring(10).toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException e) { - return null; - } + return getAdapter().adapt(biomeType); } /** @@ -346,18 +314,11 @@ public enum BukkitAdapter { * @return WorldEdit EntityType */ public static EntityType adapt(org.bukkit.entity.EntityType entityType) { - final String name = entityType.getName(); - if (name == null) { - return null; - } - return EntityTypes.get(name.toLowerCase(Locale.ROOT)); + return getAdapter().adapt(entityType); } public static org.bukkit.entity.EntityType adapt(EntityType entityType) { - if (!entityType.getId().startsWith("minecraft:")) { - throw new IllegalArgumentException("Bukkit only supports vanilla entities"); - } - return org.bukkit.entity.EntityType.fromName(entityType.getId().substring(10)); + return getAdapter().adapt(entityType); } /** @@ -367,11 +328,7 @@ public enum BukkitAdapter { * @return The blocktype */ public static BlockType asBlockType(Material material) { - checkNotNull(material); - if (!material.isBlock()) { - throw new IllegalArgumentException(material.getKey().toString() + " is not a block!"); - } - return BlockTypes.get(material.getKey().toString()); + return getAdapter().asBlockType(material); } /** @@ -381,14 +338,11 @@ public enum BukkitAdapter { * @return The itemtype */ public static ItemType asItemType(Material material) { - checkNotNull(material); - if (!material.isItem()) { - throw new IllegalArgumentException(material.getKey().toString() + " is not an item!"); - } - return ItemTypes.get(material.getKey().toString()); + return getAdapter().asItemType(material); } - + /* private static Map blockStateCache = new HashMap<>(); + /* /** * Create a WorldEdit BlockState from a Bukkit BlockData @@ -403,9 +357,9 @@ public enum BukkitAdapter { public static BlockType adapt(Material material) { return getAdapter().adapt(material); } - + /* private static Map blockDataCache = new HashMap<>(); - + */ /** * Create a Bukkit BlockData from a WorldEdit BlockStateHolder * @@ -423,12 +377,7 @@ public enum BukkitAdapter { * @return The WorldEdit BlockState */ public static BlockState asBlockState(ItemStack itemStack) throws WorldEditException { - checkNotNull(itemStack); - if (itemStack.getType().isBlock()) { - return adapt(itemStack.getType().createBlockData()); - } else { - throw new NotABlockException(); - } + return getAdapter().asBlockState(itemStack); } /** @@ -438,11 +387,7 @@ public enum BukkitAdapter { * @return The WorldEdit BaseItemStack */ public static BaseItemStack adapt(ItemStack itemStack) { - checkNotNull(itemStack); - if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) { - return WorldEditPlugin.getInstance().getBukkitImplAdapter().adapt(itemStack); - } - return new BaseItemStack(ItemTypes.get(itemStack.getType().getKey().toString()), itemStack.getAmount()); + return getAdapter().adapt(itemStack); } /** @@ -452,10 +397,6 @@ public enum BukkitAdapter { * @return The Bukkit ItemStack */ public static ItemStack adapt(BaseItemStack item) { - checkNotNull(item); - if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) { - return WorldEditPlugin.getInstance().getBukkitImplAdapter().adapt(item); - } - return new ItemStack(adapt(item.getType()), item.getAmount()); + return getAdapter().adapt(item); } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 928d0dd88..92aaf29d5 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.platform.AbstractPlayerActor; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.math.BlockVector3; @@ -73,7 +74,6 @@ public class BukkitPlayer extends AbstractPlayerActor { public BukkitPlayer(WorldEditPlugin plugin, Player player) { this.plugin = plugin; this.player = player; - Fawe.get().register(this); if (Settings.IMP.CLIPBOARD.USE_DISK) { loadClipboardFromDisk(); } @@ -176,8 +176,15 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public void setPosition(Vector3 pos, float pitch, float yaw) { - player.teleport(new Location(player.getWorld(), pos.getX(), pos.getY(), - pos.getZ(), yaw, pitch)); + org.bukkit.World world = player.getWorld(); + if (pos instanceof com.sk89q.worldedit.util.Location) { + com.sk89q.worldedit.util.Location loc = (com.sk89q.worldedit.util.Location) pos; + Extent extent = loc.getExtent(); + if (extent instanceof World) { + world = Bukkit.getWorld(((World) extent).getName()); + } + } + player.teleport(new Location(world, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch)); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java index a32f11ac0..1d0ed63ae 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java @@ -19,17 +19,19 @@ package com.sk89q.worldedit.bukkit; +import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.extent.inventory.BlockBagException; import com.sk89q.worldedit.extent.inventory.OutOfBlocksException; import com.sk89q.worldedit.extent.inventory.OutOfSpaceException; +import com.sk89q.worldedit.extent.inventory.SlottableBlockBag; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.block.BlockState; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -public class BukkitPlayerBlockBag extends BlockBag { +public class BukkitPlayerBlockBag extends BlockBag implements SlottableBlockBag { private Player player; private ItemStack[] items; @@ -180,4 +182,17 @@ public class BukkitPlayerBlockBag extends BlockBag { public void addSingleSourcePosition(Location pos) { } + @Override + public BaseItem getItem(int slot) { + loadInventory(); + return BukkitAdapter.adapt(items[slot]); + } + + @Override + public void setItem(int slot, BaseItem block) { + loadInventory(); + BaseItemStack stack = block instanceof BaseItemStack ? (BaseItemStack) block : new BaseItemStack(block.getType(), block.getNbtData(), 1); + items[slot] = BukkitAdapter.adapt(stack); + } + } 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 a247b6389..4e782bf29 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 @@ -22,6 +22,7 @@ package com.sk89q.worldedit.bukkit; import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME; +import com.bekvon.bukkit.residence.commands.message; import com.boydti.fawe.Fawe; import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.bukkit.adapter.mc1_14.Spigot_v1_14_R4; @@ -84,6 +85,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.metadata.MetadataValue; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; @@ -311,28 +314,33 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter Field descriptionField = JavaPlugin.class.getDeclaredField("dataFolder"); descriptionField.setAccessible(true); descriptionField.set(this, dir); - } catch (Throwable throwable) { - throwable.printStackTrace(); + } catch (Throwable e) { + e.printStackTrace(); } - File pluginsFolder = MainUtil.getJarFile().getParentFile(); - for (File file : pluginsFolder.listFiles()) { - if (file.length() == 2009) return; - } - Plugin plugin = Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit"); - File dummy = MainUtil.copyFile(MainUtil.getJarFile(), "DummyFawe.src", pluginsFolder, "DummyFawe.jar"); - if (dummy != null && dummy.exists() && plugin == this) { - try { - Bukkit.getPluginManager().loadPlugin(dummy); - } catch (Throwable e) { - if (Bukkit.getUpdateFolderFile().mkdirs()) { - MainUtil.copyFile(MainUtil.getJarFile(), "DummyFawe.src", pluginsFolder, Bukkit.getUpdateFolder() + File.separator + "DummyFawe.jar"); - } else { - getLogger().info("Please delete DummyFawe.jar and restart"); - } + try { + File pluginsFolder = MainUtil.getJarFile().getParentFile(); + + for (File file : pluginsFolder.listFiles()) { + if (file.length() == 2009) return; } - getLogger().info("Please restart the server if you have any plugins which depend on FAWE."); - } else if (dummy == null) { - MainUtil.copyFile(MainUtil.getJarFile(), "DummyFawe.src", pluginsFolder, "update" + File.separator + "DummyFawe.jar"); + Plugin plugin = Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit"); + File dummy = MainUtil.copyFile(MainUtil.getJarFile(), "DummyFawe.src", pluginsFolder, "DummyFawe.jar"); + if (dummy != null && dummy.exists() && plugin == this) { + try { + Bukkit.getPluginManager().loadPlugin(dummy); + } catch (Throwable e) { + if (Bukkit.getUpdateFolderFile().mkdirs()) { + MainUtil.copyFile(MainUtil.getJarFile(), "DummyFawe.src", pluginsFolder, Bukkit.getUpdateFolder() + File.separator + "DummyFawe.jar"); + } else { + getLogger().info("Please delete DummyFawe.jar and restart"); + } + } + getLogger().info("Please restart the server if you have any plugins which depend on FAWE."); + } else if (dummy == null) { + MainUtil.copyFile(MainUtil.getJarFile(), "DummyFawe.src", pluginsFolder, "update" + File.separator + "DummyFawe.jar"); + } + } catch (Throwable e) { + e.printStackTrace(); } } @@ -458,7 +466,7 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter // code of WorldEdit expects it String[] split = new String[args.length + 1]; System.arraycopy(args, 0, split, 1, args.length); - split[0] = "/" + commandLabel; + split[0] = commandLabel; CommandEvent event = new CommandEvent(wrapCommandSender(sender), Joiner.on(" ").join(split)); getWorldEdit().getEventBus().post(event); @@ -551,7 +559,15 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter * @return a wrapped player */ public BukkitPlayer wrapPlayer(Player player) { - return new BukkitPlayer(this, player); + synchronized (player) { + @NotNull List meta = player.getMetadata("WE"); + if (meta == null || meta.isEmpty()) { + BukkitPlayer wePlayer = new BukkitPlayer(this, player); + player.setMetadata("WE", new FixedMetadataValue(this, wePlayer)); + return wePlayer; + } + return (BukkitPlayer) meta.get(0).value(); + } } public Actor wrapCommandSender(CommandSender sender) { diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java index dd2a862ad..76e0392d7 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/CachedBukkitAdapter.java @@ -48,7 +48,7 @@ public abstract class CachedBukkitAdapter implements IBukkitAdapter { @Override public ItemType asItemType(Material material) { try { - return ItemTypes.get(material.getKey().getKey()); + return ItemTypes.get(itemTypes[material.ordinal()]); } catch (NullPointerException e) { if (init()) return asItemType(material); return ItemTypes.get(itemTypes[material.ordinal()]); 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 5edd1a1ce..241ef371a 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 @@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter; import static com.google.common.base.Preconditions.checkNotNull; +import com.sk89q.worldedit.NotABlockException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitEntity; import com.sk89q.worldedit.bukkit.BukkitItemStack; @@ -20,6 +21,9 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.entity.EntityTypes; +import com.sk89q.worldedit.world.gamemode.GameMode; +import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldedit.world.item.ItemType; import java.util.Locale; import org.bukkit.Bukkit; @@ -280,4 +284,62 @@ public interface IBukkitAdapter { default BiomeType adapt(Biome biome) { return BiomeTypes.get(biome.name().toLowerCase(Locale.ROOT)); } + + /** + * Checks equality between a WorldEdit BlockType and a Bukkit Material + * + * @param blockType The WorldEdit BlockType + * @param type The Bukkit Material + * @return If they are equal + */ + default boolean equals(BlockType blockType, Material type) { + return blockType == asItemType(type).getBlockType(); + } + + /** + * Create a WorldEdit world from a Bukkit world. + * + * @param world the Bukkit world + * @return a WorldEdit world + */ + default World adapt(org.bukkit.World world) { + checkNotNull(world); + return new BukkitWorld(world); + } + + /** + * Create a WorldEdit GameMode from a Bukkit one. + * + * @param gameMode Bukkit GameMode + * @return WorldEdit GameMode + */ + default GameMode adapt(org.bukkit.GameMode gameMode) { + checkNotNull(gameMode); + return GameModes.get(gameMode.name().toLowerCase(Locale.ROOT)); + } + + /** + * Create a WorldEdit EntityType from a Bukkit one. + * + * @param entityType Bukkit EntityType + * @return WorldEdit EntityType + */ + default EntityType adapt(org.bukkit.entity.EntityType entityType) { + return EntityTypes.get(entityType.getName().toLowerCase(Locale.ROOT)); + } + + /** + * Create a WorldEdit BlockStateHolder from a Bukkit ItemStack + * + * @param itemStack The Bukkit ItemStack + * @return The WorldEdit BlockState + */ + default BlockState asBlockState(ItemStack itemStack) { + checkNotNull(itemStack); + if (itemStack.getType().isBlock()) { + return adapt(itemStack.getType().createBlockData()); + } else { + throw new NotABlockException(); + } + } } diff --git a/worldedit-core/build.gradle.kts b/worldedit-core/build.gradle.kts index 6d76c69a3..361f06e93 100644 --- a/worldedit-core/build.gradle.kts +++ b/worldedit-core/build.gradle.kts @@ -29,7 +29,6 @@ dependencies { "compile"("org.slf4j:slf4j-api:1.7.26") "compile"("it.unimi.dsi:fastutil:8.2.1") "compile"("com.googlecode.json-simple:json-simple:1.1.1") { isTransitive = false } - "compileOnly"(project(":worldedit-libs:core:ap")) "annotationProcessor"(project(":worldedit-libs:core:ap")) // ensure this is on the classpath for the AP diff --git a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java index 48485a7d0..ea1d641f9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java @@ -4,6 +4,7 @@ import com.boydti.fawe.beta.implementation.QueueHandler; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.brush.visualization.VisualQueue; +import com.boydti.fawe.regions.general.integrations.plotquared.PlotSquaredFeature; import com.boydti.fawe.util.CachedTextureUtil; import com.boydti.fawe.util.CleanTextureUtil; import com.boydti.fawe.util.FaweTimer; @@ -185,6 +186,7 @@ public class Fawe { transformParser = new DefaultTransformParser(getWorldEdit()); visualQueue = new VisualQueue(3); WEManager.IMP.managers.addAll(Fawe.this.IMP.getMaskManagers()); + WEManager.IMP.managers.add(new PlotSquaredFeature()); Fawe.debug("Plugin 'PlotSquared' found. Using it now."); } catch (Throwable ignored) {} try { @@ -429,30 +431,4 @@ public class Fawe { public Thread setMainThread() { return this.thread = Thread.currentThread(); } - - private ConcurrentHashMap players = new ConcurrentHashMap<>(8, 0.9f, 1); - private ConcurrentHashMap playersUUID = new ConcurrentHashMap<>(8, 0.9f, 1); - - public void register(Player player) { - players.put(player.getName(), player); - playersUUID.put(player.getUniqueId(), player); - - } - - public void unregister(String name) { - Player player = players.remove(name); - if (player != null) playersUUID.remove(player.getUniqueId()); - } - - public Player getCachedPlayer(String name) { - return players.get(name); - } - - public Player getCachedPlayer(UUID uuid) { - return playersUUID.get(uuid); - } - - public Collection getCachedPlayers() { - return players.values(); - } } 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 89ee6e998..17a2774ae 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java @@ -3,7 +3,7 @@ package com.boydti.fawe; 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.object.collection.CleanableThreadLocal; import com.boydti.fawe.util.IOUtil; import com.boydti.fawe.util.MathMan; import com.sk89q.jnbt.ByteArrayTag; @@ -53,7 +53,7 @@ public enum FaweCache implements Trimable { public final char[] EMPTY_CHAR_4096 = new char[4096]; - private final IdentityHashMap REGISTERED_SINGLETONS = new IdentityHashMap<>(); + private final IdentityHashMap REGISTERED_SINGLETONS = new IdentityHashMap<>(); private final IdentityHashMap REGISTERED_POOLS = new IdentityHashMap<>(); public interface Pool { @@ -108,7 +108,7 @@ public enum FaweCache implements Trimable { MUTABLE_VECTOR3.clean(); MUTABLE_BLOCKVECTOR3.clean(); SECTION_BITS_TO_CHAR.clean(); - for (Map.Entry entry : REGISTERED_SINGLETONS.entrySet()) { + for (Map.Entry entry : REGISTERED_SINGLETONS.entrySet()) { entry.getValue().clean(); } for (Map.Entry entry : REGISTERED_POOLS.entrySet()) { @@ -141,13 +141,13 @@ public enum FaweCache implements Trimable { } public final T getSingleton(Class clazz) { - IterableThreadLocal cache = REGISTERED_SINGLETONS.get(clazz); + CleanableThreadLocal 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)); + cache = new CleanableThreadLocal<>(IOUtil.supplier(clazz::newInstance)); REGISTERED_SINGLETONS.put(clazz, cache); } } @@ -155,10 +155,10 @@ public enum FaweCache implements Trimable { return cache.get(); } - public synchronized IterableThreadLocal registerSingleton(Class clazz, Supplier cache) { + public synchronized CleanableThreadLocal registerSingleton(Class clazz, Supplier cache) { checkNotNull(cache); - IterableThreadLocal local = new IterableThreadLocal<>(cache); - IterableThreadLocal previous = REGISTERED_SINGLETONS.putIfAbsent(clazz, local); + CleanableThreadLocal local = new CleanableThreadLocal<>(cache); + CleanableThreadLocal previous = REGISTERED_SINGLETONS.putIfAbsent(clazz, local); if (previous != null) { throw new IllegalStateException("Previous key"); } @@ -180,27 +180,27 @@ public enum FaweCache implements Trimable { return pool; } - public final IterableThreadLocal BLOCK_TO_PALETTE = new IterableThreadLocal<>(() -> { + public final CleanableThreadLocal BLOCK_TO_PALETTE = new CleanableThreadLocal<>(() -> { 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 CleanableThreadLocal SECTION_BITS_TO_CHAR = new CleanableThreadLocal<>(() -> new char[4096]); - public final IterableThreadLocal PALETTE_TO_BLOCK = new IterableThreadLocal<>(() -> new int[Character.MAX_VALUE + 1]); + public final CleanableThreadLocal PALETTE_TO_BLOCK = new CleanableThreadLocal<>(() -> new int[Character.MAX_VALUE + 1]); - public final IterableThreadLocal PALETTE_TO_BLOCK_CHAR = new IterableThreadLocal<>( + public final CleanableThreadLocal PALETTE_TO_BLOCK_CHAR = new CleanableThreadLocal<>( () -> 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 CleanableThreadLocal BLOCK_STATES = new CleanableThreadLocal<>(() -> new long[2048]); - public final IterableThreadLocal SECTION_BLOCKS = new IterableThreadLocal<>(() -> new int[4096]); + public final CleanableThreadLocal SECTION_BLOCKS = new CleanableThreadLocal<>(() -> new int[4096]); - public final IterableThreadLocal INDEX_STORE = new IterableThreadLocal<>(() -> new int[256]); + public final CleanableThreadLocal INDEX_STORE = new CleanableThreadLocal<>(() -> new int[256]); /** * Holds data for a palette used in a chunk section @@ -219,7 +219,7 @@ public enum FaweCache implements Trimable { public long[] blockStates; } - private final IterableThreadLocal PALETTE_CACHE = new IterableThreadLocal<>(Palette::new); + private final CleanableThreadLocal PALETTE_CACHE = new CleanableThreadLocal<>(Palette::new); /** * Convert raw char array to palette @@ -315,9 +315,9 @@ public enum FaweCache implements Trimable { * Vector cache */ - public IterableThreadLocal MUTABLE_BLOCKVECTOR3 = new IterableThreadLocal<>(MutableBlockVector3::new); + public CleanableThreadLocal MUTABLE_BLOCKVECTOR3 = new CleanableThreadLocal<>(MutableBlockVector3::new); - public IterableThreadLocal MUTABLE_VECTOR3 = new IterableThreadLocal(MutableVector3::new) { + public CleanableThreadLocal MUTABLE_VECTOR3 = new CleanableThreadLocal(MutableVector3::new) { @Override public MutableVector3 init() { return new MutableVector3(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java index 6122e0f5d..c9f5ae2c6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -5,6 +5,7 @@ import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.image.ImageViewer; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.world.World; import java.io.File; @@ -35,10 +36,6 @@ public interface IFawe { return null; } - default int getPlayerCount() { - return Fawe.get().getCachedPlayers().size(); - } - String getPlatformVersion(); boolean isOnlineMode(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/ArrayFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/ArrayFilterBlock.java index 316caba0d..3fd3c0a07 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/ArrayFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/ArrayFilterBlock.java @@ -94,23 +94,12 @@ public class ArrayFilterBlock extends SimpleFilterBlock { return z; } - @Override - public > boolean setBlock(BlockVector3 position, T block) - throws WorldEditException { - return getExtent().setBlock(position.getX(),position.getY(), position.getZ(), block); - } - @Override public > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { return getExtent().setBlock(x,y, z, block); } - @Override - public boolean setBiome(BlockVector2 position, BiomeType biome) { - return getExtent().setBiome(position.getX(),0, position.getZ(), biome); - } - @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { return getExtent().setBiome(x,y, z,biome); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java index 9f4fabea4..eeabb1713 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/CharFilterBlock.java @@ -7,7 +7,6 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -79,8 +78,7 @@ public class CharFilterBlock extends ChunkFilterBlock { } @Override - public final ChunkFilterBlock init(IChunkGet iget, IChunkSet iset, - int layer) { + public final ChunkFilterBlock init(IChunkGet iget, IChunkSet iset, int layer) { this.layer = layer; final CharGetBlocks get = (CharGetBlocks) iget; if (!get.hasSection(layer)) { @@ -123,12 +121,18 @@ public class CharFilterBlock extends ChunkFilterBlock { public void filter(Filter filter, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { int yis = minY << 8; int zis = minZ << 4; + int zie = (15 - maxZ) << 4; + int xie = (15 - maxX); for (y = minY, index = yis; y <= maxY; y++) { - for (z = minZ, index += zis; z <= maxZ; z++) { - for (x = minX, index += minX; x <= maxX; x++, index++) { + index += zis; + for (z = minZ; z <= maxZ; z++) { + index += minX; + for (x = minX; x <= maxX; x++, index++) { filter.applyBlock(this); } + index += xie; } + index += zie; } } @@ -251,7 +255,7 @@ public class CharFilterBlock extends ChunkFilterBlock { @Override public final CompoundTag getNbtData() { - return get.getTag(x, y + (layer << 4), z); + return get.getTag(x, y + yy, z); } /* NORTH(Vector3.at(0, 0, -1), Flag.CARDINAL, 3, 1), @@ -410,21 +414,15 @@ public class CharFilterBlock extends ChunkFilterBlock { return getExtent().getBiomeType(x, z); } - @Override - public > boolean setBlock(BlockVector3 position, T block) - throws WorldEditException { - return false; - } - @Override public > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { - return false; + return getExtent().setBlock(x, y, z, block); } @Override public boolean setBiome(BlockVector2 position, BiomeType biome) { - return false; + return setBiome(position.getX(), 0, position.getBlockZ(), biome); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilter.java index c71da676a..21dbd8291 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/DelegateFilter.java @@ -1,6 +1,6 @@ package com.boydti.fawe.beta; -public class DelegateFilter implements IDelegateFilter { +public abstract class DelegateFilter implements IDelegateFilter { private final Filter parent; @@ -9,12 +9,7 @@ public class DelegateFilter implements IDelegateFilter { } @Override - public T getParent() { + public final T getParent() { return (T) parent; } - - @Override - public Filter newInstance(Filter other) { - return new DelegateFilter(other); - } -} +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java index 182782e62..36a4a04d9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/FilterBlock.java @@ -1,6 +1,8 @@ package com.boydti.fawe.beta; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.extent.Extent; 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 new file mode 100644 index 000000000..77806d238 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java @@ -0,0 +1,122 @@ +package com.boydti.fawe.beta; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.implementation.EmptyBatchProcessor; +import com.boydti.fawe.beta.implementation.MultiBatchProcessor; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +public interface IBatchProcessor { + /** + * Process a chunk that has been set + * @param chunk + * @param get + * @param set + * @return + */ + IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set); + + /** + * Convert this processor into an Extent based processor instead of a queue batch based on + * @param child + * @return + */ + Extent construct(Extent child); + + /** + * Utility method to trim a chunk based on min and max Y + * @param set + * @param minY + * @param maxY + * @return false if chunk is empty of blocks + */ + default boolean trimY(IChunkSet set, int minY, int maxY) { + int minLayer = (minY - 1) >> 4; + for (int layer = 0; layer <= minLayer; layer++) { + if (set.hasSection(layer)) { + if (layer == minLayer) { + char[] arr = set.getArray(layer); + int index = (minY & 15) << 12; + for (int i = 0; i < index; i++) arr[i] = 0; + set.setBlocks(layer, arr); + } else { + set.setBlocks(layer, null); + } + } + } + int maxLayer = (maxY + 1) >> 4; + for (int layer = maxLayer; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) { + if (set.hasSection(layer)) { + if (layer == minLayer) { + char[] arr = set.getArray(layer); + int index = ((maxY + 1) & 15) << 12; + for (int i = index; i < arr.length; i++) arr[i] = 0; + set.setBlocks(layer, arr); + } else { + set.setBlocks(layer, null); + } + } + } + for (int layer = (minY - 15) >> 4; layer < (maxY + 15) >> 4; layer++) { + if (set.hasSection(layer)) { + return true; + } + } + return false; + } + + /** + * Utility method to trim entity and blocks with a provided contains function + * @param set + * @param contains + * @return false if chunk is empty of NBT + */ + default boolean trimNBT(IChunkSet set, Function contains) { + Set ents = set.getEntities(); + if (!ents.isEmpty()) { + for (Iterator iter = ents.iterator(); iter.hasNext();) { + CompoundTag ent = iter.next(); + if (!contains.apply(ent.getEntityPosition().toBlockPoint())) { + iter.remove(); + } + } + } + Map tiles = set.getTiles(); + if (!tiles.isEmpty()) { + for (Iterator> iter = tiles.entrySet().iterator(); iter.hasNext();) { + if (!contains.apply(iter.next().getKey())) { + iter.remove(); + } + } + } + return !tiles.isEmpty() || !ents.isEmpty(); + } + + /** + * Join two processors and return the result + * @param other + * @return + */ + default IBatchProcessor join(IBatchProcessor other) { + return MultiBatchProcessor.of(this, other); + } + + /** + * Return a new processor after removing all are instances of a specified class + * @param clazz + * @param + * @return + */ + default IBatchProcessor remove(Class clazz) { + if (clazz.isInstance(this)) { + return EmptyBatchProcessor.INSTANCE; + } + return this; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index 7dafab979..e8150b1fa 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -1,5 +1,13 @@ package com.boydti.fawe.beta; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockState; + +import java.util.Map; +import java.util.Set; + /** * Shared interface for IGetBlocks and ISetBlocks */ @@ -7,5 +15,9 @@ public interface IBlocks extends Trimable { boolean hasSection(int layer); + char[] getArray(int layer); + + BlockState getBlock(int x, int y, int z); + IBlocks reset(); } 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 a90abe831..8b83cdc12 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 @@ -2,10 +2,14 @@ package com.boydti.fawe.beta; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.InputExtent; +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 java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.Future; /** @@ -24,6 +28,10 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent { CompoundTag getTag(int x, int y, int z); + Map getTiles(); + + Set getEntities(); + @Override boolean trim(boolean aggressive); @@ -34,4 +42,6 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent { > T call(IChunkSet set, Runnable finalize); char[] load(int layer); + + CompoundTag getEntity(UUID uuid); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java index e0c45f84c..698bfaee1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java @@ -3,6 +3,7 @@ package com.boydti.fawe.beta; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -22,6 +23,8 @@ public interface IChunkSet extends IBlocks, OutputExtent { @Override boolean setBlock(int x, int y, int z, BlockStateHolder holder); + void setBlocks(int layer, char[] data); + boolean isEmpty(); @Override @@ -31,18 +34,14 @@ public interface IChunkSet extends IBlocks, OutputExtent { void removeEntity(UUID uuid); - BlockState getBlock(int x, int y, int z); - - char[] getArray(int layer); + Set getEntityRemoves(); BiomeType[] getBiomes(); - Map getTiles(); + Map getTiles(); Set getEntities(); - Set getEntityRemoves(); - @Override IChunkSet reset(); 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 6c39316c9..eb0b7b742 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 @@ -1,11 +1,16 @@ package com.boydti.fawe.beta; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.math.BlockVector3; 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.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.annotation.Nullable; @@ -129,6 +134,26 @@ public interface IDelegateChunk extends IChunk { return getParent().isEmpty(); } + @Override + default Map getTiles() { + return getParent().getTiles(); + } + + @Override + default Set getEntities() { + return getParent().getEntities(); + } + + @Override + default CompoundTag getEntity(UUID uuid) { + return getParent().getEntity(uuid); + } + + @Override + default char[] getArray(int layer) { + return getParent().getArray(layer); + } + default T findParent(Class clazz) { IChunk root = getParent(); if (clazz.isAssignableFrom(root.getClass())) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java index a354d7ab7..89736bb90 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateFilter.java @@ -46,7 +46,5 @@ public interface IDelegateFilter extends Filter { return this; } - default Filter newInstance(Filter other) { - throw new UnsupportedOperationException("Not implemented"); - } -} + Filter newInstance(Filter other); +} \ No newline at end of file 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 5791345b1..1c64d3f30 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,7 @@ package com.boydti.fawe.beta; import com.boydti.fawe.beta.implementation.IChunkCache; +import com.boydti.fawe.beta.implementation.MultiBatchProcessor; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -80,8 +81,8 @@ public interface IDelegateQueueExtent extends IQueueExtent { } @Override - default IChunk getCachedChunk(int x, int z) { - return getParent().getCachedChunk(x, z); + default IChunk getOrCreateChunk(int x, int z) { + return getParent().getOrCreateChunk(x, z); } @Override 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 716967374..3248641ae 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,30 @@ package com.boydti.fawe.beta; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.implementation.IBatchProcessorHolder; import com.boydti.fawe.beta.implementation.IChunkCache; +import com.boydti.fawe.beta.implementation.MultiBatchProcessor; 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.function.operation.Operation; 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.BlockStateHolder; + +import javax.annotation.Nullable; import java.io.Flushable; +import java.util.List; import java.util.concurrent.Future; /** * TODO: implement Extent (need to refactor Extent first) Interface for a queue based extent which * uses chunks */ -public interface IQueueExtent extends Flushable, Trimable, Extent { +public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcessorHolder { @Override default boolean isQueueEnabled() { @@ -54,6 +60,12 @@ public interface IQueueExtent extends Flushable, Trimable, Extent { void disableQueue(); + /** + * Initialize the queue (for reusability) + * @param extent + * @param get + * @param set + */ void init(Extent extent, IChunkCache get, IChunkCache set); /** @@ -82,7 +94,7 @@ public interface IQueueExtent extends Flushable, Trimable, Extent { * @param z * @return IChunk */ - IChunk getCachedChunk(int x, int z); + IChunk getOrCreateChunk(int x, int z); /** * Submit the chunk so that it's changes are applied to the world @@ -96,36 +108,36 @@ public interface IQueueExtent extends Flushable, Trimable, Extent { @Override default boolean setBlock(int x, int y, int z, BlockStateHolder state) { - final IChunk chunk = getCachedChunk(x >> 4, z >> 4); + final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4); 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); + final IChunk chunk = getOrCreateChunk(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); + final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4); return chunk.setBiome(x & 15, y, z & 15, biome); } @Override default BlockState getBlock(int x, int y, int z) { - final IChunk chunk = getCachedChunk(x >> 4, z >> 4); + final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4); return chunk.getBlock(x & 15, y, z & 15); } @Override default BaseBlock getFullBlock(int x, int y, int z) { - final IChunk chunk = getCachedChunk(x >> 4, z >> 4); + final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4); return chunk.getFullBlock(x & 15, y, z & 15); } default BiomeType getBiome(int x, int z) { - final IChunk chunk = getCachedChunk(x >> 4, z >> 4); + final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4); return chunk.getBiomeType(x & 15, z & 15); } @@ -159,6 +171,13 @@ public interface IQueueExtent extends Flushable, Trimable, Extent { return root; } + @Nullable + @Override + default Operation commit() { + flush(); + return null; + } + /** * Flush all changes to the world - Best to call this async so it doesn't hang the server */ diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/SingleFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/SingleFilterBlock.java index 7c3d1d52d..631e14357 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/SingleFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/SingleFilterBlock.java @@ -54,11 +54,6 @@ public class SingleFilterBlock extends FilterBlock { return block; } -// @Override -// public BaseBlock getFullBlockRelative(int x, int y, int z) { -// return block; -// } - @Override public void setFullBlock(BaseBlock block) { this.block = block; @@ -99,23 +94,16 @@ public class SingleFilterBlock extends FilterBlock { return BlockVector3.at(x, y, z); } - @Override - public > boolean setBlock(BlockVector3 position, T block) - throws WorldEditException { - return getExtent().setBlock(position.getX(),position.getY(), position.getZ(), block); - } - @Override public > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { + if (x == this.x && y == this.y && z == this.z) { + setFullBlock(block.toBaseBlock()); + return true; + } return getExtent().setBlock(x,y, z, block); } - @Override - public boolean setBiome(BlockVector2 position, BiomeType biome) { - return getExtent().setBiome(position.getX(),0, position.getZ(), biome); - } - @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { return getExtent().setBiome(x,y, z,biome); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/BatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/BatchProcessorHolder.java new file mode 100644 index 000000000..7d0089be9 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/BatchProcessorHolder.java @@ -0,0 +1,22 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.IBatchProcessor; + +public class BatchProcessorHolder implements IBatchProcessorHolder { + private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE; + + @Override + public IBatchProcessor getProcessor() { + return processor; + } + + @Override + public void setProcessor(IBatchProcessor set) { + this.processor = set; + } + + @Override + public String toString() { + return super.toString() + "{" + getProcessor() + "}"; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/DelegateChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/DelegateChunkSet.java index 64e5ded9d..94e202072 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/DelegateChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/DelegateChunkSet.java @@ -64,7 +64,7 @@ public interface DelegateChunkSet extends IChunkSet { } @Override - default Map getTiles() { + default Map getTiles() { return getParent().getTiles(); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/EmptyBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/EmptyBatchProcessor.java new file mode 100644 index 000000000..e377107b1 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/EmptyBatchProcessor.java @@ -0,0 +1,26 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.sk89q.worldedit.extent.Extent; + +public enum EmptyBatchProcessor implements IBatchProcessor { + INSTANCE + ; + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + return set; + } + + @Override + public Extent construct(Extent child) { + return child; + } + + @Override + public IBatchProcessor join(IBatchProcessor other) { + return other; + } +} 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 index 9b9061d57..7579bb5de 100644 --- 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 @@ -7,14 +7,15 @@ 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.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; 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.*; import java.util.concurrent.Future; public class FallbackChunkGet implements IChunkGet { @@ -46,6 +47,40 @@ public class FallbackChunkGet implements IChunkGet { return extent.getFullBlock(bx + x, y, bz + z).getNbtData(); } + @Override + public Map getTiles() { + return null; + } + + @Override + public Set getEntities() { + List result = extent.getEntities(new CuboidRegion(BlockVector3.at(bx, 0, bz), BlockVector3.at(bx + 15, 255, bz + 15))); + if (result.isEmpty()) { + return Collections.emptySet(); + } + HashSet set = new HashSet<>(result.size()); + for (Entity entity : result) { + set.add(entity.getState().getNbtData()); + } + return set; + } + + @Override + public CompoundTag getEntity(UUID uuid) { + long checkMost = uuid.getMostSignificantBits(); + long checkLeast = uuid.getLeastSignificantBits(); + for (CompoundTag entityTag : getEntities()) { + long entMost = entityTag.getLong("UUIDMost"); + if (entMost == checkMost) { + long entLeast = entityTag.getLong("UUIDLeast"); + if (entLeast == checkLeast) { + return entityTag; + } + } + } + return null; + } + @Override public boolean trim(boolean aggressive) { return true; @@ -71,14 +106,11 @@ public class FallbackChunkGet implements IChunkGet { } } - Map tiles = set.getTiles(); + 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()); + for (Map.Entry entry : tiles.entrySet()) { + BlockVector3 pos = entry.getKey(); + extent.setTile(bx + pos.getX(), pos.getY(), bz + pos.getZ(), entry.getValue()); } } @@ -128,6 +160,11 @@ public class FallbackChunkGet implements IChunkGet { return true; } + @Override + public char[] getArray(int layer) { + return new char[0]; + } + @Override public IBlocks reset() { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IBatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IBatchProcessorHolder.java new file mode 100644 index 000000000..8cd86f640 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IBatchProcessorHolder.java @@ -0,0 +1,43 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.sk89q.worldedit.extent.Extent; + +/** + * Holds a batch processor + * (Join and remove operations affect the held processor) + */ +public interface IBatchProcessorHolder extends IBatchProcessor { + IBatchProcessor getProcessor(); + + /** + * set the held processor + * @param set + */ + void setProcessor(IBatchProcessor set); + + @Override + default IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + return getProcessor().processBatch(chunk, get, set); + } + + @Override + default Extent construct(Extent child) { + return getProcessor().construct(child); + } + + @Override + default IBatchProcessor join(IBatchProcessor other) { + setProcessor(getProcessor().join(other)); + return this; + } + + @Override + default IBatchProcessor remove(Class clazz) { + setProcessor(getProcessor().remove(clazz)); + return this; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IQueueWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IQueueWrapper.java index c7b47634d..8c4957ce2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IQueueWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IQueueWrapper.java @@ -3,7 +3,6 @@ package com.boydti.fawe.beta.implementation; import com.boydti.fawe.beta.IQueueExtent; public interface IQueueWrapper { - default IQueueExtent wrapQueue(IQueueExtent queue) { return queue; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiBatchProcessor.java new file mode 100644 index 000000000..d13b3afbc --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiBatchProcessor.java @@ -0,0 +1,99 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.util.StringMan; +import com.sk89q.worldedit.extent.Extent; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +public class MultiBatchProcessor implements IBatchProcessor { + private IBatchProcessor[] processors; + + public MultiBatchProcessor(IBatchProcessor... processors) { + this.processors = processors; + } + + public static IBatchProcessor of(IBatchProcessor... processors) { + ArrayList list = new ArrayList<>(); + for (IBatchProcessor processor : processors) { + if (processor instanceof MultiBatchProcessor) { + list.addAll(Arrays.asList(((MultiBatchProcessor) processor).processors)); + } else if (!(processor instanceof EmptyBatchProcessor)){ + list.add(processor); + } + } + switch (list.size()) { + case 0: + return EmptyBatchProcessor.INSTANCE; + case 1: + return list.get(0); + default: + return new MultiBatchProcessor(list.toArray(new IBatchProcessor[0])); + } + } + + public void addBatchProcessor(IBatchProcessor processor) { + List processors = new ArrayList<>(Arrays.asList(this.processors)); + processors.add(processor); + this.processors = processors.toArray(new IBatchProcessor[0]); + } + + public List getBatchProcessors() { + return Arrays.asList(this.processors); + } + + public void removeBatchProcessor(IBatchProcessor processor) { + List processors = new ArrayList<>(Arrays.asList(this.processors)); + processors.remove(processor); + this.processors = processors.toArray(new IBatchProcessor[0]); + } + + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + for (IBatchProcessor processor : this.processors) { + set = processor.processBatch(chunk, get, set); + if (set == null) { + return null; + } + } + return set; + } + + @Override + public Extent construct(Extent child) { + for (IBatchProcessor processor : processors) { + child = processor.construct(child); + } + return child; + } + + @Override + public IBatchProcessor remove(Class clazz) { + ArrayList list = new ArrayList<>(Arrays.asList(this.processors)); + list.removeIf(clazz::isInstance); + return of(list.toArray(new IBatchProcessor[0])); + } + + @Override + public IBatchProcessor join(IBatchProcessor other) { + if (other instanceof MultiBatchProcessor) { + for (IBatchProcessor processor : ((MultiBatchProcessor) other).processors) { + addBatchProcessor(processor); + } + } else { + addBatchProcessor(other); + } + return this; + } + + @Override + public String toString() { + return super.toString() + "{" + StringMan.join(processors, ",") + "}"; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiThreadedQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiThreadedQueue.java deleted file mode 100644 index f47d3a732..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/MultiThreadedQueue.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.boydti.fawe.beta.implementation; - -import com.boydti.fawe.beta.ChunkFilterBlock; -import com.boydti.fawe.beta.Filter; -import com.boydti.fawe.beta.IChunk; -import com.boydti.fawe.beta.IQueueExtent; -import com.boydti.fawe.beta.filters.CountFilter; -import com.boydti.fawe.beta.filters.DistrFilter; -import com.boydti.fawe.config.Settings; -import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.extent.AbstractDelegateExtent; -import com.sk89q.worldedit.extent.PassthroughExtent; -import com.sk89q.worldedit.function.mask.Mask; -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.util.Countable; -import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ForkJoinTask; -import java.util.stream.IntStream; - -public class MultiThreadedQueue extends PassthroughExtent implements IQueueWrapper { - - private final World world; - private final QueueHandler handler; - - protected MultiThreadedQueue(QueueHandler handler, World world) { - super(handler.getQueue(world)); - this.world = world; - this.handler = handler; - } - - public IQueueExtent getQueue() { - return handler.getQueue(this.world); - } - - public T apply(Region region, T filter) { - // The chunks positions to iterate over - final Set chunks = region.getChunks(); - final Iterator chunksIter = chunks.iterator(); - - // Get a pool, to operate on the chunks in parallel - final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS); - final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> { - final Filter newFilter = filter.fork(); - // Create a chunk that we will reuse/reset for each operation - final IQueueExtent queue = wrapQueue(getQueue()); - synchronized (queue) { - ChunkFilterBlock block = null; - - while (true) { - // Get the next chunk posWeakChunk - final int X, Z; - synchronized (chunksIter) { - if (!chunksIter.hasNext()) { - break; - } - final BlockVector2 pos = chunksIter.next(); - X = pos.getX(); - Z = pos.getZ(); - } - if (!newFilter.appliesChunk(X, Z)) { - continue; - } - IChunk chunk = queue.getCachedChunk(X, Z); - // Initialize - chunk.init(queue, X, Z); - - IChunk newChunk = newFilter.applyChunk(chunk, region); - if (newChunk != null) { - chunk = newChunk; - if (block == null) { - block = queue.initFilterBlock(); - } - chunk.filterBlocks(newFilter, block, region); - } - queue.submit(chunk); - } - queue.flush(); - } - })).toArray(ForkJoinTask[]::new); - // Join filters - for (ForkJoinTask task : tasks) { - if (task != null) { - task.quietlyJoin(); - } - } - filter.join(); - return filter; - } - - public int getChanges() { - return -1; - } - - @Override - public int countBlocks(Region region, Mask searchMask) { - return - // Apply a filter over a region - apply(region, searchMask - .toFilter(new CountFilter())) // Adapt the mask to a filter which counts - .getParent() // Get the counter of this mask - .getTotal(); // Get the total from the counter - } - - @Override - public > int setBlocks(Region region, B block) - throws MaxChangedBlocksException { - apply(region, block); - return getChanges(); - } - - @Override - public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { - apply(region, pattern); - return getChanges(); - } - - @Override - public int setBlocks(Set vset, Pattern pattern) { - if (vset instanceof Region) { - setBlocks((Region) vset, pattern); - } - for (BlockVector3 blockVector3 : vset) { - pattern.apply(this, blockVector3, blockVector3); - } - return getChanges(); - } - - @Override - public int replaceBlocks(Region region, Mask mask, Pattern pattern) - throws MaxChangedBlocksException { - apply(region, mask.toFilter(pattern)); - return getChanges(); - } - - @Override - public List> getBlockDistributionWithData(Region region) { - return apply(region, new DistrFilter()).getDistribution(); - } - - @Override - public List> getBlockDistribution(Region region) { - return apply(region, new DistrFilter()).getTypeDistribution(); - } -} 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 index 4aff8c605..9bf8bc6a3 100644 --- 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 @@ -5,12 +5,17 @@ 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.math.BlockVector3; 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.Collections; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.Future; public enum NullChunkGet implements IChunkGet { @@ -36,6 +41,21 @@ public enum NullChunkGet implements IChunkGet { return null; } + @Override + public Map getTiles() { + return Collections.emptyMap(); + } + + @Override + public Set getEntities() { + return null; + } + + @Override + public CompoundTag getEntity(UUID uuid) { + return null; + } + @Override public boolean trim(boolean aggressive) { return true; @@ -56,6 +76,11 @@ public enum NullChunkGet implements IChunkGet { return false; } + @Override + public char[] getArray(int layer) { + return new char[0]; + } + @Override public IBlocks reset() { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java new file mode 100644 index 000000000..b520c29ce --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java @@ -0,0 +1,286 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.filters.CountFilter; +import com.boydti.fawe.beta.filters.DistrFilter; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.changeset.FaweChangeSet; +import com.boydti.fawe.object.clipboard.WorldCopyClipboard; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.PassthroughExtent; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.BlockPattern; +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.util.Countable; +import com.sk89q.worldedit.world.World; +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 java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ForkJoinTask; +import java.util.stream.IntStream; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrapper { + + private final World world; + private final QueueHandler handler; + private final BatchProcessorHolder processor; + + public ParallelQueueExtent(QueueHandler handler, World world) { + super(handler.getQueue(world, new BatchProcessorHolder())); + this.world = world; + this.handler = handler; + this.processor = (BatchProcessorHolder) getExtent().getProcessor(); + } + + @Override + public IQueueExtent getExtent() { + return (IQueueExtent) super.getExtent(); + } + + private IQueueExtent getNewQueue() { + return wrapQueue(handler.getQueue(this.world, this.processor)); + } + + @Override + public IQueueExtent wrapQueue(IQueueExtent queue) { + // TODO wrap + queue.setProcessor(this.processor); + return queue; + } + + @Override + public Extent enableHistory(FaweChangeSet changeSet) { + return super.enableHistory(changeSet); + } + + private ChunkFilterBlock apply(ChunkFilterBlock block, Filter filter, IQueueExtent queue, Region region, int X, int Z) { + if (!filter.appliesChunk(X, Z)) { + return block; + } + IChunk chunk = queue.getOrCreateChunk(X, Z); + // Initialize + chunk.init(queue, X, Z); + + IChunk newChunk = filter.applyChunk(chunk, region); + if (newChunk != null) { + chunk = newChunk; + if (block == null) { + block = queue.initFilterBlock(); + } + chunk.filterBlocks(filter, block, region); + } + queue.submit(chunk); + return block; + } + + public T apply(Region region, T filter) { + // The chunks positions to iterate over + final Set chunks = region.getChunks(); + final Iterator chunksIter = chunks.iterator(); + + // Get a pool, to operate on the chunks in parallel + final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS); + if (size <= 1) { + BlockVector2 pos = chunksIter.next(); + apply(null, filter, getExtent(), region, pos.getX(), pos.getZ()); + } else { + final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> { + try { + final Filter newFilter = filter.fork(); + // Create a chunk that we will reuse/reset for each operation + final IQueueExtent queue = getNewQueue(); + synchronized (queue) { + ChunkFilterBlock block = null; + + while (true) { + // Get the next chunk posWeakChunk + final int X, Z; + synchronized (chunksIter) { + if (!chunksIter.hasNext()) { + break; + } + final BlockVector2 pos = chunksIter.next(); + X = pos.getX(); + Z = pos.getZ(); + } + block = apply(block, newFilter, queue, region, X, Z); + } + queue.flush(); + } + } catch (Throwable e) { + e.printStackTrace(); + } + })).toArray(ForkJoinTask[]::new); + // Join filters + for (ForkJoinTask task : tasks) { + if (task != null) { + task.quietlyJoin(); + } + } + filter.join(); + } + return filter; + } + + public int getChanges() { + return -1; + } + + @Override + public int countBlocks(Region region, Mask searchMask) { + return + // Apply a filter over a region + apply(region, searchMask + .toFilter(new CountFilter())) // Adapt the mask to a filter which counts + .getParent() // Get the counter of this mask + .getTotal(); // Get the total from the counter + } + + @Override + public > int setBlocks(Region region, B block) throws MaxChangedBlocksException { + apply(region, block); + return getChanges(); + } + + @Override + public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { + apply(region, pattern); + return getChanges(); + } + + @Override + public int setBlocks(Set vset, Pattern pattern) { + if (vset instanceof Region) { + setBlocks((Region) vset, pattern); + } + for (BlockVector3 blockVector3 : vset) { + pattern.apply(this, blockVector3, blockVector3); + } + return getChanges(); + } + + @Override + public int replaceBlocks(Region region, Mask mask, Pattern pattern) + throws MaxChangedBlocksException { + apply(region, mask.toFilter(pattern)); + return getChanges(); + } + + @Override + public List> getBlockDistributionWithData(Region region) { + return apply(region, new DistrFilter()).getDistribution(); + } + + @Override + public List> getBlockDistribution(Region region) { + return apply(region, new DistrFilter()).getTypeDistribution(); + } + + /** + * To optimize + */ + + /** + * Lazily copy a region + * + * @param region + * @return + */ + @Override + public BlockArrayClipboard lazyCopy(Region region) { + WorldCopyClipboard faweClipboard = new WorldCopyClipboard(this, region); + BlockArrayClipboard weClipboard = new BlockArrayClipboard(region, faweClipboard); + weClipboard.setOrigin(region.getMinimumPoint()); + return weClipboard; + } + + /** + * Count the number of blocks of a list of types in a region. + * + * @param region the region + * @param searchBlocks the list of blocks to search + * @return the number of blocks that matched the block + */ + @Override + public int countBlocks(Region region, Set searchBlocks) { + BlockMask mask = new BlockMask(this, searchBlocks); + return countBlocks(region, mask); + } + + /** + * Replaces all the blocks matching a given filter, within a given region, to a block + * returned by a given pattern. + * + * @param region the region to replace the blocks within + * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask} + * @param replacement the replacement block + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + @Override + public > int replaceBlocks(Region region, Set filter, B replacement) throws MaxChangedBlocksException { + return replaceBlocks(region, filter, new BlockPattern(replacement)); + } + + /** + * Replaces all the blocks matching a given filter, within a given region, to a block + * returned by a given pattern. + * + * @param region the region to replace the blocks within + * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask} + * @param pattern the pattern that provides the new blocks + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + @Override + public int replaceBlocks(Region region, Set filter, Pattern pattern) throws MaxChangedBlocksException { + Mask mask = filter == null ? new ExistingBlockMask(this) : new BlockMask(this, filter); + return replaceBlocks(region, mask, pattern); + } + + + /* + Don't need to optimize these + */ + +// /** +// * Sets the blocks at the center of the given region to the given pattern. +// * If the center sits between two blocks on a certain axis, then two blocks +// * will be placed to mark the center. +// * +// * @param region the region to find the center of +// * @param pattern the replacement pattern +// * @return the number of blocks placed +// * @throws MaxChangedBlocksException thrown if too many blocks are changed +// */ +// @Override +// public int center(Region region, Pattern pattern) throws MaxChangedBlocksException { +// checkNotNull(region); +// checkNotNull(pattern); +// +// Vector3 center = region.getCenter(); +// Region centerRegion = new CuboidRegion( +// this instanceof World ? (World) this : null, // Causes clamping of Y range +// BlockVector3.at(((int) center.getX()), ((int) center.getY()), ((int) center.getZ())), +// BlockVector3.at(MathUtils.roundHalfUp(center.getX()), +// center.getY(), MathUtils.roundHalfUp(center.getZ()))); +// return setBlocks(centerRegion, pattern); +// } +} 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 435ae93f9..e3eaff4ca 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 @@ -2,13 +2,14 @@ package com.boydti.fawe.beta.implementation; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBatchProcessor; 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; -import com.boydti.fawe.object.collection.IterableThreadLocal; +import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.wrappers.WorldWrapper; @@ -39,8 +40,8 @@ public abstract class QueueHandler implements Trimable, Runnable { private ThreadPoolExecutor blockingExecutor = FaweCache.IMP.newBlockingExecutor(); private ConcurrentLinkedQueue syncTasks = new ConcurrentLinkedQueue<>(); - private Map>> chunkCache = new HashMap<>(); - private IterableThreadLocal queuePool = new IterableThreadLocal<>(QueueHandler.this::create); + private Map>> chunkGetCache = new HashMap<>(); + private CleanableThreadLocal queuePool = new CleanableThreadLocal<>(QueueHandler.this::create); /** * Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the * server @@ -198,8 +199,8 @@ public abstract class QueueHandler implements Trimable, Runnable { public IChunkCache getOrCreateWorldCache(World world) { world = WorldWrapper.unwrap(world); - synchronized (chunkCache) { - final WeakReference> ref = chunkCache.get(world); + synchronized (chunkGetCache) { + final WeakReference> ref = chunkGetCache.get(world); if (ref != null) { final IChunkCache cached = ref.get(); if (cached != null) { @@ -207,7 +208,7 @@ public abstract class QueueHandler implements Trimable, Runnable { } } final IChunkCache created = new ChunkCache<>(world); - chunkCache.put(world, new WeakReference<>(created)); + chunkGetCache.put(world, new WeakReference<>(created)); return created; } } @@ -221,18 +222,25 @@ public abstract class QueueHandler implements Trimable, Runnable { public abstract void endSet(boolean parallel); public IQueueExtent getQueue(World world) { + return getQueue(world, null); + } + + public IQueueExtent getQueue(World world, IBatchProcessor processor) { final IQueueExtent queue = queuePool.get(); IChunkCache cacheGet = getOrCreateWorldCache(world); IChunkCache set = null; // TODO cache? queue.init(world, cacheGet, set); + if (processor != null) { + queue.setProcessor(processor); + } return queue; } @Override public boolean trim(boolean aggressive) { boolean result = true; - synchronized (chunkCache) { - final Iterator>>> iter = chunkCache + synchronized (chunkGetCache) { + final Iterator>>> iter = chunkGetCache .entrySet().iterator(); while (iter.hasNext()) { final Map.Entry>> entry = iter.next(); 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 53632ef9b..cede44cdd 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 @@ -3,6 +3,7 @@ package com.boydti.fawe.beta.implementation; import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.CharFilterBlock; import com.boydti.fawe.beta.ChunkFilterBlock; +import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; @@ -11,17 +12,16 @@ 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.object.changeset.FaweChangeSet; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MemUtil; import com.google.common.util.concurrent.Futures; -import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BlockStateHolder; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -32,7 +32,7 @@ import java.util.concurrent.Future; *

* This queue is reusable {@link #init(IChunkCache)} */ -public class SingleThreadQueueExtent implements IQueueExtent { +public class SingleThreadQueueExtent extends BatchProcessorHolder implements IQueueExtent { // // Pool discarded chunks for reuse (can safely be cleared by another thread) // private static final ConcurrentLinkedQueue CHUNK_POOL = new ConcurrentLinkedQueue<>(); @@ -86,19 +86,20 @@ public class SingleThreadQueueExtent implements IQueueExtent { * Resets the queue. */ protected synchronized void reset() { - if (!initialized) return; + if (!this.initialized) return; checkThread(); - if (!chunks.isEmpty()) { - for (IChunk chunk : chunks.values()) { + if (!this.chunks.isEmpty()) { + for (IChunk chunk : this.chunks.values()) { chunk.recycle(); } - chunks.clear(); + this.chunks.clear(); } - enabledQueue = true; - lastChunk = null; - lastPair = Long.MAX_VALUE; - currentThread = null; - initialized = false; + this.enabledQueue = true; + this.lastChunk = null; + this.lastPair = Long.MAX_VALUE; + this.currentThread = null; + this.initialized = false; + this.setProcessor(EmptyBatchProcessor.INSTANCE); } /** @@ -118,9 +119,27 @@ public class SingleThreadQueueExtent implements IQueueExtent { } this.cacheGet = get; this.cacheSet = set; + this.setProcessor(EmptyBatchProcessor.INSTANCE); initialized = true; } + @Override + public Extent addProcessor(IBatchProcessor processor) { + join(processor); + return this; + } + + @Override + public Extent enableHistory(FaweChangeSet changeSet) { + return this.addProcessor(changeSet); + } + + @Override + public Extent disableHistory() { + this.remove(FaweChangeSet.class); + return this; + } + @Override public int size() { return chunks.size() + submissions.size(); @@ -165,7 +184,6 @@ public class SingleThreadQueueExtent implements IQueueExtent { @Override public synchronized boolean trim(boolean aggressive) { - // TODO trim individial chunk sections cacheGet.trim(aggressive); cacheSet.trim(aggressive); if (Thread.currentThread() == currentThread) { @@ -200,7 +218,7 @@ public class SingleThreadQueueExtent implements IQueueExtent { } @Override - public final IChunk getCachedChunk(int x, int z) { + public final IChunk getOrCreateChunk(int x, int z) { final long pair = (long) x << 32 | z & 0xffffffffL; if (pair == lastPair) { return lastChunk; @@ -311,16 +329,4 @@ public class SingleThreadQueueExtent implements IQueueExtent { public ChunkFilterBlock initFilterBlock() { return new CharFilterBlock(this); } - - @Override - public > boolean setBlock(BlockVector3 position, T block) - throws WorldEditException { - return setBlock(position.getX(),position.getY(), position.getZ(), block); - - } - - @Override - public boolean setBiome(BlockVector2 position, BiomeType biome) { - return setBiome(position.getX(),0, position.getZ(), biome); - } } 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 dd52778c9..fae95bc04 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 @@ -38,27 +38,31 @@ public class BitSetBlocks implements IChunkSet { return true; } + @Override + public void setBlocks(int layer, char[] data) { + row.reset(layer); + int by = layer << 4; + for (int y = 0, index = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, index++) { + if (data[index] != 0) { + row.set(null, x, by + y, z); + } + } + } + } + } + @Override public boolean isEmpty() { return row.isEmpty(); } - @Override - public > boolean setBlock(BlockVector3 position, T block) - throws WorldEditException { - return setBlock(position.getX(), position.getY(), position.getZ(), block); - } - @Override public boolean setTile(int x, int y, int z, CompoundTag tile) { return false; } - @Override - public boolean setBiome(BlockVector2 position, BiomeType biome) { - return setBiome(position.getX(),0, position.getZ(), biome); - } - @Override public void setEntity(CompoundTag tag) { } @@ -112,7 +116,7 @@ public class BitSetBlocks implements IChunkSet { } @Override - public Map getTiles() { + public Map getTiles() { return null; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java index 2ec176f24..483625740 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java @@ -2,6 +2,14 @@ package com.boydti.fawe.beta.implementation.blocks; import com.boydti.fawe.beta.IBlocks; import com.boydti.fawe.beta.IChunkSet; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.Map; +import java.util.Set; public class CharBlocks implements IBlocks { @@ -81,6 +89,16 @@ public class CharBlocks implements IBlocks { return sections[layer] == FULL; } + @Override + public char[] getArray(int layer) { + return sections[layer].get(this, layer); + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypes.states[get(x, y, z)]; + } + public char get(int x, int y, int z) { final int layer = y >> 4; final int index = (y & 15) << 8 | z << 4 | x; 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 cddb22ae8..2c470eef6 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 @@ -3,6 +3,7 @@ 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.object.collection.BlockVector3ChunkMap; import com.boydti.fawe.util.MathMan; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; @@ -12,7 +13,8 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; -import java.util.HashMap; + +import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -25,7 +27,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } public BiomeType[] biomes; - public HashMap tiles; + public BlockVector3ChunkMap tiles; public HashSet entities; public HashSet entityRemoves; @@ -36,29 +38,24 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { POOL.offer(this); } - @Override - public char[] getArray(int layer) { - return sections[layer].get(this, layer); - } - @Override public BiomeType[] getBiomes() { return biomes; } @Override - public Map getTiles() { - return tiles; + public Map getTiles() { + return tiles == null ? Collections.emptyMap() : tiles; } @Override public Set getEntities() { - return entities; + return entities == null ? Collections.emptySet() : entities; } @Override public Set getEntityRemoves() { - return entityRemoves; + return entityRemoves == null ? Collections.emptySet() : entityRemoves; } @Override @@ -78,9 +75,16 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { @Override public boolean setBlock(int x, int y, int z, BlockStateHolder holder) { set(x, y, z, holder.getOrdinalChar()); + holder.applyTileEntity(this, x, y, z); return true; } + @Override + public void setBlocks(int layer, char[] data) { + this.blocks[layer] = data; + this.sections[layer] = data == null ? EMPTY : FULL; + } + @Override public > boolean setBlock(BlockVector3 position, T block) throws WorldEditException { @@ -90,10 +94,9 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { @Override public boolean setTile(int x, int y, int z, CompoundTag tile) { if (tiles == null) { - tiles = new HashMap<>(); + tiles = new BlockVector3ChunkMap(); } - final short pair = MathMan.tripleBlockCoord(x, y, z); - tiles.put(pair, tile); + tiles.put(x, y, z, tile); return true; } 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 d47808dd2..1548d67a6 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 @@ -9,18 +9,19 @@ 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.SingleThreadQueueExtent; -import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; import com.boydti.fawe.config.Settings; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.math.BlockVector3; 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.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.Future; -import java.util.function.Supplier; import javax.annotation.Nullable; /** @@ -34,10 +35,10 @@ public class ChunkHolder> implements IChunk { return POOL.poll(); } - private IChunkGet get; - private IChunkSet set; - private IBlockDelegate delegate; - private IQueueExtent extent; + private IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes) + private IChunkSet chunkSet; // The blocks to be set to the chunkExisting + private IBlockDelegate delegate; // delegate handles the abstraction of the chunk layers + private IQueueExtent extent; // the parent queue extent which has this chunk private int chunkX; private int chunkZ; @@ -73,36 +74,63 @@ public class ChunkHolder> implements IChunk { return getOrCreateGet().load(layer); } + @Override + public CompoundTag getEntity(UUID uuid) { + return delegate.get(this).getEntity(uuid); + } + public static final IBlockDelegate BOTH = new IBlockDelegate() { + @Override + public IChunkGet get(ChunkHolder chunk) { + return chunk.chunkExisting; + } + + @Override + public IChunkSet set(ChunkHolder chunk) { + return chunk.chunkSet; + } + @Override public boolean setBiome(ChunkHolder chunk, int x, int y, int z, BiomeType biome) { - return chunk.set.setBiome(x, y, z, biome); + return chunk.chunkSet.setBiome(x, y, z, biome); } @Override public boolean setBlock(ChunkHolder chunk, int x, int y, int z, BlockStateHolder block) { - return chunk.set.setBlock(x, y, z, block); + return chunk.chunkSet.setBlock(x, y, z, block); } @Override public BiomeType getBiome(ChunkHolder chunk, int x, int z) { - return chunk.get.getBiomeType(x, z); + return chunk.chunkExisting.getBiomeType(x, z); } @Override public BlockState getBlock(ChunkHolder chunk, int x, int y, int z) { - return chunk.get.getBlock(x, y, z); + return chunk.chunkExisting.getBlock(x, y, z); } @Override public BaseBlock getFullBlock(ChunkHolder chunk, int x, int y, int z) { - return chunk.get.getFullBlock(x, y, z); + return chunk.chunkExisting.getFullBlock(x, y, z); } }; public static final IBlockDelegate GET = new IBlockDelegate() { + @Override + public IChunkGet get(ChunkHolder chunk) { + return chunk.chunkExisting; + } + + @Override + public IChunkSet set(ChunkHolder chunk) { + chunk.getOrCreateSet(); + chunk.delegate = BOTH; + return chunk.chunkSet; + } + @Override public boolean setBiome(ChunkHolder chunk, int x, int y, int z, BiomeType biome) { @@ -121,31 +149,43 @@ public class ChunkHolder> implements IChunk { @Override public BiomeType getBiome(ChunkHolder chunk, int x, int z) { - return chunk.get.getBiomeType(x, z); + return chunk.chunkExisting.getBiomeType(x, z); } @Override public BlockState getBlock(ChunkHolder chunk, int x, int y, int z) { - return chunk.get.getBlock(x, y, z); + return chunk.chunkExisting.getBlock(x, y, z); } @Override public BaseBlock getFullBlock(ChunkHolder chunk, int x, int y, int z) { - return chunk.get.getFullBlock(x, y, z); + return chunk.chunkExisting.getFullBlock(x, y, z); } }; public static final IBlockDelegate SET = new IBlockDelegate() { + @Override + public IChunkGet get(ChunkHolder chunk) { + chunk.getOrCreateGet(); + chunk.delegate = BOTH; + return chunk.chunkExisting; + } + + @Override + public IChunkSet set(ChunkHolder chunk) { + return chunk.chunkSet; + } + @Override public boolean setBiome(ChunkHolder chunk, int x, int y, int z, BiomeType biome) { - return chunk.set.setBiome(x, y, z, biome); + return chunk.chunkSet.setBiome(x, y, z, biome); } @Override public boolean setBlock(ChunkHolder chunk, int x, int y, int z, BlockStateHolder block) { - return chunk.set.setBlock(x, y, z, block); + return chunk.chunkSet.setBlock(x, y, z, block); } @Override @@ -171,6 +211,20 @@ public class ChunkHolder> implements IChunk { } }; public static final IBlockDelegate NULL = new IBlockDelegate() { + @Override + public IChunkGet get(ChunkHolder chunk) { + chunk.getOrCreateGet(); + chunk.delegate = BOTH; + return chunk.chunkExisting; + } + + @Override + public IChunkSet set(ChunkHolder chunk) { + chunk.getOrCreateSet(); + chunk.delegate = BOTH; + return chunk.chunkSet; + } + @Override public boolean setBiome(ChunkHolder chunk, int x, int y, int z, BiomeType biome) { @@ -221,9 +275,24 @@ public class ChunkHolder> implements IChunk { .getNbtData(); // TODO NOT IMPLEMENTED (add getTag delegate) } + @Override + public Map getTiles() { + return delegate.get(this).getTiles(); + } + + @Override + public Set getEntities() { + return delegate.get(this).getEntities(); + } + @Override public boolean hasSection(int layer) { - return get != null && get.hasSection(layer); + return chunkExisting != null && chunkExisting.hasSection(layer); + } + + @Override + public char[] getArray(int layer) { + return delegate.get(this).getArray(layer); } @Override @@ -250,42 +319,42 @@ public class ChunkHolder> implements IChunk { @Override public boolean trim(boolean aggressive) { - if (set != null) { - final boolean result = set.trim(aggressive); + if (chunkSet != null) { + final boolean result = chunkSet.trim(aggressive); if (result) { delegate = NULL; - get = null; - set = null; + chunkExisting = null; + chunkSet = null; return true; } } if (aggressive) { - get = null; + chunkExisting = null; if (delegate == BOTH) { delegate = SET; } else if (delegate == GET) { delegate = NULL; } } else { - get.trim(false); + chunkExisting.trim(false); } return false; } @Override public boolean isEmpty() { - return set == null || set.isEmpty(); + return chunkSet == null || chunkSet.isEmpty(); } /** - * Get or create the settable part of this chunk + * Get or create the existing part of this chunk * @return */ public final IChunkGet getOrCreateGet() { - if (get == null) { - get = newWrappedGet(); + if (chunkExisting == null) { + chunkExisting = newWrappedGet(); } - return get; + return chunkExisting; } /** @@ -293,10 +362,10 @@ public class ChunkHolder> implements IChunk { * @return */ public final IChunkSet getOrCreateSet() { - if (set == null) { - set = newWrappedSet(); + if (chunkSet == null) { + chunkSet = newWrappedSet(); } - return set; + return chunkSet; } /** @@ -324,31 +393,39 @@ public class ChunkHolder> implements IChunk { this.extent = extent; this.chunkX = chunkX; this.chunkZ = chunkZ; - if (set != null) { - set.reset(); + if (chunkSet != null) { + chunkSet.reset(); delegate = SET; } else { delegate = NULL; } - get = null; + chunkExisting = null; } @Override public synchronized T call() { - if (get != null && set != null) { - return getOrCreateGet().call(getOrCreateSet(), this::recycle); + if (chunkSet != null) { + return this.call(chunkSet, this::recycle); } return null; } @Override public T call(IChunkSet set, Runnable finalize) { - if (get != null && set != null) { - return getOrCreateGet().call(set, finalize); + if (set != null) { + IChunkGet get = getOrCreateGet(); + set = getExtent().processBatch(this, get, set); + if (set != null) { + return get.call(set, finalize); + } } return null; } + /** + * Get the extent this chunk is in + * @return + */ public IQueueExtent getExtent() { return extent; } @@ -389,6 +466,8 @@ public class ChunkHolder> implements IChunk { } public interface IBlockDelegate { + IChunkGet get(ChunkHolder chunk); + IChunkSet set(ChunkHolder chunk); boolean setBiome(ChunkHolder chunk, int x, int y, int z, BiomeType biome); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/WeakChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/WeakChunk.java index 12394302d..a2c43733d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/WeakChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/holder/WeakChunk.java @@ -2,8 +2,15 @@ package com.boydti.fawe.beta.implementation.holder; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IQueueExtent; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; + import java.lang.ref.Reference; import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.Set; +import java.util.UUID; /** * A {@link ReferenceChunk} using {@link WeakReference} to hold the chunk. diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommand.java b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommand.java index 799d39fb9..1579b3604 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommand.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommand.java @@ -43,8 +43,8 @@ public class CFICommand extends CommandProcessor { } @Override - public int process(InjectedValueAccess context, List args, Object result) { - return 0; + public Object process(InjectedValueAccess context, List args, Object result) { + return result; } private List dispatch(Player player, CFISettings settings, List args, InjectedValueAccess context) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/CommandProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/command/CommandProcessor.java index 3c2f3ad2e..daf63f372 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/CommandProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/CommandProcessor.java @@ -67,13 +67,13 @@ public abstract class CommandProcessor implements CommandManager { } @Override - public final int execute(InjectedValueAccess context, List args) { + public final Object execute(InjectedValueAccess context, List args) { args = preprocess(context, args); if (args != null) { Object result = parent.execute(context, args); return process(context, args, result); // TODO NOT IMPLEMENTED (recompile piston) } else { - return 0; + return null; } } @@ -89,5 +89,5 @@ public abstract class CommandProcessor implements CommandManager { public abstract List preprocess(InjectedValueAccess context, List args); - public abstract int process(InjectedValueAccess context, List args, Object result); + public abstract Object process(InjectedValueAccess context, List args, Object result); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java b/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java index 3c13b2b55..273eb873c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java @@ -12,23 +12,17 @@ import com.sk89q.worldedit.internal.registry.InputParser; import java.util.*; public abstract class FaweParser extends InputParser { - private final Class type; - protected FaweParser(WorldEdit worldEdit, Class type) { + protected FaweParser(WorldEdit worldEdit) { super(worldEdit); - this.type = type; } public PlatformCommandManager getPlatform() { return PlatformCommandManager.getInstance(); } - public Class getType() { - return type; - } - - public Collection parse(String input, Actor actor) { - return getPlatform().parse(getType(), "pattern " + input, actor); + public T parse(String input, Actor actor) { + return getPlatform().parse("pattern " + input, actor); } public T catchSuggestion(String currentInput, String nextInput, ParserContext context) throws InputParseException { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java index dbd176031..93ba38f0b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.registry.state.PropertyKey; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BlockCategories; import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; @@ -203,93 +204,74 @@ public class SchematicStreamer extends NBTStreamer { @Override public > void run(int x, int y, int z, B block) { BlockType type = block.getBlockType(); - switch (type.getInternalId()) { - case BlockID.ACACIA_STAIRS: - case BlockID.BIRCH_STAIRS: - case BlockID.BRICK_STAIRS: - case BlockID.COBBLESTONE_STAIRS: - case BlockID.DARK_OAK_STAIRS: - case BlockID.DARK_PRISMARINE_STAIRS: - case BlockID.JUNGLE_STAIRS: - case BlockID.NETHER_BRICK_STAIRS: - case BlockID.OAK_STAIRS: - case BlockID.PRISMARINE_BRICK_STAIRS: - case BlockID.PRISMARINE_STAIRS: - case BlockID.PURPUR_STAIRS: - case BlockID.QUARTZ_STAIRS: - case BlockID.RED_SANDSTONE_STAIRS: - case BlockID.SANDSTONE_STAIRS: - case BlockID.SPRUCE_STAIRS: - case BlockID.STONE_BRICK_STAIRS: - Object half = block.getState(PropertyKey.HALF); - Direction facing = block.getState(PropertyKey.FACING); + if (BlockCategories.STAIRS.contains(type)) { + Object half = block.getState(PropertyKey.HALF); + Direction facing = block.getState(PropertyKey.FACING); - BlockVector3 forward = facing.toBlockVector(); - Direction left = facing.getLeft(); - Direction right = facing.getRight(); + BlockVector3 forward = facing.toBlockVector(); + Direction left = facing.getLeft(); + Direction right = facing.getRight(); - BlockStateHolder forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ()); - BlockType forwardType = forwardBlock.getBlockType(); - if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) { - Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING); - if (forwardFacing == left) { - BlockStateHolder rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); - BlockType rightType = rightBlock.getBlockType(); - if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_left")); - } - return; - } else if (forwardFacing == right) { - BlockStateHolder leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); - BlockType leftType = leftBlock.getBlockType(); - if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right")); - } - return; + BlockStateHolder forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ()); + BlockType forwardType = forwardBlock.getBlockType(); + if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) { + Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING); + if (forwardFacing == left) { + BlockStateHolder rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); + BlockType rightType = rightBlock.getBlockType(); + if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { + fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_left")); } - } - - BlockStateHolder backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ()); - BlockType backwardsType = backwardsBlock.getBlockType(); - if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) { - Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING); - if (backwardsFacing == left) { - BlockStateHolder rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); - BlockType rightType = rightBlock.getBlockType(); - if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_left")); - } - return; - } else if (backwardsFacing == right) { - BlockStateHolder leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); - BlockType leftType = leftBlock.getBlockType(); - if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_right")); - } - return; + return; + } else if (forwardFacing == right) { + BlockStateHolder leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); + BlockType leftType = leftBlock.getBlockType(); + if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { + fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right")); } + return; } - break; - default: - int group = group(type); - if (group == -1) return; - BlockStateHolder set = block; + } - if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true); - if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true); - if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true); - if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true); - - if (group == 2) { - int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0); - int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0); - if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) { - set = set.with(PropertyKey.UP, true); + BlockStateHolder backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ()); + BlockType backwardsType = backwardsBlock.getBlockType(); + if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) { + Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING); + if (backwardsFacing == left) { + BlockStateHolder rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); + BlockType rightType = rightBlock.getBlockType(); + if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { + fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_left")); } + return; + } else if (backwardsFacing == right) { + BlockStateHolder leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); + BlockType leftType = leftBlock.getBlockType(); + if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { + fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "outer_right")); + } + return; } + } + } else { + int group = group(type); + if (group == -1) return; + BlockStateHolder set = block; - if (set != block) fc.setBlock(x, y, z, set); - break; + if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true); + if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true); + if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true); + if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true); + + if (group == 2) { + int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0); + int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0); + if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) { + set = set.with(PropertyKey.UP, true); + } + } + + if (set != block) fc.setBlock(x, y, z, set); } } }, false); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java index b11cb3328..48dbde699 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/HistoryExtent.java @@ -160,4 +160,9 @@ public class HistoryExtent extends AbstractDelegateExtent { return this.entity.setLocation(location); } } + + @Override + public Extent disableHistory() { + return getExtent(); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/Metadatable.java b/worldedit-core/src/main/java/com/boydti/fawe/object/Metadatable.java index 266cc2232..41d005fff 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/Metadatable.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/Metadatable.java @@ -1,62 +1,15 @@ package com.boydti.fawe.object; +import com.sk89q.worldedit.entity.MapMetadatable; + +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -public class Metadatable { - +public class Metadatable implements MapMetadatable { private final ConcurrentHashMap meta = new ConcurrentHashMap<>(); - /** - * Set some session only metadata for the player - * - * @param key - * @param value - * @return previous value - */ - public void setMeta(String key, Object value) { - this.meta.put(key, value); - } - - public T getAndSetMeta(String key, T value) { - return (T) this.meta.put(key, value); - } - - public boolean hasMeta() { - return !meta.isEmpty(); - } - - /** - * Get the metadata for a key. - * - * @param - * @param key - * @return - */ - public V getMeta(String key) { - return (V) this.meta.get(key); - } - - /** - * Get the metadata for a specific key (or return the default provided) - * - * @param key - * @param def - * @param - * @return - */ - public V getMeta(String key, V def) { - V value = (V) this.meta.get(key); - return value == null ? def : value; - } - - /** - * Delete the metadata for a key. - * - metadata is session only - * - deleting other plugin's metadata may cause issues - * - * @param key - */ - public V deleteMeta(String key) { - return (V) this.meta.remove(key); + @Override + public Map getRawMeta() { + return meta; } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java index 36a80d788..8b6b3f066 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java @@ -62,7 +62,7 @@ public class BrushSettings { if (constructor == null) { return new BrushSettings(); } - BrushSettings bs = (BrushSettings) manager.parse(BrushSettings.class, constructor, player); + BrushSettings bs = manager.parse(constructor, player); bs.constructor.put(SettingType.BRUSH, constructor); if (settings.containsKey(SettingType.PERMISSIONS.name())) { bs.permissions.addAll((Collection) settings.get(SettingType.PERMISSIONS.name())); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java index b0882882e..0a1e610db 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java @@ -1,6 +1,9 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.util.StringMan; +import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper; +import com.boydti.fawe.wrappers.PlayerWrapper; +import com.boydti.fawe.wrappers.SilentPlayerWrapper; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.command.tool.brush.Brush; @@ -39,9 +42,10 @@ public class CommandBrush implements Brush { position = position.add(face.getDirection().toBlockPoint()); } player.setSelection(selector); + PlayerWrapper wePlayer = new SilentPlayerWrapper(new LocationMaskedPlayerWrapper(player, new Location(player.getExtent(), position.toVector3()))); List cmds = StringMan.split(replaced, ';'); for (String cmd : cmds) { - CommandEvent event = new CommandEvent(player, cmd); + CommandEvent event = new CommandEvent(wePlayer, cmd); PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java index 493c58a67..0b70366cd 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ErodeBrush.java @@ -18,14 +18,25 @@ import java.util.Arrays; public class ErodeBrush implements Brush { private static final BlockVector3[] FACES_TO_CHECK = Direction.valuesOf(Direction.Flag.CARDINAL).stream().map(Direction::toBlockVector).toArray(BlockVector3[]::new); + private final int erodeFaces, erodeRec, fillFaces, fillRec; + + public ErodeBrush() { + this(2, 1, 5, 1); + } + + public ErodeBrush(int erodeFaces, int erodeRec, int fillFaces, int fillRec) { + this.erodeFaces = erodeFaces; + this.erodeRec = erodeRec; + this.fillFaces = fillFaces; + this.fillRec = fillRec; + } @Override public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException { - this.erosion(editSession, 2, 1, 5, position, size); + this.erosion(editSession, erodeFaces, erodeRec, fillFaces, fillRec, position, size); } - void erosion(EditSession es, int erodeFaces, int erodeRec, int fillFaces, - BlockVector3 target, double size) { + public void erosion(final EditSession es, int erodeFaces, int erodeRec, int fillFaces, int fillRec, BlockVector3 target, double size) { int brushSize = (int) size + 1; int brushSizeSquared = (int) (size * size); int dimension = brushSize * 2 + 1; @@ -55,7 +66,7 @@ public class ErodeBrush implements Brush { swap++; } - for (int i = 0; i < 1; ++i) { + for (int i = 0; i < fillRec; ++i) { fillIteration(brushSize, brushSizeSquared, fillFaces, swap % 2 == 0 ? buffer1 : buffer2, swap % 2 == 1 ? buffer1 : buffer2); swap++; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java index f7ad7b62a..dd3089cdd 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java @@ -47,7 +47,7 @@ public class InspectBrush extends BrushTool implements DoubleActionTraceTool { } public Vector3 getTarget(Player player, boolean adjacent) { - int range = this.range > -1 ? getRange() : MAX_RANGE; + int range = this.range > -1 ? getRange() : DEFAULT_RANGE; if (adjacent) { Location face = player.getBlockTraceFace(range, true); return face.add(face.getDirection()); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/RaiseBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/RaiseBrush.java index 498ed1932..4ae931884 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/RaiseBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/RaiseBrush.java @@ -6,8 +6,10 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; public class RaiseBrush extends ErodeBrush { - @Override - public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException { - this.erosion(editSession, 6, 0, 1, position, size); + public RaiseBrush() { + this(6, 0, 1, 1); + } + public RaiseBrush(int erodeFaces, int erodeRec, int fillFaces, int fillRec) { + super(2, 1, 5, 1); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplineBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplineBrush.java index fca13dc96..92d9a7c14 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplineBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplineBrush.java @@ -31,7 +31,7 @@ public class SplineBrush implements Brush, ResettableTool { private final Player player; private BlockVector3 position; - public SplineBrush(Player player, LocalSession session) { + public SplineBrush(Player player) { this.player = player; this.positionSets = new ArrayList<>(); } 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 617c071c2..72f84ab34 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 @@ -1928,11 +1928,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return false; } - @Override - public boolean setBiome(BlockVector2 position, BiomeType biome) { - return setBiome(position.getX(), 0, position.getBlockZ(), biome); - } - @Override public int getBlockLightLevel(BlockVector3 position) { return 0; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java index 4d40ce891..51994ec62 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java @@ -1,6 +1,6 @@ package com.boydti.fawe.object.brush.visualization.cfi; -import com.boydti.fawe.object.collection.IterableThreadLocal; +import com.boydti.fawe.object.collection.CleanableThreadLocal; import com.boydti.fawe.object.io.BufferedRandomAccessFile; import com.boydti.fawe.util.MainUtil; import com.sk89q.worldedit.extent.Extent; @@ -209,8 +209,8 @@ public abstract class MCAWriter implements Extent { } pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); pool.shutdown(); - IterableThreadLocal.clean(byteStore1); - IterableThreadLocal.clean(byteStore2); - IterableThreadLocal.clean(deflateStore); + CleanableThreadLocal.clean(byteStore1); + CleanableThreadLocal.clean(byteStore2); + CleanableThreadLocal.clean(deflateStore); } } 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 a07ddd59d..ffab92ac1 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 @@ -3,8 +3,13 @@ package com.boydti.fawe.object.changeset; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.config.Settings; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; +import com.boydti.fawe.object.HistoryExtent; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.TaskManager; @@ -12,6 +17,7 @@ import com.google.common.util.concurrent.Futures; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.history.change.BlockChange; import com.sk89q.worldedit.history.change.Change; @@ -23,17 +29,19 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; + import java.util.Iterator; +import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; -public abstract class FaweChangeSet implements ChangeSet { +public abstract class FaweChangeSet implements ChangeSet, IBatchProcessor { private World world; private final String worldName; - private final boolean mainThread; - private final int layers; protected AtomicInteger waitingCombined = new AtomicInteger(0); protected AtomicInteger waitingAsync = new AtomicInteger(0); @@ -51,15 +59,11 @@ public abstract class FaweChangeSet implements ChangeSet { public FaweChangeSet(String world) { this.worldName = world; - this.mainThread = Fawe.get() == null || Fawe.isMainThread(); - this.layers = FaweCache.IMP.CHUNK_LAYERS; } public FaweChangeSet(World world) { this.world = world; this.worldName = world.getName(); - this.mainThread = Fawe.isMainThread(); - this.layers = this.world.getMaxY() + 1 >> 4; } public String getWorldName() { @@ -124,6 +128,92 @@ public abstract class FaweChangeSet implements ChangeSet { return getIterator(true); } + @Override + public Extent construct(Extent child) { + return new HistoryExtent(child, this); + } + + @Override + public synchronized IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + int bx = chunk.getX() << 4; + int bz = chunk.getZ() << 4; + + Map tilesFrom = get.getTiles(); + Map tilesTo = set.getTiles(); + if (!tilesFrom.isEmpty()) { + for (Map.Entry entry : tilesFrom.entrySet()) { + BlockVector3 pos = entry.getKey(); + BlockState fromBlock = get.getBlock(pos.getX() & 15, pos.getY(), pos.getZ() & 15); + BlockState toBlock = set.getBlock(pos.getX() & 15, pos.getY(), pos.getZ() & 15); + if (fromBlock != toBlock || tilesTo.containsKey(pos)) { + addTileRemove(entry.getValue()); + } + } + } + if (!tilesTo.isEmpty()) { + for (Map.Entry entry : tilesTo.entrySet()) { + addTileCreate(entry.getValue()); + } + } + Set entRemoves = set.getEntityRemoves(); + if (!entRemoves.isEmpty()) { + for (UUID uuid : entRemoves) { + CompoundTag found = get.getEntity(uuid); + if (found != null) { + addEntityRemove(found); + } + } + } + Set ents = set.getEntities(); + if (!ents.isEmpty()) { + for (CompoundTag tag : ents) { + addEntityCreate(tag); + } + } + for (int layer = 0; layer < 16; layer++) { + if (!set.hasSection(layer)) continue; + // add each block and tile + char[] blocksGet = get.getArray(layer); + if (blocksGet == null) { + blocksGet = FaweCache.IMP.EMPTY_CHAR_4096; + } + char[] blocksSet = set.getArray(layer); + + int by = layer << 4; + for (int y = 0, index = 0; y < 16; y++) { + int yy = y + by; + for (int z = 0; z < 16; z++) { + int zz = z + bz; + for (int x = 0; x < 16; x++, index++) { + int xx = bx + x; + int combinedFrom = blocksGet[index]; + int combinedTo = blocksSet[index]; + if (combinedTo != 0) { + add(xx, yy, zz, combinedFrom, combinedTo); + } + } + } + } + } + + BiomeType[] biomes = set.getBiomes(); + if (biomes != null) { + for (int z = 0, index = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, index++) { + BiomeType newBiome = biomes[index]; + if (newBiome != null) { + BiomeType oldBiome = get.getBiomeType(x, z); + if (oldBiome != newBiome) { + addBiomeChange(bx + x, bz + z, oldBiome, newBiome); + } + } + } + } + } + + return 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/collection/AdaptedMap.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/AdaptedMap.java new file mode 100644 index 000000000..87c9a247a --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/AdaptedMap.java @@ -0,0 +1,112 @@ +package com.boydti.fawe.object.collection; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; + +public class AdaptedMap implements IAdaptedMap { + private final Map parent; + private final Function value2; + private final Function key2; + private final Function value; + private final Function key; + + private static final Function SAME = o -> o; + + private static final Function IMMUTABLE = o -> { throw new UnsupportedOperationException("Immutable"); }; + + public static AdaptedMap keys(Map parent, Function key, Function key2) { + return new AdaptedMap(parent, key, key2, SAME, SAME); + } + + public static AdaptedMap values(Map parent, Function value, Function value2) { + return new AdaptedMap(parent, SAME, SAME, value, value2); + } + + public static AdaptedMap immutable(Map parent, Function key, Function value) { + return new AdaptedMap(parent, IMMUTABLE, key, IMMUTABLE, value); + } + + public AdaptedMap(Map parent, Function key, Function key2, Function value, Function value2) { + this.parent = parent; + this.key = key; + this.value = value; + this.key2 = key2; + this.value2 = value2; + } + + @Override + public Map getParent() { + return this.parent; + } + + @Override + public K2 adaptKey(K key) { + return this.key.apply(key); + } + + @Override + public V2 adaptValue(V value) { + return this.value.apply(value); + } + + @Override + public K adaptKey2(K2 key) { + return this.key2.apply(key); + } + + @Override + public V adaptValue2(V2 value) { + return value2.apply(value); + } + + @NotNull + @Override + public Set> entrySet() { + if (isEmpty()) return Collections.emptySet(); + return new AdaptedSetCollection<>(getParent().entrySet(), new com.google.common.base.Function, Entry>() { + private AdaptedPair entry = new AdaptedPair(); + @Override + public Entry apply(@javax.annotation.Nullable Entry input) { + entry.input = input; + return entry; + } + }); + } + + public class AdaptedPair implements Entry { + private Entry input; + + @Override + public K getKey() { + return adaptKey2(input.getKey()); + } + + @Override + public V getValue() { + return adaptValue2(input.getValue()); + } + + @Override + public V setValue(V value) { + return adaptValue2(input.setValue(adaptValue(value))); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Entry) { + return Objects.equals(((Entry) o).getKey(), getKey()) && Objects.equals(((Entry) o).getValue(), getValue()); + } + return false; + } + + @Override + public int hashCode() { + return 1337; + } + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/AdaptedSetCollection.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/AdaptedSetCollection.java index 8b9a23663..d51dbd921 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/AdaptedSetCollection.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/AdaptedSetCollection.java @@ -18,14 +18,12 @@ import org.jetbrains.annotations.NotNull; * @param */ public class AdaptedSetCollection implements Set { - private final Function adapter; private final Collection adapted; private final Collection original; public AdaptedSetCollection(Collection collection, Function adapter) { this.original = collection; this.adapted = Collections2.transform(collection, adapter); - this.adapter = adapter; } public Collection getOriginal() { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java new file mode 100644 index 000000000..bdb98ccd6 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java @@ -0,0 +1,54 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.MutableBlockVector3; +import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class BlockVector3ChunkMap implements Map, IAdaptedMap { + private final Short2ObjectArrayMap map = new Short2ObjectArrayMap<>(); + + @Override + public Map getParent() { + return map; + } + + @Override + public Short adaptKey(BlockVector3 key) { + return MathMan.tripleBlockCoord(key.getX(), key.getY(), key.getZ()); + } + + @Override + public BlockVector3 adaptKey2(Short key) { + int x = MathMan.untripleBlockCoordX(key); + int y = MathMan.untripleBlockCoordY(key); + int z = MathMan.untripleBlockCoordZ(key); + return MutableBlockVector3.get(x, y, z); + } + + @Override + public T adaptValue2(T value) { + return value; + } + + @Override + public T adaptValue(T value) { + return value; + } + + public T put(int x, int y, int z, T value) { + short key = MathMan.tripleBlockCoord(x, y, z); + return map.put(key, value); + } + + public boolean contains(int x, int y, int z) { + short key = MathMan.tripleBlockCoord(x, y, z); + return map.containsKey(key); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/CleanableThreadLocal.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/CleanableThreadLocal.java new file mode 100644 index 000000000..094b5ca1f --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/CleanableThreadLocal.java @@ -0,0 +1,122 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.util.MainUtil; + +import java.lang.ref.Reference; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +public class CleanableThreadLocal extends ThreadLocal { + private final Supplier supplier; + private final Function modifier; + private LongAdder count = new LongAdder(); + + public CleanableThreadLocal(Supplier supplier) { + this(supplier, Function.identity()); + } + + public CleanableThreadLocal(Supplier supplier, Consumer modifier) { + this(supplier, t -> { + modifier.accept(t); + return t; + }); + } + + public CleanableThreadLocal(Supplier supplier, Function modifier) { + this.supplier = supplier; + this.modifier = modifier; + } + + @Override + protected final T initialValue() { + T value = modifier.apply(init()); + if (value != null) { + count.increment(); + } + return value; + } + + public T init() { + return supplier.get(); + } + + public void clean() { + if (count.sumThenReset() > 0) { + CleanableThreadLocal.clean(this); + } + } + + public static void clean(ThreadLocal instance) { + try { + Thread[] threads = MainUtil.getThreads(); + Field tl = Thread.class.getDeclaredField("threadLocals"); + tl.setAccessible(true); + Method methodRemove = null; + for (Thread thread : threads) { + if (thread != null) { + Object tlm = tl.get(thread); + if (tlm != null) { + if (methodRemove == null) { + methodRemove = tlm.getClass().getDeclaredMethod("remove", ThreadLocal.class); + methodRemove.setAccessible(true); + } + if (methodRemove != null) { + try { + methodRemove.invoke(tlm, instance); + } catch (Throwable ignore) {} + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void cleanAll() { + try { + // Get a reference to the thread locals table of the current thread + Thread thread = Thread.currentThread(); + Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); + threadLocalsField.setAccessible(true); + Object threadLocalTable = threadLocalsField.get(thread); + + // Get a reference to the array holding the thread local variables inside the + // ThreadLocalMap of the current thread + Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); + Field tableField = threadLocalMapClass.getDeclaredField("table"); + tableField.setAccessible(true); + Object table = tableField.get(threadLocalTable); + + // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object + // is a reference to the actual ThreadLocal variable + Field referentField = Reference.class.getDeclaredField("referent"); + referentField.setAccessible(true); + + for (int i = 0; i < Array.getLength(table); i++) { + // Each entry in the table array of ThreadLocalMap is an Entry object + // representing the thread local reference and its value + Object entry = Array.get(table, i); + if (entry != null) { + // Get a reference to the thread local object and remove it from the table + ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry); + clean(threadLocal); + } + } + } catch(Exception e) { + // We will tolerate an exception here and just log it + throw new IllegalStateException(e); + } + } + + @Override + protected void finalize() throws Throwable { + clean(this); + super.finalize(); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/IAdaptedMap.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/IAdaptedMap.java new file mode 100644 index 000000000..73646b6c8 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/IAdaptedMap.java @@ -0,0 +1,100 @@ +package com.boydti.fawe.object.collection; + +import com.google.common.base.Function; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +public interface IAdaptedMap extends Map { + Map getParent(); + + K2 adaptKey(K key); + + V2 adaptValue(V value); + + K adaptKey2(K2 key); + + V adaptValue2(V2 value); + + @Override + default int size() { + return getParent().size(); + } + + @Override + default boolean isEmpty() { + return getParent().isEmpty(); + } + + @Override + default boolean containsKey(Object key) { + return getParent().containsKey(adaptKey((K) key)); + } + + @Override + default boolean containsValue(Object value) { + return getParent().containsValue(adaptValue((V) value)); + } + + @Override + default V get(Object key) { + return adaptValue2(getParent().get(adaptKey((K) key))); + } + + @Nullable + @Override + default V put(K key, V value) { + return adaptValue2(getParent().put(adaptKey(key), adaptValue(value))); + } + + @Override + default V remove(Object key) { + return adaptValue2(getParent().remove(adaptKey((K) key))); + } + + @Override + default void putAll(@NotNull Map m) { + for (Entry entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + default void clear() { + getParent().clear(); + } + + @NotNull + @Override + default Set keySet() { + if (isEmpty()) return Collections.emptySet(); + return new AdaptedSetCollection<>(getParent().keySet(), this::adaptKey2); + } + + @NotNull + @Override + default Collection values() { + if (isEmpty()) return Collections.emptySet(); + return new AdaptedSetCollection<>(getParent().values(), this::adaptValue2); + } + + @NotNull + @Override + default Set> entrySet() { + if (isEmpty()) return Collections.emptySet(); + return new AdaptedSetCollection<>(getParent().entrySet(), new Function, Entry>() { + private MutablePair entry = new MutablePair<>(); + @Override + public Entry apply(@javax.annotation.Nullable Entry input) { + entry.setKey(adaptKey2(input.getKey())); + entry.setValue(adaptValue2(input.getValue())); + return entry; + } + }); + } +} 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 eaa4fe297..5e7dadae1 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,19 +1,9 @@ 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; -import java.lang.reflect.Field; -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 class IterableThreadLocal extends ThreadLocal implements Iterable { @@ -24,14 +14,6 @@ public class IterableThreadLocal extends ThreadLocal implements Iterable supplier, Function modifier) { - this.supplier = supplier; - } - - public IterableThreadLocal(Supplier supplier, Consumer modifier) { - this.supplier = supplier; - } - @Override protected final T initialValue() { T value = init(); @@ -55,82 +37,18 @@ public class IterableThreadLocal extends ThreadLocal implements Iterable getAll() { return Collections.unmodifiableCollection(allValues); } @Override protected void finalize() throws Throwable { - clean(this); + CleanableThreadLocal.clean(this); super.finalize(); } } 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 d467d7f86..b64d3f1ad 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 @@ -869,6 +869,10 @@ public final class MemBlockSet extends BlockSet { return true; } + public void reset(int layer) { + this.rows[layer] = NULL_ROW_Y; + } + public void reset() { 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/collection/MutablePair.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/MutablePair.java new file mode 100644 index 000000000..6dc405979 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/MutablePair.java @@ -0,0 +1,28 @@ +package com.boydti.fawe.object.collection; + +import java.util.Map; + +public class MutablePair implements Map.Entry { + private K key; + private V value; + @Override + public K getKey() { + return null; + } + + @Override + public V getValue() { + return null; + } + + @Override + public V setValue(V value) { + V old = value; + this.value = value; + return old; + } + + public void setKey(K key) { + this.key = key; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java index 6ba63c070..e73a4bbe6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java @@ -1,8 +1,13 @@ package com.boydti.fawe.object.extent; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.exception.FaweException; +import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.WEManager; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; @@ -17,9 +22,12 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; import javax.annotation.Nullable; -public abstract class FaweRegionExtent extends ResettableExtent { +public abstract class FaweRegionExtent extends ResettableExtent implements IBatchProcessor { private final FaweLimit limit; /** @@ -39,7 +47,20 @@ public abstract class FaweRegionExtent extends ResettableExtent { public abstract Collection getRegions(); public boolean isGlobal() { - return getRegions().stream().anyMatch(Region::isGlobal); + for (Region r : getRegions()) { + if (r.isGlobal()) { + return true; + } + } + return false; + } + + @Override + public Extent construct(Extent child) { + if (getExtent() != child) { + new ExtentTraverser(this).setNext(child); + } + return this; } @Override 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 0949e106c..0d44eb8d5 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 @@ -1,14 +1,24 @@ package com.boydti.fawe.object.extent; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.RegionWrapper; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; -public class HeightBoundExtent extends FaweRegionExtent { +public class HeightBoundExtent extends FaweRegionExtent implements IBatchProcessor { private final int min, max; private int lastY = -1; @@ -38,4 +48,12 @@ public class HeightBoundExtent extends FaweRegionExtent { public Collection getRegions() { return Collections.singletonList(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, min, max, Integer.MIN_VALUE, Integer.MAX_VALUE)); } + + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + if (trimY(set, min, max) | trimNBT(set, this::contains)) { + return set; + } + return null; + } } 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 6c9c1fd96..25ea18553 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 @@ -1,13 +1,20 @@ package com.boydti.fawe.object.extent; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.object.FaweLimit; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionIntersection; + import java.util.Arrays; import java.util.Collection; public class MultiRegionExtent extends FaweRegionExtent { + private final RegionIntersection intersection; private Region region; private final Region[] regions; private int index; @@ -22,6 +29,7 @@ public class MultiRegionExtent extends FaweRegionExtent { this.index = 0; this.region = regions[0]; this.regions = regions; + this.intersection = new RegionIntersection(Arrays.asList(regions)); } @Override @@ -64,4 +72,9 @@ public class MultiRegionExtent extends FaweRegionExtent { public Collection getRegions() { return Arrays.asList(regions); } + + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + return intersection.processBatch(chunk, get, set); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiTransform.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiTransform.java index b07269faf..b5be6b9e1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiTransform.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/MultiTransform.java @@ -32,20 +32,26 @@ public class MultiTransform extends RandomTransform { @Override public > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { - return Arrays.stream(extents).map(extent -> extent.setBlock(x, y, z, block)) - .reduce(false, (a, b) -> a || b); + // don't use streams for each block place, it'd be incredibly slow + boolean result = false; + for (AbstractDelegateExtent extent : extents) result |= extent.setBlock(x, y, z, block); + return result; } @Override public > boolean setBlock(BlockVector3 location, T block) throws WorldEditException { - return Arrays.stream(extents).map(extent -> extent.setBlock(location, block)) - .reduce(false, (a, b) -> a || b); + // don't use streams for each block place, it'd be incredibly slow + boolean result = false; + for (AbstractDelegateExtent extent : extents) result |= extent.setBlock(location, block); + return result; } @Override public boolean setBiome(BlockVector2 position, BiomeType biome) { - return Arrays.stream(extents).map(extent -> extent.setBiome(position, biome)) - .reduce(false, (a, b) -> a || b); + // don't use streams for each block place, it'd be incredibly slow + boolean result = false; + for (AbstractDelegateExtent extent : extents) result |= extent.setBiome(position, biome); + return result; } @Nullable 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 d4857bbb1..de919d0b2 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 @@ -1,5 +1,8 @@ package com.boydti.fawe.object.extent; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.exception.FaweException; @@ -321,4 +324,9 @@ public class NullExtent extends FaweRegionExtent { public List> getBlockDistributionWithData(Region region) { throw reason; } + + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + return null; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java index 01f3c66b4..d76ab424f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java @@ -19,12 +19,12 @@ import com.sk89q.worldedit.world.block.BlockTypes; public class ProcessedWEExtent extends AbstractDelegateExtent { private final FaweLimit limit; - private final AbstractDelegateExtent extent; + private final Extent extent; public ProcessedWEExtent(Extent parent, FaweLimit limit) { super(parent); this.limit = limit; - this.extent = (AbstractDelegateExtent) parent; + this.extent = parent; } public void setLimit(FaweLimit other) { 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 5f859ae82..abc3522a0 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 @@ -1,5 +1,8 @@ package com.boydti.fawe.object.extent; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.object.FaweLimit; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.regions.Region; @@ -35,4 +38,9 @@ public class SingleRegionExtent extends FaweRegionExtent { public Collection getRegions() { return Collections.singletonList(region); } + + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + return region.processBatch(chunk, get, set); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java index f03cedcc0..6b97b8562 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java @@ -123,4 +123,10 @@ public class FuzzyRegion extends AbstractRegion { public void setExtent(EditSession extent) { this.extent = extent; } + + @Override + public boolean containsEntireCuboid(int bx, int tx, int by, int ty, int bz, int tz) { + // TODO optimize (switch from BlockVectorSet to the new bitset) + return false; + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/CFIRedirect.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/CFIRedirect.java similarity index 97% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/CFIRedirect.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/CFIRedirect.java index ec7645158..743205223 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/CFIRedirect.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/CFIRedirect.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.github.intellectualsites.plotsquared.commands.Command; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweChunkManager.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweChunkManager.java similarity index 98% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweChunkManager.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweChunkManager.java index f51d370a9..edb5c1b5b 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweChunkManager.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweChunkManager.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.TaskManager; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweLocalBlockQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweLocalBlockQueue.java similarity index 98% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweLocalBlockQueue.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweLocalBlockQueue.java index 3ac1e6c63..d73720c03 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweLocalBlockQueue.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweLocalBlockQueue.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweSchematicHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweSchematicHandler.java similarity index 98% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweSchematicHandler.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweSchematicHandler.java index e48e36977..859f9317f 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweSchematicHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweSchematicHandler.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.clipboard.ReadOnlyClipboard; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweTrim.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweTrim.java similarity index 97% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweTrim.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweTrim.java index 7302697eb..836bd11e6 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/FaweTrim.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweTrim.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.boydti.fawe.util.TaskManager; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/MoveTo512.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/MoveTo512.java similarity index 99% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/MoveTo512.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/MoveTo512.java index bd2349adc..cee667852 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/MoveTo512.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/MoveTo512.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.github.intellectualsites.plotsquared.commands.CommandDeclaration; import com.github.intellectualsites.plotsquared.plot.commands.CommandCategory; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotRegionFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotRegionFilter.java similarity index 93% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotRegionFilter.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotRegionFilter.java index 47bc3908d..d02010126 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotRegionFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotRegionFilter.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotSetBiome.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSetBiome.java similarity index 94% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotSetBiome.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSetBiome.java index 1813368be..d32477ed6 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotSetBiome.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSetBiome.java @@ -1,6 +1,7 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.TaskManager; import com.github.intellectualsites.plotsquared.commands.Command; @@ -20,7 +21,6 @@ import com.github.intellectualsites.plotsquared.plot.util.StringMan; import com.github.intellectualsites.plotsquared.plot.util.WorldUtil; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; @@ -32,7 +32,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadLocalRandom; -import org.bukkit.Bukkit; @CommandDeclaration( command = "generatebiome", @@ -74,7 +73,7 @@ public class PlotSetBiome extends Command { } plot.addRunning(); TaskManager.IMP.async(() -> { - EditSession session = new EditSessionBuilder(BukkitAdapter.adapt(Bukkit.getWorld(plot.getArea().worldname))) + EditSession session = new EditSessionBuilder(FaweAPI.getWorld(plot.getArea().worldname)) .autoQueue(false) .checkMemory(false) .allowedRegionsEverywhere() diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotSquaredFeature.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSquaredFeature.java similarity index 80% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotSquaredFeature.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSquaredFeature.java index dcab75585..723cb102b 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/PlotSquaredFeature.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/PlotSquaredFeature.java @@ -1,6 +1,7 @@ -package com.boydti.fawe.bukkit.regions.plotquared; +package com.boydti.fawe.regions.general.integrations.plotquared; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.regions.FaweMask; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.regions.general.RegionFilter; @@ -20,16 +21,18 @@ import com.github.intellectualsites.plotsquared.plot.util.SchematicHandler; import com.github.intellectualsites.plotsquared.plot.util.UUIDHandler; import com.github.intellectualsites.plotsquared.plot.util.block.GlobalBlockQueue; import com.github.intellectualsites.plotsquared.plot.util.block.QueueProvider; -import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.AbstractRegion; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.regions.RegionIntersection; + import java.util.HashSet; +import java.util.List; import java.util.UUID; -import org.bukkit.Bukkit; +import java.util.stream.Collectors; + +import com.sk89q.worldedit.world.World; public class PlotSquaredFeature extends FaweMaskManager { public PlotSquaredFeature() { @@ -138,42 +141,11 @@ public class PlotSquaredFeature extends FaweMaskManager { if (regions.size() == 1) { maskedRegion = new CuboidRegion(pos1, pos2); } else { - maskedRegion = new AbstractRegion(BukkitAdapter.adapt(Bukkit.getWorld(area.worldname))) { - @Override - public BlockVector3 getMinimumPoint() { - return pos1; - } - - @Override - public BlockVector3 getMaximumPoint() { - return pos2; - } - - @Override - public void expand(BlockVector3... changes) throws RegionOperationException { - throw new UnsupportedOperationException("Region is immutable"); - } - - @Override - public void contract(BlockVector3... changes) throws RegionOperationException { - throw new UnsupportedOperationException("Region is immutable"); - } - - @Override - public boolean contains(int x, int y, int z) { - return WEManager.maskContains(regions, x, y, z); - } - - @Override - public boolean contains(int x, int z) { - return WEManager.maskContains(regions, x, z); - } - - @Override - public boolean contains(BlockVector3 position) { - return contains(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - } - }; + World world = FaweAPI.getWorld(area.worldname); + List weRegions = regions.stream() + .map(r -> new CuboidRegion(world, BlockVector3.at(r.minX, r.minY, r.minZ), BlockVector3.at(r.maxX, r.maxY, r.maxZ))) + .collect(Collectors.toList()); + maskedRegion = new RegionIntersection(world, weRegions); } return new FaweMask(maskedRegion) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/ReplaceAll.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/ReplaceAll.java similarity index 100% rename from worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/regions/plotquared/ReplaceAll.java rename to worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/ReplaceAll.java diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java index 683213253..bb5720358 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java @@ -1,40 +1,50 @@ package com.boydti.fawe.util; -import static com.google.common.base.Preconditions.checkNotNull; - import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.implementation.ParallelQueueExtent; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.logging.LoggingChangeSet; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.HistoryExtent; import com.boydti.fawe.object.NullChangeSet; import com.boydti.fawe.object.RegionWrapper; +import com.boydti.fawe.object.brush.visualization.VirtualWorld; +import com.boydti.fawe.object.changeset.BlockBagChangeSet; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; import com.boydti.fawe.object.exception.FaweException; +import com.boydti.fawe.object.extent.FaweRegionExtent; +import com.boydti.fawe.object.extent.MultiRegionExtent; import com.boydti.fawe.object.extent.NullExtent; +import com.boydti.fawe.object.extent.SingleRegionExtent; +import com.boydti.fawe.object.extent.SlowExtent; +import com.boydti.fawe.object.extent.StripNBTExtent; +import com.boydti.fawe.wrappers.WorldWrapper; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.extent.EditSessionEvent; -import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.world.World; -import java.util.UUID; +import org.jetbrains.annotations.NotNull; + import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.jetbrains.annotations.NotNull; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkNotNull; public class EditSessionBuilder { private World world; private String worldName; - private Extent extent; private Player player; private FaweLimit limit; private FaweChangeSet changeSet; @@ -45,6 +55,7 @@ public class EditSessionBuilder { private Boolean combineStages; private EventBus eventBus; private BlockBag blockBag; + private boolean threaded = true; private EditSessionEvent event; /** @@ -65,14 +76,12 @@ public class EditSessionBuilder { public EditSessionBuilder(@Nonnull World world) { checkNotNull(world); this.world = world; - this.extent = world; this.worldName = Fawe.imp().getWorldName(world); } public EditSessionBuilder(World world, String worldName) { if (world == null && worldName == null) throw new NullPointerException("Both world and worldname cannot be null"); this.world = world; - this.extent = world; this.worldName = worldName; } @@ -84,12 +93,12 @@ public class EditSessionBuilder { public EditSessionBuilder player(@Nullable Player player) { this.player = player; - return this; + return setDirty(); } public EditSessionBuilder limit(@Nullable FaweLimit limit) { this.limit = limit; - return this; + return setDirty(); } public EditSessionBuilder limitUnlimited() { @@ -101,12 +110,12 @@ public class EditSessionBuilder { limitUnlimited(); FaweLimit tmp = fp.getLimit(); limit.INVENTORY_MODE = tmp.INVENTORY_MODE; - return this; + return setDirty(); } public EditSessionBuilder changeSet(@Nullable FaweChangeSet changeSet) { this.changeSet = changeSet; - return this; + return setDirty(); } public EditSessionBuilder changeSetNull() { @@ -117,7 +126,7 @@ public class EditSessionBuilder { checkNotNull(world); this.world = world; this.worldName = world.getName(); - return this; + return setDirty(); } /** @@ -146,23 +155,23 @@ public class EditSessionBuilder { } else { this.changeSet = new MemoryOptimizedHistory(world); } - return this; + return setDirty(); } public EditSessionBuilder allowedRegions(@Nullable Region[] allowedRegions) { this.allowedRegions = allowedRegions; - return this; + return setDirty(); } @Deprecated public EditSessionBuilder allowedRegions(@Nullable RegionWrapper[] allowedRegions) { this.allowedRegions = allowedRegions; - return this; + return setDirty(); } public EditSessionBuilder allowedRegions(@Nullable RegionWrapper allowedRegion) { this.allowedRegions = allowedRegion == null ? null : allowedRegion.toArray(); - return this; + return setDirty(); } public EditSessionBuilder allowedRegionsEverywhere() { @@ -171,47 +180,45 @@ public class EditSessionBuilder { public EditSessionBuilder autoQueue(@Nullable Boolean autoQueue) { this.autoQueue = autoQueue; - return this; + return setDirty(); } public EditSessionBuilder fastmode(@Nullable Boolean fastmode) { this.fastmode = fastmode; - return this; + return setDirty(); } public EditSessionBuilder checkMemory(@Nullable Boolean checkMemory) { this.checkMemory = checkMemory; - return this; + return setDirty(); } public EditSessionBuilder combineStages(@Nullable Boolean combineStages) { this.combineStages = combineStages; - return this; - } - - public EditSessionBuilder extent(@Nullable Extent extent) { - this.extent = extent; - return this; + return setDirty(); } public EditSessionBuilder blockBag(@Nullable BlockBag blockBag) { this.blockBag = blockBag; - return this; + return setDirty(); } public EditSessionBuilder eventBus(@Nullable EventBus eventBus) { this.eventBus = eventBus; - return this; + return setDirty(); } public EditSessionBuilder event(@Nullable EditSessionEvent event) { this.event = event; + return setDirty(); + } + + private EditSessionBuilder setDirty() { + compiled = false; return this; } - private boolean wrapped; - - private AbstractDelegateExtent wrapExtent(final AbstractDelegateExtent extent, final EventBus eventBus, EditSessionEvent event, final EditSession.Stage stage) { + private Extent wrapExtent(final Extent extent, final EventBus eventBus, EditSessionEvent event, final EditSession.Stage stage) { event = event.clone(stage); event.setExtent(extent); eventBus.post(event); @@ -222,16 +229,16 @@ public class EditSessionBuilder { if(toReturn instanceof com.sk89q.worldedit.extent.NullExtent) { return new NullExtent(toReturn, FaweException.MANUAL); } - if (!(toReturn instanceof AbstractDelegateExtent)) { - Fawe.debug("Extent " + toReturn + " must be AbstractDelegateExtent"); - return extent; - } +// if (!(toReturn instanceof AbstractDelegateExtent)) { +// Fawe.debug("Extent " + toReturn + " must be AbstractDelegateExtent"); +// return extent; +// } if (toReturn != extent) { String className = toReturn.getClass().getName().toLowerCase(); for (String allowed : Settings.IMP.EXTENT.ALLOWED_PLUGINS) { if (className.contains(allowed.toLowerCase())) { this.wrapped = true; - return (AbstractDelegateExtent) toReturn; + return toReturn; } } if (Settings.IMP.EXTENT.DEBUG) { @@ -252,13 +259,16 @@ public class EditSessionBuilder { private FaweChangeSet changeTask; private int maxY; - private HistoryExtent history; - private AbstractDelegateExtent bypassHistory; - private AbstractDelegateExtent bypassAll; + private Extent bypassHistory; + private Extent bypassAll; + private Extent extent; + private boolean compiled; + private boolean wrapped; public EditSessionBuilder compile() { - if (extent != null) return this; + if (compiled) return this; + compiled = true; wrapped = false; if (world == null && !this.worldName.isEmpty()) { world = FaweAPI.getWorld(this.worldName); @@ -302,109 +312,126 @@ public class EditSessionBuilder { } // this.originalLimit = limit; this.blockBag = limit.INVENTORY_MODE != 0 ? blockBag : null; -// this.limit = limit.copy(); + this.limit = limit.copy(); -// if (queue == null) { -// boolean placeChunks = this.fastmode || this.limit.FAST_PLACEMENT; -// World unwrapped = WorldWrapper.unwrap(world); -// if (unwrapped instanceof IQueueExtent) { -// queue = (IQueueExtent) unwrapped; -// } else if (unwrapped instanceof MCAWorld) { -// queue = ((MCAWorld) unwrapped).getQueue(); -// } else if (player != null && world.equals(player.getWorld())) { -// queue = player.getIQueueExtent(placeChunks, autoQueue); -// } else { -// queue = SetQueue.IMP.getNewQueue(world, placeChunks, autoQueue); -// } -// } -// if (combineStages == null) { -// combineStages = -// // If it's enabled in the settings -// Settings.IMP.HISTORY.COMBINE_STAGES -// // If fast placement is disabled, it's slower to perform a copy on each chunk -// && this.limit.FAST_PLACEMENT -// // If the specific queue doesn't support it -// && queue.supports(IQueueExtent.Capability.CHANGE_TASKS) -// // If the edit uses items from the inventory we can't use a delayed task -// && this.blockBag == null; -// } -// if (!Settings.IMP.QUEUE.PROGRESS.DISPLAY.equalsIgnoreCase("false") && player != null) { -// switch (Settings.IMP.QUEUE.PROGRESS.DISPLAY.toLowerCase()) { -// case "chat": -// this.queue.setProgressTask(new ChatProgressTracker(player)); -// break; -// case "title": -// case "true": -// default: -// this.queue.setProgressTask(new DefaultProgressTracker(player)); -// } -// } -// this.bypassAll = wrapExtent(new FastWorldEditExtent(world, queue), eventBus, event, EditSession.Stage.BEFORE_CHANGE); -// this.bypassHistory = (this.extent = wrapExtent(bypassAll, eventBus, event, EditSession.Stage.BEFORE_REORDER)); -// if (!this.fastmode || changeSet != null) { -// if (changeSet == null) { -// if (Settings.IMP.HISTORY.USE_DISK) { -// UUID uuid = player == null ? EditSession.CONSOLE : player.getUUID(); -// if (Settings.IMP.HISTORY.USE_DATABASE) { -// changeSet = new RollbackOptimizedHistory(world, uuid); -// } else { -// changeSet = new DiskStorageHistory(world, uuid); -// } -// } else if (combineStages && Settings.IMP.HISTORY.COMPRESSION_LEVEL == 0 && !(queue instanceof MCAQueue)) { -// changeSet = new CPUOptimizedChangeSet(world); -// } else { -// changeSet = new MemoryOptimizedHistory(world); + if (extent == null) { + IQueueExtent queue = null; + World unwrapped = WorldWrapper.unwrap(world); + boolean placeChunks = this.fastmode || this.limit.FAST_PLACEMENT; + + if (placeChunks) { + if (unwrapped instanceof IQueueExtent) { + extent = queue = (IQueueExtent) unwrapped; + } else if (Settings.IMP.QUEUE.PARALLEL_THREADS > 1 && threaded) { + ParallelQueueExtent parallel = new ParallelQueueExtent(Fawe.get().getQueueHandler(), world); + queue = parallel.getExtent(); + extent = parallel; + } else { + System.out.println("FAWE is in single threaded mode (performance reduced)"); + extent = queue = Fawe.get().getQueueHandler().getQueue(world); + } + } else { + extent = world; + } + Extent root = extent; + if (combineStages == null) { + combineStages = + // If it's enabled in the settings + Settings.IMP.HISTORY.COMBINE_STAGES + // If fast placement is disabled, it's slower to perform a copy on each chunk + && this.limit.FAST_PLACEMENT + // If the edit uses items from the inventory we can't use a delayed task + && this.blockBag == null; + } + if (!Settings.IMP.QUEUE.PROGRESS.DISPLAY.equalsIgnoreCase("false") && player != null) { + System.out.println("TODO add progress display"); +// switch (Settings.IMP.QUEUE.PROGRESS.DISPLAY.toLowerCase()) { +// case "chat": +// this.queue.setProgressTask(new ChatProgressTracker(player)); +// break; +// case "title": +// case "true": +// default: +// this.queue.setProgressTask(new DefaultProgressTracker(player)); // } -// } -// if (this.limit.SPEED_REDUCTION > 0) { -// this.bypassHistory = new SlowExtent(this.bypassHistory, this.limit.SPEED_REDUCTION); -// } -// if (changeSet instanceof NullChangeSet && Fawe.imp().getBlocksHubApi() != null && player != null) { -// changeSet = LoggingChangeSet.wrap(player, changeSet); -// } -// if (!(changeSet instanceof NullChangeSet)) { -// if (!(changeSet instanceof LoggingChangeSet) && player != null && Fawe.imp().getBlocksHubApi() != null) { -// changeSet = LoggingChangeSet.wrap(player, changeSet); -// } -// if (this.blockBag != null) { -// changeSet = new BlockBagChangeSet(changeSet, blockBag, limit.INVENTORY_MODE == 1); -// } -// if (combineStages) { -// changeTask = changeSet; -// changeSet.addChangeTask(queue); -// } else { -// this.extent = (history = new HistoryExtent(bypassHistory, changeSet, queue)); -//// if (this.blockBag != null) { -//// this.extent = new BlockBagExtent(this.extent, blockBag, limit.INVENTORY_MODE == 1); -//// } -// } -// } -// } -// if (allowedRegions == null) { -// if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions") && !(queue instanceof VirtualWorld)) { -// allowedRegions = player.getCurrentRegions(); -// } -// } -// this.maxY = world == null ? 255 : world.getMaxY(); -// if (allowedRegions != null) { -// if (allowedRegions.length == 0) { -// this.extent = new NullExtent(this.extent, FaweException.NO_REGION); -// } else { -// this.extent = new ProcessedWEExtent(this.extent, this.limit); -// if (allowedRegions.length == 1) { -// this.extent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]); -// } else { -// this.extent = new MultiRegionExtent(this.extent, this.limit, allowedRegions); -// } -// } -// } else { -// this.extent = new HeightBoundExtent(this.extent, this.limit, 0, maxY); -// } -// if (this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) { -// this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT); -// } -// this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY); -// return this; + } + extent = this.bypassAll = wrapExtent(extent, eventBus, event, EditSession.Stage.BEFORE_CHANGE); + this.bypassHistory = (this.extent = wrapExtent(bypassAll, eventBus, event, EditSession.Stage.BEFORE_REORDER)); + if (!this.fastmode || changeSet != null) { + if (changeSet == null) { + if (Settings.IMP.HISTORY.USE_DISK) { + UUID uuid = player == null ? EditSession.CONSOLE : player.getUniqueId(); + if (Settings.IMP.HISTORY.USE_DATABASE) { + changeSet = new RollbackOptimizedHistory(world, uuid); + } else { + changeSet = new DiskStorageHistory(world, uuid); + } +// } else if (combineStages && Settings.IMP.HISTORY.COMPRESSION_LEVEL == 0) { +// changeSet = new CPUOptimizedChangeSet(world); + } else { + if (combineStages && Settings.IMP.HISTORY.COMPRESSION_LEVEL == 0) { + System.out.println("TODO add CPUOptimizedChangeSet"); + } + changeSet = new MemoryOptimizedHistory(world); + } + } + if (this.limit.SPEED_REDUCTION > 0) { + this.extent = this.bypassHistory = new SlowExtent(this.bypassHistory, this.limit.SPEED_REDUCTION); + } + if (changeSet instanceof NullChangeSet && Fawe.imp().getBlocksHubApi() != null && player != null) { + changeSet = LoggingChangeSet.wrap(player, changeSet); + } + if (!(changeSet instanceof NullChangeSet)) { + if (!(changeSet instanceof LoggingChangeSet) && player != null && Fawe.imp().getBlocksHubApi() != null) { + changeSet = LoggingChangeSet.wrap(player, changeSet); + } + if (this.blockBag != null) { + System.out.println("TODO implement block bag as IBatchProcessor"); + changeSet = new BlockBagChangeSet(changeSet, blockBag, limit.INVENTORY_MODE == 1); + } + if (combineStages) { + changeTask = changeSet; + this.extent = extent.enableHistory(changeSet); + } else { + this.extent = (new HistoryExtent(extent, changeSet)); +// if (this.blockBag != null) { +// this.extent = new BlockBagExtent(this.extent, blockBag, limit.INVENTORY_MODE == 1); +// } + } + } + } + if (allowedRegions == null) { + if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions") && !(root instanceof VirtualWorld)) { + allowedRegions = player.getCurrentRegions(); + } + } + this.maxY = world == null ? 255 : world.getMaxY(); + FaweRegionExtent regionExtent = null; + if (allowedRegions != null) { + if (allowedRegions.length == 0) { + regionExtent = new NullExtent(this.extent, FaweException.NO_REGION); + } else { +// this.extent = new ProcessedWEExtent(this.extent, this.limit); + if (allowedRegions.length == 1) { + regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]); + } else { + regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions); + } + } + } else { +// this.extent = new HeightBoundExtent(this.extent, this.limit, 0, maxY); + } + if (regionExtent != null && queue != null && combineStages) { + queue.addProcessor(regionExtent); + } else if (regionExtent != null) { + this.extent = regionExtent; + } + if (this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) { + System.out.println("TODO add batch processor for strip nbt"); + this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT); + } + this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY); + } return this; } @@ -415,10 +442,6 @@ public class EditSessionBuilder { return new EditSession(this); } - public Extent getExtent() { - return extent; - } - public World getWorld() { return world; } @@ -427,6 +450,10 @@ public class EditSessionBuilder { return worldName; } + public Extent getExtent() { + return extent != null ? extent : world; + } + public boolean isWrapped() { return wrapped; } @@ -435,23 +462,16 @@ public class EditSessionBuilder { return fastmode; } - public HistoryExtent getHistory() { - return history; - } - - public AbstractDelegateExtent getBypassHistory() { + public Extent getBypassHistory() { return bypassHistory; } - public AbstractDelegateExtent getBypassAll() { + public Extent getBypassAll() { return bypassAll; } @NotNull public FaweLimit getLimit() { - if (limit == null) { - return FaweLimit.MAX; - } return limit; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/FaweTimer.java b/worldedit-core/src/main/java/com/boydti/fawe/util/FaweTimer.java index dd785ffac..4b4a1b8f1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/FaweTimer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/FaweTimer.java @@ -41,7 +41,8 @@ public class FaweTimer implements Runnable { if (tick < lastGetTPSTick + tickInterval) { return lastGetTPSValue; } - double total = Arrays.stream(history).sum(); + double total = 0; + for (double v : history) total += v; lastGetTPSValue = total / history.length; lastGetTPSTick = tick; return lastGetTPSValue; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/ImgurUtility.java b/worldedit-core/src/main/java/com/boydti/fawe/util/ImgurUtility.java index 31f90f183..fd79082e9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/ImgurUtility.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/ImgurUtility.java @@ -1,5 +1,9 @@ package com.boydti.fawe.util; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; +import com.google.gson.Gson; +import com.google.gson.JsonObject; + import java.io.*; import java.net.HttpURLConnection; import java.net.URL; @@ -10,6 +14,33 @@ import java.util.stream.Collectors; public class ImgurUtility { public static final String CLIENT_ID = "50e34b65351eb07"; + public static URL uploadImage(File file) throws IOException { + // was used until a merge deleted all the CFI/schematics functionality TODO NOT IMPLEMENTED + return uploadImage(new FileInputStream(file)); + } + + public static URL uploadImage(InputStream inputStream) throws IOException { + // was used until a merge deleted all the CFI/schematics functionality TODO NOT IMPLEMENTED + inputStream = new BufferedInputStream(inputStream); + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(Short.MAX_VALUE); + int i; + while ((i = inputStream.read()) != -1) { + baos.write(i); + } + baos.flush(); + return uploadImage(baos.toByteArray()); + } + + public static URL uploadImage(byte[] image) throws IOException { + // was used until a merge deleted all the CFI/schematics functionality TODO NOT IMPLEMENTED + String json = getImgurContent(CLIENT_ID, image); + Gson gson = new Gson(); + JsonObject obj = gson.fromJson(json, JsonObject.class); + JsonObject data = obj.get("data").getAsJsonObject(); + String link = data.get("link").getAsString(); + return new URL(link); + } + public static String getImgurContent(String clientID, byte[] image) throws IOException { String imageString = Base64.getEncoder().encodeToString(image); URL url = new URL("https://api.imgur.com/3/image"); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java index 44cd09e55..f290a47e1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -1,7 +1,5 @@ package com.boydti.fawe.util; -import static java.lang.System.arraycopy; - import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; @@ -26,7 +24,17 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.util.Location; -import java.awt.Graphics2D; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import net.jpountz.lz4.LZ4Compressor; +import net.jpountz.lz4.LZ4Factory; +import net.jpountz.lz4.LZ4FastDecompressor; +import net.jpountz.lz4.LZ4InputStream; +import net.jpountz.lz4.LZ4Utils; + +import javax.annotation.Nullable; +import javax.imageio.ImageIO; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -75,27 +83,11 @@ import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import javax.annotation.Nullable; -import javax.imageio.ImageIO; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; -import net.jpountz.lz4.LZ4Compressor; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.lz4.LZ4FastDecompressor; -import net.jpountz.lz4.LZ4InputStream; -import net.jpountz.lz4.LZ4Utils; + +import static java.lang.System.arraycopy; public class MainUtil { - public static void sendAdmin(final String s) { - for (final Player player : Fawe.get().getCachedPlayers()) { - if (player.hasPermission("fawe.admin")) { - player.print(s); - } - } - Fawe.debug(s); - } - public static List filter(String prefix, List suggestions) { if (prefix.isEmpty()) { return suggestions; @@ -754,7 +746,6 @@ public class MainUtil { if (time >= 33868800) { int years = (int) (time / 33868800); int time1 = years * 33868800; - System.out.println(time1); time -= time1; toreturn.append(years + "y "); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java index 14e9f84cc..ef3fa7a7a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java @@ -69,10 +69,10 @@ public class TextureUtil implements TextureHolder { private BiomeColor[] biomes = new BiomeColor[]{ // ID Name Temperature, rainfall, grass, foliage colors // - note: the colors here are just placeholders, they are computed in the program - new BiomeColor(0, "ocean", 0.5f, 0.5f, 0x92BD59, 7842607), + new BiomeColor(0, "ocean", 0.5f, 0.5f, 0x92BD59, 0x77AB2F), // default values of temp and rain - new BiomeColor(1, "plains", 0.8f, 0.4f, 0x92BD59, 7842607), - new BiomeColor(2, "desert", 2.0f, 0.0f, 0x92BD59, 7842607), + new BiomeColor(1, "plains", 0.8f, 0.4f, 0x92BD59, 0x77AB2F), + new BiomeColor(2, "desert", 2.0f, 0.0f, 0x92BD59, 0x77AB2F), new BiomeColor(3, "mountains", 0.2f, 0.3f, 0x92BD59, 0x77AB2F), new BiomeColor(4, "forest", 0.7f, 0.8f, 0x92BD59, 0x77AB2F), new BiomeColor(5, "taiga", 0.25f, 0.8f, 0x92BD59, 0x77AB2F), @@ -97,7 +97,7 @@ public class TextureUtil implements TextureHolder { new BiomeColor(22, "jungle_hills", 0.95f, 0.9f, 0x92BD59, 0x77AB2F), new BiomeColor(23, "jungle_edge", 0.95f, 0.8f, 0x92BD59, 0x77AB2F), new BiomeColor(24, "deep_ocean", 0.5f, 0.5f, 0x92BD59, 0x77AB2F), - new BiomeColor(25, "stone_shore", 0.2f, 0.3f, 9616729, 0x77AB2F), + new BiomeColor(25, "stone_shore", 0.2f, 0.3f, 0x92BD59, 0x77AB2F), new BiomeColor(26, "snowy_beach", 0.05f, 0.3f, 0x92BD59, 0x77AB2F), new BiomeColor(27, "birch_forest", 0.6f, 0.6f, 0x92BD59, 0x77AB2F), new BiomeColor(28, "birch_forest_hills", 0.6f, 0.6f, 0x92BD59, 0x77AB2F), diff --git a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/LocationMaskedPlayerWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/LocationMaskedPlayerWrapper.java new file mode 100644 index 000000000..d60ee83b2 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/LocationMaskedPlayerWrapper.java @@ -0,0 +1,33 @@ +package com.boydti.fawe.wrappers; + +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Location; + +public class LocationMaskedPlayerWrapper extends PlayerWrapper { + private final boolean allowTeleport; + private Location position; + + public LocationMaskedPlayerWrapper(Player parent, Location position) { + this(parent, position, false); + } + + public LocationMaskedPlayerWrapper(Player parent, Location position, boolean allowTeleport) { + super(parent); + this.position = position; + this.allowTeleport = allowTeleport; + } + + @Override + public Location getLocation() { + return position; + } + + @Override + public void setPosition(Vector3 pos, float pitch, float yaw) { + this.position = new Location(position.getExtent(), pos, pitch, yaw); + if (allowTeleport) { + super.setPosition(pos, pitch, yaw); + } + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java new file mode 100644 index 000000000..00cb5cd27 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java @@ -0,0 +1,259 @@ +package com.boydti.fawe.wrappers; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.PlayerProxy; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.TargetBlock; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockTypes; + +public class PlayerWrapper extends PlayerProxy { + public PlayerWrapper(Player parent) { + super(parent); + } + + public static PlayerWrapper wrap(Player parent) { + if (parent instanceof PlayerWrapper) { + return (PlayerWrapper) parent; + } + return new PlayerWrapper(parent); + } + + @Override + public World getWorld() { + return WorldWrapper.wrap(super.getWorld()); + } + + @Override + public void findFreePosition(Location searchPos) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + getBasePlayer().findFreePosition(searchPos); + } + }); + } + + @Override + public void setOnGround(Location searchPos) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + getBasePlayer().setOnGround(searchPos); + } + }); + } + + @Override + public void findFreePosition() { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + getBasePlayer().findFreePosition(); + } + }); + } + + @Override + public boolean ascendLevel() { + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + this.value = getBasePlayer().ascendLevel(); + } + }); + } + + @Override + public boolean descendLevel() { + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + this.value = getBasePlayer().descendLevel(); + } + }); + } + + @Override + public boolean ascendToCeiling(int clearance) { + return ascendToCeiling(clearance, true); + } + + @Override + public boolean ascendToCeiling(int clearance, boolean alwaysGlass) { + Location pos = getBlockIn(); + int x = pos.getBlockX(); + int initialY = Math.max(0, pos.getBlockY()); + int y = Math.max(0, pos.getBlockY() + 2); + int z = pos.getBlockZ(); + Extent world = getLocation().getExtent(); + + // No free space above + if (!world.getBlock(BlockVector3.at(x, y, z)).getBlockType().getMaterial().isAir()) { + return false; + } + + while (y <= world.getMaximumPoint().getY()) { + // Found a ceiling! + if (world.getBlock(BlockVector3.at(x, y, z)).getBlockType().getMaterial().isMovementBlocker()) { + int platformY = Math.max(initialY, y - 3 - clearance); + floatAt(x, platformY + 1, z, alwaysGlass); + return true; + } + + ++y; + } + + return false; + } + + @Override + public boolean ascendUpwards(int distance) { + return ascendUpwards(distance, true); + } + + @Override + public boolean ascendUpwards(int distance, boolean alwaysGlass) { + final Location pos = getBlockIn(); + final int x = pos.getBlockX(); + final int initialY = Math.max(0, pos.getBlockY()); + int y = Math.max(0, pos.getBlockY() + 1); + final int z = pos.getBlockZ(); + final int maxY = Math.min(getWorld().getMaxY() + 1, initialY + distance); + final Extent world = getLocation().getExtent(); + + while (y <= world.getMaximumPoint().getY() + 2) { + if (world.getBlock(BlockVector3.at(x, y, z)).getBlockType().getMaterial().isMovementBlocker()) { + break; // Hit something + } else if (y > maxY + 1) { + break; + } else if (y == maxY + 1) { + floatAt(x, y - 1, z, alwaysGlass); + return true; + } + + ++y; + } + + return false; + } + + @Override + public void floatAt(int x, int y, int z, boolean alwaysGlass) { + RuntimeException caught = null; + try { + EditSession edit = new EditSessionBuilder(WorldWrapper.unwrap(getWorld())).player(unwrap(getBasePlayer())).build(); + edit.setBlock(BlockVector3.at(x, y - 1, z), BlockTypes.GLASS); + edit.flushQueue(); + LocalSession session = Fawe.get().getWorldEdit().getSessionManager().get(this); + if (session != null) { + session.remember(edit, true, getBasePlayer().getLimit().MAX_HISTORY); + } + } catch (RuntimeException e) { + caught = e; + } + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value) { + setPosition(Vector3.at(x + 0.5, y, z + 0.5)); + } + }); + if (caught != null) { + throw caught; + } + } + + @Override + public Location getBlockTrace(int range, boolean useLastBlock) { + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Location value) { + TargetBlock tb = new TargetBlock(PlayerWrapper.this, range, 0.2D); + this.value = useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock(); + } + }); + } + + @Override + public Location getBlockTraceFace(int range, boolean useLastBlock) { + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Location value) { + TargetBlock tb = new TargetBlock(PlayerWrapper.this, range, 0.2D); + this.value = useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace(); + } + }); + } + + @Override + public Location getSolidBlockTrace(int range) { + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Location value) { + TargetBlock tb = new TargetBlock(PlayerWrapper.this, range, 0.2D); + this.value = tb.getSolidTargetBlock(); + } + }); + } + + @Override + public Direction getCardinalDirection() { + return getBasePlayer().getCardinalDirection(); + } + + @Override + public boolean passThroughForwardWall(int range) { + return TaskManager.IMP.sync(() -> { + int searchDist = 0; + TargetBlock hitBlox = new TargetBlock(PlayerWrapper.this, range, 0.2); + Extent world = getLocation().getExtent(); + Location block; + boolean firstBlock = true; + int freeToFind = 2; + boolean inFree = false; + + while ((block = hitBlox.getNextBlock()) != null) { + boolean free = !world.getBlock(BlockVector3.at(block.getBlockX(), block.getBlockY(), block.getBlockZ())).getBlockType().getMaterial().isMovementBlocker(); + + if (firstBlock) { + firstBlock = false; + + if (!free) { + --freeToFind; + continue; + } + } + + ++searchDist; + if (searchDist > 20) { + return false; + } + + if (inFree != free) { + if (free) { + --freeToFind; + } + } + + if (freeToFind == 0) { + setOnGround(block); + return true; + } + + inFree = free; + } + + return false; + }); + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/SilentPlayerWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/SilentPlayerWrapper.java new file mode 100644 index 000000000..2e8b51979 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/SilentPlayerWrapper.java @@ -0,0 +1,36 @@ +package com.boydti.fawe.wrappers; + +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.util.formatting.text.Component; + +import java.awt.*; + +/** + * Avoids printing any messages + */ +public class SilentPlayerWrapper extends PlayerWrapper { + public SilentPlayerWrapper(Player parent) { + super(parent); + } + + @Override + public void print(String msg) { + } + + @Override + public void print(Component component) { + super.print(component); + } + + @Override + public void printDebug(String msg) { + } + + @Override + public void printError(String msg) { + } + + @Override + public void printRaw(String msg) { + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/ByteTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/ByteTag.java index 7f8f68a51..6ea46c1b0 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/ByteTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/ByteTag.java @@ -22,7 +22,7 @@ package com.sk89q.jnbt; /** * The {@code TAG_Byte} tag. */ -public final class ByteTag extends Tag { +public final class ByteTag extends NumberTag { private final byte value; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTag.java index df5c4e030..ed75d9cef 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTag.java @@ -19,17 +19,23 @@ package com.sk89q.jnbt; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Location; + import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; /** * The {@code TAG_Compound} tag. */ public class CompoundTag extends Tag { - private final Map value; + private Map value; /** * Creates the tag with an empty name. @@ -48,7 +54,7 @@ public class CompoundTag extends Tag { * @return true if the tag contains the given key */ public boolean containsKey(String key) { - return value.containsKey(key); + return getValue().containsKey(key); } @Override @@ -63,7 +69,8 @@ public class CompoundTag extends Tag { * @return the new compound tag */ public CompoundTag setValue(Map value) { - return new CompoundTag(value); + this.value = value; + return this; } /** @@ -85,7 +92,7 @@ public class CompoundTag extends Tag { * @return a byte array */ public byte[] getByteArray(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof ByteArrayTag) { return ((ByteArrayTag) tag).getValue(); } else { @@ -103,7 +110,7 @@ public class CompoundTag extends Tag { * @return a byte */ public byte getByte(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof ByteTag) { return ((ByteTag) tag).getValue(); } else { @@ -121,7 +128,7 @@ public class CompoundTag extends Tag { * @return a double */ public double getDouble(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof DoubleTag) { return ((DoubleTag) tag).getValue(); } else { @@ -140,25 +147,9 @@ public class CompoundTag extends Tag { * @return a double */ public double asDouble(String key) { - Tag tag = value.get(key); - if (tag instanceof ByteTag) { - return ((ByteTag) tag).getValue(); - - } else if (tag instanceof ShortTag) { - return ((ShortTag) tag).getValue(); - - } else if (tag instanceof IntTag) { - return ((IntTag) tag).getValue(); - - } else if (tag instanceof LongTag) { - return ((LongTag) tag).getValue(); - - } else if (tag instanceof FloatTag) { - return ((FloatTag) tag).getValue(); - - } else if (tag instanceof DoubleTag) { - return ((DoubleTag) tag).getValue(); - + Tag tag = getValue().get(key); + if (tag instanceof NumberTag) { + return ((NumberTag) tag).getValue().doubleValue(); } else { return 0; } @@ -174,7 +165,7 @@ public class CompoundTag extends Tag { * @return a float */ public float getFloat(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof FloatTag) { return ((FloatTag) tag).getValue(); } else { @@ -192,7 +183,7 @@ public class CompoundTag extends Tag { * @return an int array */ public int[] getIntArray(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof IntArrayTag) { return ((IntArrayTag) tag).getValue(); } else { @@ -210,7 +201,7 @@ public class CompoundTag extends Tag { * @return an int */ public int getInt(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof IntTag) { return ((IntTag) tag).getValue(); } else { @@ -229,25 +220,9 @@ public class CompoundTag extends Tag { * @return an int */ public int asInt(String key) { - Tag tag = value.get(key); - if (tag instanceof ByteTag) { - return ((ByteTag) tag).getValue(); - - } else if (tag instanceof ShortTag) { - return ((ShortTag) tag).getValue(); - - } else if (tag instanceof IntTag) { - return ((IntTag) tag).getValue(); - - } else if (tag instanceof LongTag) { - return ((LongTag) tag).getValue().intValue(); - - } else if (tag instanceof FloatTag) { - return ((FloatTag) tag).getValue().intValue(); - - } else if (tag instanceof DoubleTag) { - return ((DoubleTag) tag).getValue().intValue(); - + Tag tag = getValue().get(key); + if (tag instanceof NumberTag) { + return ((NumberTag) tag).getValue().intValue(); } else { return 0; } @@ -263,7 +238,7 @@ public class CompoundTag extends Tag { * @return a list of tags */ public List getList(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof ListTag) { return ((ListTag) tag).getValue(); } else { @@ -281,7 +256,7 @@ public class CompoundTag extends Tag { * @return a tag list instance */ public ListTag getListTag(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof ListTag) { return (ListTag) tag; } else { @@ -304,7 +279,7 @@ public class CompoundTag extends Tag { */ @SuppressWarnings("unchecked") public List getList(String key, Class listType) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof ListTag) { ListTag listTag = (ListTag) tag; if (listTag.getType().equals(listType)) { @@ -327,7 +302,7 @@ public class CompoundTag extends Tag { * @return an int array */ public long[] getLongArray(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof LongArrayTag) { return ((LongArrayTag) tag).getValue(); } else { @@ -345,7 +320,7 @@ public class CompoundTag extends Tag { * @return a long */ public long getLong(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof LongTag) { return ((LongTag) tag).getValue(); } else { @@ -364,25 +339,9 @@ public class CompoundTag extends Tag { * @return a long */ public long asLong(String key) { - Tag tag = value.get(key); - if (tag instanceof ByteTag) { - return ((ByteTag) tag).getValue(); - - } else if (tag instanceof ShortTag) { - return ((ShortTag) tag).getValue(); - - } else if (tag instanceof IntTag) { - return ((IntTag) tag).getValue(); - - } else if (tag instanceof LongTag) { - return ((LongTag) tag).getValue(); - - } else if (tag instanceof FloatTag) { - return ((FloatTag) tag).getValue().longValue(); - - } else if (tag instanceof DoubleTag) { - return ((DoubleTag) tag).getValue().longValue(); - + Tag tag = getValue().get(key); + if (tag instanceof NumberTag) { + return ((NumberTag) tag).getValue().longValue(); } else { return 0L; } @@ -398,7 +357,7 @@ public class CompoundTag extends Tag { * @return a short */ public short getShort(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof ShortTag) { return ((ShortTag) tag).getValue(); } else { @@ -416,7 +375,7 @@ public class CompoundTag extends Tag { * @return a string */ public String getString(String key) { - Tag tag = value.get(key); + Tag tag = getValue().get(key); if (tag instanceof StringTag) { return ((StringTag) tag).getValue(); } else { @@ -427,18 +386,40 @@ public class CompoundTag extends Tag { @Override public Map toRaw() { HashMap raw = new HashMap<>(); - if (this.value.isEmpty()) return raw; - for (Map.Entry entry : value.entrySet()) { + if (this.getValue().isEmpty()) return raw; + for (Map.Entry entry : getValue().entrySet()) { raw.put(entry.getKey(), entry.getValue().toRaw()); } return raw; } + public UUID getUUID() { + long most = getLong("UUIDMost"); + long least = getLong("UUIDLeast"); + return new UUID(most, least); + } + + public Vector3 getEntityPosition() { + List posTags = getList("Pos"); + double x = ((NumberTag) posTags.get(0)).getValue().doubleValue(); + double y = ((NumberTag) posTags.get(1)).getValue().doubleValue(); + double z = ((NumberTag) posTags.get(2)).getValue().doubleValue(); + return Vector3.at(x, y, z); + } + + public Location getEntityLocation(Extent extent) { + List rotTag = getList("Rotation"); + float yaw = ((NumberTag) rotTag.get(0)).getValue().floatValue(); + float pitch = ((NumberTag) rotTag.get(1)).getValue().floatValue(); + return new Location(extent, getEntityPosition(), yaw, pitch); + } + @Override public String toString() { + Map value = getValue(); StringBuilder bldr = new StringBuilder(); - bldr.append("TAG_Compound").append(": ").append(value.size()).append(" entries\r\n{\r\n"); - for (Map.Entry entry : value.entrySet()) { + bldr.append("TAG_Compound").append(": ").append(getValue().size()).append(" entries\r\n{\r\n"); + for (Map.Entry entry : getValue().entrySet()) { bldr.append(" ").append(entry.getValue().toString().replaceAll("\r\n", "\r\n ")).append("\r\n"); } bldr.append("}"); diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/DoubleTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/DoubleTag.java index 8f8e3b594..447aa44fd 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/DoubleTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/DoubleTag.java @@ -23,7 +23,7 @@ package com.sk89q.jnbt; * The {@code TAG_Double} tag. * */ -public final class DoubleTag extends Tag { +public final class DoubleTag extends NumberTag { private final double value; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/FloatTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/FloatTag.java index 273da606c..7a4895810 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/FloatTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/FloatTag.java @@ -22,7 +22,7 @@ package com.sk89q.jnbt; /** * The {@code TAG_Float} tag. */ -public final class FloatTag extends Tag { +public final class FloatTag extends NumberTag { private final float value; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/IntTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/IntTag.java index 944c736d6..1fa374cce 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/IntTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/IntTag.java @@ -22,7 +22,7 @@ package com.sk89q.jnbt; /** * The {@code TAG_Int} tag. */ -public final class IntTag extends Tag { +public final class IntTag extends NumberTag { private final int value; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/ListTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/ListTag.java index 42732d92a..a13d01dec 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/ListTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/ListTag.java @@ -30,7 +30,7 @@ import javax.annotation.Nullable; /** * The {@code TAG_List} tag. */ -public final class ListTag extends Tag { +public class ListTag extends Tag { private final Class type; private final List value; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/LongTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/LongTag.java index a28ad1f64..efb8ec3d0 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/LongTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/LongTag.java @@ -23,7 +23,7 @@ package com.sk89q.jnbt; * The {@code TAG_Long} tag. * */ -public final class LongTag extends Tag { +public final class LongTag extends NumberTag { private final long value; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTConstants.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTConstants.java index 2c8d284ce..77fc3632e 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTConstants.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTConstants.java @@ -19,6 +19,8 @@ package com.sk89q.jnbt; +import com.sk89q.worldedit.math.BlockVector3; + import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -80,5 +82,4 @@ public final class NBTConstants { throw new IllegalArgumentException("Unknown tag type ID of " + id); } } - } diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NumberTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NumberTag.java new file mode 100644 index 000000000..db7b78cb1 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NumberTag.java @@ -0,0 +1,6 @@ +package com.sk89q.jnbt; + +public abstract class NumberTag extends Tag { + @Override + public abstract Number getValue(); +} diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/ShortTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/ShortTag.java index a2f4d397b..212958ef4 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/ShortTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/ShortTag.java @@ -22,7 +22,7 @@ package com.sk89q.jnbt; /** * The {@code TAG_Short} tag. */ -public final class ShortTag extends Tag { +public final class ShortTag extends NumberTag { private final short value; 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 a700f6826..69fcdabb5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -199,13 +199,13 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { private final World world; private final String worldName; private boolean wrapped; - private final HistoryExtent history; - private AbstractDelegateExtent bypassHistory; - private AbstractDelegateExtent bypassAll; + private Extent bypassHistory; + private Extent bypassAll; private final FaweLimit originalLimit; private final FaweLimit limit; private final Player player; private FaweChangeSet changeTask; + private boolean history; private final MutableBlockVector3 mutablebv = new MutableBlockVector3(); @@ -230,8 +230,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { this.world = builder.getWorld(); this.worldName = builder.getWorldName(); this.wrapped = builder.isWrapped(); -// this.fastMode = builder.hasFastMode(); Not used - this.history = builder.getHistory(); this.bypassHistory = builder.getBypassHistory(); this.bypassAll = builder.getBypassAll(); this.originalLimit = builder.getLimit(); @@ -240,6 +238,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { this.changeTask = builder.getChangeTask(); this.maxY = builder.getMaxY(); this.blockBag = builder.getBlockBag(); + this.history = changeTask != null; } /** @@ -404,7 +403,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @return the change set */ public ChangeSet getChangeSet() { - return changeTask != null ? changeTask : history != null ? history.getChangeSet() : null; + return changeTask; } /** @@ -585,8 +584,8 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { if (survivalExtent != null) { return survivalExtent.get(); } else { - SurvivalModeExtent survival = new SurvivalModeExtent(bypassAll.getExtent(), getWorld()); - new ExtentTraverser(bypassAll).setNext(survival); + SurvivalModeExtent survival = new SurvivalModeExtent(bypassAll, getWorld()); + bypassAll = survival; return survival; } } @@ -609,25 +608,18 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @param disableHistory */ public void disableHistory(boolean disableHistory) { - if (history == null) { - return; - } - ExtentTraverser traverseHistory = new ExtentTraverser<>(getExtent()).find(HistoryExtent.class); if (disableHistory) { - if (traverseHistory != null && traverseHistory.exists()) { - ExtentTraverser beforeHistory = traverseHistory.previous(); - ExtentTraverser afterHistory = traverseHistory.next(); - if (beforeHistory != null && beforeHistory.exists()) { - beforeHistory.setNext(afterHistory.get()); - } else { - new ExtentTraverser<>(getExtent()).setNext(afterHistory.get()); - } + if (this.history) { + disableHistory(); + this.history = false; + return; } - } else if (traverseHistory == null || !traverseHistory.exists()) { - ExtentTraverser traverseBypass = new ExtentTraverser<>(getExtent()).find(bypassHistory); - if (traverseBypass != null) { - ExtentTraverser beforeHistory = traverseBypass.previous(); - beforeHistory.setNext(history); + } else { + if (this.history) { + if (this.changeTask == null) { + throw new IllegalArgumentException("History was never provided, cannot enable"); + } + enableHistory(this.changeTask); } } } @@ -948,22 +940,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } } - /** - * Set blocks that are in a set of positions and return the number of times - * that the block set calls returned true. - * - * @param vset a set of positions - * @param pattern the pattern - * @return the number of changed blocks - * @throws MaxChangedBlocksException thrown if too many blocks are changed - */ - public int setBlocks(Set vset, Pattern pattern) throws MaxChangedBlocksException { - RegionVisitor visitor = new RegionVisitor(vset, new BlockReplace(getExtent(), pattern)); - Operations.completeBlindly(visitor); - changes += visitor.getAffected(); - return changes; - } - @Override @Nullable public Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) { @@ -987,8 +963,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { public void setBlocks(ChangeSet changeSet, ChangeSetExecutor.Type type) { final UndoContext context = new UndoContext(); - Extent bypass = (history == null) ? bypassAll : history; - context.setExtent(bypass); + context.setExtent(bypassAll); Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, type, getBlockBag(), getLimit().INVENTORY_MODE)); flushQueue(); changes = 1; @@ -1082,7 +1057,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { // Reset limit limit.set(originalLimit); // Enqueue it - super.commit(); if (getChangeSet() != null) { if (Settings.IMP.HISTORY.COMBINE_STAGES) { ((FaweChangeSet) getChangeSet()).closeAsync(); @@ -1161,33 +1135,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { return this.changes = visitor.getAffected(); } - /** - * Count the number of blocks of a list of types in a region. - * - * @param region the region - * @param searchBlocks the list of blocks to search - * @return the number of blocks that matched the block - */ - public int countBlocks(Region region, Set searchBlocks) { - BlockMask mask = new BlockMask(this, searchBlocks); - return countBlocks(region, mask); - } - - /** - * Count the number of blocks of a list of types in a region. - * - * @param region the region - * @param searchMask mask to match - * @return the number of blocks that matched the mask - */ - public int countBlocks(Region region, Mask searchMask) { - Counter count = new Counter(); - RegionMaskingFilter filter = new RegionMaskingFilter(searchMask, count); - RegionVisitor visitor = new RegionVisitor(region, filter); - Operations.completeBlindly(visitor); // We can't throw exceptions, nor do we expect any - return count.getCount(); - } - /** * Fills an area recursively in the X/Z directions. * @@ -1310,36 +1257,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { return this.replaceBlocks(region, mask, pattern); } - public boolean canBypassAll(Region region, boolean get, boolean set) { - if (wrapped) return false; - if (history != null) return false; - FaweRegionExtent regionExtent = getRegionExtent(); - if (!(region instanceof CuboidRegion)) return false; - if (regionExtent != null) { - BlockVector3 pos1 = region.getMinimumPoint(); - BlockVector3 pos2 = region.getMaximumPoint(); - boolean contains = false; - for (Region current : regionExtent.getRegions()) { - if (current.contains(pos1.getX(), pos1.getBlockY(), pos1.getBlockZ()) && current.contains(pos2.getBlockX(), pos2.getBlockY(), pos2.getBlockZ())) { - contains = true; - break; - } - } - if (!contains) return false; - } - long area = region.getArea(); - FaweLimit left = getLimitLeft(); - if (!left.isUnlimited() && (((get || getChangeTask() != null) && left.MAX_CHECKS <= area) || (set && left.MAX_CHANGES <= area))) - return false; - if (getChangeTask() != getChangeSet()) return false; - if (!Masks.isNull(getMask()) || !Masks.isNull(getSourceMask())) return false; - return getBlockBag() == null; - } - - public boolean hasExtraExtents() { - return wrapped || getMask() != null || getSourceMask() != null || history != null; - } - /** * Remove blocks of a certain type nearby a given position. * @@ -1362,87 +1279,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { return replaceBlocks(region, mask, BlockTypes.AIR.getDefaultState()); } - /** - * Sets all the blocks inside a region to a given block type. - * - * @param region the region - * @param block the block - * @return number of blocks affected - * @throws MaxChangedBlocksException thrown if too many blocks are changed - */ - public > int setBlocks(Region region, B block) throws MaxChangedBlocksException { - return setBlocks(region, (Pattern) block); - } - - /** - * Sets all the blocks inside a region to a given pattern. - * - * @param region the region - * @param pattern the pattern that provides the replacement block - * @return number of blocks affected - * @throws MaxChangedBlocksException thrown if too many blocks are changed - */ - public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { - checkNotNull(region); - checkNotNull(pattern); - - BlockReplace replace = new BlockReplace(this, pattern); - RegionVisitor visitor = new RegionVisitor(region, replace); - Operations.completeLegacy(visitor); - return visitor.getAffected(); - } - - /** - * Replaces all the blocks matching a given filter, within a given region, to a block - * returned by a given pattern. - * - * @param region the region to replace the blocks within - * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask} - * @param replacement the replacement block - * @return number of blocks affected - * @throws MaxChangedBlocksException thrown if too many blocks are changed - */ - public > int replaceBlocks(Region region, Set filter, B replacement) throws MaxChangedBlocksException { - return replaceBlocks(region, filter, (Pattern) replacement); - } - - /** - * Replaces all the blocks matching a given filter, within a given region, to a block - * returned by a given pattern. - * - * @param region the region to replace the blocks within - * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask} - * @param pattern the pattern that provides the new blocks - * @return number of blocks affected - * @throws MaxChangedBlocksException thrown if too many blocks are changed - */ - public int replaceBlocks(Region region, Set filter, Pattern pattern) throws MaxChangedBlocksException { - Mask mask = filter == null ? new ExistingBlockMask(this) : new BlockMask(this, filter); - return replaceBlocks(region, mask, pattern); - } - - /** - * Replaces all the blocks matching a given mask, within a given region, to a block - * returned by a given pattern. - * - * @param region the region to replace the blocks within - * @param mask the mask that blocks must match - * @param pattern the pattern that provides the new blocks - * @return number of blocks affected - * @throws MaxChangedBlocksException thrown if too many blocks are changed - */ - public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException { - checkNotNull(region); - checkNotNull(mask); - checkNotNull(pattern); - - BlockReplace replace = new BlockReplace(this, pattern); - RegionMaskingFilter filter = new RegionMaskingFilter(mask, replace); - RegionVisitor visitor = new RegionVisitor(region, filter); - Operations.completeLegacy(visitor); - return visitor.getAffected(); - } - /** * Sets the blocks at the center of the given region to the given pattern. * If the center sits between two blocks on a certain axis, then two blocks @@ -2472,109 +2308,11 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { */ public List> getBlockDistribution(Region region, boolean separateStates) { if (separateStates) return getBlockDistributionWithData(region); - int[] counter = new int[BlockTypes.size()]; - - if (region instanceof CuboidRegion) { - // Doing this for speed - final BlockVector3 min = region.getMinimumPoint(); - final BlockVector3 max = region.getMaximumPoint(); - - final int minX = min.getBlockX(); - final int minY = min.getBlockY(); - final int minZ = min.getBlockZ(); - final int maxX = max.getBlockX(); - final int maxY = max.getBlockY(); - final int maxZ = max.getBlockZ(); - - MutableBlockVector3 mutable = new MutableBlockVector3(minX, minY, minZ); - for (int x = minX; x <= maxX; ++x) { - for (int y = minY; y <= maxY; ++y) { - for (int z = minZ; z <= maxZ; ++z) { - BlockType type = getBlockType(x, y, z); - counter[type.getInternalId()]++; - } - } - } - } else { - for (final BlockVector3 pt : region) { - BlockType type = getBlock(pt).getBlockType(); - counter[type.getInternalId()]++; - } - } + List> normalDistr = getBlockDistribution(region); List> distribution = new ArrayList<>(); - for (int i = 0; i < counter.length; i++) { - int count = counter[i]; - if (count != 0) { - distribution.add(new Countable<>(BlockTypes.get(i).getDefaultState(), count)); - } + for (Countable count : normalDistr) { + distribution.add(new Countable<>(count.getID().getDefaultState(), count.getAmount())); } - Collections.sort(distribution); - return distribution; - } - - /** - * Generate a shape for the given expression. - * - * @param region the region to generate the shape in - * @return number of blocks changed - * @throws ExpressionException - * @throws MaxChangedBlocksException - */ - public List> getBlockDistributionWithData(final Region region) { - int[][] counter = new int[BlockTypes.size()][]; - - if (region instanceof CuboidRegion) { - // Doing this for speed - final BlockVector3 min = region.getMinimumPoint(); - final BlockVector3 max = region.getMaximumPoint(); - - final int minX = min.getBlockX(); - final int minY = min.getBlockY(); - final int minZ = min.getBlockZ(); - final int maxX = max.getBlockX(); - final int maxY = max.getBlockY(); - final int maxZ = max.getBlockZ(); - - for (int x = minX; x <= maxX; ++x) { - for (int y = minY; y <= maxY; ++y) { - for (int z = minZ; z <= maxZ; ++z) { - BlockState blk = getBlock(x, y, z); - BlockType type = blk.getBlockType(); - int[] stateCounter = counter[type.getInternalId()]; - if (stateCounter == null) { - counter[type.getInternalId()] = stateCounter = new int[type.getMaxStateId() + 1]; - } - stateCounter[blk.getInternalPropertiesId()]++; - } - } - } - } else { - for (BlockVector3 pt : region) { - BlockState blk = this.getBlock(pt); - BlockType type = blk.getBlockType(); - int[] stateCounter = counter[type.getInternalId()]; - if (stateCounter == null) { - counter[type.getInternalId()] = stateCounter = new int[type.getMaxStateId() + 1]; - } - stateCounter[blk.getInternalPropertiesId()]++; - } - } - List> distribution = new ArrayList<>(); - for (int typeId = 0; typeId < counter.length; typeId++) { - BlockType type = BlockTypes.get(typeId); - int[] stateCount = counter[typeId]; - if (stateCount != null) { - for (int propId = 0; propId < stateCount.length; propId++) { - int count = stateCount[propId]; - if (count != 0) { - BlockState state = type.withPropertyId(propId); - distribution.add(new Countable<>(state, count)); - } - - } - } - } - // Collections.reverse(distribution); return distribution; } @@ -2684,7 +2422,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { final Vector3 scaled = position.toVector3().subtract(zero).divide(unit); // transform - expression.evaluate(new double[]{scaled.getX(), scaled.getY(), scaled.getZ()}, timeout); + expression.evaluateTimeout(timeout, scaled.getX(), scaled.getY(), scaled.getZ()); int xv = (int) (x.getValue() * unit.getX() + zero2.getX()); int yv = (int) (y.getValue() * unit.getY() + zero2.getY()); int zv = (int) (z.getValue() * unit.getZ() + zero2.getZ()); @@ -3045,7 +2783,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { double scaledZ = (z - zero2D.getZ()) / unit2D.getZ(); try { - if (expression.evaluate(new double[]{scaledX, scaledZ}, timeout) <= 0) { + if (expression.evaluate(timeout, scaledX, scaledZ) <= 0) { return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index f2a8e442a..b0bdee36f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -50,6 +50,8 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.command.tool.BlockTool; import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.InvalidToolBindException; +import com.sk89q.worldedit.command.tool.NavigationWand; +import com.sk89q.worldedit.command.tool.SelectionWand; import com.sk89q.worldedit.command.tool.SinglePickaxe; import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.Player; @@ -78,6 +80,10 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.snapshot.Snapshot; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -85,6 +91,7 @@ import java.io.IOException; import java.time.ZoneId; import java.util.Calendar; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -130,8 +137,7 @@ public class LocalSession implements TextureHolder { private transient ClipboardHolder clipboard; private transient boolean superPickaxe = false; private transient BlockTool pickaxeMode = new SinglePickaxe(); - private transient boolean hasTool = false; - private transient Tool[] tools = new Tool[ItemTypes.size()]; + private transient final Int2ObjectOpenHashMap tools = new Int2ObjectOpenHashMap<>(0); private transient int maxBlocksChanged = -1; private transient int maxTimeoutTime; private transient boolean useInventory; @@ -153,12 +159,14 @@ public class LocalSession implements TextureHolder { private transient List> lastDistribution; private transient World worldOverride; + private transient boolean loadDefaults = true; + // Saved properties private String lastScript; private RegionSelectorType defaultSelector; private boolean useServerCUI = false; // Save this to not annoy players. - private String wandItem; - private String navWandItem; + private ItemType wandItem; + private ItemType navWandItem; /** * Construct the object. @@ -579,9 +587,11 @@ public class LocalSession implements TextureHolder { } public void unregisterTools(Player player) { - for (Tool tool : tools) { - if (tool instanceof BrushTool) { - ((BrushTool) tool).clear(player); + synchronized (tools) { + for (Tool tool : tools.values()) { + if (tool instanceof BrushTool) { + ((BrushTool) tool).clear(player); + } } } } @@ -721,7 +731,7 @@ public class LocalSession implements TextureHolder { * @return clipboard * @throws EmptyClipboardException thrown if no clipboard is set */ - public ClipboardHolder getClipboard() throws EmptyClipboardException { + public synchronized ClipboardHolder getClipboard() throws EmptyClipboardException { if (clipboard == null) { throw new EmptyClipboardException(); } @@ -729,11 +739,11 @@ public class LocalSession implements TextureHolder { } @Nullable - public ClipboardHolder getExistingClipboard() { + public synchronized ClipboardHolder getExistingClipboard() { return clipboard; } - public void addClipboard(@Nonnull MultiClipboardHolder toAppend) { + public synchronized void addClipboard(@Nonnull MultiClipboardHolder toAppend) { checkNotNull(toAppend); ClipboardHolder existing = getExistingClipboard(); MultiClipboardHolder multi; @@ -758,7 +768,7 @@ public class LocalSession implements TextureHolder { * * @param clipboard the clipboard, or null if the clipboard is to be cleared */ - public void setClipboard(@Nullable ClipboardHolder clipboard) { + public synchronized void setClipboard(@Nullable ClipboardHolder clipboard) { if (this.clipboard == clipboard) return; if (this.clipboard != null) { @@ -946,13 +956,17 @@ public class LocalSession implements TextureHolder { * @return the tool, which may be {@code null} */ @Nullable + @Deprecated public Tool getTool(ItemType item) { - return tools[item.getInternalId()]; + synchronized (this.tools) { + return tools.get(item.getInternalId()); + } } @Nullable public Tool getTool(Player player) { - if (!Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES && !hasTool) { + loadDefaults(player, false); + if (!Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES && tools.isEmpty()) { return null; } BaseItem item = player.getItemInHand(HandSide.MAIN_HAND); @@ -964,9 +978,31 @@ public class LocalSession implements TextureHolder { BrushTool tool = BrushCache.getTool(player, this, item); if (tool != null) return tool; } + loadDefaults(player, false); return getTool(item.getType()); } + public void loadDefaults(Actor actor, boolean force) { + if (loadDefaults || force) { + loadDefaults = false; + LocalConfiguration config = WorldEdit.getInstance().getConfiguration(); + if (wandItem == null) { + wandItem = ItemTypes.parse(config.wandItem); + } + if (navWandItem == null) { + navWandItem = ItemTypes.parse(config.navigationWand); + } + synchronized (this.tools) { + if (tools.get(navWandItem.getInternalId()) == null && NavigationWand.INSTANCE.canUse(actor)) { + tools.put(navWandItem.getInternalId(), NavigationWand.INSTANCE); + } + if (tools.get(wandItem.getInternalId()) == null && SelectionWand.INSTANCE.canUse(actor)) { + tools.put(wandItem.getInternalId(), SelectionWand.INSTANCE); + } + } + } + } + /** * Get the brush tool assigned to the item. If there is no tool assigned * or the tool is not assigned, the slot will be replaced with the @@ -1015,10 +1051,14 @@ public class LocalSession implements TextureHolder { public void setTool(ItemType item, @Nullable Tool tool) throws InvalidToolBindException { if (item.hasBlockType()) { throw new InvalidToolBindException(item, "Blocks can't be used"); - } else if (item.getId().equalsIgnoreCase(config.wandItem)) { - throw new InvalidToolBindException(item, "Already used for the wand"); - } else if (item.getId().equalsIgnoreCase(config.navigationWand)) { - throw new InvalidToolBindException(item, "Already used for the navigation wand"); + } else if (tool instanceof SelectionWand) { + changeTool(this.wandItem, this.wandItem = item, tool); + setDirty(); + return; + } else if (tool instanceof NavigationWand) { + changeTool(this.navWandItem, this.navWandItem = item, tool); + setDirty(); + return; } setTool(item.getDefaultState(), tool, null); } @@ -1028,14 +1068,33 @@ public class LocalSession implements TextureHolder { setTool(item, tool, player); } + private void changeTool(ItemType oldType, ItemType newType, Tool newTool) { + if (oldType != null) { + synchronized (this.tools) { + this.tools.remove(oldType.getInternalId()); + } + } + synchronized (this.tools) { + if (newTool == null) { + this.tools.remove(newType.getInternalId()); + } else { + this.tools.put(newType.getInternalId(), newTool); + } + } + } + public void setTool(BaseItem item, @Nullable Tool tool, Player player) throws InvalidToolBindException { ItemType type = item.getType(); if (type.hasBlockType() && type.getBlockType().getMaterial().isAir()) { throw new InvalidToolBindException(type, "Blocks can't be used"); - } else if (type.getId().equalsIgnoreCase(config.wandItem)) { - throw new InvalidToolBindException(type, "Already used for the wand"); - } else if (type.getId().equalsIgnoreCase(config.navigationWand)) { - throw new InvalidToolBindException(type, "Already used for the navigation wand"); + } else if (tool instanceof SelectionWand) { + changeTool(this.wandItem, this.wandItem = item.getType(), tool); + setDirty(); + return; + } else if (tool instanceof NavigationWand) { + changeTool(this.navWandItem, this.navWandItem = item.getType(), tool); + setDirty(); + return; } Tool previous; @@ -1045,18 +1104,17 @@ public class LocalSession implements TextureHolder { if (tool != null) { ((BrushTool) tool).setHolder(item); } else { - this.tools[type.getInternalId()] = null; + synchronized (this.tools) { + this.tools.remove(type.getInternalId()); + } } } else { - previous = this.tools[type.getInternalId()]; - this.tools[type.getInternalId()] = tool; - if (tool != null) { - hasTool = true; - } else { - hasTool = false; - for (Tool i : this.tools) if (i != null) { - hasTool = true; - break; + synchronized (this.tools) { + previous = this.tools.get(type.getInternalId()); + if (tool != null) { + this.tools.put(type.getInternalId(), tool); + } else { + this.tools.remove(type.getInternalId()); } } } @@ -1477,7 +1535,7 @@ public class LocalSession implements TextureHolder { * @return item id of wand item, or {@code null} */ public String getWandItem() { - return wandItem; + return wandItem.getId(); } /** @@ -1485,7 +1543,7 @@ public class LocalSession implements TextureHolder { * @return item id of nav wand item, or {@code null} */ public String getNavWandItem() { - return navWandItem; + return navWandItem.getId(); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index b1ce4974c..2e319f522 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -69,6 +69,7 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.factory.TreeGeneratorFactory; import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.InvalidToolBindException; @@ -132,6 +133,7 @@ import org.enginehub.piston.annotation.param.Arg; import org.enginehub.piston.annotation.param.ArgFlag; import org.enginehub.piston.annotation.param.Switch; import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; /** * Commands to set brush shape. @@ -159,11 +161,11 @@ public class BrushCommands { "Pic: https://i.imgur.com/cNUQUkj.png -> https://i.imgur.com/hFOFsNf.png" ) @CommandPermissions("worldedit.brush.blendball") - public void blendBallBrush(Player player, LocalSession session, + public void blendBallBrush(InjectedValueAccess context, @Arg(desc = "The radius to sample for blending", def = "5") Expression radius) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new BlendBall()).setSize(radius); + set(context, new BlendBall()).setSize(radius); } @Command( @@ -171,11 +173,16 @@ public class BrushCommands { desc = "Erodes terrain" ) @CommandPermissions("worldedit.brush.erode") - public void erodeBrush(Player player, LocalSession session, + public void erodeBrush(InjectedValueAccess context, @Arg(desc = "The radius for eroding", def = "5") - Expression radius) throws WorldEditException { + Expression radius, + @Arg(desc = "erodeFaces", def = "6") int erodefaces, + @Arg(desc = "erodeRec", def = "0") int erodeRec, + @Arg(desc = "fillFaces", def = "1") int fillFaces, + @Arg(desc = "fillRec", def = "1") int fillRec + ) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new ErodeBrush()).setSize(radius); + set(context, new ErodeBrush(erodefaces, erodeRec, fillFaces, fillRec)).setSize(radius); } @Command( @@ -183,11 +190,16 @@ public class BrushCommands { desc = "Pull terrain towards you" ) @CommandPermissions("worldedit.brush.pull") - public void pullBrush(Player player, LocalSession session, + public void pullBrush(InjectedValueAccess context, @Arg(desc = "The radius to sample for blending", def = "5") - Expression radius) throws WorldEditException { + Expression radius, + @Arg(desc = "erodeFaces", def = "2") int erodefaces, + @Arg(desc = "erodeRec", def = "1") int erodeRec, + @Arg(desc = "fillFaces", def = "5") int fillFaces, + @Arg(desc = "fillRec", def = "1") int fillRec + ) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new RaiseBrush()).setSize(radius); + set(context, new RaiseBrush(erodefaces, erodeRec, fillFaces, fillRec)).setSize(radius); } @Command( @@ -195,12 +207,12 @@ public class BrushCommands { desc = "Creates a circle which revolves around your facing direction" ) @CommandPermissions("worldedit.brush.sphere") - public void circleBrush(Player player, EditSession editSession, LocalSession session, + public void circleBrush(Player player, InjectedValueAccess context, Pattern fill, @Arg(desc = "The radius to sample for blending", def = "5") Expression radius) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new CircleBrush(player)).setSize(radius).setFill(fill); + set(context, new CircleBrush(player)).setSize(radius).setFill(fill); } @Command( @@ -211,14 +223,14 @@ public class BrushCommands { "Note: Set a mask to recurse along specific blocks" ) @CommandPermissions("worldedit.brush.recursive") - public void recursiveBrush(Player player, LocalSession session, EditSession editSession, + public void recursiveBrush(InjectedValueAccess context, EditSession editSession, Pattern fill, @Arg(desc = "The radius to sample for blending", def = "5") Expression radius, @Switch(name = 'd', desc = "Apply in depth first order") boolean depthFirst) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new RecurseBrush(depthFirst)) + set(context, new RecurseBrush(depthFirst)) .setSize(radius).setFill(fill).setMask(new IdMask(editSession)); } @@ -228,7 +240,7 @@ public class BrushCommands { desc = "Create lines" ) @CommandPermissions("worldedit.brush.line") - public void lineBrush(Player player, LocalSession session, Pattern fill, + public void lineBrush(InjectedValueAccess context, Pattern fill, @Arg(desc = "The radius to sample for blending", def = "0") Expression radius, @Switch(name = 'h', desc = "Create only a shell") @@ -238,7 +250,7 @@ public class BrushCommands { @Switch(name = 'f', desc = "Create a flat line") boolean flat) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new LineBrush(shell, select, flat)).setSize(radius).setFill(fill); + set(context, new LineBrush(shell, select, flat)).setSize(radius).setFill(fill); } @Command( @@ -252,14 +264,14 @@ public class BrushCommands { "Tutorial: https://www.planetminecraft.com/blog/fawe-tutorial/" ) @CommandPermissions("worldedit.brush.spline") - public void splineBrush(Player player, EditSession editSession, LocalSession session, + public void splineBrush(Player player, InjectedValueAccess context, Pattern fill, @Arg(desc = "The radius to sample for blending", def = "25") Expression radius) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); player.print(BBC.BRUSH_SPLINE.format(radius)); - set(player, session, - new SplineBrush(player, session)) + set(context, + new SplineBrush(player)) .setSize(radius) .setFill(fill); } @@ -273,10 +285,10 @@ public class BrushCommands { "Set [copies] to a value > 0 if you want to have your selection pasted a limited amount of times equally spaced on the curve" ) @CommandPermissions("worldedit.brush.sweep") - public void sweepBrush(Player player, LocalSession session, + public void sweepBrush(Player player, InjectedValueAccess context, @Arg(name = "copies", desc = "int", def = "-1") int copies) throws WorldEditException { player.print(BBC.BRUSH_SPLINE.s()); - set(player, session, new SweepBrush(copies)); + set(context, new SweepBrush(copies)); } @Command( @@ -285,7 +297,7 @@ public class BrushCommands { desc = "Create a hanging line between two points" ) @CommandPermissions("worldedit.brush.spline") - public void catenaryBrush(Player player, LocalSession session, Pattern fill, + public void catenaryBrush(InjectedValueAccess context, Pattern fill, @Arg(def = "1.2", desc = "Length of wire compared to distance between points") @Range(min = 1) double lengthFactor, @Arg(desc = "The radius to sample for blending", def = "0") Expression radius, @@ -297,7 +309,7 @@ public class BrushCommands { boolean facingDirection) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); Brush brush = new CatenaryBrush(shell, select, facingDirection, lengthFactor); - set(player, session, + set(context, new CatenaryBrush(shell, select, facingDirection, lengthFactor)) .setSize(radius) .setFill(fill); @@ -311,7 +323,7 @@ public class BrushCommands { "Video: https://www.youtube.com/watch?v=zSN-2jJxXlM" ) @CommandPermissions("worldedit.brush.surfacespline") // 0, 0, 0, 10, 0, - public void surfaceSpline(Player player, LocalSession session, Pattern fill, + public void surfaceSpline(Player player, InjectedValueAccess context, Pattern fill, @Arg(desc = "The radius to sample for blending", def = "0") Expression radius, @Arg(name = "tension", desc = "double", def = "0") double tension, @Arg(name = "bias", desc = "double", def = "0") double bias, @@ -319,7 +331,7 @@ public class BrushCommands { @Arg(name = "quality", desc = "double", def = "10") double quality) throws WorldEditException { player.print(BBC.BRUSH_SPLINE.format(radius)); worldEdit.checkMaxBrushRadius(radius); - set(player, session, + set(context, new SurfaceSpline(tension, bias, continuity, quality)) .setSize(radius) .setFill(fill); @@ -331,7 +343,7 @@ public class BrushCommands { desc = "Creates a distorted sphere" ) @CommandPermissions("worldedit.brush.rock") - public void blobBrush(Player player, LocalSession session, Pattern fill, + public void blobBrush(InjectedValueAccess context, Pattern fill, @Arg(name = "radius", desc = "Vector3", def = "10") Vector3 radius, @Arg(name = "sphericity", desc = "double", def = "100") @@ -343,7 +355,7 @@ public class BrushCommands { double max = MathMan.max(radius.getX(), radius.getY(), radius.getZ()); worldEdit.checkMaxBrushRadius(max); Brush brush = new BlobBrush(radius.divide(max), frequency / 100, amplitude / 100, sphericity / 100); - set(player, session, brush).setSize(max).setFill(fill); + set(context, brush).setSize(max).setFill(fill); } @Command( @@ -352,7 +364,7 @@ public class BrushCommands { desc = "Choose the sphere brush" ) @CommandPermissions("worldedit.brush.sphere") - public void sphereBrush(Player player, LocalSession session, + public void sphereBrush(Player player, InjectedValueAccess context, @Arg(desc = "The pattern of blocks to set") Pattern pattern, @Arg(desc = "The radius of the sphere", def = "2") @@ -382,7 +394,7 @@ public class BrushCommands { } } - set(player, session, brush).setSize(radius).setFill(pattern); + set(context, brush).setSize(radius).setFill(pattern); } @Command( @@ -393,13 +405,13 @@ public class BrushCommands { "Pic: https://i.imgur.com/2xKsZf2.png" ) @CommandPermissions("worldedit.brush.shatter") - public void shatterBrush(Player player, EditSession editSession, LocalSession session, + public void shatterBrush(EditSession editSession, InjectedValueAccess context, Pattern fill, @Arg(desc = "The radius to sample for blending", def = "10") Expression radius, @Arg(desc = "Lines", def = "10") int count) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, + set(context, new ShatterBrush(count)) .setSize(radius) .setFill(fill) @@ -412,7 +424,7 @@ public class BrushCommands { descFooter = "Use a height map to paint any surface.\n" ) @CommandPermissions("worldedit.brush.stencil") - public void stencilBrush(Player player, LocalSession session, Pattern fill, + public void stencilBrush(LocalSession session, InjectedValueAccess context, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation, @@ -430,7 +442,7 @@ public class BrushCommands { if (randomRotate) { brush.setRandomRotate(true); } - set(player, session, + set(context, brush) .setSize(radius) .setFill(fill); @@ -442,7 +454,7 @@ public class BrushCommands { desc = "Use a height map to paint a surface", descFooter = "Use a height map to paint any surface.\n") @CommandPermissions("worldedit.brush.stencil") - public void imageBrush(Player player, LocalSession session, + public void imageBrush(LocalSession session, InjectedValueAccess context, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, ProvideBindings.ImageUri imageUri, @@ -463,7 +475,7 @@ public class BrushCommands { alpha = true; } ImageBrush brush = new ImageBrush(image, session, alpha); - set(player, session, brush).setSize(radius); + set(context, brush).setSize(radius); } @Command( @@ -475,11 +487,11 @@ public class BrushCommands { "The -r flag will apply random rotation" ) @CommandPermissions("worldedit.brush.surface") - public void surfaceBrush(Player player, LocalSession session, Pattern fill, + public void surfaceBrush(InjectedValueAccess context, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new SurfaceSphereBrush()).setFill(fill).setSize(radius); + set(context, new SurfaceSphereBrush()).setFill(fill).setSize(radius); } @Command( @@ -489,7 +501,7 @@ public class BrushCommands { "Video: https://youtu.be/RPZIaTbqoZw?t=34s" ) @CommandPermissions("worldedit.brush.scatter") - public void scatterBrush(Player player, LocalSession session, Pattern fill, + public void scatterBrush(InjectedValueAccess context, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "points", desc = "double", def = "5") double pointsOpt, @Arg(name = "distance", desc = "double", def = "1") double distanceOpt, @@ -501,7 +513,7 @@ public class BrushCommands { } else { brush = new ScatterBrush((int) pointsOpt, (int) distanceOpt); } - set(player, session, brush).setSize(radius).setFill(fill); + set(context, brush).setSize(radius).setFill(fill); } @Command( @@ -510,7 +522,7 @@ public class BrushCommands { desc = "Scatter a schematic on a surface" ) @CommandPermissions("worldedit.brush.populateschematic") - public void scatterSchemBrush(Player player, LocalSession session, Mask mask, + public void scatterSchemBrush(Player player, InjectedValueAccess context, Mask mask, @Arg(name = "clipboard", desc = "Clipboard uri") String clipboardStr, @Arg(name = "radius", desc = "Expression", def = "30") Expression radius, @Arg(name = "density", desc = "double", def = "50") double density, @@ -528,7 +540,7 @@ public class BrushCommands { return; } - set(player, session, + set(context, new PopulateSchem(mask, holders, (int) density, rotate)).setSize(radius); } catch (IOException e) { throw new RuntimeException(e); @@ -543,10 +555,10 @@ public class BrushCommands { "Pic: https://i.imgur.com/XV0vYoX.png" ) @CommandPermissions("worldedit.brush.layer") - public void surfaceLayer(Player player, LocalSession session, + public void surfaceLayer(InjectedValueAccess context, @Arg(name = "radius", desc = "Expression") Expression radius, List blockLayers) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new LayerBrush(blockLayers.toArray(new BlockState[0]))).setSize(radius); + set(context, new LayerBrush(blockLayers.toArray(new BlockState[0]))).setSize(radius); } @Command( @@ -558,13 +570,13 @@ public class BrushCommands { "Note: The seeds define how many splotches there are, recursion defines how large, solid defines whether the pattern is applied per seed, else per block." ) @CommandPermissions("worldedit.brush.splatter") - public void splatterBrush(Player player, LocalSession session, Pattern fill, + public void splatterBrush(InjectedValueAccess context, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "points", desc = "double", def = "1") double pointsOpt, @Arg(name = "recursion", desc = "double", def = "5") double recursion, @Arg(name = "solid", desc = "boolean", def = "true") boolean solid) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new SplatterBrush((int) pointsOpt, (int) recursion, solid)).setSize(radius).setFill(fill); + set(context, new SplatterBrush((int) pointsOpt, (int) recursion, solid)).setSize(radius).setFill(fill); } @Command( @@ -578,11 +590,11 @@ public class BrushCommands { " - Placeholders: {x}, {y}, {z}, {world}, {size}" ) @CommandPermissions("worldedit.brush.scattercommand") - public void scatterCommandBrush(Player player, EditSession editSession, LocalSession session, + public void scatterCommandBrush(Player player, InjectedValueAccess context, @Arg(name = "radius", desc = "Expression") Expression radius, double points, double distance, List commandStr) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, + set(context, new ScatterCommand((int) points, (int) distance, StringMan.join(commandStr, " "))) .setSize(radius); } @@ -593,7 +605,7 @@ public class BrushCommands { desc = "Choose the cylinder brush" ) @CommandPermissions("worldedit.brush.cylinder") - public void cylinderBrush(Player player, LocalSession session, + public void cylinderBrush(InjectedValueAccess context, @Arg(desc = "The pattern of blocks to set") Pattern pattern, @Arg(desc = "The radius of the cylinder", def = "2") @@ -607,9 +619,9 @@ public class BrushCommands { BrushSettings settings; if (hollow) { - settings = set(player, session, new HollowCylinderBrush(height)); + settings = set(context, new HollowCylinderBrush(height)); } else { - settings = set(player, session, new CylinderBrush(height)); + settings = set(context, new CylinderBrush(height)); } settings.setSize(radius).setFill(pattern); } @@ -624,7 +636,7 @@ public class BrushCommands { ) @Deprecated @CommandPermissions("worldedit.brush.clipboard") - public void clipboardBrush(Player player, LocalSession session, + public void clipboardBrush(LocalSession session, InjectedValueAccess context, @Switch(name = 'a', desc = "Don't paste air from the clipboard") boolean ignoreAir, @Switch(name = 'o', desc = "Paste starting at the target location, instead of centering on it") @@ -648,7 +660,7 @@ public class BrushCommands { worldEdit.checkMaxBrushRadius(size.getBlockY() / 2D - 1); worldEdit.checkMaxBrushRadius(size.getBlockZ() / 2D - 1); - set(player, session, + set(context, new ClipboardBrush(newHolder, ignoreAir, usingOrigin, !skipEntities, pasteBiomes, sourceMask)); } @@ -658,7 +670,7 @@ public class BrushCommands { descFooter = "Example: '/brush smooth 2 4 grass_block,dirt,stone'" ) @CommandPermissions("worldedit.brush.smooth") - public void smoothBrush(Player player, LocalSession session, EditSession editSession, + public void smoothBrush(Player player, InjectedValueAccess context, EditSession editSession, @Arg(desc = "The radius to sample for softening", def = "2") Expression radius, @Arg(desc = "The number of iterations to perform", def = "4") @@ -670,7 +682,7 @@ public class BrushCommands { FaweLimit limit = Settings.IMP.getLimit(player); iterations = Math.min(limit.MAX_ITERATIONS, iterations); - set(player, session, + set(context, new SmoothBrush(iterations, maskOpt)) .setSize(radius); } @@ -681,12 +693,12 @@ public class BrushCommands { desc = "Shortcut fire extinguisher brush" ) @CommandPermissions("worldedit.brush.ex") - public void extinguishBrush(Player player, LocalSession session, EditSession editSession, + public void extinguishBrush(InjectedValueAccess context, EditSession editSession, @Arg(desc = "The radius to extinguish", def = "5") Expression radius) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, + set(context, new SphereBrush()) .setSize(radius) .setFill(BlockTypes.AIR.getDefaultState()) @@ -699,14 +711,14 @@ public class BrushCommands { desc = "Gravity brush, simulates the effect of gravity" ) @CommandPermissions("worldedit.brush.gravity") - public void gravityBrush(Player player, LocalSession session, + public void gravityBrush(InjectedValueAccess context, @Arg(desc = "The radius to apply gravity in", def = "5") Expression radius, @Switch(name = 'h', desc = "Affect blocks starting at max Y, rather than the target location Y + radius") boolean fromMaxY) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - set(player, session, new GravityBrush(fromMaxY)).setSize(radius); + set(context, new GravityBrush(fromMaxY)).setSize(radius); } @Command( @@ -721,8 +733,8 @@ public class BrushCommands { "Snow Pic: https://i.imgur.com/Hrzn0I4.png" ) @CommandPermissions("worldedit.brush.height") - public void heightBrush(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "TODO") boolean randomRotate, @Switch(name = 'l', desc = "TODO") boolean layers, @Switch(name = 's', desc = "TODO") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException { - terrainBrush(player, session, radius, image, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE); + public void heightBrush(LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "TODO") boolean randomRotate, @Switch(name = 'l', desc = "TODO") boolean layers, @Switch(name = 's', desc = "TODO") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException { + terrainBrush(session, radius, image, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context); } @Command( @@ -732,8 +744,8 @@ public class BrushCommands { descFooter = "This brush flattens terrain and creates cliffs." ) @CommandPermissions("worldedit.brush.height") - public void cliffBrush(Player player, LocalSession session, - @Arg(name = "radius", desc = "Expression", def = "5") + public void cliffBrush(LocalSession session, + @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @@ -747,7 +759,7 @@ public class BrushCommands { boolean layers, @Switch(name = 's', desc = "Disables smoothing") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException { - terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CYLINDER); + terrainBrush(session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CYLINDER, context); } @Command( @@ -756,20 +768,20 @@ public class BrushCommands { desc = "This brush raises or lowers land towards the clicked point" ) @CommandPermissions("worldedit.brush.height") - public void flattenBrush(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Step(90) @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale, + public void flattenBrush(LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Step(90) @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "Enables random off-axis rotation") boolean randomRotate, @Switch(name = 'l', desc = "Will work on snow layers") boolean layers, @Switch(name = 's', desc = "Disables smoothing") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException { - terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE); + terrainBrush(session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context); } - private void terrainBrush(Player player, LocalSession session, + private void terrainBrush(LocalSession session, @Arg(name = "radius", desc = "Expression") Expression radius, String image, int rotation, double yscale, boolean flat, boolean randomRotate, boolean layers, boolean smooth, - Shape shape) throws WorldEditException, FileNotFoundException { + Shape shape, InjectedValueAccess context) throws WorldEditException, FileNotFoundException { worldEdit.checkMaxBrushRadius(radius); InputStream stream = getHeightmapStream(image); HeightBrush brush; @@ -789,7 +801,7 @@ public class BrushCommands { if (randomRotate) { brush.setRandomRotate(true); } - set(player, session, + set(context, brush) .setSize(radius); } @@ -814,14 +826,14 @@ public class BrushCommands { "Video: https://www.youtube.com/watch?v=RPZIaTbqoZw" ) @CommandPermissions("worldedit.brush.copy") - public void copy(Player player, LocalSession session, + public void copy(Player player, LocalSession session, InjectedValueAccess context, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Switch(name = 'r', desc = "Apply random rotation on paste") boolean randomRotate, @Switch(name = 'a', desc = "Apply auto view based rotation on paste") boolean autoRotate) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); player.print(BBC.BRUSH_COPY.format(radius)); - set(player, session, + set(context, new CopyPastaBrush(player, session, randomRotate, autoRotate)) .setSize(radius); } @@ -835,12 +847,12 @@ public class BrushCommands { " - Placeholders: {x}, {y}, {z}, {world}, {size}" ) @CommandPermissions("worldedit.brush.command") - public void command(Player player, LocalSession session, + public void command(InjectedValueAccess context, @Arg(name = "radius", desc = "Expression") Expression radius, @Arg(desc = "Command to run") List input) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); String cmd = StringMan.join(input, " "); - set(player, session, + set(context, new CommandBrush(cmd)) .setSize(radius); } @@ -851,7 +863,7 @@ public class BrushCommands { desc = "Butcher brush, kills mobs within a radius" ) @CommandPermissions("worldedit.brush.butcher") - public void butcherBrush(Player player, LocalSession session, + public void butcherBrush(Player player, @Arg(desc = "Radius to kill mobs in", def = "5") Expression radius, @Switch(name = 'p', desc = "Also kill pets") @@ -869,7 +881,8 @@ public class BrushCommands { @Switch(name = 'f', desc = "Also kill all friendly mobs (Applies the flags `-abgnpt`)") boolean killFriendly, @Switch(name = 'r', desc = "Also destroy armor stands") - boolean killArmorStands) throws WorldEditException { + boolean killArmorStands, + InjectedValueAccess context) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); CreatureButcher flags = new CreatureButcher(player); @@ -882,24 +895,25 @@ public class BrushCommands { flags.or(CreatureButcher.Flags.TAGGED , killWithName, "worldedit.butcher.tagged"); flags.or(CreatureButcher.Flags.ARMOR_STAND , killArmorStands, "worldedit.butcher.armorstands"); - set(player, session, new ButcherBrush(flags)).setSize(radius); + set(context, new ButcherBrush(flags)).setSize(radius); } - public BrushSettings process(CommandLocals locals, BrushSettings settings) throws WorldEditException { - Actor actor = locals.get(Actor.class); - LocalSession session = worldEdit.getSessionManager().get(actor); - session.setTool((Player) actor, null); - BrushTool tool = session.getBrushTool((Player) actor); + public BrushSettings process(Player player, Arguments arguments, BrushSettings settings) throws WorldEditException { + LocalSession session = worldEdit.getSessionManager().get(player); + session.setTool(player, null); + BrushTool tool = session.getBrushTool(player); if (tool != null) { tool.setPrimary(settings); tool.setSecondary(settings); - BBC.BRUSH_EQUIPPED.send(actor, ((String) locals.get("arguments")).split(" ")[1]); + + BBC.BRUSH_EQUIPPED.send(player, arguments.get().split(" ")[1]); } - return null; + return settings; } - public BrushSettings set(Player player, LocalSession session, - Brush brush) throws InvalidToolBindException { + public BrushSettings set(InjectedValueAccess context, Brush brush) throws InvalidToolBindException { + Player player = context.injectedValue(Key.of(Player.class)).orElseThrow(() -> new IllegalStateException("No player")); + LocalSession session = player.getSession(); BrushSettings bs = new BrushSettings(); BrushTool tool = session.getBrushTool(player, false); if (tool != null) { @@ -911,7 +925,17 @@ public class BrushCommands { } } } - return bs; + Arguments arguments = context.injectedValue(Key.of(Arguments.class)).orElse(null); + if (arguments != null) { + String args = arguments.get(); + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + CommandPermissions perms = context.injectedValue(Key.of(CommandPermissions.class)).orElse(null); + if (perms != null) { + bs.addPermissions(perms.value()); + } + bs.setBrush(brush); + return process(player, arguments, bs); } @Command( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java index fb7c5818c..7c98d39f1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java @@ -171,27 +171,16 @@ public class ChunkCommands { .clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, "/stop")))); } - private static class ChunkListPaginationBox extends PaginationBox { + private static class ChunkListPaginationBox extends PaginationBox.ListPaginationBox { //private final Region region; - private final List chunks; ChunkListPaginationBox(Region region) { - super("Selected Chunks", "/listchunks -p %page%"); - // TODO make efficient/streamable/calculable implementations of this - // for most region types, so we can just store the region and random-access get one page of chunks - // (this is non-trivial for some types of selections...) - //this.region = region.clone(); - this.chunks = new ArrayList<>(region.getChunks()); + super("Selected Chunks", "/listchunks -p %page%", region.getChunks()); } @Override public Component getComponent(int number) { - return TextComponent.of(chunks.get(number).toString()); - } - - @Override - public int getComponentsSize() { - return chunks.size(); + return create(number); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index e9731968b..58f8f12f1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -116,6 +116,7 @@ public class ClipboardCommands { @Command( name = "/copy", + aliases = "/cp", desc = "Copy the selection to the clipboard" ) @CommandPermissions("worldedit.clipboard.copy") @@ -365,13 +366,7 @@ public class ClipboardCommands { ClipboardWriter writer = format.getWriter(baos); writer.write(target); baos.flush(); - String json = ImgurUtility - .getImgurContent(ImgurUtility.CLIENT_ID, baos.toByteArray()); - Gson gson = new Gson(); - JsonObject obj = gson.fromJson(json, JsonObject.class); - JsonObject data = obj.get("data").getAsJsonObject(); - String link = data.get("link").getAsString(); - url = new URL(link); + url = ImgurUtility.uploadImage(baos.toByteArray()); } catch (IOException e) { e.printStackTrace(); url = null; @@ -434,6 +429,7 @@ public class ClipboardCommands { @Command( name = "/paste", + aliases = { "/p", "/pa" }, desc = "Paste the clipboard's contents" ) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index a89e22cb6..0a6cbbf4b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -92,7 +92,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.caves") @Logging(PLACEMENT) - public void caves(Player fp, LocalSession session, EditSession editSession, @Selection Region region, + public void caves(Actor actor, LocalSession session, EditSession editSession, @Selection Region region, @Arg(name = "size", desc = "TODO", def = "8") int sizeOpt, @Arg(name = "frequency", desc = "TODO", def = "40") int frequencyOpt, @Arg(name = "rarity", desc = "TODO", def = "7") int rarityOpt, @@ -103,10 +103,10 @@ public class GenerationCommands { @Arg(name = "pocketChance", desc = "TODO", def = "0") int pocketChanceOpt, @Arg(name = "pocketMin", desc = "TODO", def = "0") int pocketMinOpt, @Arg(name = "pocketMax", desc = "TODO", def = "3") int pocketMaxOpt, InjectedValueAccess context) throws WorldEditException { - fp.checkConfirmationRegion(() -> { + actor.checkConfirmationRegion(() -> { CavesGen gen = new CavesGen(sizeOpt, frequencyOpt, rarityOpt, minYOpt, maxYOpt, systemFrequencyOpt, individualRarityOpt, pocketChanceOpt, pocketMinOpt, pocketMaxOpt); editSession.generate(region, gen); - BBC.VISITOR_BLOCK.send(fp, editSession.getBlockChangeCount()); + BBC.VISITOR_BLOCK.send(actor, editSession.getBlockChangeCount()); }, "/caves", region, context); } @@ -117,10 +117,10 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.ore") @Logging(PLACEMENT) - public void ores(Player fp, LocalSession session, EditSession editSession, @Selection Region region, Mask mask, InjectedValueAccess context) throws WorldEditException { - fp.checkConfirmationRegion(() -> { + public void ores(Actor actor, LocalSession session, EditSession editSession, @Selection Region region, Mask mask, InjectedValueAccess context) throws WorldEditException { + actor.checkConfirmationRegion(() -> { editSession.addOres(region, mask); - BBC.VISITOR_BLOCK.send(fp, editSession.getBlockChangeCount()); + BBC.VISITOR_BLOCK.send(actor, editSession.getBlockChangeCount()); }, "/ores", region, context); } @@ -130,7 +130,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.image") @Logging(PLACEMENT) - public void image(Player player, LocalSession session, EditSession editSession, String argStr, @Arg(name = "randomize", desc = "boolean", def = "true") boolean randomize, + public void image(Actor actor, LocalSession session, EditSession editSession, String argStr, @Arg(name = "randomize", desc = "boolean", def = "true") boolean randomize, @Arg(desc = "TODO", def = "100") int threshold, @Arg(name = "dimensions", desc = "BlockVector2", def = "") BlockVector2 dimensions) throws WorldEditException, IOException { TextureUtil tu = Fawe.get().getCachedTextureUtil(randomize, 0, threshold); URL url = new URL(argStr); @@ -142,7 +142,7 @@ public class GenerationCommands { image = ImageUtil.getScaledInstance(image, dimensions.getBlockX(), dimensions.getBlockZ(), RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); } - BlockVector3 pos1 = player.getLocation().toBlockPoint(); + BlockVector3 pos1 = session.getPlacementPosition(actor); BlockVector3 pos2 = pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1); CuboidRegion region = new CuboidRegion(pos1, pos2); int[] count = new int[1]; @@ -162,7 +162,7 @@ public class GenerationCommands { return false; }); Operations.completeBlindly(visitor); - BBC.VISITOR_BLOCK.send(player, editSession.getBlockChangeCount()); + BBC.VISITOR_BLOCK.send(actor, editSession.getBlockChangeCount()); } @Command( @@ -171,10 +171,10 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.ore") @Logging(PLACEMENT) - public void ore(Player fp, LocalSession session, EditSession editSession, @Selection Region region, Mask mask, Pattern material, @Arg(name="size", desc="Ore vein size") @Range(min = 0) int size, int freq, @Range(min = 0, max = 100) int rarity, @Range(min = 0, max = 255) int minY, @Range(min = 0, max = 255) int maxY, InjectedValueAccess context) throws WorldEditException { - fp.checkConfirmationRegion(() -> { + public void ore(Actor actor, LocalSession session, EditSession editSession, @Selection Region region, Mask mask, Pattern material, @Arg(name="size", desc="Ore vein size") @Range(min = 0) int size, int freq, @Range(min = 0, max = 100) int rarity, @Range(min = 0, max = 255) int minY, @Range(min = 0, max = 255) int maxY, InjectedValueAccess context) throws WorldEditException { + actor.checkConfirmationRegion(() -> { editSession.addOre(region, mask, material, size, freq, rarity, minY, maxY); - BBC.VISITOR_BLOCK.send(fp, editSession.getBlockChangeCount()); + BBC.VISITOR_BLOCK.send(actor, editSession.getBlockChangeCount()); }, "/ore", region, context); } @@ -186,7 +186,7 @@ public class GenerationCommands { @Logging(PLACEMENT) public void hcyl(Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") - Pattern pattern, + Pattern pattern, BlockVector2 radius, @Arg(desc = "The height of the cylinder", def = "1") int height, @@ -208,12 +208,12 @@ public class GenerationCommands { @Logging(PLACEMENT) public void cyl(Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") - Pattern pattern, + Pattern pattern, BlockVector2 radius, @Arg(desc = "The height of the cylinder", def = "1") - int height, + int height, @Switch(name = 'h', desc = "Make a hollow cylinder") - boolean hollow, InjectedValueAccess context) throws WorldEditException { + boolean hollow, InjectedValueAccess context) throws WorldEditException { double max = Math.max(radius.getBlockX(), radius.getBlockZ()); worldEdit.checkMaxRadius(max); BlockVector3 pos = session.getPlacementPosition(actor); @@ -260,9 +260,7 @@ public class GenerationCommands { BlockVector3 finalPos = raised ? pos.add(0, radii.getY(), 0) : pos; actor.checkConfirmationRadius(() -> { int affected = editSession.makeSphere(finalPos, pattern, radii.getX(), radii.getY(), radii.getZ(), !hollow); - if (actor instanceof Player) { - ((Player) actor).findFreePosition(); - } + if (actor instanceof Player) ((Player) actor).findFreePosition(); BBC.VISITOR_BLOCK.send(actor, affected); }, "sphere", (int) max, context); } @@ -274,16 +272,15 @@ public class GenerationCommands { @CommandPermissions("worldedit.generation.forest") @Logging(POSITION) public int forestGen(Actor actor, LocalSession session, EditSession editSession, - @Arg(desc = "The size of the forest, in blocks", def = "10") - int size, + @Arg(name = "size", desc = "The size of the forest, in blocks", def = "10") + int sizeOpt, @Arg(desc = "The type of forest", def = "tree") TreeType type, - @Arg(desc = "The density of the forest, between 0 and 100", def = "5") + @Range(min = 0, max = 100) @Arg(desc = "The density of the forest, between 0 and 100", def = "5") double density) throws WorldEditException { checkCommandArgument(0 <= density && density <= 100, "Density must be between 0 and 100"); - worldEdit.checkMaxRadius(size); density /= 100; - int affected = editSession.makeForest(session.getPlacementPosition(actor), size, density, type); + int affected = editSession.makeForest(session.getPlacementPosition(actor), sizeOpt, density, type); actor.print(affected + " trees created."); return affected; } @@ -295,14 +292,13 @@ public class GenerationCommands { @CommandPermissions("worldedit.generation.pumpkins") @Logging(POSITION) public int pumpkins(Actor actor, LocalSession session, EditSession editSession, - @Arg(desc = "The size of the patch", def = "10") - int size, + @Arg(name = "size", desc = "The size of the patch", def = "10") + int sizeOpt, @Arg(desc = "//TODO", def = "10") int apothem, @Arg(desc = "//TODO ", def = "0.02") double density) throws WorldEditException { checkCommandArgument(0 <= density && density <= 100, "Density must be between 0 and 100"); - worldEdit.checkMaxRadius(size); int affected = editSession.makePumpkinPatches(session.getPlacementPosition(actor), apothem, density); BBC.COMMAND_PUMPKIN.send(actor, affected); return affected; @@ -340,9 +336,7 @@ public class GenerationCommands { worldEdit.checkMaxRadius(size); actor.checkConfirmationRadius(() -> { int affected = editSession.makePyramid(pos, pattern, size, !hollow); - if (actor instanceof Player) { - ((Player) actor).findFreePosition(); - } + if (actor instanceof Player) ((Player) actor).findFreePosition(); BBC.VISITOR_BLOCK.send(actor, affected); }, getArguments(context), size, context); } @@ -403,9 +397,7 @@ public class GenerationCommands { actor.checkConfirmationRegion(() -> { try { final int affected = editSession.makeShape(region, zero, unit1, pattern, String.join(" ", expression), hollow, session.getTimeout()); - if (actor instanceof Player) { - ((Player) actor).findFreePosition(); - } + if (actor instanceof Player) ((Player) actor).findFreePosition(); BBC.VISITOR_BLOCK.send(actor, affected); } catch (ExpressionException e) { actor.printError(e.getMessage()); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java index 5fcae45ed..82a6f46ab 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java @@ -1,448 +1,448 @@ -//package com.sk89q.worldedit.command; -// -//import com.boydti.fawe.object.mask.AdjacentAnyMask; -//import com.boydti.fawe.object.mask.AdjacentMask; -//import com.boydti.fawe.object.mask.AngleMask; -//import com.boydti.fawe.object.mask.BiomeMask; -//import com.boydti.fawe.object.mask.BlockLightMask; -//import com.boydti.fawe.object.mask.BrightnessMask; -//import com.boydti.fawe.object.mask.DataMask; -//import com.boydti.fawe.object.mask.ExtremaMask; -//import com.boydti.fawe.object.mask.IdDataMask; -//import com.boydti.fawe.object.mask.IdMask; -//import com.boydti.fawe.object.mask.LightMask; -//import com.boydti.fawe.object.mask.OpacityMask; -//import com.boydti.fawe.object.mask.ROCAngleMask; -//import com.boydti.fawe.object.mask.RadiusMask; -//import com.boydti.fawe.object.mask.RandomMask; -//import com.boydti.fawe.object.mask.SimplexMask; -//import com.boydti.fawe.object.mask.SkyLightMask; -//import com.boydti.fawe.object.mask.SurfaceMask; -//import com.boydti.fawe.object.mask.WallMask; -//import com.boydti.fawe.object.mask.XAxisMask; -//import com.boydti.fawe.object.mask.YAxisMask; -//import com.boydti.fawe.object.mask.ZAxisMask; -//import com.sk89q.worldedit.IncompleteRegionException; -//import com.sk89q.worldedit.LocalSession; -//import com.sk89q.worldedit.WorldEdit; -//import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; -//import com.sk89q.worldedit.entity.Player; -//import com.sk89q.worldedit.extent.Extent; -//import com.sk89q.worldedit.function.mask.BlockMaskBuilder; -//import com.sk89q.worldedit.function.mask.ExistingBlockMask; -//import com.sk89q.worldedit.function.mask.ExpressionMask; -//import com.sk89q.worldedit.function.mask.Mask; -//import com.sk89q.worldedit.function.mask.MaskIntersection; -//import com.sk89q.worldedit.function.mask.MaskUnion; -//import com.sk89q.worldedit.function.mask.Masks; -//import com.sk89q.worldedit.function.mask.OffsetMask; -//import com.sk89q.worldedit.function.mask.RegionMask; -//import com.sk89q.worldedit.function.mask.SolidBlockMask; -//import com.sk89q.worldedit.internal.expression.Expression; -//import com.sk89q.worldedit.internal.expression.ExpressionException; -//import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment; -//import com.sk89q.worldedit.math.BlockVector3; -//import com.sk89q.worldedit.math.Vector3; -//import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; -//import com.sk89q.worldedit.session.request.RequestSelection; -//import com.sk89q.worldedit.world.biome.BiomeType; -//import org.enginehub.piston.annotation.Command; -//import org.enginehub.piston.annotation.CommandContainer; -//import org.enginehub.piston.annotation.param.Arg; -//import org.enginehub.piston.annotation.param.Switch; -// -////@Command(aliases = {"masks"}, -//// desc = "Help for the various masks. [More Info](https://git.io/v9r4K)", -//// descFooter = "Masks determine if a block can be placed\n" + -//// " - Use [brackets] for arguments\n" + -//// " - Use , to OR multiple\n" + -//// " - Use & to AND multiple\n" + -//// "e.g. >[stone,dirt],#light[0][5],$jungle\n" + -//// "More Info: https://git.io/v9r4K" -////) -//@CommandContainer//(superTypes = CommandPermissionsConditionGenerator.Registration.class) -//public class MaskCommands { -// private final WorldEdit worldEdit; -// -// public MaskCommands(WorldEdit worldEdit) { -// this.worldEdit = worldEdit; -// } -// -// @Command( -// name = "#simplex", -// desc = "Use simplex noise as the mask" -// ) -// public Mask simplex(double scale, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { -// scale = 1d / Math.max(1, scale); -// minInt = (minInt - 50) / 50; -// maxInt = (maxInt - 50) / 50; -// return new SimplexMask(scale, minInt, maxInt); -// } -// -// @Command( -// name = "#light", -// desc = "Restrict to specific light levels" -// ) -// public Mask light(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { -// return new LightMask(extent, (int) minInt, (int) maxInt); -// } -// -// @Command( -// name = "#false", -// desc = "Always false" -// ) -// public Mask falseMask(Extent extent) { -// return Masks.alwaysFalse(); -// } -// -// @Command( -// name = "#true", -// desc = "Always true" -// ) -// public Mask trueMask(Extent extent) { -// return Masks.alwaysTrue(); -// } -// -// @Command( -// name = "#skylight", -// desc = "Restrict to specific sky light levels" -// ) -// public Mask skylight(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { -// return new SkyLightMask(extent, (int) minInt, (int) maxInt); -// } -// -// @Command( -// name = "#blocklight", -// aliases = {"#emittedlight"}, -// desc = "Restrict to specific block light levels" -// ) -// public Mask blocklight(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { -// return new BlockLightMask(extent, (int) minInt, (int) maxInt); -// } -// -// @Command( -// name = "#opacity", -// desc = "Restrict to specific opacity levels" -// ) -// public Mask opacity(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { -// return new OpacityMask(extent, (int) minInt, (int) maxInt); -// } -// -// @Command( -// name = "#brightness", -// desc = "Restrict to specific block brightness" -// ) -// public Mask brightness(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { -// return new BrightnessMask(extent, (int) minInt, (int) maxInt); -// } -// -// @Command( -// name = "#offset", -// desc = "Offset a mask" -// ) -// public Mask offset(double x, double y, double z, Mask mask) { -// return new OffsetMask(mask, BlockVector3.at(x, y, z)); -// } -// -// @Command( -// name = "#haslight", -// desc = "Restricts to blocks with light (sky or emitted)" -// ) -// public Mask haslight(Extent extent) { -// return new LightMask(extent, 1, Integer.MAX_VALUE); -// } -// -// @Command( -// name = "#nolight", -// desc = "Restrict to blocks without light (sky or emitted)" -// ) -// public Mask nolight(Extent extent) { -// return new LightMask(extent, 0, 0); -// } -// -// @Command( -// name = "#existing", -// desc = "If there is a non air block" -// ) -// public Mask existing(Extent extent) { -// return new ExistingBlockMask(extent); -// } -// -// @Command( -// name = "#solid", -// desc = "If there is a solid block" -// ) -// public Mask solid(Extent extent) { -// return new SolidBlockMask(extent); -// } -// -// @Command( -// name = "#liquid", -// desc = "If there is a solid block" -// ) -// public Mask liquid(Extent extent) { -// return new BlockMaskBuilder().addAll(b -> b.getMaterial().isLiquid()).build(extent); -// } -// -// @Command( -// name = "#dregion", -// aliases = {"#dselection", "#dsel"}, -// desc = "inside the player's selection" -// ) -// public Mask dregion() { -// return new RegionMask(new RequestSelection()); -// } -// -// @Command( -// name = "#region", -// aliases = {"#selection", "#sel"}, -// desc = "inside the provided selection" -// ) -// public Mask selection(Player player, LocalSession session) throws IncompleteRegionException { -// return new RegionMask(session.getSelection(player.getWorld()).clone()); -// } -// -// @Command( -// name = "#xaxis", -// desc = "Restrict to initial x axis" -// ) -// public Mask xaxis() { -// return new XAxisMask(); -// } -// -// @Command( -// name = "#yaxis", -// desc = "Restrict to initial y axis" -// ) -// public Mask yaxis() { -// return new YAxisMask(); -// } -// -// @Command( -// name = "#zaxis", -// desc = "Restrict to initial z axis" -// ) -// public Mask zaxis() { -// return new ZAxisMask(); -// } -// -// @Command( -// name = "#id", -// desc = "Restrict to initial id" -// ) -// public Mask id(Extent extent) { -// return new IdMask(extent); -// } -// -// @Command( -// name = "#data", -// desc = "Restrict to initial data" -// ) -// public Mask data(Extent extent) { -// return new DataMask(extent); -// } -// -// @Command( -// name = "#iddata", -// desc = "Restrict to initial block id and data" -// ) -// public Mask iddata(Extent extent) { -// return new IdDataMask(extent); -// } -// -// @Command( -// name = "#air", -// desc = "Restrict to types of air" -// ) -// public Mask air(Extent extent) { -// return new BlockMaskBuilder().addAll(b -> b.getMaterial().isAir()).build(extent); -// } -// -// @Command( -// name = "#wall", -// desc = "Restrict to walls (any block n,e,s,w of air)" -// ) -// public Mask wall(Extent extent) { -// Mask blockMask = air(extent); -// return new MaskUnion(new ExistingBlockMask(extent), new WallMask(blockMask, 1, 8)); -// } -// -// @Command( -// name = "#surface", -// desc = "Restrict to surfaces (any solid block touching air)" -// ) -// public Mask surface(Extent extent) { -// return new SurfaceMask(extent); -// } -// -// @Command( -// name = "\\", -// aliases = {"/", "#angle", "#\\", "#/"}, -// desc = "Restrict to specific terrain angle", -// descFooter = "Restrict to specific terrain angle\n" + -// "The -o flag will only overlay\n" + -// "Example: /[0d][45d]\n" + -// "Explanation: Allows any block where the adjacent block is between 0 and 45 degrees.\n" + -// "Example: /[3][20]\n" + -// "Explanation: Allows any block where the adjacent block is between 3 and 20 blocks below" +package com.sk89q.worldedit.command; + +import com.boydti.fawe.object.mask.AdjacentAnyMask; +import com.boydti.fawe.object.mask.AdjacentMask; +import com.boydti.fawe.object.mask.AngleMask; +import com.boydti.fawe.object.mask.BiomeMask; +import com.boydti.fawe.object.mask.BlockLightMask; +import com.boydti.fawe.object.mask.BrightnessMask; +import com.boydti.fawe.object.mask.DataMask; +import com.boydti.fawe.object.mask.ExtremaMask; +import com.boydti.fawe.object.mask.IdDataMask; +import com.boydti.fawe.object.mask.IdMask; +import com.boydti.fawe.object.mask.LightMask; +import com.boydti.fawe.object.mask.OpacityMask; +import com.boydti.fawe.object.mask.ROCAngleMask; +import com.boydti.fawe.object.mask.RadiusMask; +import com.boydti.fawe.object.mask.RandomMask; +import com.boydti.fawe.object.mask.SimplexMask; +import com.boydti.fawe.object.mask.SkyLightMask; +import com.boydti.fawe.object.mask.SurfaceMask; +import com.boydti.fawe.object.mask.WallMask; +import com.boydti.fawe.object.mask.XAxisMask; +import com.boydti.fawe.object.mask.YAxisMask; +import com.boydti.fawe.object.mask.ZAxisMask; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.BlockMaskBuilder; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.ExpressionMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.mask.MaskUnion; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.mask.OffsetMask; +import com.sk89q.worldedit.function.mask.RegionMask; +import com.sk89q.worldedit.function.mask.SolidBlockMask; +import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; +import com.sk89q.worldedit.session.request.RequestSelection; +import com.sk89q.worldedit.world.biome.BiomeType; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; +import org.enginehub.piston.annotation.param.Switch; + +//@Command(aliases = {"masks"}, +// desc = "Help for the various masks. [More Info](https://git.io/v9r4K)", +// descFooter = "Masks determine if a block can be placed\n" + +// " - Use [brackets] for arguments\n" + +// " - Use , to OR multiple\n" + +// " - Use & to AND multiple\n" + +// "e.g. >[stone,dirt],#light[0][5],$jungle\n" + +// "More Info: https://git.io/v9r4K" //) -// public Mask angle(Extent extent, @Arg(name="min", desc = "min angle") String minStr, @Arg(name="max", desc = "max angle") String maxStr, @Switch(name = 'o', desc = "TODO") boolean overlay, @Arg(name = "distance", desc = "int", def = "1") int distanceOpt) throws ExpressionException { -// double y1, y2; -// boolean override; -// if (maxStr.endsWith("d")) { -// double y1d = Expression.compile(minStr.substring(0, minStr.length() - 1)).evaluate(); -// double y2d = Expression.compile(maxStr.substring(0, maxStr.length() - 1)).evaluate(); -// y1 = Math.tan(y1d * (Math.PI / 180)); -// y2 = Math.tan(y2d * (Math.PI / 180)); -// } else { -// y1 = Expression.compile(minStr).evaluate(); -// y2 = Expression.compile(maxStr).evaluate(); -// } -// return new AngleMask(extent, y1, y2, overlay, distanceOpt); -// } -// -// @Command( -// name = "(", -// aliases = {")", "#roc", "#(", "#)"}, -// desc = "Restrict to near specific terrain slope rate of change", -// descFooter = "Restrict to near specific terrain slope rate of change\n" + -// "The -o flag will only overlay\n" + -// "Example: ([0d][45d][5]\n" + -// "Explanation: Restrict near where the angle changes between 0-45 degrees within 5 blocks\n" + -// "Note: Use negatives for decreasing slope" -//) -// public Mask roc(Extent extent, @Arg(name="min", desc = "min angle") String minStr, @Arg(name="max", desc = "max angle") String maxStr, @Switch(name = 'o', desc = "TODO") boolean overlay, @Arg(name = "distance", desc = "int", def = "4") int distanceOpt) throws ExpressionException { -// double y1, y2; -// boolean override; -// if (maxStr.endsWith("d")) { -// double y1d = Expression.compile(minStr.substring(0, minStr.length() - 1)).evaluate(); -// double y2d = Expression.compile(maxStr.substring(0, maxStr.length() - 1)).evaluate(); -// y1 = Math.tan(y1d * (Math.PI / 180)); -// y2 = Math.tan(y2d * (Math.PI / 180)); -// } else { -// y1 = Expression.compile(minStr).evaluate(); -// y2 = Expression.compile(maxStr).evaluate(); -// } -// return new ROCAngleMask(extent, y1, y2, overlay, distanceOpt); -// } -// -// @Command( -// name = "^", -// aliases = {"#extrema", "#^"}, -// desc = "Restrict to near specific terrain extrema", -// descFooter = "Restrict to near specific terrain extrema\n" + -// "The -o flag will only overlay\n" + -// "Example: ([0d][45d][5]\n" + -// "Explanation: Restrict to near 45 degrees of local maxima\n" + -// "Note: Use negatives for local minima" -//) -// public Mask extrema(Extent extent, @Arg(name="min", desc = "min angle") String minStr, @Arg(name="max", desc = "max angle") String maxStr, @Switch(name = 'o', desc = "TODO") boolean overlay, @Arg(name = "distance", desc = "int", def = "4") int distanceOpt) throws ExpressionException { -// double y1, y2; -// boolean override; -// if (maxStr.endsWith("d")) { -// double y1d = Expression.compile(minStr.substring(0, minStr.length() - 1)).evaluate(); -// double y2d = Expression.compile(maxStr.substring(0, maxStr.length() - 1)).evaluate(); -// y1 = Math.tan(y1d * (Math.PI / 180)); -// y2 = Math.tan(y2d * (Math.PI / 180)); -// } else { -// y1 = Expression.compile(minStr).evaluate(); -// y2 = Expression.compile(maxStr).evaluate(); -// } -// return new ExtremaMask(extent, y1, y2, overlay, distanceOpt); -// } -// -// @Command( -// name = "{", -// aliases = {"#{"}, -// desc = "Restricts blocks to within a specific radius range of the initial block" -//) -// public Mask radius(@Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) throws ExpressionException { -// return new RadiusMask((int) minInt, (int) maxInt); -// } -// -// @Command( -// name = "|", -// aliases = {"#|", "#side"}, -// desc = "sides with a specific number of other blocks" -//) -// public Mask wall(Mask mask, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) throws ExpressionException { -// return new WallMask(mask, (int) minInt, (int) maxInt); -// } -// -// @Command( -// name = "~", -// aliases = {"#~", "#adjacent"}, -// desc = "Adjacent to a specific number of other blocks" -//) -// public Mask adjacent(Mask mask, @Arg(name = "min", desc = "double", def = "-1") double min, @Arg(name = "max", desc = "double", def = "-1") double max) throws ExpressionException { -// if (min == -1 && max == -1) { -// min = 1; -// max = 8; -// } else if (max == -1) max = min; -// if (max >= 8 && min == 1) { -// return new AdjacentAnyMask(mask); -// } -// return new AdjacentMask(mask, (int) min, (int) max); -// } -// -// @Command( -// name = "<", -// aliases = {"#<", "#below"}, -// desc = "below a specific block" -//) -// public Mask below(Mask mask) throws ExpressionException { -// OffsetMask offsetMask = new OffsetMask(mask, BlockVector3.at(0, 1, 0)); -// return new MaskIntersection(offsetMask, Masks.negate(mask)); -// } -// -// @Command( -// name = ">", -// aliases = {"#>", "#above"}, -// desc = "above a specific block" -//) -// public Mask above(Mask mask) throws ExpressionException { -// OffsetMask offsetMask = new OffsetMask(mask, BlockVector3.at(0, -1, 0)); -// return new MaskIntersection(offsetMask, Masks.negate(mask)); -// } -// -// @Command( -// name = "$", -// aliases = {"#biome", "#$"}, -// desc = "in a specific biome", -// descFooter = "in a specific biome. For a list of biomes use //biomelist" -//) -// public Mask biome(Extent extent, BiomeType biome) throws ExpressionException { -// return new BiomeMask(extent, biome); -// } -// -// @Command( -// name = "%", -// aliases = {"#%", "#percent"}, -// desc = "percentage chance" -//) -// public Mask random(double chance) throws ExpressionException { -// chance = chance / 100; -// return new RandomMask(chance); -// } -// -// @Command( -// name = "=", -// aliases = {"#=", "#expression"}, -// desc = "expression mask" -//) -// public Mask expression(Extent extent, String input) throws ExpressionException { -// Expression exp = Expression.compile(input, "x", "y", "z"); -// ExpressionEnvironment env = new WorldEditExpressionEnvironment(extent, Vector3.ONE, Vector3.ZERO); -// exp.setEnvironment(env); -// return new ExpressionMask(exp); -// } -// -// @Command( -// name = "!", -// aliases = {"#not", "#negate", "#!"}, -// desc = "Negate another mask" -//) -// public Mask expression(Mask mask) throws ExpressionException { -// return Masks.negate(mask); -// } -//} +@CommandContainer//(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class MaskCommands { + private final WorldEdit worldEdit; + + public MaskCommands(WorldEdit worldEdit) { + this.worldEdit = worldEdit; + } + + @Command( + name = "#simplex", + desc = "Use simplex noise as the mask" + ) + public Mask simplex(double scale, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { + scale = 1d / Math.max(1, scale); + minInt = (minInt - 50) / 50; + maxInt = (maxInt - 50) / 50; + return new SimplexMask(scale, minInt, maxInt); + } + + @Command( + name = "#light", + desc = "Restrict to specific light levels" + ) + public Mask light(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { + return new LightMask(extent, (int) minInt, (int) maxInt); + } + + @Command( + name = "#false", + desc = "Always false" + ) + public Mask falseMask(Extent extent) { + return Masks.alwaysFalse(); + } + + @Command( + name = "#true", + desc = "Always true" + ) + public Mask trueMask(Extent extent) { + return Masks.alwaysTrue(); + } + + @Command( + name = "#skylight", + desc = "Restrict to specific sky light levels" + ) + public Mask skylight(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { + return new SkyLightMask(extent, (int) minInt, (int) maxInt); + } + + @Command( + name = "#blocklight", + aliases = {"#emittedlight"}, + desc = "Restrict to specific block light levels" + ) + public Mask blocklight(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { + return new BlockLightMask(extent, (int) minInt, (int) maxInt); + } + + @Command( + name = "#opacity", + desc = "Restrict to specific opacity levels" + ) + public Mask opacity(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { + return new OpacityMask(extent, (int) minInt, (int) maxInt); + } + + @Command( + name = "#brightness", + desc = "Restrict to specific block brightness" + ) + public Mask brightness(Extent extent, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) { + return new BrightnessMask(extent, (int) minInt, (int) maxInt); + } + + @Command( + name = "#offset", + desc = "Offset a mask" + ) + public Mask offset(double x, double y, double z, Mask mask) { + return new OffsetMask(mask, BlockVector3.at(x, y, z)); + } + + @Command( + name = "#haslight", + desc = "Restricts to blocks with light (sky or emitted)" + ) + public Mask haslight(Extent extent) { + return new LightMask(extent, 1, Integer.MAX_VALUE); + } + + @Command( + name = "#nolight", + desc = "Restrict to blocks without light (sky or emitted)" + ) + public Mask nolight(Extent extent) { + return new LightMask(extent, 0, 0); + } + + @Command( + name = "#existing", + desc = "If there is a non air block" + ) + public Mask existing(Extent extent) { + return new ExistingBlockMask(extent); + } + + @Command( + name = "#solid", + desc = "If there is a solid block" + ) + public Mask solid(Extent extent) { + return new SolidBlockMask(extent); + } + + @Command( + name = "#liquid", + desc = "If there is a solid block" + ) + public Mask liquid(Extent extent) { + return new BlockMaskBuilder().addAll(b -> b.getMaterial().isLiquid()).build(extent); + } + + @Command( + name = "#dregion", + aliases = {"#dselection", "#dsel"}, + desc = "inside the player's selection" + ) + public Mask dregion() { + return new RegionMask(new RequestSelection()); + } + + @Command( + name = "#region", + aliases = {"#selection", "#sel"}, + desc = "inside the provided selection" + ) + public Mask selection(Player player, LocalSession session) throws IncompleteRegionException { + return new RegionMask(session.getSelection(player.getWorld()).clone()); + } + + @Command( + name = "#xaxis", + desc = "Restrict to initial x axis" + ) + public Mask xaxis() { + return new XAxisMask(); + } + + @Command( + name = "#yaxis", + desc = "Restrict to initial y axis" + ) + public Mask yaxis() { + return new YAxisMask(); + } + + @Command( + name = "#zaxis", + desc = "Restrict to initial z axis" + ) + public Mask zaxis() { + return new ZAxisMask(); + } + + @Command( + name = "#id", + desc = "Restrict to initial id" + ) + public Mask id(Extent extent) { + return new IdMask(extent); + } + + @Command( + name = "#data", + desc = "Restrict to initial data" + ) + public Mask data(Extent extent) { + return new DataMask(extent); + } + + @Command( + name = "#iddata", + desc = "Restrict to initial block id and data" + ) + public Mask iddata(Extent extent) { + return new IdDataMask(extent); + } + + @Command( + name = "#air", + desc = "Restrict to types of air" + ) + public Mask air(Extent extent) { + return new BlockMaskBuilder().addAll(b -> b.getMaterial().isAir()).build(extent); + } + + @Command( + name = "#wall", + desc = "Restrict to walls (any block n,e,s,w of air)" + ) + public Mask wall(Extent extent) { + Mask blockMask = air(extent); + return new MaskUnion(new ExistingBlockMask(extent), new WallMask(blockMask, 1, 8)); + } + + @Command( + name = "#surface", + desc = "Restrict to surfaces (any solid block touching air)" + ) + public Mask surface(Extent extent) { + return new SurfaceMask(extent); + } + + @Command( + name = "\\", + aliases = {"/", "#angle", "#\\", "#/"}, + desc = "Restrict to specific terrain angle", + descFooter = "Restrict to specific terrain angle\n" + + "The -o flag will only overlay\n" + + "Example: /[0d][45d]\n" + + "Explanation: Allows any block where the adjacent block is between 0 and 45 degrees.\n" + + "Example: /[3][20]\n" + + "Explanation: Allows any block where the adjacent block is between 3 and 20 blocks below" +) + public Mask angle(Extent extent, @Arg(name="min", desc = "min angle") String minStr, @Arg(name="max", desc = "max angle") String maxStr, @Switch(name = 'o', desc = "TODO") boolean overlay, @Arg(name = "distance", desc = "int", def = "1") int distanceOpt) throws ExpressionException { + double y1, y2; + boolean override; + if (maxStr.endsWith("d")) { + double y1d = Expression.compile(minStr.substring(0, minStr.length() - 1)).evaluate(); + double y2d = Expression.compile(maxStr.substring(0, maxStr.length() - 1)).evaluate(); + y1 = Math.tan(y1d * (Math.PI / 180)); + y2 = Math.tan(y2d * (Math.PI / 180)); + } else { + y1 = Expression.compile(minStr).evaluate(); + y2 = Expression.compile(maxStr).evaluate(); + } + return new AngleMask(extent, y1, y2, overlay, distanceOpt); + } + + @Command( + name = "(", + aliases = {")", "#roc", "#(", "#)"}, + desc = "Restrict to near specific terrain slope rate of change", + descFooter = "Restrict to near specific terrain slope rate of change\n" + + "The -o flag will only overlay\n" + + "Example: ([0d][45d][5]\n" + + "Explanation: Restrict near where the angle changes between 0-45 degrees within 5 blocks\n" + + "Note: Use negatives for decreasing slope" +) + public Mask roc(Extent extent, @Arg(name="min", desc = "min angle") String minStr, @Arg(name="max", desc = "max angle") String maxStr, @Switch(name = 'o', desc = "TODO") boolean overlay, @Arg(name = "distance", desc = "int", def = "4") int distanceOpt) throws ExpressionException { + double y1, y2; + boolean override; + if (maxStr.endsWith("d")) { + double y1d = Expression.compile(minStr.substring(0, minStr.length() - 1)).evaluate(); + double y2d = Expression.compile(maxStr.substring(0, maxStr.length() - 1)).evaluate(); + y1 = Math.tan(y1d * (Math.PI / 180)); + y2 = Math.tan(y2d * (Math.PI / 180)); + } else { + y1 = Expression.compile(minStr).evaluate(); + y2 = Expression.compile(maxStr).evaluate(); + } + return new ROCAngleMask(extent, y1, y2, overlay, distanceOpt); + } + + @Command( + name = "^", + aliases = {"#extrema", "#^"}, + desc = "Restrict to near specific terrain extrema", + descFooter = "Restrict to near specific terrain extrema\n" + + "The -o flag will only overlay\n" + + "Example: ([0d][45d][5]\n" + + "Explanation: Restrict to near 45 degrees of local maxima\n" + + "Note: Use negatives for local minima" +) + public Mask extrema(Extent extent, @Arg(name="min", desc = "min angle") String minStr, @Arg(name="max", desc = "max angle") String maxStr, @Switch(name = 'o', desc = "TODO") boolean overlay, @Arg(name = "distance", desc = "int", def = "4") int distanceOpt) throws ExpressionException { + double y1, y2; + boolean override; + if (maxStr.endsWith("d")) { + double y1d = Expression.compile(minStr.substring(0, minStr.length() - 1)).evaluate(); + double y2d = Expression.compile(maxStr.substring(0, maxStr.length() - 1)).evaluate(); + y1 = Math.tan(y1d * (Math.PI / 180)); + y2 = Math.tan(y2d * (Math.PI / 180)); + } else { + y1 = Expression.compile(minStr).evaluate(); + y2 = Expression.compile(maxStr).evaluate(); + } + return new ExtremaMask(extent, y1, y2, overlay, distanceOpt); + } + + @Command( + name = "{", + aliases = {"#{"}, + desc = "Restricts blocks to within a specific radius range of the initial block" +) + public Mask radius(@Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) throws ExpressionException { + return new RadiusMask((int) minInt, (int) maxInt); + } + + @Command( + name = "|", + aliases = {"#|", "#side"}, + desc = "sides with a specific number of other blocks" +) + public Mask wall(Mask mask, @Arg(name="mine", desc = "min light") double minInt, @Arg(name="mine", desc = "max light") double maxInt) throws ExpressionException { + return new WallMask(mask, (int) minInt, (int) maxInt); + } + + @Command( + name = "~", + aliases = {"#~", "#adjacent"}, + desc = "Adjacent to a specific number of other blocks" +) + public Mask adjacent(Mask mask, @Arg(name = "min", desc = "double", def = "-1") double min, @Arg(name = "max", desc = "double", def = "-1") double max) throws ExpressionException { + if (min == -1 && max == -1) { + min = 1; + max = 8; + } else if (max == -1) max = min; + if (max >= 8 && min == 1) { + return new AdjacentAnyMask(mask); + } + return new AdjacentMask(mask, (int) min, (int) max); + } + + @Command( + name = "<", + aliases = {"#<", "#below"}, + desc = "below a specific block" +) + public Mask below(Mask mask) throws ExpressionException { + OffsetMask offsetMask = new OffsetMask(mask, BlockVector3.at(0, 1, 0)); + return new MaskIntersection(offsetMask, Masks.negate(mask)); + } + + @Command( + name = ">", + aliases = {"#>", "#above"}, + desc = "above a specific block" +) + public Mask above(Mask mask) throws ExpressionException { + OffsetMask offsetMask = new OffsetMask(mask, BlockVector3.at(0, -1, 0)); + return new MaskIntersection(offsetMask, Masks.negate(mask)); + } + + @Command( + name = "$", + aliases = {"#biome", "#$"}, + desc = "in a specific biome", + descFooter = "in a specific biome. For a list of biomes use //biomelist" +) + public Mask biome(Extent extent, BiomeType biome) throws ExpressionException { + return new BiomeMask(extent, biome); + } + + @Command( + name = "%", + aliases = {"#%", "#percent"}, + desc = "percentage chance" +) + public Mask random(double chance) throws ExpressionException { + chance = chance / 100; + return new RandomMask(chance); + } + + @Command( + name = "=", + aliases = {"#=", "#expression"}, + desc = "expression mask" +) + public Mask expression(Extent extent, String input) throws ExpressionException { + Expression exp = Expression.compile(input, "x", "y", "z"); + ExpressionEnvironment env = new WorldEditExpressionEnvironment(extent, Vector3.ONE, Vector3.ZERO); + exp.setEnvironment(env); + return new ExpressionMask(exp); + } + + @Command( + name = "!", + aliases = {"#not", "#negate", "#!"}, + desc = "Negate another mask" +) + public Mask expression(Mask mask) throws ExpressionException { + return Masks.negate(mask); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java index 55bde4382..f1c81e4ac 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java @@ -1,403 +1,403 @@ -//package com.sk89q.worldedit.command; -// -//import com.boydti.fawe.object.DataAnglePattern; -//import com.boydti.fawe.object.clipboard.MultiClipboardHolder; -//import com.boydti.fawe.object.collection.RandomCollection; -//import com.boydti.fawe.object.pattern.AngleColorPattern; -//import com.boydti.fawe.object.pattern.AverageColorPattern; -//import com.boydti.fawe.object.pattern.BiomePattern; -//import com.boydti.fawe.object.pattern.BufferedPattern; -//import com.boydti.fawe.object.pattern.BufferedPattern2D; -//import com.boydti.fawe.object.pattern.DataPattern; -//import com.boydti.fawe.object.pattern.DesaturatePattern; -//import com.boydti.fawe.object.pattern.ExistingPattern; -//import com.boydti.fawe.object.pattern.ExpressionPattern; -//import com.boydti.fawe.object.pattern.FullClipboardPattern; -//import com.boydti.fawe.object.pattern.IdDataMaskPattern; -//import com.boydti.fawe.object.pattern.IdPattern; -//import com.boydti.fawe.object.pattern.Linear2DBlockPattern; -//import com.boydti.fawe.object.pattern.Linear3DBlockPattern; -//import com.boydti.fawe.object.pattern.LinearBlockPattern; -//import com.boydti.fawe.object.pattern.MaskedPattern; -//import com.boydti.fawe.object.pattern.NoXPattern; -//import com.boydti.fawe.object.pattern.NoYPattern; -//import com.boydti.fawe.object.pattern.NoZPattern; -//import com.boydti.fawe.object.pattern.OffsetPattern; -//import com.boydti.fawe.object.pattern.PropertyPattern; -//import com.boydti.fawe.object.pattern.RandomFullClipboardPattern; -//import com.boydti.fawe.object.pattern.RandomOffsetPattern; -//import com.boydti.fawe.object.pattern.RelativePattern; -//import com.boydti.fawe.object.pattern.SaturatePattern; -//import com.boydti.fawe.object.pattern.ShadePattern; -//import com.boydti.fawe.object.pattern.SolidRandomOffsetPattern; -//import com.boydti.fawe.object.pattern.SurfaceRandomOffsetPattern; -//import com.boydti.fawe.object.random.SimplexRandom; -//import com.boydti.fawe.util.ColorUtil; -//import com.boydti.fawe.util.TextureUtil; -//import com.sk89q.worldedit.EmptyClipboardException; -//import com.sk89q.worldedit.LocalSession; -//import com.sk89q.worldedit.entity.Player; -//import com.sk89q.worldedit.extension.input.InputParseException; -//import com.sk89q.worldedit.extension.platform.Actor; -//import com.sk89q.worldedit.extent.Extent; -//import com.sk89q.worldedit.extent.clipboard.Clipboard; -//import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; -//import com.sk89q.worldedit.function.mask.Mask; -//import com.sk89q.worldedit.function.pattern.ClipboardPattern; -//import com.sk89q.worldedit.function.pattern.Pattern; -//import com.sk89q.worldedit.function.pattern.RandomPattern; -//import com.sk89q.worldedit.internal.annotation.Range; -//import com.sk89q.worldedit.internal.expression.Expression; -//import com.sk89q.worldedit.internal.expression.ExpressionException; -//import com.sk89q.worldedit.math.Vector3; -//import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; -//import com.sk89q.worldedit.session.ClipboardHolder; -//import com.sk89q.worldedit.world.biome.BiomeType; -//import java.awt.Color; -//import java.io.IOException; -//import java.util.Collections; -//import java.util.List; -//import java.util.Set; -//import org.enginehub.piston.annotation.Command; -//import org.enginehub.piston.annotation.CommandContainer; -//import org.enginehub.piston.annotation.param.Arg; -// -////@Command(aliases = {"patterns"}, -//// desc = "Help for the various patterns. [More Info](https://git.io/vSPmA)", -//// descFooter = "Patterns determine what blocks are placed\n" + -//// " - Use [brackets] for arguments\n" + -//// " - Use , to OR multiple\n" + -//// "e.g. #surfacespread[10][#existing],andesite\n" + -//// "More Info: https://git.io/vSPmA" -////) -//@CommandContainer//(superTypes = CommandPermissionsConditionGenerator.Registration.class) -//public class PatternCommands { -// -// @Command( -// name = "#existing", -// aliases = {"#*", "*", ".*"}, -// desc = "Use the block that is already there" -// ) -// public Pattern existing(Extent extent, @Arg(name = "properties", desc = "String", def = "") String properties) { // TODO FIXME , @Arg(name = "properties", desc = "String", def = "") String properties -// if (properties == null) return new ExistingPattern(extent); -// return new PropertyPattern(extent).addRegex(".*[" + properties + "]"); -// } -// -// @Command( -// name = "#clipboard", -// aliases = {"#copy"}, -// desc = "Use the blocks in your clipboard as the pattern") -// public Pattern clipboard(LocalSession session) throws EmptyClipboardException { -// ClipboardHolder holder = session.getClipboard(); -// Clipboard clipboard = holder.getClipboard(); -// return new ClipboardPattern(clipboard); -// } -// -// @Command( -// name = "#simplex", -// desc = "Use simplex noise to randomize blocks. Tutorial: https://imgur.com/a/rwVAE" +package com.sk89q.worldedit.command; + +import com.boydti.fawe.object.DataAnglePattern; +import com.boydti.fawe.object.clipboard.MultiClipboardHolder; +import com.boydti.fawe.object.collection.RandomCollection; +import com.boydti.fawe.object.pattern.AngleColorPattern; +import com.boydti.fawe.object.pattern.AverageColorPattern; +import com.boydti.fawe.object.pattern.BiomePattern; +import com.boydti.fawe.object.pattern.BufferedPattern; +import com.boydti.fawe.object.pattern.BufferedPattern2D; +import com.boydti.fawe.object.pattern.DataPattern; +import com.boydti.fawe.object.pattern.DesaturatePattern; +import com.boydti.fawe.object.pattern.ExistingPattern; +import com.boydti.fawe.object.pattern.ExpressionPattern; +import com.boydti.fawe.object.pattern.FullClipboardPattern; +import com.boydti.fawe.object.pattern.IdDataMaskPattern; +import com.boydti.fawe.object.pattern.IdPattern; +import com.boydti.fawe.object.pattern.Linear2DBlockPattern; +import com.boydti.fawe.object.pattern.Linear3DBlockPattern; +import com.boydti.fawe.object.pattern.LinearBlockPattern; +import com.boydti.fawe.object.pattern.MaskedPattern; +import com.boydti.fawe.object.pattern.NoXPattern; +import com.boydti.fawe.object.pattern.NoYPattern; +import com.boydti.fawe.object.pattern.NoZPattern; +import com.boydti.fawe.object.pattern.OffsetPattern; +import com.boydti.fawe.object.pattern.PropertyPattern; +import com.boydti.fawe.object.pattern.RandomFullClipboardPattern; +import com.boydti.fawe.object.pattern.RandomOffsetPattern; +import com.boydti.fawe.object.pattern.RelativePattern; +import com.boydti.fawe.object.pattern.SaturatePattern; +import com.boydti.fawe.object.pattern.ShadePattern; +import com.boydti.fawe.object.pattern.SolidRandomOffsetPattern; +import com.boydti.fawe.object.pattern.SurfaceRandomOffsetPattern; +import com.boydti.fawe.object.random.SimplexRandom; +import com.boydti.fawe.util.ColorUtil; +import com.boydti.fawe.util.TextureUtil; +import com.sk89q.worldedit.EmptyClipboardException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.ClipboardPattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.RandomPattern; +import com.sk89q.worldedit.internal.annotation.Range; +import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.world.biome.BiomeType; +import java.awt.Color; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; + +//@Command(aliases = {"patterns"}, +// desc = "Help for the various patterns. [More Info](https://git.io/vSPmA)", +// descFooter = "Patterns determine what blocks are placed\n" + +// " - Use [brackets] for arguments\n" + +// " - Use , to OR multiple\n" + +// "e.g. #surfacespread[10][#existing],andesite\n" + +// "More Info: https://git.io/vSPmA" //) -// public Pattern simplex(@Arg(desc = "scale factor") double scale, Pattern other) { -// if (other instanceof RandomPattern) { -// scale = (1d / Math.max(1, scale)); -// RandomCollection collection = ((RandomPattern) other).getCollection(); -// collection.setRandom(new SimplexRandom(scale)); -// } -// return other; -// } -// -// @Command( -// name = "#color", -// desc = "Use the block closest to a specific color" -//) -// public Pattern color(TextureUtil textureUtil, String color) { -// Color colorObj = ColorUtil.parseColor(color); -// return textureUtil.getNearestBlock(colorObj.getRGB()).getDefaultState(); -// } -// -// @Command( -// name = "#anglecolor", -// desc = "A darker block based on the existing terrain angle" -//) -// public Pattern anglecolor(Extent extent, LocalSession session, @Arg(name = "randomize", desc = "boolean", def = "true") boolean randomize, @Arg(name = "maxcomplexity", desc = "double", def = "100") double maxComplexity, @Arg(name = "distance", desc = "int", def = "1") int distanceOpt) { -// return new AngleColorPattern(extent, session, distanceOpt); -// } -// -// @Command( -// name = "#angledata", -// desc = "Block data based on the existing terrain angle" -// ) -// public Pattern angledata(Extent extent, @Arg(name = "distance", desc = "int", def = "1") int distanceOpt) { -// return new DataAnglePattern(extent, distanceOpt); -// } -// -// @Command( -// name = "#saturate", -// desc = "Saturate the existing block with a color" -//) -// public Pattern saturate(Extent extent, LocalSession session, String colorStr) { -// Color color = ColorUtil.parseColor(colorStr); -// return new SaturatePattern(extent, color.getRGB(), session); -// } -// -// @Command( -// name = "#averagecolor", -// desc = "Average between the existing block and a color" -//) -// public Pattern averagecolor(Extent extent, LocalSession session, String colorStr) { -// Color color = ColorUtil.parseColor(colorStr); -// return new AverageColorPattern(extent, color.getRGB(), session); -// } -// -// @Command( -// name = "#desaturate", -// desc = "Desaturated color of the existing block" -//) -// public Pattern desaturate(Extent extent, LocalSession session, @Arg(name = "percent", desc = "double", def = "100") double percent) { -// return new DesaturatePattern(extent, percent / 100d, session); -// } -// -// @Command( -// name = "#lighten", -// desc = "Lighten the existing block" -//) -// public Pattern lighten(Extent extent, TextureUtil util) { -// return new ShadePattern(extent, false, util); -// } -// -// @Command( -// name = "#darken", -// desc = "Darken the existing block" -//) -// public Pattern darken(Extent extent, TextureUtil util) { -// return new ShadePattern(extent, true, util); -// } -// -// @Command( -// name = "#fullcopy", -// desc = "Places your full clipboard at each block" -//) -// public Pattern fullcopy(Player player, Extent extent, LocalSession session, @Arg(name = "location", desc = "String", def = "#copy") String location, @Arg(name = "rotate", desc = "boolean", def = "false") boolean rotate, @Arg(name = "flip", desc = "boolean", def = "false") boolean flip) throws EmptyClipboardException, InputParseException, IOException { -// List clipboards; -// switch (location.toLowerCase()) { -// case "#copy": -// case "#clipboard": -// ClipboardHolder clipboard = session.getExistingClipboard(); -// if (clipboard == null) { -// throw new InputParseException("To use #fullcopy, please first copy something to your clipboard"); -// } -// if (!rotate && !flip) { -// return new FullClipboardPattern(extent, clipboard.getClipboard()); -// } -// clipboards = Collections.singletonList(clipboard); -// break; -// default: -// MultiClipboardHolder multi = ClipboardFormats.loadAllFromInput(player, location, null, true); -// clipboards = multi != null ? multi.getHolders() : null; -// break; -// } -// if (clipboards == null) { -// throw new InputParseException("#fullcopy:"); -// } -// return new RandomFullClipboardPattern(extent, clipboards, rotate, flip); -// } -// -// @Command( -// name = "#buffer", -// desc = "Only place a block once while a pattern is in use", -// descFooter = "Only place a block once while a pattern is in use\n" + -// "Use with a brush when you don't want to apply to the same spot twice" -//) -// public Pattern buffer(Actor actor, Pattern pattern) { -// return new BufferedPattern(actor, pattern); -// } -// -// @Command( -// name = "#buffer2d", -// desc = "Only place a block once in a column while a pattern is in use" -//) -// public Pattern buffer2d(Actor actor, Pattern pattern) { -// return new BufferedPattern2D(actor, pattern); -// } -// -// @Command( -// name = "#iddatamask", -// desc = "Use the pattern's id and the existing blocks data with the provided mask", -// descFooter = "Use the pattern's id and the existing blocks data with the provided mask\n" + -// " - Use to replace slabs or where the data values needs to be shifted instead of set" -//) -// public Pattern iddatamask(Actor actor, LocalSession session, Extent extent, @Range(min = 0, max = 15) int bitmask, Pattern pattern) { -// -// return new IdDataMaskPattern(extent, pattern, bitmask); -// } -// -// @Command( -// name = "#id", -// desc = "Only change the block id" -//) -// public Pattern id(Actor actor, LocalSession session, Extent extent, Pattern pattern) { -// -// return new IdPattern(extent, pattern); -// } -// -// @Command( -// name = "#data", -// desc = "Only change the block data" -//) -// public Pattern data(Actor actor, LocalSession session, Extent extent, Pattern pattern) { -// -// return new DataPattern(extent, pattern); -// } -// -// @Command( -// name = "#biome", -// aliases = {"$"}, -// desc = "Set the biome" -//) -// public Pattern biome(Actor actor, LocalSession session, Extent extent, BiomeType biome) { -// -// return new BiomePattern(extent, biome); -// } -// -// @Command( -// name = "#relative", -// aliases = {"#~", "#r", "#rel"}, -// desc = "Offset the pattern to where you click" -//) -// public Pattern relative(Actor actor, LocalSession session, Extent extent, Pattern pattern) { -// -// return new RelativePattern(pattern); -// } -// -// @Command( -// name = "#!x", -// aliases = {"#nx", "#nox"}, -// desc = "The pattern will not be provided the x axis info", -// descFooter = "The pattern will not be provided the z axis info.\n" + -// "Example: #!x[#!z[#~[#l3d[pattern]]]]" -//) -// public Pattern nox(Actor actor, LocalSession session, Extent extent, Pattern pattern) { -// -// return new NoXPattern(pattern); -// } -// -// @Command( -// name = "#!y", -// aliases = {"#ny", "#noy"}, -// desc = "The pattern will not be provided the y axis info" -//) -// public Pattern noy(Actor actor, LocalSession session, Extent extent, Pattern pattern) { -// -// return new NoYPattern(pattern); -// } -// -// @Command( -// name = "#!z", -// aliases = {"#nz", "#noz"}, -// desc = "The pattern will not be provided the z axis info" -//) -// public Pattern noz(Actor actor, LocalSession session, Extent extent, Pattern pattern) { -// -// return new NoZPattern(pattern); -// } -// -// @Command( -// name = "#mask", -// desc = "Apply a pattern depending on a mask" -//) -// public Pattern mask(Actor actor, LocalSession session, Mask mask, Pattern pass, Pattern fail) { -// return new MaskedPattern(mask, pass, fail); -// } -// -// @Command( -// name = "#offset", -// desc = "Offset a pattern" -//) -// public Pattern offset(Actor actor, LocalSession session, double x, double y, double z, Pattern pattern) { -// -// return new OffsetPattern(pattern, (int) x, (int) y, (int) z); -// } -// -// @Command( -// name = "#surfacespread", -// desc = "Applies to only blocks on a surface. Selects a block from provided pattern with a given ranomized offset `[0, )`. e.g. Use `#existing` to randomly offset blocks in the world, or `#copy` to offset blocks in your clipboard" -//) -// public Pattern surfacespread(Actor actor, LocalSession session, double distance, Pattern pattern) { -// -// return new SurfaceRandomOffsetPattern(pattern, (int) distance); -// } -// -// @Command( -// name = "#solidspread", -// desc = "Randomly spread solid blocks" -//) -// public Pattern solidspread(Actor actor, LocalSession session, double x, double y, double z, Pattern pattern) { -// -// return new SolidRandomOffsetPattern(pattern, (int) x, (int) y, (int) z); -// } -// -// @Command( -// name = "#spread", -// aliases = {"#randomoffset"}, -// desc = "Randomly spread blocks" -//) -// public Pattern spread(Actor actor, LocalSession session, double x, double y, double z, Pattern pattern) { -// -// return new RandomOffsetPattern(pattern, (int) x, (int) y, (int) z); -// } -// -// @Command( -// name = "#linear", -// aliases = {"#l"}, -// desc = "Sequentially set blocks from a list of patterns" -//) -// public Pattern linear(Actor actor, LocalSession session, Pattern other) { -// -// if (other instanceof RandomPattern) { -// Set patterns = ((RandomPattern) other).getPatterns(); -// return new LinearBlockPattern(patterns.toArray(new Pattern[patterns.size()])); -// } -// return other; -// } -// -// @Command( -// name = "#linear3d", -// aliases = {"#l3d"}, -// desc = "Use the x,y,z coordinate to pick a block from the list" -//) -// public Pattern linear3d(Actor actor, LocalSession session, Pattern other) { -// -// if (other instanceof RandomPattern) { -// Set patterns = ((RandomPattern) other).getPatterns(); -// return new Linear3DBlockPattern(patterns.toArray(new Pattern[patterns.size()])); -// } -// return other; -// } -// -// @Command( -// name = "#linear2d", -// aliases = {"#l2d"}, -// desc = "Use the x,z coordinate to pick a block from the list" -//) -// public Pattern linear2d(Actor actor, LocalSession session, Pattern other) { -// -// if (other instanceof RandomPattern) { -// Set patterns = ((RandomPattern) other).getPatterns(); -// return new Linear2DBlockPattern(patterns.toArray(new Pattern[patterns.size()])); -// } -// return other; -// } -// -// @Command( -// name = "=", -// aliases = {"#=", "#expression"}, -// desc = "Expression pattern: http://wiki.sk89q.com/wiki/WorldEdit/Expression_syntax" -//) -// public Pattern expression(Actor actor, LocalSession session, Extent extent, String input) throws ExpressionException { -// -// Expression exp = Expression.compile(input, "x", "y", "z"); -// WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(extent, Vector3.ONE, Vector3.ZERO); -// exp.setEnvironment(env); -// return new ExpressionPattern(exp); -// } -//} +@CommandContainer//(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class PatternCommands { + + @Command( + name = "#existing", + aliases = {"#*", "*", ".*"}, + desc = "Use the block that is already there" + ) + public Pattern existing(Extent extent, @Arg(name = "properties", desc = "String", def = "") String properties) { // TODO FIXME , @Arg(name = "properties", desc = "String", def = "") String properties + if (properties == null) return new ExistingPattern(extent); + return new PropertyPattern(extent).addRegex(".*[" + properties + "]"); + } + + @Command( + name = "#clipboard", + aliases = {"#copy"}, + desc = "Use the blocks in your clipboard as the pattern") + public Pattern clipboard(LocalSession session) throws EmptyClipboardException { + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + return new ClipboardPattern(clipboard); + } + + @Command( + name = "#simplex", + desc = "Use simplex noise to randomize blocks. Tutorial: https://imgur.com/a/rwVAE" +) + public Pattern simplex(@Arg(desc = "scale factor") double scale, @Arg(desc = "Pattern") Pattern other) { + if (other instanceof RandomPattern) { + scale = (1d / Math.max(1, scale)); + RandomCollection collection = ((RandomPattern) other).getCollection(); + collection.setRandom(new SimplexRandom(scale)); + } + return other; + } + + @Command( + name = "#color", + desc = "Use the block closest to a specific color" +) + public Pattern color(TextureUtil textureUtil, String color) { + Color colorObj = ColorUtil.parseColor(color); + return textureUtil.getNearestBlock(colorObj.getRGB()).getDefaultState(); + } + + @Command( + name = "#anglecolor", + desc = "A darker block based on the existing terrain angle" +) + public Pattern anglecolor(Extent extent, LocalSession session, @Arg(name = "distance", desc = "int", def = "1") int distanceOpt) { + return new AngleColorPattern(extent, session, distanceOpt); + } + + @Command( + name = "#angledata", + desc = "Block data based on the existing terrain angle" + ) + public Pattern angledata(Extent extent, @Arg(name = "distance", desc = "int", def = "1") int distanceOpt) { + return new DataAnglePattern(extent, distanceOpt); + } + + @Command( + name = "#saturate", + desc = "Saturate the existing block with a color" +) + public Pattern saturate(Extent extent, LocalSession session, @Arg(desc = "Color code") String colorStr) { + Color color = ColorUtil.parseColor(colorStr); + return new SaturatePattern(extent, color.getRGB(), session); + } + + @Command( + name = "#averagecolor", + desc = "Average between the existing block and a color" +) + public Pattern averagecolor(Extent extent, LocalSession session, @Arg(desc = "Color code") String colorStr) { + Color color = ColorUtil.parseColor(colorStr); + return new AverageColorPattern(extent, color.getRGB(), session); + } + + @Command( + name = "#desaturate", + desc = "Desaturated color of the existing block" +) + public Pattern desaturate(Extent extent, LocalSession session, @Arg(name = "percent", desc = "double", def = "100") double percent) { + return new DesaturatePattern(extent, percent / 100d, session); + } + + @Command( + name = "#lighten", + desc = "Lighten the existing block" +) + public Pattern lighten(Extent extent, TextureUtil util) { + return new ShadePattern(extent, false, util); + } + + @Command( + name = "#darken", + desc = "Darken the existing block" +) + public Pattern darken(Extent extent, TextureUtil util) { + return new ShadePattern(extent, true, util); + } + + @Command( + name = "#fullcopy", + desc = "Places your full clipboard at each block" +) + public Pattern fullcopy(Player player, Extent extent, LocalSession session, @Arg(name = "location", desc = "String", def = "#copy") String location, @Arg(name = "rotate", desc = "boolean", def = "false") boolean rotate, @Arg(name = "flip", desc = "boolean", def = "false") boolean flip) throws EmptyClipboardException, InputParseException, IOException { + List clipboards; + switch (location.toLowerCase()) { + case "#copy": + case "#clipboard": + ClipboardHolder clipboard = session.getExistingClipboard(); + if (clipboard == null) { + throw new InputParseException("To use #fullcopy, please first copy something to your clipboard"); + } + if (!rotate && !flip) { + return new FullClipboardPattern(extent, clipboard.getClipboard()); + } + clipboards = Collections.singletonList(clipboard); + break; + default: + MultiClipboardHolder multi = ClipboardFormats.loadAllFromInput(player, location, null, true); + clipboards = multi != null ? multi.getHolders() : null; + break; + } + if (clipboards == null) { + throw new InputParseException("#fullcopy:"); + } + return new RandomFullClipboardPattern(extent, clipboards, rotate, flip); + } + + @Command( + name = "#buffer", + desc = "Only place a block once while a pattern is in use", + descFooter = "Only place a block once while a pattern is in use\n" + + "Use with a brush when you don't want to apply to the same spot twice" +) + public Pattern buffer(Actor actor, @Arg(desc = "Pattern")Pattern pattern) { + return new BufferedPattern(actor, pattern); + } + + @Command( + name = "#buffer2d", + desc = "Only place a block once in a column while a pattern is in use" +) + public Pattern buffer2d(Actor actor, @Arg(desc = "Pattern")Pattern pattern) { + return new BufferedPattern2D(actor, pattern); + } + + @Command( + name = "#iddatamask", + desc = "Use the pattern's id and the existing blocks data with the provided mask", + descFooter = "Use the pattern's id and the existing blocks data with the provided mask\n" + + " - Use to replace slabs or where the data values needs to be shifted instead of set" +) + public Pattern iddatamask(Extent extent, @Range(min = 0, max = 15) @Arg(desc = "bit mask") int bitmask, @Arg(desc = "Pattern")Pattern pattern) { + + return new IdDataMaskPattern(extent, pattern, bitmask); + } + + @Command( + name = "#id", + desc = "Only change the block id" +) + public Pattern id(Extent extent, @Arg(desc = "Pattern")Pattern pattern) { + + return new IdPattern(extent, pattern); + } + + @Command( + name = "#data", + desc = "Only change the block data" +) + public Pattern data(Extent extent, @Arg(desc = "Pattern")Pattern pattern) { + + return new DataPattern(extent, pattern); + } + + @Command( + name = "#biome", + aliases = {"$"}, + desc = "Set the biome" +) + public Pattern biome(Extent extent, @Arg(desc = "Biome type") BiomeType biome) { + + return new BiomePattern(extent, biome); + } + + @Command( + name = "#relative", + aliases = {"#~", "#r", "#rel"}, + desc = "Offset the pattern to where you click" +) + public Pattern relative(@Arg(desc = "Pattern")Pattern pattern) { + + return new RelativePattern(pattern); + } + + @Command( + name = "#!x", + aliases = {"#nx", "#nox"}, + desc = "The pattern will not be provided the x axis info", + descFooter = "The pattern will not be provided the z axis info.\n" + + "Example: #!x[#!z[#~[#l3d[pattern]]]]" +) + public Pattern nox(@Arg(desc = "Pattern")Pattern pattern) { + + return new NoXPattern(pattern); + } + + @Command( + name = "#!y", + aliases = {"#ny", "#noy"}, + desc = "The pattern will not be provided the y axis info" +) + public Pattern noy(@Arg(desc = "Pattern")Pattern pattern) { + + return new NoYPattern(pattern); + } + + @Command( + name = "#!z", + aliases = {"#nz", "#noz"}, + desc = "The pattern will not be provided the z axis info" +) + public Pattern noz(@Arg(desc = "Pattern")Pattern pattern) { + + return new NoZPattern(pattern); + } + + @Command( + name = "#mask", + desc = "Apply a pattern depending on a mask" +) + public Pattern mask(@Arg(desc = "Mask") Mask mask, @Arg(desc = "Pattern")Pattern pass, @Arg(desc = "Pattern")Pattern fail) { + return new MaskedPattern(mask, pass, fail); + } + + @Command( + name = "#offset", + desc = "Offset a pattern" +) + public Pattern offset(@Arg(name = "x", desc = "x offset") double x, @Arg(name = "y", desc = "y offset") double y, @Arg(name = "z", desc = "z offset") double z, @Arg(desc = "Pattern")Pattern pattern) { + + return new OffsetPattern(pattern, (int) x, (int) y, (int) z); + } + + @Command( + name = "#surfacespread", + desc = "Applies to only blocks on a surface. Selects a block from provided pattern with a given ranomized offset `[0, )`. e.g. Use `#existing` to randomly offset blocks in the world, or `#copy` to offset blocks in your clipboard" +) + public Pattern surfacespread(@Arg(desc = "spread distance (blocks)") double distance, @Arg(desc = "Pattern")Pattern pattern) { + + return new SurfaceRandomOffsetPattern(pattern, (int) distance); + } + + @Command( + name = "#solidspread", + desc = "Randomly spread solid blocks" +) + public Pattern solidspread(@Arg(name = "x", desc = "x offset") double x, @Arg(name = "y", desc = "y offset") double y, @Arg(name = "z", desc = "z offset") double z, @Arg(desc = "Pattern")Pattern pattern) { + + return new SolidRandomOffsetPattern(pattern, (int) x, (int) y, (int) z); + } + + @Command( + name = "#spread", + aliases = {"#randomoffset"}, + desc = "Randomly spread blocks" +) + public Pattern spread(@Arg(name = "x", desc = "x offset") double x, @Arg(name = "y", desc = "y offset") double y, @Arg(name = "z", desc = "z offset") double z, @Arg(desc = "Pattern")Pattern pattern) { + + return new RandomOffsetPattern(pattern, (int) x, (int) y, (int) z); + } + + @Command( + name = "#linear", + aliases = {"#l"}, + desc = "Sequentially set blocks from a list of patterns" +) + public Pattern linear(@Arg(desc = "Pattern") Pattern other) { + + if (other instanceof RandomPattern) { + Set patterns = ((RandomPattern) other).getPatterns(); + return new LinearBlockPattern(patterns.toArray(new Pattern[patterns.size()])); + } + return other; + } + + @Command( + name = "#linear3d", + aliases = {"#l3d"}, + desc = "Use the x,y,z coordinate to pick a block from the list" +) + public Pattern linear3d(@Arg(desc = "Pattern") Pattern other) { + + if (other instanceof RandomPattern) { + Set patterns = ((RandomPattern) other).getPatterns(); + return new Linear3DBlockPattern(patterns.toArray(new Pattern[patterns.size()])); + } + return other; + } + + @Command( + name = "#linear2d", + aliases = {"#l2d"}, + desc = "Use the x,z coordinate to pick a block from the list" +) + public Pattern linear2d(@Arg(desc = "Pattern") Pattern other) { + + if (other instanceof RandomPattern) { + Set patterns = ((RandomPattern) other).getPatterns(); + return new Linear2DBlockPattern(patterns.toArray(new Pattern[patterns.size()])); + } + return other; + } + + @Command( + name = "=", + aliases = {"#=", "#expression"}, + desc = "Expression pattern: http://wiki.sk89q.com/wiki/WorldEdit/Expression_syntax" +) + public Pattern expression(Extent extent, @Arg(desc = "Expression") String input) throws ExpressionException { + + Expression exp = Expression.compile(input, "x", "y", "z"); + WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(extent, Vector3.ONE, Vector3.ZERO); + exp.setEnvironment(env); + return new ExpressionPattern(exp); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 39821f6c6..0fdba57a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -168,6 +168,7 @@ public class RegionCommands { @Command( name = "/nbtinfo", + aliases = "/nbt", desc = "View nbt info for a block" ) @CommandPermissions("worldedit.nbtinfo") @@ -266,7 +267,7 @@ public class RegionCommands { @Command( name = "/replace", - aliases = { "/re", "/rep" }, + aliases = { "/re", "/rep", "/r" }, desc = "Replace all blocks in the selection with another" ) @CommandPermissions("worldedit.region.replace") 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 80ef774a8..65fcea901 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 @@ -98,6 +98,7 @@ public class SelectionCommands { @Command( name = "/pos1", + aliases = "/1", desc = "Set position 1" ) @Logging(POSITION) @@ -126,6 +127,7 @@ public class SelectionCommands { @Command( name = "/pos2", + aliases = "/2", desc = "Set position 2" ) @Logging(POSITION) @@ -261,6 +263,7 @@ public class SelectionCommands { @CommandPermissions("worldedit.wand") public void wand(Player player, LocalSession session, @Switch(name = 'n', desc = "Get a navigation wand") boolean navWand) throws WorldEditException { + session.loadDefaults(player, true); String wandId = navWand ? session.getNavWandItem() : session.getWandItem(); if (wandId == null) { wandId = navWand ? we.getConfiguration().navigationWand : we.getConfiguration().wandItem; @@ -272,10 +275,10 @@ public class SelectionCommands { } player.giveItem(new BaseItemStack(itemType, 1)); if (navWand) { - session.setTool(itemType, new NavigationWand()); + session.setTool(itemType, NavigationWand.INSTANCE); player.print("Left click: jump to location; Right click: pass through walls"); } else { - session.setTool(itemType, new SelectionWand()); + session.setTool(itemType, SelectionWand.INSTANCE); player.print(BBC.SELECTION_WAND.s()); } if (!player.hasPermission("fawe.tips")) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java index e9a44d460..09125c666 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java @@ -20,19 +20,29 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.brush.BrushSettings; import com.boydti.fawe.object.brush.InspectBrush; -import com.google.common.collect.Collections2; +import com.boydti.fawe.object.brush.TargetMode; +import com.boydti.fawe.object.brush.scroll.ScrollAction; +import com.boydti.fawe.object.brush.visualization.VisualMode; +import com.boydti.fawe.object.extent.ResettableExtent; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.StringMan; +import com.google.common.collect.Iterables; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.tool.BlockDataCyler; import com.sk89q.worldedit.command.tool.BlockReplacer; +import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.DistanceWand; import com.sk89q.worldedit.command.tool.FloatingTreeRemover; import com.sk89q.worldedit.command.tool.FloodFillTool; -import com.sk89q.worldedit.command.tool.InvalidToolBindException; import com.sk89q.worldedit.command.tool.LongRangeBuildTool; import com.sk89q.worldedit.command.tool.NavigationWand; import com.sk89q.worldedit.command.tool.QueryTool; @@ -41,94 +51,25 @@ import com.sk89q.worldedit.command.tool.TreePlanter; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; -import com.sk89q.worldedit.internal.command.CommandUtil; -import com.sk89q.worldedit.command.ToolCommandsRegistration; +import com.sk89q.worldedit.internal.annotation.Range; +import com.sk89q.worldedit.internal.command.CommandArgParser; +import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.TreeGenerator; -import com.sk89q.worldedit.util.formatting.text.TextComponent; -import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.world.item.ItemType; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import org.enginehub.piston.CommandManager; -import org.enginehub.piston.CommandManagerService; -import org.enginehub.piston.CommandMetadata; -import org.enginehub.piston.CommandParameters; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; -import org.enginehub.piston.part.SubCommandPart; +import org.enginehub.piston.annotation.param.Switch; + +import java.util.List; @CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class ToolCommands { - - public static void register(CommandRegistrationHandler registration, - CommandManager commandManager, - CommandManagerService commandManagerService, - WorldEdit worldEdit) { - // Collect the tool commands - CommandManager collect = commandManagerService.newCommandManager(); - - registration.register( - collect, - ToolCommandsRegistration.builder(), - new ToolCommands(worldEdit) - ); - - // Register deprecated global commands - Set commands = collect.getAllCommands() - .collect(Collectors.toSet()); - for (org.enginehub.piston.Command command : commands) { - if (command.getAliases().contains("unbind")) { - // Don't register new /tool unbind alias - command = command.toBuilder().aliases( - Collections2.filter(command.getAliases(), alias -> !"unbind".equals(alias)) - ).build(); - } - commandManager.register(CommandUtil.deprecate( - command, "Global tool names cause conflicts " + - "and will be removed in WorldEdit 8", ToolCommands::asNonGlobal - )); - } - - // Remove aliases with / in them, since it doesn't make sense for sub-commands. - Set nonGlobalCommands = commands.stream() - .map(command -> - command.toBuilder().aliases( - Collections2.filter(command.getAliases(), alias -> !alias.startsWith("/")) - ).build() - ) - .collect(Collectors.toSet()); - commandManager.register("tool", command -> { - command.addPart(SubCommandPart.builder( - TranslatableComponent.of("tool"), - TextComponent.of("The tool to bind") - ) - .withCommands(nonGlobalCommands) - .required() - .build()); - command.description(TextComponent.of("Binds a tool to the item in your hand")); - }); - } - - private static String asNonGlobal(org.enginehub.piston.Command oldCommand, - CommandParameters oldParameters) { - String name = Optional.ofNullable(oldParameters.getMetadata()) - .map(CommandMetadata::getCalledName) - .filter(n -> !n.startsWith("/")) - .orElseGet(oldCommand::getName); - return "/tool " + name; - } - - static void setToolNone(Player player, LocalSession session, String type) - throws InvalidToolBindException { - session.setTool(player.getItemInHand(HandSide.MAIN_HAND).getType(), null); - player.print(type + " unbound from your current item."); - } - private final WorldEdit we; public ToolCommands(WorldEdit we) { @@ -154,7 +95,7 @@ public class ToolCommands { public void selwand(Player player, LocalSession session) throws WorldEditException { final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType(); - session.setTool(player, new SelectionWand()); + session.setTool(player, SelectionWand.INSTANCE); player.print("Selection wand bound to " + itemType.getName() + "."); } @@ -167,7 +108,7 @@ public class ToolCommands { public void navwand(Player player, LocalSession session) throws WorldEditException { BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); - session.setTool(player, new NavigationWand()); + session.setTool(player, NavigationWand.INSTANCE); player.print("Navigation wand bound to " + itemStack.getType().getName() + "."); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index fc3d8d8c7..f25a1244f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -88,7 +88,6 @@ public class ToolUtilCommands { } BrushSettings settings = offHand ? tool.getOffHand() : tool.getContext(); String lastArg = Iterables.getLast(CommandArgParser.spaceSplit(arguments.get())).getSubstring(); - System.out.println(lastArg + " TODO check this is not the whole command"); settings.addSetting(BrushSettings.SettingType.MASK, lastArg); settings.setMask(maskOpt); tool.update(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/TransformCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/TransformCommands.java index 63e6d3015..f93ebe210 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/TransformCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/TransformCommands.java @@ -1,104 +1,104 @@ -//package com.sk89q.worldedit.command; -// -//import com.boydti.fawe.object.extent.Linear3DTransform; -//import com.boydti.fawe.object.extent.LinearTransform; -//import com.boydti.fawe.object.extent.OffsetExtent; -//import com.boydti.fawe.object.extent.PatternTransform; -//import com.boydti.fawe.object.extent.RandomOffsetTransform; -//import com.boydti.fawe.object.extent.RandomTransform; -//import com.boydti.fawe.object.extent.ResettableExtent; -//import com.boydti.fawe.object.extent.ScaleTransform; -//import com.boydti.fawe.object.extent.TransformExtent; -//import com.boydti.fawe.util.ExtentTraverser; -//import com.sk89q.worldedit.LocalSession; -//import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; -//import com.sk89q.worldedit.entity.Player; -//import com.sk89q.worldedit.extension.platform.Actor; -//import com.sk89q.worldedit.extent.transform.BlockTransformExtent; -//import com.sk89q.worldedit.function.pattern.Pattern; -//import com.sk89q.worldedit.math.transform.AffineTransform; -//import java.util.Set; -//import org.enginehub.piston.annotation.Command; -//import org.enginehub.piston.annotation.CommandContainer; -//import org.enginehub.piston.annotation.param.Arg; -// -//@CommandContainer//(superTypes = CommandPermissionsConditionGenerator.Registration.class) -//public class TransformCommands { -// -// @Command( -// name = "#linear", -// aliases = {"#l"}, -// desc = "Sequentially pick from a list of transform" -// ) -// public ResettableExtent linear(Actor actor, LocalSession session, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { -// if (other instanceof RandomTransform) { -// Set extents = ((RandomTransform) other).getExtents(); -// return new LinearTransform(extents.toArray(new ResettableExtent[0])); -// } -// return other; -// } -// -// @Command( -// name = "#linear3d", -// aliases = {"#l3d"}, -// desc = "Use the x,y,z coordinate to pick a transform from the list" -// ) -// public ResettableExtent linear3d(Actor actor, LocalSession session, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { -// if (other instanceof RandomTransform) { -// Set extents = ((RandomTransform) other).getExtents(); -// return new Linear3DTransform(extents.toArray(new ResettableExtent[0])); -// } -// return other; -// } -// -// @Command( -// name = "#pattern", -// desc = "Always use a specific pattern" -// ) -// public ResettableExtent pattern(Actor actor, LocalSession session, Pattern pattern, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { -// return new PatternTransform(other, pattern); -// } -// -// @Command( -// name = "#offset", -// desc = "Offset transform" -// ) -// public ResettableExtent offset(Actor actor, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { -// return new OffsetExtent(other, (int) x, (int) y, (int) z); -// } -// -// @Command( -// name = "#spread", -// aliases = {"#randomoffset"}, -// desc = "Random offset transform" -//) -// public ResettableExtent randomOffset(Actor actor, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { -// return new RandomOffsetTransform(other, (int) x, (int) y, (int) z); -// } -// -// @Command( -// name = "#scale", -// desc = "All changes will be scaled" -// ) -// public ResettableExtent scale(Actor actor, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { -// return new ScaleTransform(other, x, y, z); -// } -// -// @Command( -// name = "#rotate", -// desc = "All changes will be rotate around the initial position" -// ) -// public ResettableExtent rotate(Player player, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { -// ExtentTraverser traverser = new ExtentTraverser<>(other).find(TransformExtent.class); -// BlockTransformExtent affine = traverser != null ? traverser.get() : null; -// if (affine == null) { -// other = affine = new TransformExtent(other); -// } -// AffineTransform transform = (AffineTransform) affine.getTransform(); -// transform = transform.rotateX(x); -// transform = transform.rotateY(y); -// transform = transform.rotateZ(z); -// affine.setTransform(transform); -// return other; -// } -//} +package com.sk89q.worldedit.command; + +import com.boydti.fawe.object.extent.Linear3DTransform; +import com.boydti.fawe.object.extent.LinearTransform; +import com.boydti.fawe.object.extent.OffsetExtent; +import com.boydti.fawe.object.extent.PatternTransform; +import com.boydti.fawe.object.extent.RandomOffsetTransform; +import com.boydti.fawe.object.extent.RandomTransform; +import com.boydti.fawe.object.extent.ResettableExtent; +import com.boydti.fawe.object.extent.ScaleTransform; +import com.boydti.fawe.object.extent.TransformExtent; +import com.boydti.fawe.util.ExtentTraverser; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.transform.BlockTransformExtent; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.transform.AffineTransform; +import java.util.Set; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; + +@CommandContainer//(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class TransformCommands { + + @Command( + name = "#linear", + aliases = {"#l"}, + desc = "Sequentially pick from a list of transform" + ) + public ResettableExtent linear(Actor actor, LocalSession session, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { + if (other instanceof RandomTransform) { + Set extents = ((RandomTransform) other).getExtents(); + return new LinearTransform(extents.toArray(new ResettableExtent[0])); + } + return other; + } + + @Command( + name = "#linear3d", + aliases = {"#l3d"}, + desc = "Use the x,y,z coordinate to pick a transform from the list" + ) + public ResettableExtent linear3d(Actor actor, LocalSession session, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { + if (other instanceof RandomTransform) { + Set extents = ((RandomTransform) other).getExtents(); + return new Linear3DTransform(extents.toArray(new ResettableExtent[0])); + } + return other; + } + + @Command( + name = "#pattern", + desc = "Always use a specific pattern" + ) + public ResettableExtent pattern(Actor actor, LocalSession session, Pattern pattern, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { + return new PatternTransform(other, pattern); + } + + @Command( + name = "#offset", + desc = "Offset transform" + ) + public ResettableExtent offset(Actor actor, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { + return new OffsetExtent(other, (int) x, (int) y, (int) z); + } + + @Command( + name = "#spread", + aliases = {"#randomoffset"}, + desc = "Random offset transform" +) + public ResettableExtent randomOffset(Actor actor, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { + return new RandomOffsetTransform(other, (int) x, (int) y, (int) z); + } + + @Command( + name = "#scale", + desc = "All changes will be scaled" + ) + public ResettableExtent scale(Actor actor, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { + return new ScaleTransform(other, x, y, z); + } + + @Command( + name = "#rotate", + desc = "All changes will be rotate around the initial position" + ) + public ResettableExtent rotate(Player player, LocalSession session, double x, double y, double z, @Arg(name = "other", desc = "ResettableExtent", def = "#null") ResettableExtent other) { + ExtentTraverser traverser = new ExtentTraverser<>(other).find(TransformExtent.class); + BlockTransformExtent affine = traverser != null ? traverser.get() : null; + if (affine == null) { + other = affine = new TransformExtent(other); + } + AffineTransform transform = (AffineTransform) affine.getTransform(); + transform = transform.rotateX(x); + transform = transform.rotateY(y); + transform = transform.rotateZ(z); + affine.setTransform(transform); + return other; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index af33fcc56..45c48d418 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -39,7 +39,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; -import com.sk89q.worldedit.command.util.CommandQueued; +import com.sk89q.worldedit.command.util.SkipQueue; import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.command.util.EntityRemover; import com.sk89q.worldedit.command.util.Logging; @@ -51,7 +51,6 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.function.EntityFunction; -import com.sk89q.worldedit.function.mask.BlockTypeMask; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operations; @@ -174,7 +173,7 @@ public class UtilityCommands { desc = "Cancel your current command" ) @CommandPermissions("fawe.cancel") - @CommandQueued(false) + @SkipQueue public void cancel(Player fp) { int cancelled = fp.cancel(false); BBC.WORLDEDIT_CANCEL_COUNT.send(fp, cancelled); @@ -523,7 +522,7 @@ public class UtilityCommands { int size = radius != null ? Math.max(1, radius) : defaultRadius; we.checkMaxRadius(size); - Mask mask = new BlockTypeMask(editSession, BlockTypes.FIRE); + Mask mask = BlockTypes.FIRE.toMask(); int affected = editSession.removeNear(session.getPlacementPosition(actor), mask, size); BBC.VISITOR_BLOCK.send(actor, affected); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index 2395b65bf..cd73e2b8b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -29,7 +29,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; -import com.sk89q.worldedit.command.util.CommandQueued; +import com.sk89q.worldedit.command.util.SkipQueue; import com.sk89q.worldedit.command.util.CommandQueuedConditionGenerator; import com.sk89q.worldedit.command.util.PrintCommandHelp; import com.sk89q.worldedit.entity.Player; @@ -73,7 +73,7 @@ public class WorldEditCommands { aliases = { "ver" }, desc = "Get WorldEdit/FAWE version" ) - @CommandQueued + @SkipQueue public void version(Actor actor) { FaweVersion fVer = Fawe.get().getVersion(); String fVerStr = fVer == null ? "unknown" : "-" + fVer.build; @@ -128,7 +128,7 @@ public class WorldEditCommands { aliases = { "debugpaste" }, desc = "Writes a report of latest.log, config.yml, message.yml https://athion.net/ISPaster/paste" ) - @CommandQueued + @SkipQueue @CommandPermissions({"worldedit.report", "worldedit.debugpaste"}) public void report(Actor actor) throws WorldEditException, IOException { BBC.DOWNLOAD_LINK.send(actor, IncendoPaster.debugPaste()); @@ -138,7 +138,7 @@ public class WorldEditCommands { name = "threads", desc = "Print all thread stacks" ) - @CommandQueued + @SkipQueue @CommandPermissions("worldedit.threads") public void threads(Actor actor) throws WorldEditException { Map stacks = Thread.getAllStackTraces(); @@ -188,7 +188,7 @@ public class WorldEditCommands { name = "help", desc = "Displays help for FAWE commands" ) - @CommandQueued + @SkipQueue @CommandPermissions("worldedit.help") public void help(Actor actor, @Switch(name = 's', desc = "List sub-commands of the given command, if applicable") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index fe1f9cca3..744a99965 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -91,6 +91,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool } protected static int MAX_RANGE = 500; + protected static int DEFAULT_RANGE = 240; // 500 is laggy as the default protected int range = -1; private VisualMode visualMode = VisualMode.NONE; private TargetMode targetMode = TargetMode.TARGET_BLOCK_RANGE; @@ -177,7 +178,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool if (targetMode != TargetMode.TARGET_BLOCK_RANGE) { map.put("target", targetMode); } - if (range != -1 && range != MAX_RANGE) { + if (range != -1 && range != DEFAULT_RANGE) { map.put("range", range); } if (targetOffset != 0) { @@ -408,7 +409,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool * @return the range of the brush in blocks */ public int getRange() { - return (range < 0) ? MAX_RANGE : Math.min(range, MAX_RANGE); + return (range < 0) ? DEFAULT_RANGE : Math.min(range, MAX_RANGE); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java index 8fdbac6f7..f862d10a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java @@ -74,8 +74,9 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { private Location getTarget(Player player) { Location target; Mask mask = getTraceMask(); - if (this.range > -1) { - target = player.getBlockTrace(getRange(), true, mask); + int range = getRange(); + if (range < MAX_RANGE) { + target = player.getBlockTrace(range, true, mask); } else { target = player.getBlockTrace(MAX_RANGE, false, mask); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/NavigationWand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/NavigationWand.java index ca4d12745..b1e734097 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/NavigationWand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/NavigationWand.java @@ -26,30 +26,26 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.util.Location; -public class NavigationWand implements DoubleActionTraceTool { - @Override - public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) { - if (!player.hasPermission("worldedit.navigation.jumpto.tool")) { - return false; - } - final int maxDist = config.navigationWandMaxDistance; - if (maxDist <= 0) { - return false; - } - Location pos = player.getSolidBlockTrace(maxDist); - if (pos != null) { - player.findFreePosition(pos); - } else { - player.printError("No block in sight (or too far)!"); - } - return true; - } +public enum NavigationWand implements DoubleActionTraceTool { + INSTANCE; + + @Override + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) { + final int maxDist = config.navigationWandMaxDistance; + if (maxDist <= 0) { + return false; + } + Location pos = player.getSolidBlockTrace(maxDist); + if (pos != null) { + player.findFreePosition(pos); + } else { + player.printError("No block in sight (or too far)!"); + } + return true; + } @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { - if (!player.hasPermission("worldedit.navigation.thru.tool")) { - return false; - } final int maxDist = config.navigationWandMaxDistance; if (maxDist <= 0) { return false; @@ -63,6 +59,6 @@ public class NavigationWand implements DoubleActionTraceTool { @Override public boolean canUse(Actor actor) { - return true; // skip check here - checked separately for primary/secondary + return actor.hasPermission("worldedit.navigation.jumpto.tool"); // check should be here } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SelectionWand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SelectionWand.java index fcc83209c..7cac075ec 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SelectionWand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SelectionWand.java @@ -29,7 +29,8 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.util.Location; -public class SelectionWand implements DoubleActionBlockTool { +public enum SelectionWand implements DoubleActionBlockTool { + INSTANCE; @Override public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java index 03b50a7e9..fbd406865 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java @@ -34,4 +34,4 @@ public class SphereBrush implements Brush { } editSession.makeSphere(position, pattern, size, size, size, true); } -} +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedCondition.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedCondition.java index c8f3492b2..7ced6f48b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedCondition.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedCondition.java @@ -7,14 +7,14 @@ import org.enginehub.piston.inject.InjectedValueAccess; * Dummy class */ public class CommandQueuedCondition implements Command.Condition { - private final boolean value; + private final boolean isQueued; - public CommandQueuedCondition(boolean value) { - this.value = value; + public CommandQueuedCondition(boolean isQueued) { + this.isQueued = isQueued; } public boolean isQueued() { - return value; + return isQueued; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedConditionGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedConditionGenerator.java index 686f275e3..1708c45db 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedConditionGenerator.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueuedConditionGenerator.java @@ -17,10 +17,10 @@ public final class CommandQueuedConditionGenerator implements CommandConditionGe @Override public Command.Condition generateCondition(Method commandMethod) { - CommandQueued annotation = commandMethod.getAnnotation(CommandQueued.class); - if (annotation == null) { + SkipQueue skip = commandMethod.getAnnotation(SkipQueue.class); + if (skip == null) { return Command.Condition.TRUE; } - return new CommandQueuedCondition(annotation.value()); + return new CommandQueuedCondition(!skip.value()); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/DelegateCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/DelegateCommandManager.java index 8b284e8d9..6985eba1d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/DelegateCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/DelegateCommandManager.java @@ -61,7 +61,7 @@ public class DelegateCommandManager implements CommandManager { } @Override - public int execute(InjectedValueAccess context, List args) { + public Object execute(InjectedValueAccess context, List args) { return parent.execute(context, args); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/HookMode.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/HookMode.java index 113a45613..993942615 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/HookMode.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/HookMode.java @@ -21,4 +21,4 @@ package com.sk89q.worldedit.command.util; public enum HookMode { ACTIVE, INACTIVE -} +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PermissionCondition.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PermissionCondition.java index 99a4622d6..64856a89e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PermissionCondition.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PermissionCondition.java @@ -24,6 +24,8 @@ import org.enginehub.piston.Command; import org.enginehub.piston.inject.InjectedValueAccess; import org.enginehub.piston.inject.Key; +import java.lang.annotation.Annotation; +import java.util.Optional; import java.util.Set; public class PermissionCondition implements Command.Condition { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueued.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SkipQueue.java similarity index 82% rename from worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueued.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SkipQueue.java index 9e01e6afc..c67f82248 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandQueued.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SkipQueue.java @@ -10,6 +10,6 @@ import java.lang.annotation.RetentionPolicy; */ @Retention(RetentionPolicy.RUNTIME) @CommandCondition(CommandQueuedConditionGenerator.class) -public @interface CommandQueued { - boolean value() default false; +public @interface SkipQueue { + boolean value() default true; } 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 c5e4b4dd3..2151bc33f 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 @@ -24,6 +24,8 @@ 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.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NbtValued; import com.sk89q.worldedit.world.entity.EntityType; @@ -85,14 +87,7 @@ public class BaseEntity implements NbtValued { } 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); + return nbtData.getEntityLocation(extent); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/MapMetadatable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/MapMetadatable.java new file mode 100644 index 000000000..aacf9b49c --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/MapMetadatable.java @@ -0,0 +1,60 @@ +package com.sk89q.worldedit.entity; + +import java.util.Map; + +public interface MapMetadatable extends Metadatable { + + Map getRawMeta(); + /** + * Set some session only metadata for the player + * + * @param key + * @param value + */ + default void setMeta(String key, Object value) { + getRawMeta().put(key, value); + } + + default T getAndSetMeta(String key, T value) { + return (T) getRawMeta().put(key, value); + } + + default boolean hasMeta() { + return !getRawMeta().isEmpty(); + } + + /** + * Get the metadata for a key. + * + * @param + * @param key + * @return + */ + default V getMeta(String key) { + return (V) getRawMeta().get(key); + } + + /** + * Get the metadata for a specific key (or return the default provided) + * + * @param key + * @param def + * @param + * @return + */ + default V getMeta(String key, V def) { + V value = (V) getRawMeta().get(key); + return value == null ? def : value; + } + + /** + * Delete the metadata for a key. + * - metadata is session only + * - deleting other plugin's metadata may cause issues + * + * @param key + */ + default V deleteMeta(String key) { + return (V) getRawMeta().remove(key); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java index ec9206f81..d7bbdef2c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.entity; import com.boydti.fawe.Fawe; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.brush.visualization.VirtualWorld; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; @@ -38,6 +39,7 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Direction; @@ -48,7 +50,10 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.gamemode.GameMode; import java.io.File; +import java.text.NumberFormat; import javax.annotation.Nullable; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.jetbrains.annotations.NotNull; /** * Represents a player @@ -367,14 +372,6 @@ public interface Player extends Entity, Actor { return WorldEdit.getInstance().getPlatformManager().getWorldForEditing(getWorld()); } - default boolean runAsyncIfFree(Runnable r) { - return runAction(r, true, true); - } - - default boolean runIfFree(Runnable r) { - return runAction(r, true, false); - } - /** * Unregister this player (deletes all metadata etc) - Usually called on logout */ @@ -385,7 +382,6 @@ public interface Player extends Entity, Actor { getSession().clearHistory(); getSession().unregisterTools(this); } - Fawe.get().unregister(getName()); } default int cancel(boolean close) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java index 54ef4986f..3124196f7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java @@ -15,7 +15,6 @@ import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.internal.command.ActorAuthorizer; import com.sk89q.worldedit.internal.expression.Expression; import java.util.ArrayList; import java.util.List; @@ -24,7 +23,7 @@ import java.util.Map; public class DefaultTransformParser extends FaweParser { public DefaultTransformParser(WorldEdit worldEdit) { - super(worldEdit, ResettableExtent.class); + super(worldEdit); } @Override @@ -54,7 +53,7 @@ public class DefaultTransformParser extends FaweParser { List args = entry.getValue(); String cmdArgs = ((args.isEmpty()) ? "" : " " + StringMan.join(args, " ")); try { - transform = Iterables.getFirst(parse(cmdArgs, actor), null); + transform = parse(cmdArgs, actor); } catch (SuggestInputParseException rethrow) { throw rethrow; } catch (Throwable e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index 1519405c3..c944ba359 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -19,17 +19,23 @@ package com.sk89q.worldedit.extension.factory.parser; +import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.config.BBC; -import com.google.common.collect.Maps; +import com.boydti.fawe.jnbt.JSON2NBT; +import com.boydti.fawe.jnbt.NBTException; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.StringMan; + import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.NotABlockException; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.blocks.SignBlock; import com.sk89q.worldedit.blocks.SkullBlock; -import com.sk89q.worldedit.command.util.SuggestionHelper; +import com.sk89q.worldedit.blocks.metadata.MobType; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.DisallowedUsageException; import com.sk89q.worldedit.extension.input.InputParseException; @@ -37,6 +43,8 @@ import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.extent.inventory.SlottableBlockBag; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; @@ -49,12 +57,11 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.FuzzyBlockState; -import com.sk89q.worldedit.world.entity.EntityType; -import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; -import java.util.HashMap; + +import java.util.Arrays; import java.util.Locale; -import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -104,8 +111,6 @@ public class DefaultBlockParser extends InputParser { } } - private static String[] EMPTY_STRING_ARRAY = {}; - /** * Backwards compatibility for wool colours in block syntax. * @@ -157,75 +162,8 @@ public class DefaultBlockParser extends InputParser { } } - private static Map, Object> parseProperties(BlockType type, String[] stateProperties, ParserContext context) throws NoMatchException { - Map, Object> blockStates = new HashMap<>(); - - if (stateProperties.length > 0) { // Block data not yet detected - // Parse the block data (optional) - for (String parseableData : stateProperties) { - try { - String[] parts = parseableData.split("="); - if (parts.length != 2) { - throw new NoMatchException("Bad state format in " + parseableData); - } - - @SuppressWarnings("unchecked") - Property propertyKey = (Property) type.getPropertyMap().get(parts[0]); - if (propertyKey == null) { - if (context.getActor() != null) { - throw new NoMatchException("Unknown property " + parts[0] + " for block " + type.getId()); - } else { - WorldEdit.logger.warn("Unknown property " + parts[0] + " for block " + type.getId()); - } - return Maps.newHashMap(); - } - if (blockStates.containsKey(propertyKey)) { - throw new NoMatchException("Duplicate property " + parts[0]); - } - Object value; - try { - value = propertyKey.getValueFor(parts[1]); - } catch (IllegalArgumentException e) { - throw new NoMatchException("Unknown value " + parts[1] + " for state " + parts[0]); - } - - blockStates.put(propertyKey, value); - } catch (NoMatchException e) { - throw e; // Pass-through - } catch (Exception e) { - WorldEdit.logger.warn("Unknown state '" + parseableData + "'", e); - throw new NoMatchException("Unknown state '" + parseableData + "'"); - } - } - } - - return blockStates; - } - - @Override - public Stream getSuggestions(String input) { - final int idx = input.lastIndexOf('['); - if (idx < 0) { - return SuggestionHelper.getNamespacedRegistrySuggestions(BlockType.REGISTRY, input); - } - String blockType = input.substring(0, idx); - BlockType type = BlockTypes.get(blockType.toLowerCase(Locale.ROOT)); - if (type == null) { - return Stream.empty(); - } - - String props = input.substring(idx + 1); - if (props.isEmpty()) { - return type.getProperties().stream().map(p -> input + p.getName() + "="); - } - - return SuggestionHelper.getBlockPropertySuggestions(blockType, props); - } - private BaseBlock parseLogic(String input, ParserContext context) throws InputParseException { - BlockType blockType = null; - Map, Object> blockStates = new HashMap<>(); - String[] blockAndExtraData = input.trim().split("\\|"); + String[] blockAndExtraData = input.trim().split("\\|", 2); blockAndExtraData[0] = woolMapper(blockAndExtraData[0]); BlockState state = null; @@ -238,16 +176,28 @@ public class DefaultBlockParser extends InputParser { throw new InputParseException("Invalid colon."); } else if (split.length == 1) { state = LegacyMapper.getInstance().getBlockFromLegacy(Integer.parseInt(split[0])); + } else if (MathMan.isInteger(split[0])) { + int id = Integer.parseInt(split[0]); + int data = Integer.parseInt(split[1]); + if (data < 0 || data >= 16) { + throw new InputParseException("Invalid data " + data); + } + state = LegacyMapper.getInstance().getBlockFromLegacy(id, data); } else { - state = LegacyMapper.getInstance().getBlockFromLegacy(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + BlockType type = BlockTypes.get(split[0].toLowerCase(Locale.ROOT)); + if (type != null) { + int data = Integer.parseInt(split[1]); + if (data < 0 || data >= 16) { + throw new InputParseException("Invalid data " + data); + } + state = LegacyMapper.getInstance().getBlockFromLegacy(type.getLegacyCombinedId() >> 4, data); + } } - if (state != null) { - blockType = state.getBlockType(); - } - } catch (NumberFormatException ignored) { + } catch (NumberFormatException e) { } } + CompoundTag nbt = null; if (state == null) { String typeString; String stateString = null; @@ -256,101 +206,102 @@ public class DefaultBlockParser extends InputParser { typeString = blockAndExtraData[0]; } else { typeString = blockAndExtraData[0].substring(0, stateStart); - if (stateStart + 1 >= blockAndExtraData[0].length()) { - throw new InputParseException("Invalid format. Hanging bracket @ " + stateStart + "."); - } - int stateEnd = blockAndExtraData[0].lastIndexOf(']'); - if (stateEnd < 0) { - throw new InputParseException("Invalid format. Unclosed property."); - } stateString = blockAndExtraData[0].substring(stateStart + 1, blockAndExtraData[0].length() - 1); } if (typeString.isEmpty()) { throw new InputParseException("Invalid format"); } - String[] stateProperties = EMPTY_STRING_ARRAY; - if (stateString != null) { - stateProperties = stateString.split(","); - } - - if ("hand".equalsIgnoreCase(typeString)) { - // Get the block type from the item in the user's hand. - final BaseBlock blockInHand = getBlockInHand(context.requireActor(), HandSide.MAIN_HAND); - if (blockInHand.getClass() != BaseBlock.class) { - return blockInHand; - } - - blockType = blockInHand.getBlockType(); - blockStates.putAll(blockInHand.getStates()); - } else if ("offhand".equalsIgnoreCase(typeString)) { - // Get the block type from the item in the user's off hand. - final BaseBlock blockInHand = getBlockInHand(context.requireActor(), HandSide.OFF_HAND); - if (blockInHand.getClass() != BaseBlock.class) { - return blockInHand; - } - - blockType = blockInHand.getBlockType(); - blockStates.putAll(blockInHand.getStates()); - } else if ("pos1".equalsIgnoreCase(typeString)) { + // PosX + if (typeString.matches("pos[0-9]+")) { + int index = Integer.parseInt(typeString.replaceAll("[a-z]+", "")); // Get the block type from the "primary position" final World world = context.requireWorld(); final BlockVector3 primaryPosition; try { - primaryPosition = context.requireSession().getRegionSelector(world).getPrimaryPosition(); + primaryPosition = context.requireSession().getRegionSelector(world).getVertices().get(index - 1); } catch (IncompleteRegionException e) { throw new InputParseException("Your selection is not complete."); } - final BlockState blockInHand = world.getBlock(primaryPosition); - - blockType = blockInHand.getBlockType(); - blockStates.putAll(blockInHand.getStates()); + state = world.getBlock(primaryPosition); } else { - // Attempt to lookup a block from ID or name. - blockType = BlockTypes.get(typeString.toLowerCase(Locale.ROOT)); - } + if ("hand".equalsIgnoreCase(typeString)) { + // Get the block type from the item in the user's hand. + BaseBlock block = getBlockInHand(context.requireActor(), HandSide.MAIN_HAND); + state = block.toBlockState(); + nbt = block.getNbtData(); + } else if ("offhand".equalsIgnoreCase(typeString)) { + // Get the block type from the item in the user's off hand. + BaseBlock block = getBlockInHand(context.requireActor(), HandSide.OFF_HAND); + state = block.toBlockState(); + nbt = block.getNbtData(); + } else if (typeString.matches("slot[0-9]+")) { + int slot = Integer.parseInt(typeString.substring(4)) - 1; + Actor actor = context.requireActor(); + if (!(actor instanceof Player)) { + throw new InputParseException("The user is not a player!"); + } + Player player = (Player) actor; + BlockBag bag = player.getInventoryBlockBag(); + if (bag == null || !(bag instanceof SlottableBlockBag)) { + throw new InputParseException("Unsupported!"); + } + SlottableBlockBag slottable = (SlottableBlockBag) bag; + BaseItem item = slottable.getItem(slot); - if (blockType == null) { - throw new NoMatchException("Does not match a valid block type: '" + input + "'"); - } + if (!item.getType().hasBlockType()) { + throw new InputParseException("You're not holding a block!"); + } + state = item.getType().getBlockType().getDefaultState(); + nbt = item.getNbtData(); + } else { + BlockType type = BlockTypes.parse(typeString.toLowerCase(Locale.ROOT)); - blockStates.putAll(parseProperties(blockType, stateProperties, context)); - - if (context.isPreferringWildcard()) { - FuzzyBlockState.Builder fuzzyBuilder = FuzzyBlockState.builder(); - fuzzyBuilder.type(blockType); - for (Map.Entry, Object> blockState : blockStates.entrySet()) { - @SuppressWarnings("unchecked") - Property objProp = (Property) blockState.getKey(); - fuzzyBuilder.withProperty(objProp, blockState.getValue()); + if (type != null) { + state = type.getDefaultState(); + } + if (state == null) { + throw new NoMatchException("Does not match a valid block type: '" + input + "'"); + } } - state = fuzzyBuilder.build(); - } else { - // No wildcards allowed => eliminate them. (Start with default state) - state = blockType.getDefaultState(); - for (Map.Entry, Object> blockState : blockStates.entrySet()) { - @SuppressWarnings("unchecked") - Property objProp = (Property) blockState.getKey(); - state = state.with(objProp, blockState.getValue()); + } + if (nbt == null) nbt = state.getNbtData(); + + if (stateString != null) { + state = BlockState.get(state.getBlockType(), "[" + stateString + "]", state); + if (context.isPreferringWildcard()) { + if (stateString.isEmpty()) { + state = new FuzzyBlockState(state); + } else { + BlockType type = state.getBlockType(); + FuzzyBlockState.Builder fuzzyBuilder = FuzzyBlockState.builder(); + fuzzyBuilder.type(type); + String[] entries = stateString.split(","); + for (String entry : entries) { + String[] split = entry.split("="); + String key = split[0]; + String val = split[1]; + Property prop = type.getProperty(key); + fuzzyBuilder.withProperty(prop, prop.getValueFor(val)); + } + state = fuzzyBuilder.build(); + } } } } - // this should be impossible but IntelliJ isn't that smart - if (blockType == null) { - throw new NoMatchException("Does not match a valid block type: '" + input + "'"); + + if (blockAndExtraData.length > 1 && blockAndExtraData[1].startsWith("{")) { + String joined = StringMan.join(Arrays.copyOfRange(blockAndExtraData, 1, blockAndExtraData.length), "|"); + try { + nbt = JSON2NBT.getTagFromJson(joined); + } catch (NBTException e) { + throw new NoMatchException(e.getMessage()); + } } // Check if the item is allowed - if (context.isRestricted()) { - Actor actor = context.requireActor(); - if (actor != null && !actor.hasPermission("worldedit.anyblock") - && worldEdit.getConfiguration().disallowedBlocks.contains(blockType.getId())) { - throw new DisallowedUsageException("You are not allowed to use '" + input + "'"); - } - } + BlockType blockType = state.getBlockType(); - if (!context.isTryingLegacy()) { - return state.toBaseBlock(); - } + if (nbt != null) return validate(context, state.toBaseBlock(nbt)); if (blockType == BlockTypes.SIGN || blockType == BlockTypes.WALL_SIGN || BlockCategories.SIGNS.contains(blockType)) { @@ -365,17 +316,22 @@ public class DefaultBlockParser extends InputParser { // Allow setting mob spawn type if (blockAndExtraData.length > 1) { String mobName = blockAndExtraData[1]; - EntityType ent = EntityTypes.get(mobName.toLowerCase(Locale.ROOT)); - if (ent == null) { - throw new NoMatchException("Unknown entity type '" + mobName + "'"); + for (MobType mobType : MobType.values()) { + if (mobType.getName().toLowerCase().equals(mobName.toLowerCase(Locale.ROOT))) { + mobName = mobType.getName(); + break; + } } - mobName = ent.getId(); if (!worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS).isValidMobType(mobName)) { - throw new NoMatchException("Unknown mob type '" + mobName + "'"); + String finalMobName = mobName.toLowerCase(Locale.ROOT); + throw new SuggestInputParseException("Unknown mob type '" + mobName + "'", mobName, () -> Stream.of(MobType.values()) + .map(m -> m.getName().toLowerCase(Locale.ROOT)) + .filter(s -> s.startsWith(finalMobName)) + .collect(Collectors.toList())); } return validate(context, new MobSpawnerBlock(state, mobName)); } else { - return validate(context, new MobSpawnerBlock(state, EntityTypes.PIG.getId())); + return validate(context, new MobSpawnerBlock(state, MobType.PIG.getName())); } } else if (blockType == BlockTypes.PLAYER_HEAD || blockType == BlockTypes.PLAYER_WALL_HEAD) { // allow setting type/player/rotation @@ -406,4 +362,4 @@ public class DefaultBlockParser extends InputParser { } return holder; } -} +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/DefaultMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/DefaultMaskParser.java index dd005f6b5..2c4975001 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/DefaultMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/DefaultMaskParser.java @@ -49,7 +49,7 @@ import java.util.stream.Stream; public class DefaultMaskParser extends FaweParser { public DefaultMaskParser(WorldEdit worldEdit) { - super(worldEdit, Mask.class); + super(worldEdit); } @Override @@ -80,7 +80,7 @@ public class DefaultMaskParser extends FaweParser { List args = entry.getValue(); String cmdArgs = ((args.isEmpty()) ? "" : " " + StringMan.join(args, " ")); try { - mask = Iterables.getFirst(parse(cmdArgs, actor), null); + mask = parse(cmdArgs, actor); } catch (SuggestInputParseException rethrow) { throw rethrow; } catch (Throwable e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java index 7438e2759..0727a1f51 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java @@ -61,8 +61,8 @@ public class ExpressionMaskParser extends InputParser { exp.setEnvironment(env); if (context.getActor() != null) { SessionOwner owner = context.getActor(); - IntSupplier timeout = () -> WorldEdit.getInstance().getSessionManager().get(owner).getTimeout(); - return new ExpressionMask(exp, timeout); + Integer timeout = WorldEdit.getInstance().getSessionManager().get(owner).getTimeout(); + return new ExpressionMask(exp, timeout::intValue); } return new ExpressionMask(exp); } catch (ExpressionException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/DefaultPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/DefaultPatternParser.java index 41359b87d..ae0bdde3b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/DefaultPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/DefaultPatternParser.java @@ -21,27 +21,22 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.boydti.fawe.command.FaweParser; import com.boydti.fawe.command.SuggestInputParseException; -import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.random.TrueRandom; import com.boydti.fawe.util.StringMan; import com.google.common.collect.Iterables; -import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.worldedit.WorldEdit; -//import com.sk89q.worldedit.command.PatternCommands; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.RandomPattern; -import com.sk89q.worldedit.internal.command.ActorAuthorizer; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.world.block.BlockTypes; + import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -49,7 +44,7 @@ import java.util.stream.Stream; public class DefaultPatternParser extends FaweParser { public DefaultPatternParser(WorldEdit worldEdit) { - super(worldEdit, Pattern.class); + super(worldEdit); } @Override @@ -77,7 +72,7 @@ public class DefaultPatternParser extends FaweParser { List args = entry.getValue(); String cmdArgs = ((args.isEmpty()) ? "" : " " + StringMan.join(args, " ")); try { - pattern = Iterables.getFirst(parse(cmdArgs, actor), null); + pattern = parse(cmdArgs, actor); } catch (SuggestInputParseException rethrow) { throw rethrow; } catch (Throwable e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java index 19820255d..046417574 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java @@ -26,6 +26,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.internal.cui.CUIEvent; import java.io.File; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -33,6 +34,11 @@ public abstract class AbstractNonPlayerActor implements Actor { private final ConcurrentHashMap meta = new ConcurrentHashMap<>(); + @Override + public Map getRawMeta() { + return meta; + } + // Queue for async tasks private AtomicInteger runningCount = new AtomicInteger(); private SimpleAsyncNotifyQueue asyncNotifyQueue = new SimpleAsyncNotifyQueue( @@ -106,51 +112,4 @@ public abstract class AbstractNonPlayerActor implements Actor { } return true; } - - /** - * Set some session only metadata for the player - * - * @param key - * @param value - * @return previous value - */ - @Override - public final void setMeta(String key, Object value) { - this.meta.put(key, value); - } - - @Override - public final T getAndSetMeta(String key, T value) { - return (T) this.meta.put(key, value); - } - - @Override - public final boolean hasMeta() { - return !meta.isEmpty(); - } - - /** - * Get the metadata for a key. - * - * @param - * @param key - * @return - */ - @Override - public final V getMeta(String key) { - return (V) this.meta.get(key); - } - - /** - * Delete the metadata for a key. - * - metadata is session only - * - deleting other plugin's metadata may cause issues - * - * @param key - */ - @Override - public final V deleteMeta(String key) { - return (V) this.meta.remove(key); - } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index 3bf4cc037..8bbd89a97 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extension.platform; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.task.SimpleAsyncNotifyQueue; import com.boydti.fawe.regions.FaweMaskManager; @@ -29,6 +30,7 @@ import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.cui.CUIEvent; @@ -39,6 +41,7 @@ import com.sk89q.worldedit.regions.ConvexPolyhedralRegion; import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.ConvexPolyhedralRegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; @@ -62,9 +65,13 @@ import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.registry.BlockMaterial; import java.io.File; +import java.text.NumberFormat; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.jetbrains.annotations.NotNull; /** * An abstract implementation of both a {@link Actor} and a {@link Player} @@ -75,6 +82,11 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { private final ConcurrentHashMap meta = new ConcurrentHashMap<>(); + @Override + public Map getRawMeta() { + return meta; + } + // Queue for async tasks private AtomicInteger runningCount = new AtomicInteger(); private SimpleAsyncNotifyQueue asyncNotifyQueue = new SimpleAsyncNotifyQueue( @@ -638,48 +650,6 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { } - /** - * Set some session only metadata for the player - * - * @param key - * @param value - * @return previous value - */ - public final void setMeta(String key, Object value) { - this.meta.put(key, value); - } - - public final T getAndSetMeta(String key, T value) { - return (T) this.meta.put(key, value); - } - - public final boolean hasMeta() { - return !meta.isEmpty(); - } - - /** - * Get the metadata for a key. - * - * @param - * @param key - * @return - */ - public final V getMeta(String key) { - return (V) this.meta.get(key); - } - - - /** - * Delete the metadata for a key. - * - metadata is session only - * - deleting other plugin's metadata may cause issues - * - * @param key - */ - public final V deleteMeta(String key) { - return (V) this.meta.remove(key); - } - /** * Run a task either async, or on the current thread * @@ -710,6 +680,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { return true; } + /** * Get the player's current allowed WorldEdit regions * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java index f1d5dacf4..a8b6c9a50 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java @@ -23,7 +23,7 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLimit; -import com.sk89q.worldedit.entity.Metadatable; +import com.sk89q.worldedit.entity.MapMetadatable; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.math.BlockVector3; @@ -33,16 +33,16 @@ import com.sk89q.worldedit.session.SessionOwner; import com.sk89q.worldedit.util.Identifiable; import com.sk89q.worldedit.util.auth.Subject; import com.sk89q.worldedit.util.formatting.text.Component; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.text.NumberFormat; -import org.enginehub.piston.inject.InjectedValueAccess; -import org.jetbrains.annotations.NotNull; /** * An object that can perform actions in WorldEdit. */ -public interface Actor extends Identifiable, SessionOwner, Subject, Metadatable { +public interface Actor extends Identifiable, SessionOwner, Subject, MapMetadatable { /** * Get the name of the actor. @@ -135,20 +135,20 @@ public interface Actor extends Identifiable, SessionOwner, Subject, Metadatable boolean runAction(Runnable ifFree, boolean checkFree, boolean async); default void checkConfirmationStack(@NotNull Runnable task, @NotNull String command, - Region region, int times, InjectedValueAccess context) throws RegionOperationException { + Region region, int times, InjectedValueAccess context) throws RegionOperationException { if (!getMeta("cmdConfirmRunning", false)) { if (region != null) { BlockVector3 min = region.getMinimumPoint(); BlockVector3 max = region.getMaximumPoint(); long area = - (long) ((max.getX() - min.getX()) * (max.getZ() - min.getZ() + 1)) * times; + (long) ((max.getX() - min.getX()) * (max.getZ() - min.getZ() + 1)) * times; if (area > 2 << 18) { setConfirmTask(task, context, command); BlockVector3 base = max.subtract(min).add(BlockVector3.ONE); long volume = (long) base.getX() * base.getZ() * base.getY() * times; throw new RegionOperationException(BBC.WORLDEDIT_CANCEL_REASON_CONFIRM - .format(min, max, command, - NumberFormat.getNumberInstance().format(volume))); + .format(min, max, command, + NumberFormat.getNumberInstance().format(volume))); } } } @@ -156,7 +156,7 @@ public interface Actor extends Identifiable, SessionOwner, Subject, Metadatable } default void checkConfirmationRegion(@NotNull Runnable task, @NotNull String command, - Region region, InjectedValueAccess context) throws RegionOperationException { + Region region, InjectedValueAccess context) throws RegionOperationException { if (!getMeta("cmdConfirmRunning", false)) { if (region != null) { BlockVector3 min = region.getMinimumPoint(); @@ -167,8 +167,8 @@ public interface Actor extends Identifiable, SessionOwner, Subject, Metadatable BlockVector3 base = max.subtract(min).add(BlockVector3.ONE); long volume = (long) base.getX() * base.getZ() * base.getY(); throw new RegionOperationException(BBC.WORLDEDIT_CANCEL_REASON_CONFIRM - .format(min, max, command, - NumberFormat.getNumberInstance().format(volume))); + .format(min, max, command, + NumberFormat.getNumberInstance().format(volume))); } } } @@ -176,7 +176,7 @@ public interface Actor extends Identifiable, SessionOwner, Subject, Metadatable } default void setConfirmTask(@NotNull Runnable task, InjectedValueAccess context, - @NotNull String command) { + @NotNull String command) { CommandEvent event = new CommandEvent(this, command); Runnable newTask = () -> PlatformCommandManager.getInstance().handleCommandTask(() -> { task.run(); @@ -186,28 +186,28 @@ public interface Actor extends Identifiable, SessionOwner, Subject, Metadatable } default void checkConfirmation(@NotNull Runnable task, @NotNull String command, int times, - int limit, InjectedValueAccess context) throws RegionOperationException { + int limit, InjectedValueAccess context) throws RegionOperationException { if (!getMeta("cmdConfirmRunning", false)) { if (times > limit) { setConfirmTask(task, context, command); String volume = ""; throw new RegionOperationException( - BBC.WORLDEDIT_CANCEL_REASON_CONFIRM.format(0, times, command, volume)); + BBC.WORLDEDIT_CANCEL_REASON_CONFIRM.format(0, times, command, volume)); } } task.run(); } default void checkConfirmationRadius(@NotNull Runnable task, String command, int radius, - InjectedValueAccess context) throws RegionOperationException { + InjectedValueAccess context) throws RegionOperationException { if (command != null && !getMeta("cmdConfirmRunning", false)) { if (radius > 0) { if (radius > 448) { setConfirmTask(task, context, command); long volume = (long) (Math.PI * ((double) radius * radius)); throw new RegionOperationException(BBC.WORLDEDIT_CANCEL_REASON_CONFIRM - .format(0, radius, command, - NumberFormat.getNumberInstance().format(volume))); + .format(0, radius, command, + NumberFormat.getNumberInstance().format(volume))); } } } @@ -249,4 +249,12 @@ public interface Actor extends Identifiable, SessionOwner, Subject, Metadatable default FaweLimit getLimit() { return Settings.IMP.getLimit(this); } + + default boolean runAsyncIfFree(Runnable r) { + return runAction(r, true, true); + } + + default boolean runIfFree(Runnable r) { + return runAction(r, true, false); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 6a0fb6ecf..6e4497b0f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -19,8 +19,6 @@ package com.sk89q.worldedit.extension.platform; -import static com.google.common.base.Preconditions.checkNotNull; - import com.boydti.fawe.Fawe; import com.boydti.fawe.command.AnvilCommands; import com.boydti.fawe.command.AnvilCommandsRegistration; @@ -31,6 +29,7 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.task.ThrowableSupplier; import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; @@ -57,9 +56,13 @@ import com.sk89q.worldedit.command.GenerationCommands; import com.sk89q.worldedit.command.GenerationCommandsRegistration; import com.sk89q.worldedit.command.HistoryCommands; import com.sk89q.worldedit.command.HistoryCommandsRegistration; +import com.sk89q.worldedit.command.MaskCommands; +import com.sk89q.worldedit.command.MaskCommandsRegistration; import com.sk89q.worldedit.command.NavigationCommands; import com.sk89q.worldedit.command.NavigationCommandsRegistration; import com.sk89q.worldedit.command.PaintBrushCommands; +import com.sk89q.worldedit.command.PatternCommands; +import com.sk89q.worldedit.command.PatternCommandsRegistration; import com.sk89q.worldedit.command.RegionCommands; import com.sk89q.worldedit.command.RegionCommandsRegistration; import com.sk89q.worldedit.command.SchematicCommands; @@ -75,8 +78,11 @@ import com.sk89q.worldedit.command.SnapshotUtilCommandsRegistration; import com.sk89q.worldedit.command.SuperPickaxeCommands; import com.sk89q.worldedit.command.SuperPickaxeCommandsRegistration; import com.sk89q.worldedit.command.ToolCommands; +import com.sk89q.worldedit.command.ToolCommandsRegistration; import com.sk89q.worldedit.command.ToolUtilCommands; import com.sk89q.worldedit.command.ToolUtilCommandsRegistration; +import com.sk89q.worldedit.command.TransformCommands; +import com.sk89q.worldedit.command.TransformCommandsRegistration; import com.sk89q.worldedit.command.UtilityCommands; import com.sk89q.worldedit.command.UtilityCommandsRegistration; import com.sk89q.worldedit.command.WorldEditCommands; @@ -95,6 +101,7 @@ import com.sk89q.worldedit.command.argument.RegistryConverter; import com.sk89q.worldedit.command.argument.VectorConverter; import com.sk89q.worldedit.command.argument.WorldConverter; import com.sk89q.worldedit.command.argument.ZonedDateTimeConverter; +import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandQueuedCondition; import com.sk89q.worldedit.command.util.PermissionCondition; import com.sk89q.worldedit.command.util.SubCommandPermissionCondition; @@ -102,6 +109,10 @@ import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; +import com.sk89q.worldedit.extension.platform.binding.Bindings; +import com.sk89q.worldedit.extension.platform.binding.ConsumeBindings; +import com.sk89q.worldedit.extension.platform.binding.PrimitiveBindings; +import com.sk89q.worldedit.extension.platform.binding.ProvideBindings; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.internal.command.CommandArgParser; @@ -113,6 +124,7 @@ import com.sk89q.worldedit.internal.util.Substring; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.util.auth.AuthorizationException; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; @@ -120,26 +132,10 @@ import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.util.logging.DynamicStreamHandler; import com.sk89q.worldedit.util.logging.LogFormat; import com.sk89q.worldedit.world.World; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.logging.FileHandler; -import java.util.logging.Level; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javax.annotation.Nullable; import org.enginehub.piston.Command; import org.enginehub.piston.CommandManager; -import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.config.TextConfig; import org.enginehub.piston.converter.ArgumentConverters; -import org.enginehub.piston.converter.ConversionResult; import org.enginehub.piston.exception.CommandException; import org.enginehub.piston.exception.CommandExecutionException; import org.enginehub.piston.exception.ConditionFailedException; @@ -159,6 +155,25 @@ import org.enginehub.piston.util.ValueProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.logging.FileHandler; +import java.util.logging.Level; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.google.common.base.Preconditions.checkNotNull; + /** * Handles the registration and invocation of commands. @@ -192,6 +207,7 @@ public final class PlatformCommandManager { checkNotNull(worldEdit); checkNotNull(platformManager); INSTANCE = this; + this.worldEdit = worldEdit; this.platformManager = platformManager; this.exceptionConverter = new WorldEditExceptionConverter(worldEdit); @@ -240,6 +256,14 @@ public final class PlatformCommandManager { RegionFactoryConverter.register(commandManager); WorldConverter.register(commandManager); ExpressionConverter.register(commandManager); + + registerBindings(new ConsumeBindings(worldEdit)); + registerBindings(new PrimitiveBindings(worldEdit)); + registerBindings(new ProvideBindings(worldEdit)); + } + + private void registerBindings(Bindings bindings) { + bindings.register(globalInjectedValues, commandManager); } private void registerAlwaysInjectedValues() { @@ -282,24 +306,64 @@ public final class PlatformCommandManager { } else { throw new MissingWorldException(); } + } catch (MissingWorldException e) { exceptionConverter.convert(e); throw new AssertionError("Should have thrown a new exception.", e); } }); }); + globalInjectedValues.injectValue(Key.of(InjectedValueAccess.class), Optional::of); + } + + private Actor wrapActor(Actor actor, InjectedValueStore context) { + if (actor instanceof Player) { + final Set failedPermissions = new LinkedHashSet<>(); + Player player = (Player) actor; + Player unwrapped = PlayerProxy.unwrap(player); + actor = new LocationMaskedPlayerWrapper(unwrapped, unwrapped.getLocation(), true) { + @Override + public boolean hasPermission(String permission) { + if (!super.hasPermission(permission)) { + failedPermissions.add(permission); + return false; + } + return true; + } + @Override + public void checkPermission(String permission) throws AuthorizationException { + try { + super.checkPermission(permission); + } catch (AuthorizationException e) { + failedPermissions.add(permission); + throw e; + } + } + }; + context.injectValue(Key.of(CommandPermissions.class), i -> Optional.of(new CommandPermissions() { + @Override + public Class annotationType() { + return CommandPermissions.class; + } + @Override + public String[] value() { + return failedPermissions.toArray(new String[0]); + } + })); + } + return actor; } private void registerSubCommands(String name, List aliases, String desc, - CommandManager commandManager, - Consumer> handlerInstance) { + CommandManager commandManager, + Consumer> handlerInstance) { registerSubCommands(name, aliases, desc, commandManager, handlerInstance, m -> {}); } private void registerSubCommands(String name, List aliases, String desc, - CommandManager commandManager, - Consumer> handlerInstance, - Consumer additionalConfig) { + CommandManager commandManager, + Consumer> handlerInstance, + Consumer additionalConfig) { commandManager.register(name, cmd -> { cmd.aliases(aliases); cmd.description(TextComponent.of(desc)); @@ -307,20 +371,21 @@ public final class PlatformCommandManager { CommandManager manager = commandManagerService.newCommandManager(); - handlerInstance.accept((handler, instance) -> - this.registration.register( - manager, - handler, - instance - )); - additionalConfig.accept(manager); + handlerInstance.accept((handler, instance) -> { + this.registration.register( + manager, + handler, + instance + ); + }); + if (additionalConfig != null) additionalConfig.accept(manager); final List subCommands = manager.getAllCommands().collect(Collectors.toList()); cmd.addPart(SubCommandPart.builder(TranslatableComponent.of("worldedit.argument.action"), - TextComponent.of("Sub-command to run.")) - .withCommands(subCommands) - .required() - .build()); + TextComponent.of("Sub-command to run.")) + .withCommands(subCommands) + .required() + .build()); cmd.condition(new SubCommandPermissionCondition.Generator(subCommands).build()); }); @@ -333,27 +398,27 @@ public final class PlatformCommandManager { public void registerAllCommands() { if (Settings.IMP.ENABLED_COMPONENTS.COMMANDS) { -// registerSubCommands( -// "patterns", -// ImmutableList.of(), -// "Patterns determine what blocks are placed", -// PatternCommandsRegistration.builder(), -// new PatternCommands() -// ); -// registerSubCommands( -// "masks", -// ImmutableList.of(), -// "Masks determine which blocks are placed", -// MaskCommandsRegistration.builder(), -// new MaskCommands(worldEdit) -// ); -// registerSubCommands( -// "transforms", -// ImmutableList.of(), -// "Transforms modify how a block is placed", -// TransformCommandsRegistration.builder(), -// new TransformCommands() -// ); + registerSubCommands( + "patterns", + ImmutableList.of(), + "Patterns determine what blocks are placed", + PatternCommandsRegistration.builder(), + new PatternCommands() + ); + registerSubCommands( + "masks", + ImmutableList.of(), + "Masks determine which blocks are placed", + MaskCommandsRegistration.builder(), + new MaskCommands(worldEdit) + ); + registerSubCommands( + "transforms", + ImmutableList.of(), + "Transforms modify how a block is placed", + TransformCommandsRegistration.builder(), + new TransformCommands() + ); registerSubCommands( "schematic", ImmutableList.of("schem", "/schematic", "/schem"), @@ -377,11 +442,12 @@ public final class PlatformCommandManager { ); registerSubCommands( "brush", - Lists.newArrayList("br", "/brush", "/br"), + Lists.newArrayList("br", "/brush", "/br", "/tool", "tool"), "Brushing commands", commandManager, c -> { c.accept(BrushCommandsRegistration.builder(), new BrushCommands(worldEdit)); + c.accept(ToolCommandsRegistration.builder(), new ToolCommands(worldEdit)); }, manager -> { PaintBrushCommands.register(commandManagerService, manager, registration); @@ -390,7 +456,7 @@ public final class PlatformCommandManager { ); registerSubCommands( "worldedit", - ImmutableList.of("we"), + ImmutableList.of("we", "fawe", "fastasyncworldedit"), "WorldEdit commands", WorldEditCommandsRegistration.builder(), new WorldEditCommands(worldEdit) @@ -465,7 +531,11 @@ public final class PlatformCommandManager { SnapshotUtilCommandsRegistration.builder(), new SnapshotUtilCommands(worldEdit) ); - ToolCommands.register(registration, commandManager, commandManagerService, worldEdit); + this.registration.register( + commandManager, + ToolCommandsRegistration.builder(), + new ToolCommands(worldEdit) + ); this.registration.register( commandManager, ToolUtilCommandsRegistration.builder(), @@ -490,6 +560,13 @@ public final class PlatformCommandManager { void registerCommandsWith(Platform platform) { log.info("Registering commands with " + platform.getClass().getCanonicalName()); + // Delay command registration to allow time for other plugins to hook into FAWE + try { +// new CommandScriptLoader().load(); + } catch (Throwable e) { + e.printStackTrace(); + } + LocalConfiguration config = platform.getConfiguration(); boolean logging = config.logCommands; String path = config.logFile; @@ -521,19 +598,18 @@ public final class PlatformCommandManager { } private Stream parseArgs(String input) { - return CommandArgParser.forArgString(input.substring(1)).parseArgs(); + return CommandArgParser.forArgString(input).parseArgs(); } - public Collection parse(Class clazz, String arguments, @Nullable Actor actor) { - List def = Collections.emptyList(); - Optional> converterOptional = commandManager.getConverter(Key.of(clazz)); - if (converterOptional.isPresent()) { - ArgumentConverter converter = converterOptional.get(); - MemoizingValueAccess injectedValues = initializeInjectedValues(() -> arguments, actor); - ConversionResult result = converter.convert(arguments, injectedValues); - return result.orElse(def); - } - return def; + public T parse(String args, Actor actor) { + return parse(args, actor); + } + + public T parse(String args, InjectedValueAccess access) { + String[] split = parseArgs(args) + .map(Substring::getSubstring) + .toArray(String[]::new); + return (T) commandManager.execute(access, ImmutableList.copyOf(split)); } @Subscribe @@ -544,9 +620,6 @@ public final class PlatformCommandManager { TaskManager.IMP.taskNow(() -> { int space0 = args.indexOf(' '); String arg0 = space0 == -1 ? args : args.substring(0, space0); - if (arg0.startsWith("/")) { - arg0 = arg0.substring(1); - } Optional optional = commandManager.getCommand(arg0); if (!optional.isPresent()) { System.out.println("No command for '" + arg0 + "' " + StringMan.getString(commandManager.getAllCommands().map( @@ -598,7 +671,7 @@ public final class PlatformCommandManager { } public void handleCommandTask(ThrowableSupplier task, InjectedValueAccess context, @Nullable LocalSession session, CommandEvent event) { - Actor actor = event.getActor(); + Actor actor = context.injectedValue(Key.of(Actor.class)).orElseThrow(() -> new IllegalStateException("No player")); long start = System.currentTimeMillis(); @@ -608,7 +681,7 @@ public final class PlatformCommandManager { // exceptions without writing a hook into every dispatcher, we need to unwrap these // exceptions and rethrow their converted form, if their is one. try { - task.get(); + Object result = task.get(); } catch (Throwable t) { // Use the exception converter to convert the exception if any of its causes // can be converted, otherwise throw the original exception @@ -663,7 +736,7 @@ public final class PlatformCommandManager { long time = System.currentTimeMillis() - start; if (time > 1000) { - BBC.ACTION_COMPLETE.send(event.getActor(), time / 1000D); + BBC.ACTION_COMPLETE.send(actor, time / 1000D); } worldEdit.flushBlockBag(actor, editSession); @@ -674,8 +747,9 @@ public final class PlatformCommandManager { event.setCancelled(true); } - private MemoizingValueAccess initializeInjectedValues(Arguments arguments, Actor actor) { + private MemoizingValueAccess initializeInjectedValues(Arguments arguments, Actor tmp) { InjectedValueStore store = MapBackedValueStore.create(); + Actor actor = wrapActor(tmp, store); store.injectValue(Key.of(Actor.class), ValueProvider.constant(actor)); if (actor instanceof Player) { store.injectValue(Key.of(Player.class), ValueProvider.constant((Player) actor)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 4b669f087..5519751b5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -56,6 +56,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; + +import com.sk89q.worldedit.world.block.BlockType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -351,7 +353,7 @@ public class PlatformManager { } } - Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); + Tool tool = session.getTool(player); if (tool instanceof DoubleActionBlockTool && tool.canUse(player)) { player.runAction(() -> reset(((DoubleActionBlockTool) tool)) .actSecondary(queryCapability(Capability.WORLD_EDITING), @@ -360,19 +362,16 @@ public class PlatformManager { } } else if (event.getType() == Interaction.OPEN) { - Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); + Tool tool = session.getTool(player); if (tool instanceof BlockTool && tool.canUse(player)) { if (player.checkAction()) { player.runAction(() -> { - if (tool instanceof BrushTool) { - ((BlockTool) tool) - .actPrimary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session, location); - } else { - reset((BlockTool) tool) - .actPrimary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session, location); + BlockTool blockTool = (BlockTool) tool; + if (!(tool instanceof BrushTool)) { + blockTool = reset(blockTool); } + ((BlockTool) blockTool).actPrimary(queryCapability(Capability.WORLD_EDITING), + getConfiguration(), player, session, location); }, false, true); event.setCancelled(true); } @@ -427,7 +426,7 @@ public class PlatformManager { event.setCancelled(true); return; } - Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); + Tool tool = session.getTool(player); if (tool instanceof DoubleActionTraceTool && tool.canUse(player)) { player.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session)); @@ -451,7 +450,7 @@ public class PlatformManager { event.setCancelled(true); return; } - Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); + Tool tool = session.getTool(player); if (tool instanceof TraceTool && tool.canUse(player)) { //todo this needs to be fixed so the event is canceled after actPrimary is used and returns true player.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java index ae31e2dd0..fb67fdc6c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -20,6 +20,8 @@ package com.sk89q.worldedit.extension.platform; import static com.google.common.base.Preconditions.checkNotNull; + +import com.boydti.fawe.wrappers.PlayerWrapper; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; @@ -48,6 +50,10 @@ public class PlayerProxy extends AbstractPlayerActor { private final World world; private Vector3 offset = Vector3.ZERO; + public PlayerProxy(Player player) { + this(player, player, player, player.getWorld()); + } + public PlayerProxy(Player basePlayer, Actor permActor, Actor cuiActor, World world) { checkNotNull(basePlayer); checkNotNull(permActor); @@ -59,6 +65,13 @@ public class PlayerProxy extends AbstractPlayerActor { this.world = world; } + public static Player unwrap(Player player) { + if (player instanceof PlayerProxy) { + return unwrap(((PlayerProxy) player).getBasePlayer()); + } + return player; + } + public void setOffset(Vector3 position) { this.offset = position; } @@ -121,7 +134,7 @@ public class PlayerProxy extends AbstractPlayerActor { @Override public World getWorld() { - return world; + return world == null ? basePlayer.getWorld() : world; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/AnnotatedBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/AnnotatedBindings.java new file mode 100644 index 000000000..6626d4b35 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/AnnotatedBindings.java @@ -0,0 +1,46 @@ +package com.sk89q.worldedit.extension.platform.binding; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.internal.annotation.Validate; + +import java.lang.annotation.Annotation; + +public class AnnotatedBindings extends Bindings { + + public AnnotatedBindings(WorldEdit worldEdit) { + super(worldEdit); + } + + @Validate() + public String getText(String argument, Validate modifier) { + return validate(argument, modifier); + } + + /** + * Validate a string value using relevant modifiers. + * + * @param string the string + * @param modifiers the list of modifiers to scan + * @throws InputParseException on a validation error + */ + private static String validate(String string, Annotation... modifiers) { + if (string != null) { + for (Annotation modifier : modifiers) { + if (modifier instanceof Validate) { + Validate validate = (Validate) modifier; + + if (!validate.value().isEmpty()) { + if (!string.matches(validate.value())) { + throw new InputParseException( + String.format( + "The given text doesn't match the right format (technically speaking, the 'format' is %s)", + validate.value())); + } + } + } + } + } + return string; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/Binding.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/Binding.java new file mode 100644 index 000000000..f6a27e276 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/Binding.java @@ -0,0 +1,9 @@ +package com.sk89q.worldedit.extension.platform.binding; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Binding { + String value() default ""; +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/Bindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/Bindings.java index 14a4dce71..50c535cde 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/Bindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/Bindings.java @@ -1,4 +1,124 @@ package com.sk89q.worldedit.extension.platform.binding; +import com.boydti.fawe.util.StringMan; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.InjectedValueStore; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.util.ValueProvider; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + public class Bindings { + + private final WorldEdit worldEdit; + + public Bindings(WorldEdit worldEdit) { + this.worldEdit = worldEdit; + } + + public WorldEdit getWorldEdit() { + return worldEdit; + } + + public void register(InjectedValueStore store, CommandManager manager) { + for (Method method : getClass().getDeclaredMethods()) { + register(method, store, manager); + } + } + + private boolean register(Method method, InjectedValueStore store, CommandManager manager) { + // Check that it has the binding + Binding binding = method.getAnnotation(Binding.class); + if (binding == null) return false; + Annotation[] annotations = method.getAnnotations(); + + // Get the key + Class ret = method.getReturnType(); + Key key; + if ( annotations.length == 1) { + key = Key.of(ret); + }else if (annotations.length == 2) { + Annotation annotation = annotations[0] == binding ? annotations[1] : annotations[0]; + key = Key.of(ret, annotation); + } else { + System.out.println("Cannot annotate: " + method + " with " + StringMan.getString(annotations)); + return false; + } + + // Get the provided parameters + Class[] params = method.getParameterTypes(); + Annotation[][] paramAnns = method.getParameterAnnotations(); + Function[] argsFunc = new Function[params.length]; + + boolean provide = true; + for (int i = 0; i < params.length; i++) { + Class param = params[i]; + if (param != String.class) { + Annotation[] paramAnn = paramAnns[i]; + Key paramKey; + if (paramAnn.length == 1) { + paramKey = Key.of(param, paramAnn[0]); + } else if (paramAnn.length == 0) { + paramKey = Key.of(param); + } else { + throw new UnsupportedOperationException("Only one annotation is permitted for " + method); + } + argsFunc[i] = v -> v.injectedValue(paramKey); + } else if (provide) { + provide = false; + } else { + throw new UnsupportedOperationException("Only one argument is allowed"); + } + } + + // If the method provides all parameters + if (provide) { + store.injectValue(key, access -> Optional.of(invoke(null, argsFunc, access, method))); + } else { // If the method consumes a String argument + manager.registerConverter(key, new ArgumentConverter() { + @Override + public Component describeAcceptableArguments() { + return TextComponent.of(binding.value()); + } + + @Override + public ConversionResult convert(String s, InjectedValueAccess access) { + return SuccessfulConversion.fromSingle(invoke(s, argsFunc, access, method)); + } + }); + } + return true; + } + + private Object invoke(String arg, Function[] argsFunc, InjectedValueAccess access, Method method) { + try { + Object[] args = new Object[argsFunc.length]; + for (int i = 0; i < argsFunc.length; i++) { + Function func = argsFunc[i]; + if (func != null) { + Optional optional = (Optional) func.apply(access); + args[i] = optional.get(); + } else { + args[i] = arg; + } + } + return method.invoke(this, args); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/CommandBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/CommandBindings.java index 351d7754b..54dfbc5d5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/CommandBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/CommandBindings.java @@ -3,9 +3,8 @@ package com.sk89q.worldedit.extension.platform.binding; import com.sk89q.worldedit.WorldEdit; public class CommandBindings extends Bindings { - private final WorldEdit worldEdit; public CommandBindings(WorldEdit worldEdit) { - this.worldEdit = worldEdit; + super(worldEdit); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java index 57d034bca..e95e41af8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java @@ -1,13 +1,131 @@ package com.sk89q.worldedit.extension.platform.binding; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.image.ImageUtil; +import com.sk89q.worldedit.UnknownDirectionException; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.NoMatchException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.annotation.Validate; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.biome.Biomes; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.registry.BiomeRegistry; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.inject.InjectedValueStore; +import org.enginehub.piston.inject.Key; + +import java.util.Collection; public class ConsumeBindings extends Bindings { - private final WorldEdit worldEdit; - public ConsumeBindings(WorldEdit worldEdit) { - this.worldEdit = worldEdit; + super(worldEdit); } + @Binding + public ProvideBindings.ImageUri getImage(String argument) { + return new ProvideBindings.ImageUri(ImageUtil.getImageURI(argument)); + } + + @Binding + public BlockType blockType(Actor actor, String argument) { + return blockState(actor, argument).getBlockType(); + } + + @Binding + public BlockStateHolder blockStateHolder(Actor actor, String argument) { + return blockState(actor, argument); + } + + @Binding + public BlockState blockState(Actor actor, String argument) { + return baseBlock(actor, argument).toBlockState(); + } + + @Binding + public BaseBlock baseBlock(Actor actor, String argument) { + ParserContext parserContext = new ParserContext(); + parserContext.setActor(actor); + if (actor instanceof Entity) { + Extent extent = ((Entity) actor).getExtent(); + if (extent instanceof World) { + parserContext.setWorld((World) extent); + } + } + parserContext.setSession(getWorldEdit().getSessionManager().get(actor)); + try { + return getWorldEdit().getBlockFactory().parseFromInput(argument, parserContext); + } catch (NoMatchException e) { + throw new InputParseException(e.getMessage()); + } + } + + /** + * Gets an {@link TreeType} from a {@link ArgumentStack}. + * + * @param context the context + * @return a TreeType + * @throws ParameterException on error + * @throws WorldEditException on error + */ + @Binding + public TreeGenerator.TreeType getTreeType(String argument) throws WorldEditException { + if (argument != null) { + TreeGenerator.TreeType type = TreeGenerator.lookup(argument); + if (type != null) { + return type; + } else { + throw new InputParseException( + String.format("Can't recognize tree type '%s' -- choose from: %s", argument, + TreeGenerator.TreeType.getPrimaryAliases())); + } + } else { + return TreeGenerator.TreeType.TREE; + } + } + + /** + * Gets an {@link BiomeType} from a {@link ArgumentStack}. + * + * @param context the context + * @return a BiomeType + * @throws ParameterException on error + * @throws WorldEditException on error + */ + @Binding + public BiomeType getBiomeType(String argument) throws WorldEditException { + if (argument != null) { + + if (MathMan.isInteger(argument)) return BiomeTypes.getLegacy(Integer.parseInt(argument)); + BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); + Collection knownBiomes = BiomeType.REGISTRY.values(); + BiomeType biome = Biomes.findBiomeByName(knownBiomes, argument, biomeRegistry); + if (biome != null) { + return biome; + } else { + throw new InputParseException( + String.format("Can't recognize biome type '%s' -- use /biomelist to list available types", argument)); + } + } else { + throw new InputParseException( + "This command takes a 'default' biome if one is not set, except there is no particular " + + "biome that should be 'default', so the command should not be taking a default biome"); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java index 486313bd1..397843b8a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java @@ -19,16 +19,23 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector2; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.inject.InjectedValueStore; import javax.annotation.Nullable; import java.lang.annotation.Annotation; import java.util.Locale; public class PrimitiveBindings extends Bindings { + public PrimitiveBindings(WorldEdit worldEdit) { + super(worldEdit); + } /* Parsers */ + @Binding public Expression getExpression(String argument) throws ExpressionException { try { return new Expression(Double.parseDouble(argument)); @@ -54,6 +61,7 @@ public class PrimitiveBindings extends Bindings { * @return an extent * @throws InputParseException on other error */ + @Binding public ResettableExtent getResettableExtent(Actor actor, String argument) throws InputParseException { if (argument.equalsIgnoreCase("#null")) { return new NullExtent(); @@ -77,6 +85,7 @@ public class PrimitiveBindings extends Bindings { * @return the requested type * @throws InputParseException on error */ + @Binding public Boolean getBoolean(String argument) { switch (argument.toLowerCase(Locale.ROOT)) { case "": @@ -107,6 +116,7 @@ public class PrimitiveBindings extends Bindings { * @return the requested type * @throws InputParseException on error */ + @Binding public Vector3 getVector3(String argument) { String radiusString = argument; String[] radii = radiusString.split(","); @@ -136,6 +146,7 @@ public class PrimitiveBindings extends Bindings { * @return the requested type * @throws InputParseException on error */ + @Binding public Vector2 getVector2(String argument) { String radiusString = argument; String[] radii = radiusString.split(","); @@ -163,6 +174,7 @@ public class PrimitiveBindings extends Bindings { * @return the requested type * @throws InputParseException on error */ + @Binding public BlockVector3 getBlockVector3(String argument) { String radiusString = argument; String[] radii = radiusString.split(","); @@ -192,18 +204,19 @@ public class PrimitiveBindings extends Bindings { * @return the requested type * @throws InputParseException on error */ + @Binding public BlockVector2 getBlockVector2(String argument) { String radiusString = argument; String[] radii = radiusString.split(","); final double radiusX, radiusZ; switch (radii.length) { case 1: - radiusX = radiusZ = Math.max(1, PrimitiveBindings.parseNumericInput(radii[0])); + radiusX = radiusZ = Math.max(1, parseNumericInput(radii[0])); break; case 2: - radiusX = Math.max(1, PrimitiveBindings.parseNumericInput(radii[0])); - radiusZ = Math.max(1, PrimitiveBindings.parseNumericInput(radii[1])); + radiusX = Math.max(1, parseNumericInput(radii[0])); + radiusZ = Math.max(1, parseNumericInput(radii[1])); break; default: @@ -212,40 +225,6 @@ public class PrimitiveBindings extends Bindings { return BlockVector2.at(radiusX, radiusZ); } - /* - - */ - - public Long getLong(String argument, Annotation... modifiers) { - try { - Long v = Long.parseLong(argument); - validate(v, modifiers); - return v; - - } catch (NumberFormatException ignore) { - return null; - } - } - - private static void validate(long number, Annotation... modifiers) { - for (Annotation modifier : modifiers) { - if (modifier instanceof Range) { - Range range = (Range) modifier; - if (number < range.min()) { - throw new InputParseException( - String.format( - "A valid value is greater than or equal to %s " + - "(you entered %s)", range.min(), number)); - } else if (number > range.max()) { - throw new InputParseException( - String.format( - "A valid value is less than or equal to %s " + - "(you entered %s)", range.max(), number)); - } - } - } - } - /** * Try to parse numeric input as either a number or a mathematical expression. * @@ -253,9 +232,7 @@ public class PrimitiveBindings extends Bindings { * @return a number * @throws InputParseException thrown on parse error */ - public static - @Nullable - Double parseNumericInput(@Nullable String input) { + public static @Nullable Double parseNumericInput(@Nullable String input) { if (input == null) { return null; } @@ -274,119 +251,4 @@ public class PrimitiveBindings extends Bindings { } } } - - /** - * Gets a type from a {@link ArgumentStack}. - * - * @param context the context - * @param modifiers a list of modifiers - * @return the requested type - * @throws InputParseException on error - */ - public Integer getInteger(String argument, Annotation[] modifiers) { - Double v = parseNumericInput(argument); - if (v != null) { - int intValue = v.intValue(); - validate(intValue, modifiers); - return intValue; - } else { - return null; - } - } - - /** - * Gets a type from a {@link ArgumentStack}. - * - * @param context the context - * @param modifiers a list of modifiers - * @return the requested type - * @throws InputParseException on error - */ - public Short getShort(String argument, Annotation[] modifiers) { - Integer v = getInteger(argument, modifiers); - if (v != null) { - return v.shortValue(); - } - return null; - } - - /** - * Gets a type from a {@link ArgumentStack}. - * - * @param context the context - * @param modifiers a list of modifiers - * @return the requested type - * @throws InputParseException on error - */ - public Double getDouble(String argument, Annotation[] modifiers) { - Double v = parseNumericInput(argument); - if (v != null) { - validate(v, modifiers); - return v; - } else { - return null; - } - } - - /** - * Gets a type from a {@link ArgumentStack}. - * - * @param context the context - * @param modifiers a list of modifiers - * @return the requested type - * @throws InputParseException on error - */ - public Float getFloat(String argument, Annotation[] modifiers) { - Double v = getDouble(argument, modifiers); - if (v != null) { - return v.floatValue(); - } - return null; - } - - /** - * Validate a number value using relevant modifiers. - * - * @param number the number - * @param modifiers the list of modifiers to scan - * @throws InputParseException on a validation error - */ - private static void validate(double number, Annotation[] modifiers) { - for (Annotation modifier : modifiers) { - if (modifier instanceof Range) { - Range range = (Range) modifier; - if (number < range.min()) { - throw new InputParseException( - String.format("A valid value is greater than or equal to %s (you entered %s)", range.min(), number)); - } else if (number > range.max()) { - throw new InputParseException( - String.format("A valid value is less than or equal to %s (you entered %s)", range.max(), number)); - } - } - } - } - - /** - * Validate a number value using relevant modifiers. - * - * @param number the number - * @param modifiers the list of modifiers to scan - * @throws InputParseException on a validation error - */ - private static void validate(int number, Annotation[] modifiers) { - for (Annotation modifier : modifiers) { - if (modifier instanceof Range) { - Range range = (Range) modifier; - if (number < range.min()) { - throw new InputParseException( - String.format( - "A valid value is greater than or equal to %s (you entered %s)", range.min(), number)); - } else if (number > range.max()) { - throw new InputParseException( - String.format( - "A valid value is less than or equal to %s (you entered %s)", range.max(), number)); - } - } - } - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ProvideBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ProvideBindings.java index 4ec9adee1..3c679963c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ProvideBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ProvideBindings.java @@ -1,24 +1,15 @@ package com.sk89q.worldedit.extension.platform.binding; -import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.image.ImageUtil; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.UnknownDirectionException; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.InputParseException; -import com.sk89q.worldedit.extension.input.NoMatchException; -import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.annotation.Selection; -import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.TreeGenerator; @@ -39,17 +30,19 @@ import org.enginehub.piston.inject.InjectedValueStore; import org.enginehub.piston.inject.Key; import org.enginehub.piston.util.ValueProvider; -public class ProvideBindings extends Bindings { - private final WorldEdit worldEdit; +import java.awt.image.BufferedImage; +import java.net.URI; +import java.util.Optional; +public class ProvideBindings extends Bindings { public ProvideBindings(WorldEdit worldEdit) { - this.worldEdit = worldEdit; + super(worldEdit); } /* Provided */ - + @Binding public Player getPlayer(Actor actor) { if (actor.isPlayer()) { return (Player) actor; @@ -57,10 +50,12 @@ public class ProvideBindings extends Bindings { throw new InputParseException("This command must be used with a player."); } + @Binding public LocalSession getLocalSession(Player player) { - return worldEdit.getSessionManager().get(player); + return getWorldEdit().getSessionManager().get(player); } + @Binding public EditSession editSession(LocalSession localSession, Player player) { EditSession editSession = localSession.createEditSession(player); editSession.enableStandardMode(); @@ -68,15 +63,17 @@ public class ProvideBindings extends Bindings { } @Selection + @Binding public Region selection(LocalSession localSession, Player player) { return localSession.getSelection(player.getWorld()); } + @Binding public TextureUtil getTexture(LocalSession session) { return session.getTextureUtil(); } - public class ImageUri { + public static class ImageUri { public final URI uri; private BufferedImage image; @@ -92,6 +89,7 @@ public class ProvideBindings extends Bindings { } } + @Binding public Extent getExtent(Actor actor, InjectedValueAccess access, InjectedValueStore store) { Optional editSessionOpt = access.injectedValue(Key.of(EditSession.class)); if (editSessionOpt.isPresent()) { @@ -107,106 +105,4 @@ public class ProvideBindings extends Bindings { store.injectValue(Key.of(EditSession.class), ValueProvider.constant(editSession)); return editSession; } - - /* - Parsed - */ - public ImageUri getImage(String argument) { - return new ImageUri(ImageUtil.getImageURI(argument)); - } - - public BlockType blockType(Actor actor, String argument) { - return blockState(actor, argument).getBlockType(); - } - - public BlockState blockState(Actor actor, String argument) { - return baseBlock(actor, argument).toBlockState(); - } - - public BaseBlock baseBlock(Actor actor, String argument) { - ParserContext parserContext = new ParserContext(); - parserContext.setActor(actor); - if (actor instanceof Entity) { - Extent extent = ((Entity) actor).getExtent(); - if (extent instanceof World) { - parserContext.setWorld((World) extent); - } - } - parserContext.setSession(worldEdit.getSessionManager().get(actor)); - try { - return worldEdit.getBlockFactory().parseFromInput(argument, parserContext); - } catch (NoMatchException e) { - throw new InputParseException(e.getMessage()); - } - } - - /** - * Get a direction from the player. - * - * @param context the context - * @param direction the direction annotation - * @return a BlockVector3 - * @throws ParameterException on error - * @throws UnknownDirectionException on an unknown direction - */ - @Direction - public BlockVector3 getDirection(Player player, Direction direction, String argument) throws UnknownDirectionException { - if (direction.includeDiagonals()) { - return worldEdit.getDiagonalDirection(player, argument); - } else { - return worldEdit.getDirection(player, argument); - } - } - - /** - * Gets an {@link TreeType} from a {@link ArgumentStack}. - * - * @param context the context - * @return a TreeType - * @throws ParameterException on error - * @throws WorldEditException on error - */ - public TreeGenerator.TreeType getTreeType(String argument) throws WorldEditException { - if (argument != null) { - TreeGenerator.TreeType type = TreeGenerator.lookup(argument); - if (type != null) { - return type; - } else { - throw new InputParseException( - String.format("Can't recognize tree type '%s' -- choose from: %s", argument, - TreeGenerator.TreeType.getPrimaryAliases())); - } - } else { - return TreeGenerator.TreeType.TREE; - } - } - - /** - * Gets an {@link BiomeType} from a {@link ArgumentStack}. - * - * @param context the context - * @return a BiomeType - * @throws ParameterException on error - * @throws WorldEditException on error - */ - public BiomeType getBiomeType(String argument) throws WorldEditException { - if (argument != null) { - - if (MathMan.isInteger(argument)) return BiomeTypes.getLegacy(Integer.parseInt(argument)); - BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); - Collection knownBiomes = BiomeType.REGISTRY.values(); - BiomeType biome = Biomes.findBiomeByName(knownBiomes, argument, biomeRegistry); - if (biome != null) { - return biome; - } else { - throw new InputParseException( - String.format("Can't recognize biome type '%s' -- use /biomelist to list available types", argument)); - } - } else { - throw new InputParseException( - "This command takes a 'default' biome if one is not set, except there is no particular " + - "biome that should be 'default', so the command should not be taking a default biome"); - } - } } 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 8bb3252a5..349f29aed 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 @@ -22,6 +22,7 @@ package com.sk89q.worldedit.extent; import static com.google.common.base.Preconditions.checkNotNull; import com.boydti.fawe.Fawe; +import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.object.HistoryExtent; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.exception.FaweException; @@ -262,4 +263,22 @@ public class AbstractDelegateExtent implements Extent, LightingExtent { return null; } } + + @Override + public Extent addProcessor(IBatchProcessor processor) { + Extent result = this.extent.addProcessor(processor); + if (result != this.extent) { + new ExtentTraverser(this).setNext(result); + } + return this; + } + + @Override + public Extent disableHistory() { + Extent result = this.extent.disableHistory(); + if (result != this.extent) { + new ExtentTraverser(this).setNext(result); + } + return this; + } } 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 3e1388fff..a73b91c61 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 @@ -19,6 +19,9 @@ package com.sk89q.worldedit.extent; +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.object.HistoryExtent; +import com.boydti.fawe.object.changeset.FaweChangeSet; import static com.google.common.base.Preconditions.checkNotNull; import com.boydti.fawe.object.clipboard.WorldCopyClipboard; @@ -629,4 +632,21 @@ public interface Extent extends InputExtent, OutputExtent { return count; } + /** + * Have an extent processed + * - Either block (Extent) processing or chunk processing + * @param processor + * @return processed Extent + */ + default Extent addProcessor(IBatchProcessor processor) { + return processor.construct(this); + } + + default Extent enableHistory(FaweChangeSet changeSet) { + return addProcessor(changeSet); + } + + default Extent disableHistory() { + return this; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java index 6e60a7e9c..317e070a7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java @@ -24,6 +24,8 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.MutableBlockVector2; +import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -53,9 +55,14 @@ public interface OutputExtent { * @deprecated It is recommended that you use {@link #setBlock(int, int, int, BlockStateHolder)} in FAWE */ @Deprecated - > boolean setBlock(BlockVector3 position, T block) throws WorldEditException; + default > boolean setBlock(BlockVector3 position, T block) throws WorldEditException { + return setBlock(position.getX(), position.getY(), position.getZ(), block); + } - > boolean setBlock(int x, int y, int z, T block) throws WorldEditException; + // The defaults need to remain for compatibility (the actual implementation still needs to override one of these) + default > boolean setBlock(int x, int y, int z, T block) throws WorldEditException { + return setBlock(MutableBlockVector3.get(x, y, z), block); + } boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException; @@ -66,9 +73,14 @@ public interface OutputExtent { * @param biome the biome to set to * @return true if the biome was successfully set (return value may not be accurate) */ - boolean setBiome(BlockVector2 position, BiomeType biome); + default boolean setBiome(BlockVector2 position, BiomeType biome) { + return setBiome(position.getX(), 0, position.getBlockZ(), biome); + } - boolean setBiome(int x, int y, int z, BiomeType biome); + // The defaults need to remain for compatibility (the actual implementation still needs to override one of these) + default boolean setBiome(int x, int y, int z, BiomeType biome) { + return setBiome(MutableBlockVector2.get(x, z), biome); + } /** * Return an {@link Operation} that should be called to tie up loose ends diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/PassthroughExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/PassthroughExtent.java index 26d5ddca8..6cdc967dd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/PassthroughExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/PassthroughExtent.java @@ -1,5 +1,7 @@ package com.sk89q.worldedit.extent; +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; @@ -68,23 +70,6 @@ public class PassthroughExtent extends AbstractDelegateExtent { getExtent().removeEntity(x, y, z, uuid); } - public boolean isQueueEnabled() { - return getExtent().isQueueEnabled(); - } - - public void enableQueue() { - getExtent().enableQueue(); - } - - public void disableQueue() { - getExtent().disableQueue(); - } - - @Override - public boolean isWorld() { - return getExtent().isWorld(); - } - @Override public boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) { return getExtent().regenerateChunk(x, z, type, seed); @@ -175,17 +160,6 @@ public class PassthroughExtent extends AbstractDelegateExtent { return getExtent().getBlockDistributionWithData(region); } - @Override - @Nullable - public Operation commit() { - return getExtent().commit(); - } - - @Override - public boolean cancel() { - return getExtent().cancel(); - } - @Override public int getMaxY() { return getExtent().getMaxY(); @@ -292,4 +266,46 @@ public class PassthroughExtent extends AbstractDelegateExtent { public boolean setBiome(int x, int y, int z, BiomeType biome) { return getExtent().setBiome(x, y, z, biome); } + + // special + public Extent disableHistory() { + return super.disableHistory(); + } + + @Override + public Extent addProcessor(IBatchProcessor processor) { + return super.addProcessor(processor); + } + + public Extent enableHistory(FaweChangeSet changeSet) { + return super.enableHistory(changeSet); + } + + @Override + @Nullable + public Operation commit() { + return getExtent().commit(); + } + + @Override + public boolean cancel() { + return getExtent().cancel(); + } + + public boolean isQueueEnabled() { + return getExtent().isQueueEnabled(); + } + + public void enableQueue() { + getExtent().enableQueue(); + } + + public void disableQueue() { + getExtent().disableQueue(); + } + + @Override + public boolean isWorld() { + return getExtent().isWorld(); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/Naturalizer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/Naturalizer.java index 2242e83a7..da02904bd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/Naturalizer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/Naturalizer.java @@ -80,6 +80,8 @@ public class Naturalizer implements LayerFunction { } private boolean naturalize(BlockVector3 position, int depth) throws WorldEditException { + return editSession.setBlock(position, getTargetBlock(depth)); + /* BlockState block = editSession.getBlock(position); BlockState targetBlock = getTargetBlock(depth); @@ -88,6 +90,7 @@ public class Naturalizer implements LayerFunction { } return editSession.setBlock(position, targetBlock); + */ } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java index 4510c2432..cb761de44 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.TreeGenerator; +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; @@ -53,22 +54,28 @@ public class ForestGenerator implements RegionFunction { BlockState block = editSession.getBlock(position); BlockType t = block.getBlockType(); - if (t == BlockTypes.GRASS_BLOCK || t == BlockTypes.DIRT || t == BlockTypes.PODZOL || t == BlockTypes.COARSE_DIRT) { - return treeType.generate(editSession, position.add(0, 1, 0)); - } else if (t.getMaterial().isReplacedDuringPlacement()) { - // since the implementation's tree generators generally don't generate in non-air spots, - // we trick editsession history here in the first call - editSession.setBlock(position, BlockTypes.AIR.getDefaultState()); - // and then trick the generator here by directly setting into the world - editSession.getWorld().setBlock(position, BlockTypes.AIR.getDefaultState()); - // so that now the generator can generate the tree - boolean success = treeType.generate(editSession, position); - if (!success) { - editSession.setBlock(position, block); // restore on failure - } - return success; - } else { // Trees won't grow on this! - return false; + switch (t.getInternalId()) { + case BlockID.GRASS_BLOCK: + case BlockID.DIRT: + case BlockID.PODZOL: + case BlockID.COARSE_DIRT: + return treeType.generate(editSession, position.add(0, 1, 0)); + default: + if (t.getMaterial().isReplacedDuringPlacement()) { + // since the implementation's tree generators generally don't generate in non-air spots, + // we trick editsession history here in the first call + editSession.setBlock(position, BlockTypes.AIR.getDefaultState()); + // and then trick the generator here by directly setting into the world + editSession.getWorld().setBlock(position, BlockTypes.AIR.getDefaultState()); + // so that now the generator can generate the tree + boolean success = treeType.generate(editSession, position); + if (!success) { + editSession.setBlock(position, block); // restore on failure + } + return success; + } else { // Trees won't grow on this! + return false; + } } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java index 302c2ed7a..3920fd644 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java @@ -151,7 +151,9 @@ public class BlockMask extends ABlockMask { @Deprecated public void add(Collection blocks) { checkNotNull(blocks); - blocks.forEach(baseBlock -> add(baseBlock.toBlockState())); + for (BaseBlock block : blocks) { + add(block.toBlockState()); + } } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java index 3d77e7b6d..3fd433e89 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java @@ -149,14 +149,13 @@ public class BlockMaskBuilder { Collection types = type != null ? Collections.singleton(type) : blockTypeList; throw new SuggestInputParseException("No value for " + input, input, () -> { HashSet values = new HashSet<>(); - types.forEach(t -> { - if (t.hasProperty(fKey)) { - Property p = t.getProperty(fKey); - for (int j = 0; j < p.getValues().size(); j++) { - if (has(t, p, j)) { - String o = p.getValues().get(j).toString(); - if (o.startsWith(value)) values.add(o); - } + types.stream().filter(t -> t.hasProperty(fKey)).forEach(t -> { + Property p = t.getProperty(fKey); + for (int j = 0; j < p.getValues().size(); j++) { + if (has(t, p, j)) { + String o = p.getValues().get(j).toString(); + if (o.startsWith(value)) + values.add(o); } } }); @@ -224,7 +223,6 @@ public class BlockMaskBuilder { AbstractProperty prop = (AbstractProperty) property; long[] states = bitSets[type.getInternalId()]; if (states == null) return false; - List values = prop.getValues(); int localI = index << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; return (states == ALL || FastBitSet.get(states, localI)); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java index e2dd4eda3..9c10590d9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java @@ -40,7 +40,7 @@ public class ExistingBlockMask extends AbstractExtentMask { @Override public boolean test(BlockVector3 vector) { - return !getExtent().getBlock(vector).getBlockType().getMaterial().isAir(); + return !vector.getBlock(getExtent()).getMaterial().isAir(); } @Nullable diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java index eba02d0a7..37ba15339 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java @@ -75,8 +75,7 @@ public class ExpressionMask extends AbstractMask { if (timeout == null) { return expression.evaluate(vector.getX(), vector.getY(), vector.getZ()) > 0; } else { - return expression.evaluate(new double[]{vector.getX(), vector.getY(), vector.getZ()}, - timeout.getAsInt()) > 0; + return expression.evaluateTimeout(timeout.getAsInt(), vector.getX(), vector.getY(), vector.getZ()) > 0; } } catch (EvaluationException e) { return false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java index f2bc9bace..eab959f1d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java @@ -61,10 +61,10 @@ public class ExpressionMask2D extends AbstractMask2D { @Override public boolean test(BlockVector2 vector) { try { - if (timeout != null) { + if (timeout == null) { return expression.evaluate(vector.getX(), 0, vector.getZ()) > 0; } else { - return expression.evaluate(new double[]{vector.getX(), 0, vector.getZ()}, timeout.getAsInt()) > 0; + return expression.evaluate(timeout.getAsInt(), vector.getX(), 0, vector.getZ()) > 0; } } catch (EvaluationException e) { return false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index f335411f8..43bb43346 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.function.mask; -import com.boydti.fawe.beta.DelegateFilter; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.FilterBlock; import com.sk89q.worldedit.math.BlockVector3; @@ -38,15 +37,8 @@ public interface Mask { */ boolean test(BlockVector3 vector); - default DelegateFilter toFilter(T filter) { - return new DelegateFilter(filter) { - @Override - public void applyBlock(FilterBlock block) { - if (test(block)) { - filter.applyBlock(block); - } - } - }; + default MaskFilter toFilter(T filter) { + return new MaskFilter<>(filter, this); } /** @@ -81,15 +73,15 @@ public interface Mask { return value == null ? this : value; } -// default Mask and(Mask other) { -// Mask value = and(other); -// return value == null ? new MaskIntersection(this, other) : value; -// } + default Mask and(Mask other) { + Mask value = and(other); + return value == null ? MaskIntersection.of(this, other) : value; + } -// default Mask or(Mask other) { -// Mask value = or(other); -// return value == null ? new MaskUnion(this, other) : value; -// } + default Mask or(Mask other) { + Mask value = or(other); + return value == null ? MaskUnion.of(this, other) : value; + } default Mask inverse() { if (this instanceof Masks.AlwaysTrue) { @@ -99,4 +91,15 @@ public interface Mask { } return new InverseMask(this); } + + default Filter toFilter(Runnable run) { + return new Filter() { + @Override + public void applyBlock(FilterBlock block) { + if (test(block)) { + run.run(); + } + } + }; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskFilter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskFilter.java new file mode 100644 index 000000000..5e61405d5 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskFilter.java @@ -0,0 +1,38 @@ +package com.sk89q.worldedit.function.mask; + +import com.boydti.fawe.beta.DelegateFilter; +import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.FilterBlock; + +import java.util.function.Supplier; + +public class MaskFilter extends DelegateFilter { + private final Supplier supplier; + private final Mask mask; + + public MaskFilter(T other, Mask mask) { + this(other, () -> mask); + } + + public MaskFilter(T other, Supplier supplier) { + this(other, supplier, supplier.get()); + } + + public MaskFilter(T other, Supplier supplier, Mask root) { + super(other); + this.supplier = supplier; + this.mask = root; + } + + @Override + public void applyBlock(FilterBlock block) { + if (mask.test(block)) { + getParent().applyBlock(block); + } + } + + @Override + public MaskFilter newInstance(Filter other) { + return new MaskFilter<>(other, supplier); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java index a6a91513c..90c973c20 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java @@ -44,8 +44,9 @@ import javax.annotation.Nullable; */ public class MaskIntersection extends AbstractMask { - private final Set masks = new LinkedHashSet<>(); + private final Set masks; private Mask[] masksArray; + private boolean defaultReturn; /** * Create a new intersection. @@ -54,7 +55,7 @@ public class MaskIntersection extends AbstractMask { */ public MaskIntersection(Collection masks) { checkNotNull(masks); - this.masks.addAll(masks); + this.masks = new LinkedHashSet<>(masks); formArray(); } @@ -97,6 +98,7 @@ public class MaskIntersection extends AbstractMask { } else { masksArray = masks.toArray(new Mask[0]); } + this.defaultReturn = masksArray.length != 0; } public Function, Mask> pairingFunction() { @@ -155,7 +157,7 @@ public class MaskIntersection extends AbstractMask { } // Return result formArray(); - if (masks.size() == 0) return Masks.alwaysTrue(); + if (masks.isEmpty()) return Masks.alwaysTrue(); if (masks.size() == 1) return masks.iterator().next(); return changed ? this : null; } @@ -222,17 +224,13 @@ public class MaskIntersection extends AbstractMask { @Override public boolean test(BlockVector3 vector) { - if (masksArray.length == 0) { - return false; - } - for (Mask mask : masksArray) { if (!mask.test(vector)) { return false; } } - return true; + return defaultReturn; } @Nullable diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java index 41418430f..c4090ba1e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ChangeSetExecutor.java @@ -35,7 +35,23 @@ import java.util.List; */ public class ChangeSetExecutor implements Operation { - public enum Type {UNDO, REDO} + public enum Type { + UNDO { + @Override + public void perform(Change change, UndoContext context) { + change.undo(context); + } + }, + REDO { + @Override + public void perform(Change change, UndoContext context) { + change.redo(context); + } + } + ; + + public void perform(Change change, UndoContext context) {} + } private final Iterator iterator; private final Type type; @@ -68,13 +84,8 @@ public class ChangeSetExecutor implements Operation { public Operation resume(RunContext run) throws WorldEditException { while (iterator.hasNext()) { Change change = iterator.next(); - if (type == Type.UNDO) { - change.undo(context); - } else { - change.redo(context); - } + type.perform(change, context); } - return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java index 9fed46511..3ff8c65ac 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/WaterloggedRemover.java @@ -65,10 +65,9 @@ public class WaterloggedRemover extends AbstractExtentPattern { @Override public BaseBlock apply(BlockVector3 position) { BaseBlock block = getExtent().getFullBlock(position); - @SuppressWarnings("unchecked") - Property prop = (Property) remap[block.getOrdinal()].getBlockType().getPropertyMap().getOrDefault("waterlogged", null); - if (prop != null) { - return block.with(prop, false); + BlockState newState = remap[block.getOrdinal()]; + if (newState != null) { + return newState.toBaseBlock(block.getNbtData()); } return BlockTypes.AIR.getDefaultState().toBaseBlock(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java index 97fe6ec80..8ab83bf7b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java @@ -41,11 +41,24 @@ public class RegionVisitor implements Operation { public final Iterable iterable; + /** + * Deprecated in favor of the other constructors which will preload chunks during iteration + * + * @param region + * @param function + */ @Deprecated public RegionVisitor(Region region, RegionFunction function) { this((Iterable) region, function); } + /** + * Deprecated in favor of the other constructors which will preload chunks during iteration + * + * @param region + * @param function + */ + @Deprecated public RegionVisitor(Iterable iterable, RegionFunction function) { this.region = iterable instanceof Region ? (Region) iterable : null; this.function = function; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/ConvexPolyhedralRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/ConvexPolyhedralRegion.java index ce1fc5b22..68fe98b8f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/ConvexPolyhedralRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/ConvexPolyhedralRegion.java @@ -331,4 +331,9 @@ public class ConvexPolyhedralRegion extends AbstractRegion { public AbstractRegion clone() { return new ConvexPolyhedralRegion(this); } + + @Override + public boolean containsEntireCuboid(int bx, int tx, int by, int ty, int bz, int tz) { + return false; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 7135a6528..8e13a87d0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -22,22 +22,28 @@ package com.sk89q.worldedit.regions; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BlockVectorSet; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MutableBlockVector2; import com.sk89q.worldedit.math.MutableBlockVector3; +import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.storage.ChunkStore; import java.util.AbstractSet; +import java.util.Arrays; import java.util.Iterator; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.jetbrains.annotations.NotNull; @@ -631,14 +637,15 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { int z = chunk.getZ(); block = block.init(x, z, get); + if ((minX + 15) >> 4 <= x && (maxX - 15) >> 4 >= x && (minZ + 15) >> 4 <= z && (maxZ - 15) >> 4 >= z) { filter(chunk, filter, block, get, set, minY, maxY); return; } int localMinX = Math.max(minX, x << 4) & 15; - int localMaxX = Math.min(maxX, 15 + x << 4) & 15; + int localMaxX = Math.min(maxX, 15 + (x << 4)) & 15; int localMinZ = Math.max(minZ, z << 4) & 15; - int localMaxZ = Math.min(maxZ, 15 + z << 4) & 15; + int localMaxZ = Math.min(maxZ, 15 + (z << 4)) & 15; int yStart = (minY & 15); int yEnd = (maxY & 15); @@ -657,8 +664,87 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { filter(chunk, filter, block, get, set, minSection, localMinX, 0, localMinZ, localMaxX, 15, localMaxZ); maxSection--; } - for (int layer = minSection; layer < maxSection; layer++) { + for (int layer = minSection; layer <= maxSection; layer++) { filter(chunk, filter, block, get, set, layer, localMinX, yStart, localMinZ, localMaxX, yEnd, localMaxZ); } } + + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + int bx = chunk.getX() << 4; + int bz = chunk.getZ() << 4; + int tx = bx + 15; + int tz = bz + 15; + if (bx >= minX && tx <= maxX && bz >= minZ && tz <= maxZ) { + // contains all X/Z + if (minY <= 0 && maxY >= 255) { + return set; + } + trimY(set, minY, maxY); + trimNBT(set, this::contains); + return set; + } + else if (tx >= minX && bx <= maxX && tz >= minZ && bz <= maxZ) { + trimY(set, minY, maxY); + final int lowerX = Math.max(0, minX - bx); + final int upperX = Math.min(15, 15 + maxX - tx); + + final int lowerZ = Math.max(0, minZ - bz); + final int upperZ = Math.min(15, 15 + maxZ - tz); + + final int upperZi = ((upperZ + 1) << 4); + final int lowerZi = (lowerZ << 4); + + boolean trimX = lowerX != 0 || upperX != 15; + boolean trimZ = lowerZ != 0 || upperZ != 15; + + int indexY, index; + for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) { + if (set.hasSection(layer)) { + char[] arr = set.getArray(layer); + if (trimX || trimZ) { + indexY = 0; + for (int y = 0; y < 16; y++, indexY += 256) { + if (trimZ) { + index = indexY; + for (int z = 0; z < lowerZ; z++) { + // null the z values + for (int x = 0; x < 16; x++, index++) { + arr[index] = 0; + } + } + index = indexY + upperZi; + for (int z = upperZ + 1; z < 16; z++) { + // null the z values + for (int x = 0; x < 16; x++, index++) { + arr[index] = 0; + } + } + } + if (trimX) { + index = indexY + lowerZi; + for (int z = lowerZ; z <= upperZ; z++, index += 16) { + for (int x = 0; x < lowerX; x++) { + // null the x values + arr[index + x] = 0; + } + for (int x = upperX + 1; x < 16; x++) { + // null the x values + arr[index + x] = 0; + } + } + } + } + set.setBlocks(layer, arr); + } + } + } + trimNBT(set, this::contains); + return set; + } else { + return null; + } + } + + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java index 09368b52a..4fbcc9577 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/EllipsoidRegion.java @@ -246,10 +246,18 @@ public class EllipsoidRegion extends AbstractRegion { return cxd + cyd + czd <= 1; } + /* + /* Slow and unnecessary @Override public boolean contains(BlockVector3 position) { return position.subtract(center).toVector3().divide(radius).lengthSq() <= 1; } + */ + + @Override + public boolean contains(BlockVector3 position) { + return contains(position.getX(), position.getY(), position.getZ()); + } @Override public boolean contains(int x, int z) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java index 1027b7d4e..d51f6e465 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java @@ -473,4 +473,20 @@ public class Polygonal2DRegion extends AbstractRegion implements FlatRegion { return points; } + @Override + public boolean containsEntireCuboid(int bx, int tx, int by, int ty, int bz, int tz) { + for (int x = bx; x <= tx; x++) { + if (!contains(x, 0, bz)) return false; + } + for (int x = bx; x <= tx; x++) { + if (!contains(x, 0, tz)) return false; + } + for (int z = bz; z <= tz; z++) { + if (!contains(bx, 0, z)) return false; + } + for (int z = bz; z <= tz; z++) { + if (!contains(tx, 0, z)) return false; + } + return true; + } } 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 6dccae560..4283afad5 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 @@ -19,23 +19,32 @@ package com.sk89q.worldedit.regions; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.ChunkFilterBlock; import com.boydti.fawe.beta.Filter; +import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.object.FaweLimit; +import com.boydti.fawe.object.extent.SingleRegionExtent; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; + +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * Represents a physical shape. */ -public interface Region extends Iterable, Cloneable { +public interface Region extends Iterable, Cloneable, IBatchProcessor { /** * Get the lower point of a region. @@ -221,7 +230,7 @@ public interface Region extends Iterable, Cloneable { filter(chunk, filter, block, get, set, minSection, 0, yEnd); maxSection--; } - for (int layer = minSection; layer < maxSection; layer++) { + for (int layer = minSection; layer <= maxSection; layer++) { filter(chunk, filter, block, get, set, layer); } return; @@ -244,4 +253,66 @@ public interface Region extends Iterable, Cloneable { block = block.init(get, set, layer); block.filter(filter, yStart, yEnd); } + + default boolean containsEntireCuboid(int bx, int tx, int by, int ty, int bz, int tz) { + return contains(bx, by, bz) && + contains(bx, by, tz) && + contains(tx, by, bz) && + contains(tx, by, tz) && + contains(bx, ty, bz) && + contains(bx, ty, tz) && + contains(tx, ty, bz) && + contains(tx, ty, tz); + } + + default IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + int bx = chunk.getX() << 4; + int bz = chunk.getZ() << 4; + int tx = bx + 15; + int tz = bz + 15; + + BlockVector3 min = getMinimumPoint(); + BlockVector3 max = getMaximumPoint(); + boolean processExtra = false; + if (tx >= min.getX() && bx <= max.getX() && tz >= min.getZ() && bz <= max.getZ()) { + // contains some + for (int layer = 0; layer < 16; layer++) { + int by = layer << 4; + int ty = by + 15; + if (containsEntireCuboid(bx, tx, by, ty, bz, tz)) { + continue; + } else { + boolean changed = true; + processExtra = true; + char[] arr = set.getArray(layer); + for (int y = 0, index = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, index++) { + if (arr[index] != 0 && !contains(x, y, z)) { + changed = true; + arr[index] = 0; + } + } + } + } + if (changed) { + set.setBlocks(layer, arr); + } + } + } + if (processExtra) { + trimNBT(set, this::contains); + } + return set; + } else { + return null; + } + } + + default Extent construct(Extent child) { + if (isGlobal()) { + return child; + } + return new SingleRegionExtent(child, FaweLimit.MAX, this); + } } 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 cc1dbd954..e8c5067f3 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 @@ -19,6 +19,9 @@ package com.sk89q.worldedit.regions; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; import com.google.common.collect.Iterators; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.World; @@ -141,4 +144,29 @@ public class RegionIntersection extends AbstractRegion { return Iterators.concat(iterators); } + @Override + public boolean containsEntireCuboid(int bx, int tx, int by, int ty, int bz, int tz) { + for (Region region : regions) { + if (region.containsEntireCuboid(bx, tx, by, ty, bz, tz)) { + return true; + } + } + return false; + } + + @Override + public IChunkSet processBatch(IChunk chunk, IChunkGet get, IChunkSet set) { + int bx = chunk.getX() << 4; + int bz = chunk.getZ() << 4; + int tx = bx + 15; + int tz = bz + 15; + for (Region region : regions) { + BlockVector3 regMin = region.getMinimumPoint(); + BlockVector3 regMax = region.getMaximumPoint(); + if (tx >= regMin.getX() && bx <= regMax.getX() && tz >= regMin.getZ() && bz <= regMax.getZ()) { + return region.processBatch(chunk, get, set); + } + } + return null; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java index d13c58c98..f91c34480 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java @@ -165,6 +165,7 @@ public class SessionManager { session.setConfiguration(config); session.setBlockChangeLimit(config.defaultChangeLimit); session.setTimeout(config.calculationTimeout); + /* try { if (owner.hasPermission("worldedit.selection.pos")) { setDefaultWand(session.getWandItem(), config.wandItem, session, new SelectionWand()); @@ -178,6 +179,7 @@ public class SessionManager { log.warn("Invalid wand tool set in config. Tool will not be assigned: " + e.getItemType()); } } + */ // Remember the session regardless of if it's currently active or not. // And have the SessionTracker FLUSH inactive sessions. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java index 6125643ed..54ca79f09 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java @@ -139,16 +139,16 @@ public abstract class PaginationBox extends MessageBox { return new ListPaginationBox(header, pageCommand, lines); } - private static class ListPaginationBox extends PaginationBox { + public static class ListPaginationBox extends PaginationBox { private final Collection lines; private int iterIndex; private Iterator iterator; - ListPaginationBox(String header, String pageCommand, List lines) { + public ListPaginationBox(String header, String pageCommand, List lines) { this(header, pageCommand, (Collection) lines); } - ListPaginationBox(String header, String pageCommand, Collection lines) { + public ListPaginationBox(String header, String pageCommand, Collection lines) { super(header, pageCommand); this.lines = lines; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java index 3299f6a14..6a240e91a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.function.mask.BlockMaskBuilder; import com.sk89q.worldedit.function.mask.BlockTypeMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; @@ -71,7 +72,7 @@ public abstract class AbstractWorld implements World { @Override public Mask createLiquidMask() { - return new BlockTypeMask(this, BlockTypes.LAVA, BlockTypes.WATER); + return new BlockMaskBuilder().addTypes(BlockTypes.LAVA, BlockTypes.WATER).build(this); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java index 5cc6a232e..7bd73161a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java @@ -27,6 +27,7 @@ import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.PropertyKey; @@ -230,6 +231,12 @@ public class BaseBlock implements BlockStateHolder, TileEntityBlock { return true; } + @Override + public void applyTileEntity(OutputExtent output, int x, int y, int z) { + CompoundTag nbt = getNbtData(); + if (nbt != null) output.setTile(x, y, z, nbt); + } + @Override public BaseBlock withPropertyId(int propertyId) { return getBlockType().withPropertyId(propertyId).toBaseBlock(getNbtData()); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java index 19a1cbc82..956f5ea6b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java @@ -32,7 +32,7 @@ import java.util.Set; * blocks such as wool into separate ids. */ public class BlockCategory extends Category implements Keyed { - + private boolean[] flat_map; public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block tag"); public BlockCategory(final String id) { @@ -41,9 +41,19 @@ public class BlockCategory extends Category implements Keyed { @Override protected Set load() { - return WorldEdit.getInstance().getPlatformManager() + Set result = WorldEdit.getInstance().getPlatformManager() .queryCapability(Capability.GAME_HOOKS).getRegistries() .getBlockCategoryRegistry().getAll(this); + + int max = -1; + for (BlockType type : result) { + max = Math.max(max, type.getInternalId()); + } + this.flat_map = new boolean[max + 1]; + for (BlockType type : result) { + this.flat_map[type.getInternalId()] = true; + } + return result; } /** @@ -54,6 +64,7 @@ public class BlockCategory extends Category implements Keyed { * @return If it's a part of this category */ public > boolean contains(B blockStateHolder) { - return this.getAll().contains(blockStateHolder.getBlockType()); + int typeId = blockStateHolder.getBlockType().getInternalId(); + return flat_map.length > typeId && flat_map[typeId]; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index a0187ac4f..b91f19510 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.function.pattern.FawePattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.AbstractProperty; @@ -225,6 +226,11 @@ public class BlockState implements BlockStateHolder, FawePattern { return this.toBaseBlock(); } + @Override + public void applyTileEntity(OutputExtent output, int x, int y, int z) { + + } + /** * The internal id with no type information * @return @@ -296,7 +302,7 @@ public class BlockState implements BlockStateHolder, FawePattern { BlockType type = this.getBlockType(); // Lazily initialize the map Map map = Maps.asMap(type.getPropertiesSet(), (Function) this::getState); - return (Map, Object>) map; + return Collections.unmodifiableMap((Map, Object>) map); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java index e76db1bb1..1f0fd8850 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.world.block; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.blocks.TileEntityBlock; +import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.function.pattern.FawePattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; @@ -153,6 +154,8 @@ public interface BlockStateHolder> extends FawePat return toBaseBlock(); } + void applyTileEntity(OutputExtent output, int x, int y, int z); + /** * Return the name of the title entity ID. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java index 11302a619..421238f17 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.NullExtent; import com.sk89q.worldedit.function.mask.SingleBlockTypeMask; import com.sk89q.worldedit.function.pattern.FawePattern; import com.sk89q.worldedit.math.BlockVector3; @@ -287,6 +288,10 @@ public class BlockType implements FawePattern, Keyed { return this.getDefaultState().toBaseBlock(); } + public SingleBlockTypeMask toMask() { + return toMask(new NullExtent()); + } + public SingleBlockTypeMask toMask(Extent extent) { return new SingleBlockTypeMask(extent, this); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java index 6ab3de9b4..7d1be652a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java @@ -55,7 +55,7 @@ import java.util.stream.Stream; */ public final class BlockTypes { // Doesn't really matter what the hardcoded values are, as FAWE will update it on load - @Nullable public static final BlockType __RESERVED__ = null; + @Nullable public static final BlockType __RESERVED__ = null; // Placeholder for null index (i.e. when block types are represented as primitives) @Nullable public static final BlockType ACACIA_BUTTON = null; @Nullable public static final BlockType ACACIA_DOOR = null; @Nullable public static final BlockType ACACIA_FENCE = null; @@ -931,10 +931,6 @@ public final class BlockTypes { } } - // Add to $Registry -// for (BlockType type : values) { -// BlockType.REGISTRY.register(type.getId().toLowerCase(Locale.ROOT), type); -// } states = stateList.toArray(new BlockState[stateList.size()]); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java index 708e9c70e..bb86f0b65 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/ImmutableBaseBlock.java @@ -4,6 +4,7 @@ import com.boydti.fawe.beta.FilterBlock; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.OutputExtent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; @@ -35,6 +36,11 @@ public final class ImmutableBaseBlock extends BaseBlock { return set.setBlock(extent, toBlockState()); } + @Override + public void applyTileEntity(OutputExtent output, int x, int y, int z) { + + } + @Override public BaseBlock with(Property property, V value) { return toImmutableState().with(property, value).toBaseBlock(); diff --git a/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json b/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json index 293193290..27cc33be2 100644 --- a/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json +++ b/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json @@ -316,11 +316,17 @@ "47:0": "minecraft:bookshelf", "48:0": "minecraft:mossy_cobblestone", "49:0": "minecraft:obsidian", + "50:0": "minecraft:torch", "50:1": "minecraft:wall_torch[facing=east]", "50:2": "minecraft:wall_torch[facing=west]", "50:3": "minecraft:wall_torch[facing=south]", "50:4": "minecraft:wall_torch[facing=north]", "50:5": "minecraft:torch", + "50:9": "minecraft:wall_torch[facing=east]", + "50:10": "minecraft:wall_torch[facing=west]", + "50:11": "minecraft:wall_torch[facing=south]", + "50:12": "minecraft:wall_torch[facing=north]", + "50:13": "minecraft:torch", "51:0": "minecraft:fire[east=false,south=false,north=false,west=false,up=false,age=0]", "51:1": "minecraft:fire[east=false,south=false,north=false,west=false,up=false,age=1]", "51:2": "minecraft:fire[east=false,south=false,north=false,west=false,up=false,age=2]", @@ -346,10 +352,15 @@ "53:5": "minecraft:oak_stairs[half=top,shape=outer_right,facing=west]", "53:6": "minecraft:oak_stairs[half=top,shape=outer_right,facing=south]", "53:7": "minecraft:oak_stairs[half=top,shape=outer_right,facing=north]", + "54:0": "minecraft:chest", "54:2": "minecraft:chest[facing=north,type=single]", "54:3": "minecraft:chest[facing=south,type=single]", "54:4": "minecraft:chest[facing=west,type=single]", "54:5": "minecraft:chest[facing=east,type=single]", + "54:10": "minecraft:chest[facing=north]", + "54:11": "minecraft:chest[facing=south]", + "54:12": "minecraft:chest[facing=west]", + "54:13": "minecraft:chest[facing=east]", "55:0": "minecraft:redstone_wire[east=none,south=none,north=none,west=none,power=0]", "55:1": "minecraft:redstone_wire[east=none,south=none,north=none,west=none,power=1]", "55:2": "minecraft:redstone_wire[east=none,south=none,north=none,west=none,power=2]", @@ -385,14 +396,24 @@ "60:5": "minecraft:farmland[moisture=5]", "60:6": "minecraft:farmland[moisture=6]", "60:7": "minecraft:farmland[moisture=7]", + "61:0": "minecraft:furnace", "61:2": "minecraft:furnace[facing=north,lit=false]", "61:3": "minecraft:furnace[facing=south,lit=false]", "61:4": "minecraft:furnace[facing=west,lit=false]", "61:5": "minecraft:furnace[facing=east,lit=false]", + "61:10": "minecraft:furnace[facing=north,lit=false]", + "61:11": "minecraft:furnace[facing=south,lit=false]", + "61:12": "minecraft:furnace[facing=west,lit=false]", + "61:13": "minecraft:furnace[facing=east,lit=false]", + "62:0": "minecraft:furnace[lit=true]", "62:2": "minecraft:furnace[facing=north,lit=true]", "62:3": "minecraft:furnace[facing=south,lit=true]", "62:4": "minecraft:furnace[facing=west,lit=true]", "62:5": "minecraft:furnace[facing=east,lit=true]", + "62:10": "minecraft:furnace[facing=north,lit=true]", + "62:11": "minecraft:furnace[facing=south,lit=true]", + "62:12": "minecraft:furnace[facing=west,lit=true]", + "62:13": "minecraft:furnace[facing=east,lit=true]", "63:0": "minecraft:oak_sign[rotation=0]", "63:1": "minecraft:oak_sign[rotation=1]", "63:2": "minecraft:oak_sign[rotation=2]", @@ -421,10 +442,15 @@ "64:9": "minecraft:oak_door[hinge=right,half=upper,powered=false,facing=east,open=false]", "64:10": "minecraft:oak_door[hinge=left,half=upper,powered=true,facing=east,open=false]", "64:11": "minecraft:oak_door[hinge=right,half=upper,powered=true,facing=east,open=false]", + "65:0": "minecraft:ladder", "65:2": "minecraft:ladder[facing=north]", "65:3": "minecraft:ladder[facing=south]", "65:4": "minecraft:ladder[facing=west]", "65:5": "minecraft:ladder[facing=east]", + "65:10": "minecraft:ladder[facing=north]", + "65:11": "minecraft:ladder[facing=south]", + "65:12": "minecraft:ladder[facing=west]", + "65:13": "minecraft:ladder[facing=east]", "66:0": "minecraft:rail[shape=north_south]", "66:1": "minecraft:rail[shape=east_west]", "66:2": "minecraft:rail[shape=ascending_east]", @@ -443,10 +469,15 @@ "67:5": "minecraft:cobblestone_stairs[half=top,shape=straight,facing=west]", "67:6": "minecraft:cobblestone_stairs[half=top,shape=straight,facing=south]", "67:7": "minecraft:cobblestone_stairs[half=top,shape=straight,facing=north]", + "68:0": "minecraft:oak_wall_sign", "68:2": "minecraft:oak_wall_sign[facing=north]", "68:3": "minecraft:oak_wall_sign[facing=south]", "68:4": "minecraft:oak_wall_sign[facing=west]", "68:5": "minecraft:oak_wall_sign[facing=east]", + "68:10": "minecraft:oak_wall_sign[facing=north]", + "68:11": "minecraft:oak_wall_sign[facing=south]", + "68:12": "minecraft:oak_wall_sign[facing=west]", + "68:13": "minecraft:oak_wall_sign[facing=east]", "69:0": "minecraft:lever[powered=false,facing=north,face=ceiling]", "69:1": "minecraft:lever[powered=false,facing=east,face=wall]", "69:2": "minecraft:lever[powered=false,facing=west,face=wall]", @@ -481,16 +512,28 @@ "72:1": "minecraft:oak_pressure_plate[powered=true]", "73:0": "minecraft:redstone_ore[lit=false]", "74:0": "minecraft:redstone_ore[lit=true]", + "75:0": "minecraft:redstone_torch[lit=false]", "75:1": "minecraft:redstone_wall_torch[facing=east,lit=false]", "75:2": "minecraft:redstone_wall_torch[facing=west,lit=false]", "75:3": "minecraft:redstone_wall_torch[facing=south,lit=false]", "75:4": "minecraft:redstone_wall_torch[facing=north,lit=false]", "75:5": "minecraft:redstone_torch[lit=false]", + "75:9": "minecraft:redstone_wall_torch[facing=east,lit=false]", + "75:10": "minecraft:redstone_wall_torch[facing=west,lit=false]", + "75:11": "minecraft:redstone_wall_torch[facing=south,lit=false]", + "75:12": "minecraft:redstone_wall_torch[facing=north,lit=false]", + "75:13": "minecraft:redstone_wall_torch[lit=false]", + "76:0": "minecraft:redstone_torch[lit=true]", "76:1": "minecraft:redstone_wall_torch[facing=east,lit=true]", "76:2": "minecraft:redstone_wall_torch[facing=west,lit=true]", "76:3": "minecraft:redstone_wall_torch[facing=south,lit=true]", "76:4": "minecraft:redstone_wall_torch[facing=north,lit=true]", - "76:5": "minecraft:redstone_torch[lit=true]", + "76:5": "minecraft:redstone_wall_torch[lit=true]", + "76:9": "minecraft:redstone_wall_torch[facing=east,lit=true]", + "76:10": "minecraft:redstone_wall_torch[facing=west,lit=true]", + "76:11": "minecraft:redstone_wall_torch[facing=south,lit=true]", + "76:12": "minecraft:redstone_wall_torch[facing=north,lit=true]", + "76:13": "minecraft:redstone_wall_torch[lit=true]", "77:0": "minecraft:stone_button[powered=false,facing=east,face=ceiling]", "77:1": "minecraft:stone_button[powered=false,facing=east,face=wall]", "77:2": "minecraft:stone_button[powered=false,facing=west,face=wall]", @@ -556,8 +599,15 @@ "87:0": "minecraft:netherrack", "88:0": "minecraft:soul_sand", "89:0": "minecraft:glowstone", + "90:0": "minecraft:nether_portal", "90:1": "minecraft:nether_portal[axis=x]", "90:2": "minecraft:nether_portal[axis=z]", + "90:5": "minecraft:nether_portal[axis=x]", + "90:6": "minecraft:nether_portal[axis=z]", + "90:9": "minecraft:nether_portal[axis=x]", + "90:10": "minecraft:nether_portal[axis=z]", + "90:13": "minecraft:nether_portal[axis=x]", + "90:14": "minecraft:nether_portal[axis=z]", "91:0": "minecraft:jack_o_lantern[facing=south]", "91:1": "minecraft:jack_o_lantern[facing=west]", "91:2": "minecraft:jack_o_lantern[facing=north]", @@ -817,10 +867,15 @@ "128:6": "minecraft:sandstone_stairs[half=top,shape=straight,facing=south]", "128:7": "minecraft:sandstone_stairs[half=top,shape=straight,facing=north]", "129:0": "minecraft:emerald_ore", + "130:0": "minecraft:ender_chest", "130:2": "minecraft:ender_chest[facing=north]", "130:3": "minecraft:ender_chest[facing=south]", "130:4": "minecraft:ender_chest[facing=west]", "130:5": "minecraft:ender_chest[facing=east]", + "130:10": "minecraft:ender_chest[facing=north]", + "130:11": "minecraft:ender_chest[facing=south]", + "130:12": "minecraft:ender_chest[facing=west]", + "130:13": "minecraft:ender_chest[facing=east]", "131:0": "minecraft:tripwire_hook[powered=false,attached=false,facing=south]", "131:1": "minecraft:tripwire_hook[powered=false,attached=false,facing=west]", "131:2": "minecraft:tripwire_hook[powered=false,attached=false,facing=north]", @@ -953,10 +1008,15 @@ "145:9": "minecraft:damaged_anvil[facing=west]", "145:10": "minecraft:damaged_anvil[facing=north]", "145:11": "minecraft:damaged_anvil[facing=east]", + "146:0": "minecraft:trapped_chest", "146:2": "minecraft:trapped_chest[facing=north,type=single]", "146:3": "minecraft:trapped_chest[facing=south,type=single]", "146:4": "minecraft:trapped_chest[facing=west,type=single]", "146:5": "minecraft:trapped_chest[facing=east,type=single]", + "146:10": "minecraft:trapped_chest[facing=north,type=single]", + "146:11": "minecraft:trapped_chest[facing=south,type=single]", + "146:12": "minecraft:trapped_chest[facing=west,type=single]", + "146:13": "minecraft:trapped_chest[facing=east,type=single]", "147:0": "minecraft:light_weighted_pressure_plate[power=0]", "147:1": "minecraft:light_weighted_pressure_plate[power=1]", "147:2": "minecraft:light_weighted_pressure_plate[power=2]", @@ -1224,10 +1284,15 @@ "176:13": "minecraft:white_banner[rotation=13]", "176:14": "minecraft:white_banner[rotation=14]", "176:15": "minecraft:white_banner[rotation=15]", + "177:0": "minecraft:white_wall_banner", "177:2": "minecraft:white_wall_banner[facing=north]", "177:3": "minecraft:white_wall_banner[facing=south]", "177:4": "minecraft:white_wall_banner[facing=west]", "177:5": "minecraft:white_wall_banner[facing=east]", + "177:10": "minecraft:white_wall_banner[facing=north]", + "177:11": "minecraft:white_wall_banner[facing=south]", + "177:12": "minecraft:white_wall_banner[facing=west]", + "177:13": "minecraft:white_wall_banner[facing=east]", "178:0": "minecraft:daylight_detector[inverted=true,power=0]", "178:1": "minecraft:daylight_detector[inverted=true,power=1]", "178:2": "minecraft:daylight_detector[inverted=true,power=2]", diff --git a/worldedit-libs/core/ap/build.gradle.kts b/worldedit-libs/core/ap/build.gradle.kts index 44374359b..cfac0cf24 100644 --- a/worldedit-libs/core/ap/build.gradle.kts +++ b/worldedit-libs/core/ap/build.gradle.kts @@ -1,6 +1,9 @@ applyLibrariesConfiguration() dependencies { + "shade"("FAWE-Piston:core/build/libs/core-${Versions.PISTON}:lastSuccessfulBuild@jar") + "shade"("FAWE-Piston:core-ap/annotations/build/libs/annotations-${Versions.PISTON}:lastSuccessfulBuild@jar") + "shade"("FAWE-Piston:core-ap/processor/build/libs/processor-${Versions.PISTON}:lastSuccessfulBuild@jar") "shade"("org.enginehub.piston.core-ap:annotations:${Versions.PISTON}") "shade"("org.enginehub.piston.core-ap:processor:${Versions.PISTON}") } diff --git a/worldedit-libs/core/build.gradle.kts b/worldedit-libs/core/build.gradle.kts index 6e2505b51..4a115d889 100644 --- a/worldedit-libs/core/build.gradle.kts +++ b/worldedit-libs/core/build.gradle.kts @@ -11,7 +11,8 @@ dependencies { "shade"("com.github.luben:zstd-jni:1.4.3-1") "shade"("com.thoughtworks.paranamer:paranamer:2.6") "shade"("com.sk89q.lib:jlibnoise:1.0.0") - "shade"("org.enginehub.piston:core:${Versions.PISTON}") - "shade"("org.enginehub.piston.core-ap:runtime:${Versions.PISTON}") - "shade"("org.enginehub.piston:default-impl:${Versions.PISTON}") + "shade"("FAWE-Piston:core/build/libs/core-${Versions.PISTON}:lastSuccessfulBuild@jar") + "shade"("FAWE-Piston:core-ap/runtime/build/libs/runtime-${Versions.PISTON}:lastSuccessfulBuild@jar") + "shade"("FAWE-Piston:default-impl/build/libs/default-impl-${Versions.PISTON}:lastSuccessfulBuild@jar") + }