diff --git a/build.gradle b/build.gradle index 4ac8782c2..0c2f51f8e 100644 --- a/build.gradle +++ b/build.gradle @@ -38,30 +38,31 @@ println """ ******************************************* """ -group = 'com.boydti.fawe' +allprojects { + group = 'com.boydti.fawe' -def rootVersion = "1.13" -def revision = "" -def buildNumber = "" -def date = "" -ext { - git = Grgit.open(dir: '.git') - date = git.head().getDate().format("yy.MM.dd") - revision = "-${git.head().abbreviatedId}" - parents = git.head().parentIds; - if (project.hasProperty('buildnumber')) { - buildNumber = "$buildnumber" - } else { - index = -2109; // Offset to match CI - for (; parents != null && !parents.isEmpty(); index++) { - parents = git.getResolve().toCommit(parents.get(0)).getParentIds() + def rootVersion = "1.13" + def revision = "" + def buildNumber = "" + def date = "" + ext { + git = Grgit.open(dir: '.git') + date = git.head().getDate().format("yy.MM.dd") + revision = "-${git.head().abbreviatedId}" + parents = git.head().parentIds; + if (project.hasProperty('buildnumber')) { + buildNumber = "$buildnumber" + } else { + index = -2109; // Offset to match CI + for (; parents != null && !parents.isEmpty(); index++) { + parents = git.getResolve().toCommit(parents.get(0)).getParentIds() + } + buildNumber = "${index}" } - buildNumber = "${index}" } + + version = String.format("%s.%s", rootVersion, buildNumber) } - -version = String.format("%s.%s", rootVersion, buildNumber) - description = rootProject.name subprojects { @@ -97,6 +98,7 @@ configure(['worldedit-core', 'worldedit-bukkit', 'favs'].collect { project("$it" apply plugin: 'maven' // apply plugin: 'checkstyle' apply plugin: 'com.github.johnrengelman.shadow' +// apply plugin: 'com.jfrog.artifactory' // Enable this requires putting license header files in many, many FAWE files // apply plugin: 'net.minecrell.licenser' @@ -117,7 +119,7 @@ configure(['worldedit-core', 'worldedit-bukkit', 'favs'].collect { project("$it" } } - if (!(name.equals('worldedit-forge') || name.equals('worldedit-sponge'))) { + if (name == "worldedit-core" || name == "worldedit-bukkit") { task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' from sourceSets.main.allSource @@ -131,6 +133,11 @@ configure(['worldedit-core', 'worldedit-bukkit', 'favs'].collect { project("$it" // build.dependsOn(checkstyleMain) // build.dependsOn(checkstyleTest) +// build.dependsOn(javadocJar) +// +// artifactoryPublish { +// publishConfigs('archives') +// } dependencies { compileOnly 'org.jetbrains:annotations:17.0.0' @@ -176,5 +183,4 @@ configure(['worldedit-core', 'worldedit-bukkit', 'favs'].collect { project("$it" } } - } diff --git a/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java b/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java index a024f7d23..41fce3f5d 100644 --- a/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java +++ b/favs/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java @@ -51,6 +51,7 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.world.block.BlockTypes; import com.thevoxelbox.voxelsniper.brush.IBrush; +import com.thevoxelbox.voxelsniper.brush.JockeyBrush; import com.thevoxelbox.voxelsniper.brush.SnipeBrush; import com.thevoxelbox.voxelsniper.brush.perform.PerformBrush; import com.thevoxelbox.voxelsniper.brush.perform.Performer; @@ -201,22 +202,21 @@ public class Sniper { changeQueue = new ChangeSetFaweQueue(changeSet, maskQueue); } LocalSession session = fp.getSession(); - { // Set mask etc - Mask destMask = session.getMask(); - if (!Masks.isNull(destMask)) { - new MaskTraverser(destMask).reset(changeQueue); - changeQueue = new FaweQueueDelegateExtent(changeQueue, new MaskingExtent(changeQueue, destMask)); - } - Mask sourceMask = session.getSourceMask(); - if (!Masks.isNull(sourceMask)) { - new MaskTraverser(sourceMask).reset(changeQueue); - changeQueue = new FaweQueueDelegateExtent(changeQueue, new SourceMaskExtent(changeQueue, sourceMask)); - } - ResettableExtent transform = session.getTransform(); - if (transform != null) { - transform.setExtent(changeQueue); - changeQueue = new FaweQueueDelegateExtent(changeQueue, transform); - } + // Set mask etc + Mask destMask = session.getMask(); + if (!Masks.isNull(destMask)) { + new MaskTraverser(destMask).reset(changeQueue); + changeQueue = new FaweQueueDelegateExtent(changeQueue, new MaskingExtent(changeQueue, destMask)); + } + Mask sourceMask = session.getSourceMask(); + if (!Masks.isNull(sourceMask)) { + new MaskTraverser(sourceMask).reset(changeQueue); + changeQueue = new FaweQueueDelegateExtent(changeQueue, new SourceMaskExtent(changeQueue, sourceMask)); + } + ResettableExtent transform = session.getTransform(); + if (transform != null) { + transform.setExtent(changeQueue); + changeQueue = new FaweQueueDelegateExtent(changeQueue, transform); } AsyncWorld world = getWorld(); @@ -369,22 +369,19 @@ public class Sniper { snipeData.setExtent(world); Request.reset(); Request.request().setExtent(world); - switch (brush.getClass().getSimpleName()) { - case "JockeyBrush": - TaskManager.IMP.sync(new RunnableVal() { - @Override - public void run(Object value) { - brush.perform(snipeAction, snipeData, targetBlock, lastBlock); - } - }); - break; - default: - if (sniperTool.getCurrentBrush() instanceof PerformBrush) { - PerformBrush performerBrush = (PerformBrush) sniperTool.getCurrentBrush(); - performerBrush.initP(snipeData); + if (brush instanceof JockeyBrush) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value) { + brush.perform(snipeAction, snipeData, targetBlock, lastBlock); } - brush.perform(snipeAction, snipeData, targetBlock, lastBlock); - break; + }); + } else { + if (sniperTool.getCurrentBrush() instanceof PerformBrush) { + PerformBrush performerBrush = (PerformBrush) sniperTool.getCurrentBrush(); + performerBrush.initP(snipeData); + } + brush.perform(snipeAction, snipeData, targetBlock, lastBlock); } } finally { snipeData.setExtent(null); diff --git a/favs/src/main/java/com/thevoxelbox/voxelsniper/VoxelSniperListener.java b/favs/src/main/java/com/thevoxelbox/voxelsniper/VoxelSniperListener.java index 1d0eae2d9..eb0844e3f 100644 --- a/favs/src/main/java/com/thevoxelbox/voxelsniper/VoxelSniperListener.java +++ b/favs/src/main/java/com/thevoxelbox/voxelsniper/VoxelSniperListener.java @@ -3,7 +3,7 @@ package com.thevoxelbox.voxelsniper; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FawePlayer; import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.util.command.parametric.ExceptionConverter; import com.thevoxelbox.voxelsniper.api.command.VoxelCommand; import com.thevoxelbox.voxelsniper.command.*; @@ -71,7 +71,7 @@ public class VoxelSniperListener implements Listener { FawePlayer fp = FawePlayer.wrap(player); if (!fp.runAction(() -> { - ExceptionConverter exceptionConverter = CommandManager.getInstance().getExceptionConverter(); + ExceptionConverter exceptionConverter = PlatformCommandManager.getInstance().getExceptionConverter(); try { try { found.onCommand(player, split); diff --git a/worldedit-bukkit/build.gradle b/worldedit-bukkit/build.gradle index fb0785413..0b5f717b4 100644 --- a/worldedit-bukkit/build.gradle +++ b/worldedit-bukkit/build.gradle @@ -37,8 +37,6 @@ dependencies { compile 'com.thevoxelbox.voxelsniper:voxelsniper:5.171.0' compile 'com.comphenix.protocol:ProtocolLib-API:4.4.0-SNAPSHOT' compile 'com.wasteofplastic:askyblock:3.0.8.2' - compileOnly 'com.sk89q.worldguard:worldguard-core:7.0.0-20190215.210421-39' - compileOnly 'com.sk89q.worldguard:worldguard-legacy:7.0.0-20190215.210421-39' } processResources { @@ -49,6 +47,10 @@ processResources { ) include 'plugin.yml' } +// from (zipTree('src/main/resources/worldedit-adapters.jar').matching { +// exclude 'META-INF/' +// }) +// exclude '**/worldedit-adapters.jar' } jar.archiveName="fawe-bukkit-${project.parent.version}.jar" 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 dae7cba92..0f3846a80 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 @@ -3,22 +3,10 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; import com.boydti.fawe.bukkit.chat.BukkitChatManager; -import com.boydti.fawe.bukkit.listener.AsyncTabCompleteListener; import com.boydti.fawe.bukkit.listener.BrushListener; import com.boydti.fawe.bukkit.listener.BukkitImageListener; import com.boydti.fawe.bukkit.listener.RenderListener; -import com.boydti.fawe.bukkit.listener.SyncTabCompleteListener; -import com.boydti.fawe.bukkit.regions.ASkyBlockHook; -import com.boydti.fawe.bukkit.regions.FactionsFeature; -import com.boydti.fawe.bukkit.regions.FactionsOneFeature; -import com.boydti.fawe.bukkit.regions.FactionsUUIDFeature; -import com.boydti.fawe.bukkit.regions.FreeBuildRegion; -import com.boydti.fawe.bukkit.regions.GriefPreventionFeature; -import com.boydti.fawe.bukkit.regions.PreciousStonesFeature; -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.*; import com.boydti.fawe.bukkit.util.BukkitReflectionUtils; import com.boydti.fawe.bukkit.util.BukkitTaskMan; import com.boydti.fawe.bukkit.util.ItemUtil; @@ -39,7 +27,7 @@ import com.boydti.fawe.util.Jars; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.image.ImageViewer; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; + import com.sk89q.worldedit.world.World; import org.bukkit.Bukkit; import org.bukkit.command.ConsoleCommandSender; @@ -127,18 +115,6 @@ public class FaweBukkit implements IFawe, Listener { new ChunkListener_9(); } - try { - Class.forName("com.destroystokyo.paper.event.server.AsyncTabCompleteEvent"); - Bukkit.getPluginManager().registerEvents(new AsyncTabCompleteListener(WorldEditPlugin.getInstance()), plugin); - } catch (Throwable ignore) { - debug("====== USE PAPER ======"); - debug("DOWNLOAD: https://papermc.io/ci/job/Paper-1.13/"); - debug("GUIDE: https://www.spigotmc.org/threads/21726/"); - debug(" - This is only a recommendation"); - debug(" - Allows the use of Async Tab Completetion as provided by Paper"); - debug("=============================="); - Bukkit.getPluginManager().registerEvents(new SyncTabCompleteListener(WorldEditPlugin.getInstance()), plugin); - } }); } @@ -405,7 +381,7 @@ public class FaweBukkit implements IFawe, Listener { public Collection getMaskManagers() { final Plugin worldguardPlugin = Bukkit.getServer().getPluginManager().getPlugin("WorldGuard"); final ArrayList managers = new ArrayList<>(); - if ((worldguardPlugin != null) && worldguardPlugin.isEnabled()) { + if (worldguardPlugin != null && worldguardPlugin.isEnabled()) { try { managers.add(new Worldguard(worldguardPlugin, this)); managers.add(new WorldguardFlag(worldguardPlugin, this)); @@ -415,7 +391,7 @@ public class FaweBukkit implements IFawe, Listener { } } final Plugin townyPlugin = Bukkit.getServer().getPluginManager().getPlugin("Towny"); - if ((townyPlugin != null) && townyPlugin.isEnabled()) { + if (townyPlugin != null && townyPlugin.isEnabled()) { try { managers.add(new TownyFeature(townyPlugin, this)); Fawe.debug("Plugin 'Towny' found. Using it now."); @@ -424,7 +400,7 @@ public class FaweBukkit implements IFawe, Listener { } } final Plugin factionsPlugin = Bukkit.getServer().getPluginManager().getPlugin("Factions"); - if ((factionsPlugin != null) && factionsPlugin.isEnabled()) { + if (factionsPlugin != null && factionsPlugin.isEnabled()) { try { managers.add(new FactionsFeature(factionsPlugin)); Fawe.debug("Plugin 'Factions' found. Using it now."); @@ -444,7 +420,7 @@ public class FaweBukkit implements IFawe, Listener { } } final Plugin residencePlugin = Bukkit.getServer().getPluginManager().getPlugin("Residence"); - if ((residencePlugin != null) && residencePlugin.isEnabled()) { + if (residencePlugin != null && residencePlugin.isEnabled()) { try { managers.add(new ResidenceFeature(residencePlugin, this)); Fawe.debug("Plugin 'Residence' found. Using it now."); @@ -453,7 +429,7 @@ public class FaweBukkit implements IFawe, Listener { } } final Plugin griefpreventionPlugin = Bukkit.getServer().getPluginManager().getPlugin("GriefPrevention"); - if ((griefpreventionPlugin != null) && griefpreventionPlugin.isEnabled()) { + if (griefpreventionPlugin != null && griefpreventionPlugin.isEnabled()) { try { managers.add(new GriefPreventionFeature(griefpreventionPlugin)); Fawe.debug("Plugin 'GriefPrevention' found. Using it now."); @@ -462,7 +438,7 @@ public class FaweBukkit implements IFawe, Listener { } } final Plugin preciousstonesPlugin = Bukkit.getServer().getPluginManager().getPlugin("PreciousStones"); - if ((preciousstonesPlugin != null) && preciousstonesPlugin.isEnabled()) { + if (preciousstonesPlugin != null && preciousstonesPlugin.isEnabled()) { try { managers.add(new PreciousStonesFeature(preciousstonesPlugin, this)); Fawe.debug("Plugin 'PreciousStones' found. Using it now."); @@ -473,7 +449,7 @@ public class FaweBukkit implements IFawe, Listener { final Plugin aSkyBlock = Bukkit.getServer().getPluginManager().getPlugin("ASkyBlock"); - if ((aSkyBlock != null) && aSkyBlock.isEnabled()) { + if (aSkyBlock != null && aSkyBlock.isEnabled()) { try { managers.add(new ASkyBlockHook(aSkyBlock)); Fawe.debug("Plugin 'ASkyBlock' found. Using it now."); @@ -492,20 +468,6 @@ public class FaweBukkit implements IFawe, Listener { return managers; } -// -// @EventHandler -// public void onWorldLoad(WorldLoadEvent event) { -// org.bukkit.World world = event.getWorld(); -// world.setKeepSpawnInMemory(false); -// WorldServer nmsWorld = ((CraftWorld) world).getHandle(); -// ChunkProviderServer provider = nmsWorld.getChunkProviderServer(); -// try { -// Field fieldChunkLoader = provider.getClass().getDeclaredField("chunkLoader"); -// ReflectionUtils.setFailsafeFieldValue(fieldChunkLoader, provider, new FaweChunkLoader()); -// } catch (Throwable e) { -// e.printStackTrace(); -// } -// } @EventHandler(priority = EventPriority.MONITOR) public void onPlayerQuit(PlayerQuitEvent event) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ATabCompleteListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ATabCompleteListener.java deleted file mode 100644 index 48a79232e..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ATabCompleteListener.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.boydti.fawe.bukkit.listener; - -import com.boydti.fawe.object.string.MutableCharSequence; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; -import com.sk89q.worldedit.extension.platform.CommandManager; -import com.sk89q.worldedit.util.command.CommandMapping; -import com.sk89q.worldedit.util.command.Dispatcher; - -import org.bukkit.command.CommandSender; -import org.bukkit.event.Listener; - -import java.util.List; - -public class ATabCompleteListener implements Listener { - private final WorldEditPlugin worldEdit; - - public ATabCompleteListener(WorldEditPlugin worldEdit) { - this.worldEdit = worldEdit; - } - public List onTab(String buffer, CommandSender sender) { - int firstSpace = buffer.indexOf(' '); - if (firstSpace == -1) return null; - MutableCharSequence mBuffer = MutableCharSequence.getTemporal(); - mBuffer.setString(buffer); - mBuffer.setSubstring(0, firstSpace); - int index; - String label = buffer.substring(index = (mBuffer.indexOf(':') == -1 ? 1 : mBuffer.indexOf(':') + 1), firstSpace); - Dispatcher dispatcher = CommandManager.getInstance().getDispatcher(); - CommandMapping weCommand = dispatcher.get(label); - if (weCommand != null) { - CommandSuggestionEvent event = new CommandSuggestionEvent(worldEdit.wrapCommandSender(sender), buffer.substring(index)); - worldEdit.getWorldEdit().getEventBus().post(event); - List suggestions = event.getSuggestions(); - if (suggestions != null && !suggestions.isEmpty()) { - return suggestions; - } - } - return null; - } -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/AsyncTabCompleteListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/AsyncTabCompleteListener.java deleted file mode 100644 index 33253f629..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/AsyncTabCompleteListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.boydti.fawe.bukkit.listener; - -import com.boydti.fawe.util.TaskManager; -import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.event.EventHandler; -import org.bukkit.event.server.TabCompleteEvent; - -import java.util.Collections; -import java.util.List; - -public class AsyncTabCompleteListener extends ATabCompleteListener { - public AsyncTabCompleteListener(WorldEditPlugin worldEdit) { - super(worldEdit); - } - - @EventHandler - public void onTabComplete(AsyncTabCompleteEvent event) { - if (event.isCommand()) { - List result = this.onTab(event.getBuffer(), event.getSender()); - if (result != null && !result.isEmpty()) { - event.setCompletions(result); - event.setHandled(true); - } - } - } -} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BrushListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BrushListener.java index 9e41c4620..a560352ff 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BrushListener.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BrushListener.java @@ -1,6 +1,5 @@ package com.boydti.fawe.bukkit.listener; -import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.brush.MovableTool; import com.boydti.fawe.object.brush.ResettableTool; @@ -17,8 +16,6 @@ import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; import org.bukkit.plugin.Plugin; public class BrushListener implements Listener { @@ -47,16 +44,7 @@ public class BrushListener implements Listener { } ScrollTool scrollable = (ScrollTool) tool; if (scrollable.increment(player, ri)) { - if (Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES) { - bukkitPlayer.getInventory().setHeldItemSlot(oldSlot); - } else { - final PlayerInventory inv = bukkitPlayer.getInventory(); - final ItemStack item = inv.getItem(slot); - final ItemStack newItem = inv.getItem(oldSlot); - inv.setItem(slot, newItem); - inv.setItem(oldSlot, item); - bukkitPlayer.updateInventory(); - } + bukkitPlayer.getInventory().setHeldItemSlot(oldSlot); } } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java index 8d995846c..ca8478e7f 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/BukkitImageListener.java @@ -49,6 +49,7 @@ import java.util.Set; import java.util.UUID; public class BukkitImageListener implements Listener { + private Location mutable = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); public BukkitImageListener(Plugin plugin) { @@ -61,19 +62,20 @@ public class BukkitImageListener implements Listener { Iterator iter = recipients.iterator(); while (iter.hasNext()) { Player player = iter.next(); - if (player.equals(event.getPlayer())) continue; - FawePlayer fp = FawePlayer.wrap(player); - if (!fp.hasMeta()) continue; - CFICommands.CFISettings settings = fp.getMeta("CFISettings"); - if (settings == null || !settings.hasGenerator()) continue; + if (player.equals(event.getPlayer()) || !fp.hasMeta() || settings == null || !settings.hasGenerator()) { + continue; + } String name = player.getName().toLowerCase(); if (!event.getMessage().toLowerCase().contains(name)) { ArrayDeque buffered = fp.getMeta("CFIBufferedMessages"); - if (buffered == null) fp.setMeta("CFIBufferedMessaged", buffered = new ArrayDeque<>()); - String full = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); + if (buffered == null) { + fp.setMeta("CFIBufferedMessaged", buffered = new ArrayDeque<>()); + } + String full = String.format(event.getFormat(), event.getPlayer().getDisplayName(), + event.getMessage()); buffered.add(full); iter.remove(); } @@ -82,29 +84,42 @@ public class BukkitImageListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onHangingBreakByEntity(HangingBreakByEntityEvent event) { - if(!(event.getRemover() instanceof Player)) return; + if (!(event.getRemover() instanceof Player)) { + return; + } handleInteract(event, (Player) event.getRemover(), event.getEntity(), false); } @EventHandler(priority = EventPriority.LOWEST) public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { - if(!(event.getDamager() instanceof Player)) return; + if (!(event.getDamager() instanceof Player)) { + return; + } handleInteract(event, (Player) event.getDamager(), event.getEntity(), false); } @EventHandler(priority = EventPriority.LOWEST) public void onPlayerInteract(PlayerInteractEvent event) { - if (event.useItemInHand() == Event.Result.DENY) return; + if (event.useItemInHand() == Event.Result.DENY) { + return; + } Player player = event.getPlayer(); FawePlayer fp = FawePlayer.wrap(player); - if (fp.getMeta("CFISettings") == null) return; + if (fp.getMeta("CFISettings") == null) { + return; + } try { - if (event.getHand() == EquipmentSlot.OFF_HAND) return; - } catch (NoSuchFieldError | NoSuchMethodError ignored) {} + if (event.getHand() == EquipmentSlot.OFF_HAND) { + return; + } + } catch (NoSuchFieldError | NoSuchMethodError ignored) { + } List target = player.getLastTwoTargetBlocks(null, 100); - if (target.isEmpty()) return; + if (target.isEmpty()) { + return; + } Block targetBlock = target.get(0); World world = player.getWorld(); @@ -116,7 +131,8 @@ public class BukkitImageListener implements Listener { if (!entities.isEmpty()) { Action action = event.getAction(); - boolean primary = action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; + boolean primary = + action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; double minDist = Integer.MAX_VALUE; ItemFrame minItemFrame = null; @@ -137,7 +153,9 @@ public class BukkitImageListener implements Listener { } if (minItemFrame != null) { handleInteract(event, minItemFrame, primary); - if (event.isCancelled()) return; + if (event.isCancelled()) { + return; + } } } } @@ -148,10 +166,14 @@ public class BukkitImageListener implements Listener { } private BukkitImageViewer get(HeightMapMCAGenerator generator) { - if (generator == null) return null; + if (generator == null) { + return null; + } ImageViewer viewer = generator.getImageViewer(); - if (!(viewer instanceof BukkitImageViewer)) return null; + if (!(viewer instanceof BukkitImageViewer)) { + return null; + } return (BukkitImageViewer) viewer; } @@ -161,14 +183,18 @@ public class BukkitImageListener implements Listener { } private void handleInteract(Event event, Player player, Entity entity, boolean primary) { - if (!(entity instanceof ItemFrame)) return; + if (!(entity instanceof ItemFrame)) { + return; + } ItemFrame itemFrame = (ItemFrame) entity; FawePlayer fp = FawePlayer.wrap(player); CFICommands.CFISettings settings = fp.getMeta("CFISettings"); HeightMapMCAGenerator generator = settings == null ? null : settings.getGenerator(); BukkitImageViewer viewer = get(generator); - if (viewer == null) return; + if (viewer == null) { + return; + } if (itemFrame.getRotation() != Rotation.NONE) { itemFrame.setRotation(Rotation.NONE); @@ -178,7 +204,9 @@ public class BukkitImageListener implements Listener { BrushTool tool; try { tool = session.getBrushTool(fp.getPlayer(), false); - } catch (InvalidToolBindException e) { return; } + } catch (InvalidToolBindException e) { + return; + } ItemFrame[][] frames = viewer.getItemFrames(); if (frames == null || tool == null) { @@ -190,7 +218,9 @@ public class BukkitImageListener implements Listener { BrushSettings context = primary ? tool.getPrimary() : tool.getSecondary(); Brush brush = context.getBrush(); - if (brush == null) return; + if (brush == null) { + return; + } tool.setContext(context); if (event instanceof Cancellable) { @@ -208,7 +238,7 @@ public class BukkitImageListener implements Listener { double zRat = Math.sin(yawRad) * a; BlockFace facing = itemFrame.getFacing(); - double thickness = 1/32D + 1/128D; + double thickness = 1 / 32D + 1 / 128D; double modX = facing.getModX(); double modZ = facing.getModZ(); double dx = source.getX() - target.getX() - modX * thickness; @@ -225,8 +255,8 @@ public class BukkitImageListener implements Listener { localX = (modZ) * (dx - offset * xRat); } double localY = dy - offset * Math.sin(pitchRad); - int localPixelX = (int)((localX + 0.5) * 128); - int localPixelY = (int)((localY + 0.5) * 128); + int localPixelX = (int) ((localX + 0.5) * 128); + int localPixelY = (int) ((localY + 0.5) * 128); UUID uuid = itemFrame.getUniqueId(); for (int blockX = 0; blockX < frames.length; blockX++) { @@ -240,18 +270,25 @@ public class BukkitImageListener implements Listener { int worldX = (int) (pixelX * width / (frames.length * 128d)); int worldZ = (int) (pixelY * length / (frames[0].length * 128d)); - if (worldX < 0 || worldX > width || worldZ < 0 || worldZ > length) return; - + if (worldX < 0 || worldX > width || worldZ < 0 || worldZ > length) { + return; + } fp.runAction(() -> { BlockVector3 wPos = BlockVector3.at(worldX, 0, worldZ); viewer.refresh(); - int topY = generator.getNearestSurfaceTerrainBlock(wPos.getBlockX(), wPos.getBlockZ(), 255, 0, 255); + int topY = generator + .getNearestSurfaceTerrainBlock(wPos.getBlockX(), wPos.getBlockZ(), 255, + 0, 255); wPos = wPos.withY(topY); - EditSession es = new EditSessionBuilder(fp.getWorld()).player(fp).combineStages(false).autoQueue(false).blockBag(null).limitUnlimited().build(); + EditSession es = new EditSessionBuilder(fp.getWorld()).player(fp) + .combineStages(false).autoQueue(false).blockBag(null).limitUnlimited() + .build(); ExtentTraverser last = new ExtentTraverser(es.getExtent()).last(); - if (last.get() instanceof FastWorldEditExtent) last = last.previous(); + if (last.get() instanceof FastWorldEditExtent) { + last = last.previous(); + } last.setNext(generator); try { brush.build(es, wPos, context.getMaterial(), context.getSize()); @@ -262,9 +299,6 @@ public class BukkitImageListener implements Listener { viewer.view(generator); }, true, true); - - - return; } } 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 8824f369c..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 @@ -2,7 +2,6 @@ package com.boydti.fawe.bukkit.listener; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.util.TaskManager; import java.util.Iterator; import java.util.Map; @@ -14,7 +13,9 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.plugin.Plugin; @@ -46,7 +47,6 @@ public class RenderListener implements Listener { OFFSET = 8; timeOut = 2; } else { - int tpsSqr = tps32 * tps32; OFFSET = 1 + (tps32 / 102400); timeOut = 162 - (tps32 / 2560); } @@ -75,7 +75,7 @@ public class RenderListener implements Listener { }, 1); } - public void setViewDistance(Player player, int value) { + private void setViewDistance(Player player, int value) { UUID uuid = player.getUniqueId(); if (value == Settings.IMP.EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING) { views.remove(uuid); @@ -99,7 +99,7 @@ public class RenderListener implements Listener { player.setViewDistance(value); } - public int getViewDistance(Player player) { + private int getViewDistance(Player player) { int[] value = views.get(player.getUniqueId()); return value == null ? Settings.IMP.EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING : value[0]; } @@ -121,16 +121,15 @@ public class RenderListener implements Listener { } @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) { + public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); setViewDistance(player, 1); - FawePlayer fp = FawePlayer.wrap(player); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerLeave(org.bukkit.event.player.PlayerQuitEvent event) { + public void onPlayerLeave(PlayerQuitEvent event) { Player player = event.getPlayer(); UUID uid = player.getUniqueId(); views.remove(uid); } -} \ No newline at end of file +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/SyncTabCompleteListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/SyncTabCompleteListener.java deleted file mode 100644 index 02ebe9a85..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/SyncTabCompleteListener.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.boydti.fawe.bukkit.listener; - -import com.sk89q.worldedit.bukkit.WorldEditPlugin; - -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.event.EventHandler; -import org.bukkit.event.server.TabCompleteEvent; - -import java.util.List; - -public class SyncTabCompleteListener extends ATabCompleteListener { - public SyncTabCompleteListener(WorldEditPlugin worldEdit) { - super(worldEdit); - } - - @EventHandler - public void onTabComplete(TabCompleteEvent event) { - if (event.getSender() instanceof ConsoleCommandSender || event.getBuffer().startsWith("/")) { - List result = this.onTab(event.getBuffer(), event.getSender()); - if (result != null) { - event.setCompletions(result); - } - } - } -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 269cf41ab..47080122f 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -158,6 +158,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue return parent; } + @Override public FaweQueue getQueue() { return queue; } @@ -174,6 +175,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue return wrap(world); } + @Override public Operation commit() { flush(); return null; @@ -1271,7 +1273,7 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue public void spawnParticle(Particle arg0, double arg1, double arg2, double arg3, int arg4, double arg5, double arg6, double arg7, double arg8, T arg9, boolean arg10) { parent.spawnParticle(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - + } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java index 5f8bfbc8c..cdea2fda7 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java @@ -19,7 +19,7 @@ package com.sk89q.bukkit.util; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.CommandsManager; import org.bukkit.command.CommandExecutor; @@ -31,6 +31,8 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +@Deprecated +@SuppressWarnings("deprecation") public class CommandsManagerRegistration extends CommandRegistration { protected CommandsManager commands; diff --git a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java index dc65e60d8..77d95d2f5 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java @@ -22,6 +22,8 @@ package com.sk89q.bukkit.util; import com.sk89q.minecraft.util.commands.CommandsManager; import com.sk89q.util.StringUtil; import com.sk89q.wepif.PermissionsResolverManager; +import java.util.Arrays; +import java.util.List; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -29,12 +31,10 @@ import org.bukkit.command.PluginIdentifiableCommand; import org.bukkit.command.TabCompleter; import org.bukkit.plugin.Plugin; -import java.util.Arrays; -import java.util.List; - /** * An implementation of a dynamically registered {@link org.bukkit.command.Command} attached to a plugin */ +@SuppressWarnings("deprecation") public class DynamicPluginCommand extends org.bukkit.command.Command implements PluginIdentifiableCommand { protected final CommandExecutor owner; @@ -78,14 +78,14 @@ public class DynamicPluginCommand extends org.bukkit.command.Command implements return owningPlugin; } -// @Override -// public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { -// if (registeredWith instanceof CommandInspector) { -// return ((TabCompleter) owner).onTabComplete(sender, this, alias, args); -// } else { -// return super.tabComplete(sender, alias, args); -// } -// } + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + if (registeredWith instanceof CommandInspector) { + return ((TabCompleter) owner).onTabComplete(sender, this, alias, args); + } else { + return super.tabComplete(sender, alias, args); + } + } @SuppressWarnings("unchecked") @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java index 6f4cec526..7635d174e 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java @@ -30,6 +30,7 @@ import org.bukkit.help.HelpTopicFactory; import java.util.Map; +@SuppressWarnings("deprecation") public class DynamicPluginCommandHelpTopic extends HelpTopic { private final DynamicPluginCommand cmd; diff --git a/worldedit-bukkit/src/main/java/com/sk89q/wepif/FlatFilePermissionsResolver.java b/worldedit-bukkit/src/main/java/com/sk89q/wepif/FlatFilePermissionsResolver.java index 9b5229cf3..242a86f45 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/wepif/FlatFilePermissionsResolver.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/wepif/FlatFilePermissionsResolver.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -159,8 +160,8 @@ public class FlatFilePermissionsResolver implements PermissionsResolver { } } - userPermissionsCache.put(key.toLowerCase(), permsCache); - userGroups.put(key.toLowerCase(), new HashSet<>(Arrays.asList(groups))); + userPermissionsCache.put(key.toLowerCase(Locale.ROOT), permsCache); + userGroups.put(key.toLowerCase(Locale.ROOT), new HashSet<>(Arrays.asList(groups))); } } } catch (IOException e) { @@ -184,7 +185,7 @@ public class FlatFilePermissionsResolver implements PermissionsResolver { } } - Set perms = userPermissionsCache.get(player.toLowerCase()); + Set perms = userPermissionsCache.get(player.toLowerCase(Locale.ROOT)); if (perms == null) { return defaultPermissionsCache.contains(permission) || defaultPermissionsCache.contains("*"); @@ -201,13 +202,13 @@ public class FlatFilePermissionsResolver implements PermissionsResolver { @Override public boolean inGroup(String player, String group) { - Set groups = userGroups.get(player.toLowerCase()); + Set groups = userGroups.get(player.toLowerCase(Locale.ROOT)); return groups != null && groups.contains(group); } @Override public String[] getGroups(String player) { - Set groups = userGroups.get(player.toLowerCase()); + Set groups = userGroups.get(player.toLowerCase(Locale.ROOT)); if (groups == null) { return new String[0]; } 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 b85ec5341..88c7685d2 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,31 +21,25 @@ package com.sk89q.worldedit.bukkit; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.base.Function; import com.sk89q.worldedit.NotABlockException; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; 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.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; 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.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.item.ItemType; - import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -74,6 +68,12 @@ public enum BukkitAdapter { return INSTANCE.adapter; } + private static final ParserContext TO_BLOCK_CONTEXT = new ParserContext(); + + static { + TO_BLOCK_CONTEXT.setRestricted(false); + } + /** * Checks equality between a WorldEdit BlockType and a Bukkit Material * @@ -112,6 +112,26 @@ public enum BukkitAdapter { return getAdapter().adapt(world); } + /** + * Create a WorldEdit Player from a Bukkit Player. + * + * @param player The Bukkit player + * @return The WorldEdit player + */ + public static BukkitPlayer adapt(Player player) { + return getAdapter().adapt(player); + } + + /** + * Create a Bukkit Player from a WorldEdit Player. + * + * @param player The WorldEdit player + * @return The Bukkit player + */ + public static Player adapt(com.sk89q.worldedit.entity.Player player) { + return getAdapter().adapt(player); + } + /** * Create a WorldEdit location from a Bukkit location. * @@ -134,10 +154,24 @@ public enum BukkitAdapter { return getAdapter().adapt(location); } + /** + * Create a Bukkit location from a WorldEdit position with a Bukkit world. + * + * @param world the Bukkit world + * @param position the WorldEdit position + * @return a Bukkit location + */ public static org.bukkit.Location adapt(org.bukkit.World world, Vector3 position) { return getAdapter().adapt(world, position); } + /** + * Create a Bukkit location from a WorldEdit position with a Bukkit world. + * + * @param world the Bukkit world + * @param position the WorldEdit position + * @return a Bukkit location + */ public static org.bukkit.Location adapt(org.bukkit.World world, BlockVector3 position) { checkNotNull(world); checkNotNull(position); @@ -157,19 +191,26 @@ public enum BukkitAdapter { return getAdapter().adapt(world, location); } - public static Vector3 asVector(org.bukkit.Location location) { - return getAdapter().asVector(location); - } - /** * Create a WorldEdit Vector from a Bukkit location. * * @param location The Bukkit location * @return a WorldEdit vector */ + public static Vector3 asVector(org.bukkit.Location location) { + checkNotNull(location); + return Vector3.at(location.getX(), location.getY(), location.getZ()); + } + + /** + * Create a WorldEdit BlockVector from a Bukkit location. + * + * @param location The Bukkit location + * @return a WorldEdit vector + */ public static BlockVector3 asBlockVector(org.bukkit.Location location) { checkNotNull(location); - return getAdapter().asBlockVector(location); + return BlockVector3.at(location.getX(), location.getY(), location.getZ()); } /** @@ -329,12 +370,4 @@ public enum BukkitAdapter { checkNotNull(item); return getAdapter().adapt(item); } - - public static BukkitPlayer adapt(Player player) { - return getAdapter().adapt(player); - } - - public static Player adapt(com.sk89q.worldedit.entity.Player player) { - return getAdapter().adapt(player); - } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBiomeRegistry.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBiomeRegistry.java index d7e2f3c5a..27474678f 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBiomeRegistry.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBiomeRegistry.java @@ -40,4 +40,5 @@ class BukkitBiomeRegistry implements BiomeRegistry { final Biome bukkitBiome = BukkitAdapter.adapt(biome); return bukkitBiome == null ? null : bukkitBiome::name; } + } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java index fa3f2917f..2cf7713f4 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java @@ -19,26 +19,28 @@ package com.sk89q.worldedit.bukkit; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.bukkit.BukkitTextAdapter.reduceToText; + import com.sk89q.bukkit.util.CommandInspector; -import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.command.CommandMapping; -import com.sk89q.worldedit.util.command.Description; -import com.sk89q.worldedit.util.command.Dispatcher; +import java.util.Optional; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.inject.InjectedValueStore; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.inject.MapBackedValueStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkNotNull; - class BukkitCommandInspector implements CommandInspector { private static final Logger logger = LoggerFactory.getLogger(BukkitCommandInspector.class); private final WorldEditPlugin plugin; - private final Dispatcher dispatcher; + private final CommandManager dispatcher; - BukkitCommandInspector(WorldEditPlugin plugin, Dispatcher dispatcher) { + BukkitCommandInspector(WorldEditPlugin plugin, CommandManager dispatcher) { checkNotNull(plugin); checkNotNull(dispatcher); this.plugin = plugin; @@ -47,9 +49,9 @@ class BukkitCommandInspector implements CommandInspector { @Override public String getShortText(Command command) { - CommandMapping mapping = dispatcher.get(command.getName()); - if (mapping != null) { - return mapping.getDescription().getDescription(); + Optional mapping = dispatcher.getCommand(command.getName()); + if (mapping.isPresent()) { + return reduceToText(mapping.get().getDescription()); } else { logger.warn("BukkitCommandInspector doesn't know how about the command '" + command + "'"); return "Help text not available"; @@ -58,10 +60,9 @@ class BukkitCommandInspector implements CommandInspector { @Override public String getFullText(Command command) { - CommandMapping mapping = dispatcher.get(command.getName()); - if (mapping != null) { - Description description = mapping.getDescription(); - return "Usage: " + description.getUsage() + (description.getHelp() != null ? "\n" + description.getHelp() : ""); + Optional mapping = dispatcher.getCommand(command.getName()); + if (mapping.isPresent()) { + return reduceToText(mapping.get().getFullHelp()); } else { logger.warn("BukkitCommandInspector doesn't know how about the command '" + command + "'"); return "Help text not available"; @@ -70,11 +71,12 @@ class BukkitCommandInspector implements CommandInspector { @Override public boolean testPermission(CommandSender sender, Command command) { - CommandMapping mapping = dispatcher.get(command.getName()); - if (mapping != null) { - CommandLocals locals = new CommandLocals(); - locals.put(Actor.class, plugin.wrapCommandSender(sender)); - return mapping.getCallable().testPermission(locals); + Optional mapping = dispatcher.getCommand(command.getName()); + if (mapping.isPresent()) { + InjectedValueStore store = MapBackedValueStore.create(); + store.injectValue(Key.of(Actor.class), context -> + Optional.of(plugin.wrapCommandSender(sender))); + return mapping.get().getCondition().satisfied(store); } else { logger.warn("BukkitCommandInspector doesn't know how about the command '" + command + "'"); return false; diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java index 4059bf080..36f7d248d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java @@ -21,17 +21,21 @@ package com.sk89q.worldedit.bukkit; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.auth.AuthorizationException; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.adapter.bukkit.TextAdapter; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import javax.annotation.Nullable; import java.io.File; import java.util.UUID; +import javax.annotation.Nullable; + public class BukkitCommandSender implements Actor { /** @@ -89,6 +93,11 @@ public class BukkitCommandSender implements Actor { } } + @Override + public void print(Component component) { + TextAdapter.sendComponent(sender, component); + } + @Override public boolean canDestroyBedrock() { return true; 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 8f1ddc153..3dee53bf9 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 @@ -23,7 +23,6 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.TaskManager; - import com.sk89q.util.StringUtil; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; @@ -38,12 +37,19 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.HandSide; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.adapter.bukkit.TextAdapter; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nullable; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -53,11 +59,6 @@ import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - public class BukkitPlayer extends AbstractPlayerActor { private Player player; @@ -162,6 +163,11 @@ public class BukkitPlayer extends AbstractPlayerActor { } } + @Override + public void print(Component component) { + TextAdapter.sendComponent(player, component); + } + @Override public void setPosition(Vector3 pos, float pitch, float yaw) { if (pos instanceof com.sk89q.worldedit.util.Location) { @@ -187,12 +193,12 @@ public class BukkitPlayer extends AbstractPlayerActor { @Override public GameMode getGameMode() { - return GameModes.get(player.getGameMode().name().toLowerCase()); + return GameModes.get(player.getGameMode().name().toLowerCase(Locale.ROOT)); } @Override public void setGameMode(GameMode gameMode) { - player.setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase())); + player.setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT))); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitRegistries.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitRegistries.java index 12ff860cf..31923c69f 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitRegistries.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitRegistries.java @@ -65,10 +65,10 @@ class BukkitRegistries extends BundledRegistries { public BlockCategoryRegistry getBlockCategoryRegistry() { return blockCategoryRegistry; } - + @Override public ItemCategoryRegistry getItemCategoryRegistry() { - return itemCategoryRegistry; + return itemCategoryRegistry; } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 2415699ff..67fd7c1fa 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -19,29 +19,32 @@ package com.sk89q.worldedit.bukkit; +import static com.sk89q.worldedit.bukkit.BukkitTextAdapter.reduceToText; + import com.sk89q.bukkit.util.CommandInfo; import com.sk89q.bukkit.util.CommandRegistration; import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.command.util.PermissionCondition; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; -import com.sk89q.worldedit.util.command.CommandMapping; -import com.sk89q.worldedit.util.command.Description; -import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.registry.Registries; -import org.bukkit.Bukkit; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.entity.EntityType; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.EntityType; +import org.enginehub.piston.CommandManager; public class BukkitServerInterface implements MultiUserPlatform { public Server server; @@ -64,6 +67,22 @@ public class BukkitServerInterface implements MultiUserPlatform { return BukkitRegistries.getInstance(); } + @Override + public int getDataVersion() { + if (plugin.getBukkitImplAdapter() != null) { + return plugin.getBukkitImplAdapter().getDataVersion(); + } + return -1; + } + + @Override + public DataFixer getDataFixer() { + if (plugin.getBukkitImplAdapter() != null) { + return plugin.getBukkitImplAdapter().getDataFixer(); + } + return null; + } + @Override public boolean isValidMobType(String type) { final EntityType entityType = EntityType.fromName(type); @@ -115,20 +134,25 @@ public class BukkitServerInterface implements MultiUserPlatform { } @Override - public void registerCommands(Dispatcher dispatcher) { - List toRegister = new ArrayList<>(); + public void registerCommands(CommandManager dispatcher) { BukkitCommandInspector inspector = new BukkitCommandInspector(plugin, dispatcher); - for (CommandMapping command : dispatcher.getCommands()) { - Description description = command.getDescription(); - List permissions = description.getPermissions(); - String[] permissionsArray = new String[permissions.size()]; - permissions.toArray(permissionsArray); + dynamicCommands.register(dispatcher.getAllCommands() + .map(command -> { + String[] permissionsArray = command.getCondition() + .as(PermissionCondition.class) + .map(PermissionCondition::getPermissions) + .map(s -> s.toArray(new String[0])) + .orElseGet(() -> new String[0]); - toRegister.add(new CommandInfo(description.getUsage(), description.getDescription(), command.getAllAliases(), inspector, permissionsArray)); - } - - dynamicCommands.register(toRegister); + String[] aliases = Stream.concat( + Stream.of(command.getName()), + command.getAliases().stream() + ).toArray(String[]::new); + return new CommandInfo(reduceToText(command.getUsage()), + reduceToText(command.getDescription()), aliases, + inspector, permissionsArray); + }).collect(Collectors.toList())); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitTextAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitTextAdapter.java new file mode 100644 index 000000000..1ff04325f --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitTextAdapter.java @@ -0,0 +1,48 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit; + +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; + +public class BukkitTextAdapter { + + public static String reduceToText(Component component) { + StringBuilder text = new StringBuilder(); + appendTextTo(text, component); + return text.toString(); + } + + private static void appendTextTo(StringBuilder builder, Component component) { + if (component instanceof TextComponent) { + builder.append(((TextComponent) component).content()); + } else if (component instanceof TranslatableComponent) { + builder.append(((TranslatableComponent) component).key()); + } + for (Component child : component.children()) { + appendTextTo(builder, child); + } + } + + private BukkitTextAdapter() { + } + +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 887b48e7b..3391475a4 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.bukkit; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; @@ -38,6 +39,14 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; +import java.lang.ref.WeakReference; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; import org.bukkit.Effect; import org.bukkit.TreeType; import org.bukkit.World; @@ -47,16 +56,9 @@ import org.bukkit.block.Chest; import org.bukkit.entity.Entity; import org.bukkit.inventory.DoubleChestInventory; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; import org.slf4j.Logger; -import javax.annotation.Nullable; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class BukkitWorld extends AbstractWorld { private static final Logger logger = WorldEdit.logger; @@ -154,6 +156,11 @@ public class BukkitWorld extends AbstractWorld { return getWorld().getName(); } + @Override + public Path getStoragePath() { + return getWorld().getWorldFolder().toPath(); + } + @Override public int getBlockLightLevel(BlockVector3 pt) { return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel(); @@ -239,11 +246,11 @@ public class BukkitWorld extends AbstractWorld { return false; } BlockState state = block.getState(); - if (!(state instanceof org.bukkit.inventory.InventoryHolder)) { + if (!(state instanceof InventoryHolder)) { return false; } - org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state; + InventoryHolder chest = (InventoryHolder) state; Inventory inven = chest.getInventory(); if (chest instanceof Chest) { inven = getBlockInventory((Chest) chest); @@ -311,10 +318,13 @@ public class BukkitWorld extends AbstractWorld { @Override public boolean equals(Object other) { - if (other == null) { + if (worldRef.get() == null) { + return false; + } else if (other == null) { return false; } else if ((other instanceof BukkitWorld)) { - return ((BukkitWorld) other).getWorld().equals(getWorld()); + World otherWorld = ((BukkitWorld) other).worldRef.get(); + return otherWorld != null && otherWorld.equals(getWorld()); } else if (other instanceof com.sk89q.worldedit.world.World) { return ((com.sk89q.worldedit.world.World) other).getName().equals(getName()); } else { diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/CUIChannelListener.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/CUIChannelListener.java index fad38e27c..11dc865cf 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/CUIChannelListener.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/CUIChannelListener.java @@ -42,8 +42,9 @@ public class CUIChannelListener implements PluginMessageListener { public void onPluginMessageReceived(String channel, Player player, byte[] message) { LocalSession session = plugin.getSession(player); String text = new String(message, UTF_8_CHARSET); - session.handleCUIInitializationMessage(text); - session.describeCUI(plugin.wrapPlayer(player)); + final BukkitPlayer actor = plugin.wrapPlayer(player); + session.handleCUIInitializationMessage(text, actor); + session.describeCUI(actor); } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java index 3f1e3cfc1..f32ca1ac5 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java @@ -21,14 +21,10 @@ package com.sk89q.worldedit.bukkit; -import com.sk89q.minecraft.util.commands.CommandLocals; -import com.sk89q.util.StringUtil; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.command.CommandMapping; -import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.world.World; import org.bukkit.block.Block; import org.bukkit.event.Event.Result; @@ -36,14 +32,16 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandSendEvent; import org.bukkit.event.player.PlayerGameModeChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.inject.InjectedValueStore; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.inject.MapBackedValueStore; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.Optional; /** * Handles all events thrown in relation to a Player @@ -52,12 +50,6 @@ public class WorldEditListener implements Listener { private WorldEditPlugin plugin; - /** - * Called when a player plays an animation, such as an arm swing - * - * @param event Relevant event details - */ - /** * Construct the object; * @@ -77,48 +69,18 @@ public class WorldEditListener implements Listener { WorldEdit.getInstance().getSessionManager().get(plugin.wrapPlayer(event.getPlayer())); } - /** - * Called when a player attempts to use a command - * - * @param event Relevant event details - */ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { - String[] split = event.getMessage().split(" "); - - if (split.length > 0) { - split[0] = split[0].substring(1); - split = plugin.getWorldEdit().getPlatformManager().getCommandManager().commandDetection(split); - } - - final String newMessage = "/" + StringUtil.joinString(split, " "); - - if (!newMessage.equals(event.getMessage())) { - event.setMessage(newMessage); - plugin.getServer().getPluginManager().callEvent(event); - - if (!event.isCancelled()) { - if (!event.getMessage().isEmpty()) { - plugin.getServer().dispatchCommand(event.getPlayer(), event.getMessage().substring(1)); - } - - event.setCancelled(true); - } - } - } - - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onPlayerCommand(PlayerCommandSendEvent event) { - Dispatcher dispatcher = plugin.getWorldEdit().getPlatformManager().getCommandManager().getDispatcher(); - if (dispatcher != null) { - CommandLocals locals = new CommandLocals(); - locals.put(Actor.class, plugin.wrapCommandSender(event.getPlayer())); - Set toRemove = plugin.getWorldEdit().getPlatformManager().getCommandManager().getDispatcher().getCommands().stream() - .filter(commandMapping -> !commandMapping.getCallable().testPermission(locals)) - .map(CommandMapping::getPrimaryAlias) - .collect(Collectors.toSet()); - event.getCommands().removeIf(toRemove::contains); - } + public void onPlayerCommandSend(PlayerCommandSendEvent event) { + InjectedValueStore store = MapBackedValueStore.create(); + store.injectValue(Key.of(Actor.class), context -> + Optional.of(plugin.wrapCommandSender(event.getPlayer()))); + CommandManager commandManager = plugin.getWorldEdit().getPlatformManager().getPlatformCommandManager().getCommandManager(); + event.getCommands().removeIf(name -> + // remove if in the manager and not satisfied + commandManager.getCommand(name) + .filter(command -> !command.getCondition().satisfied(store)) + .isPresent() + ); } /** @@ -136,12 +98,8 @@ public class WorldEditListener implements Listener { return; } - try { - if (event.getHand() == EquipmentSlot.OFF_HAND) { - return; // TODO api needs to be able to get either hand depending on event - // for now just ignore all off hand interacts - } - } catch (NoSuchMethodError | NoSuchFieldError ignored) { + if (event.getHand() == EquipmentSlot.OFF_HAND) { + return; } final Player player = plugin.wrapPlayer(event.getPlayer()); @@ -167,7 +125,6 @@ public class WorldEditListener implements Listener { event.setCancelled(true); } - } else if (action == Action.RIGHT_CLICK_BLOCK) { final Block clickedBlock = event.getClickedBlock(); final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); 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 15b79a4d9..1613322f3 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 @@ -19,8 +19,6 @@ package com.sk89q.worldedit.bukkit; -import com.bekvon.bukkit.residence.commands.message; -import com.bekvon.bukkit.residence.containers.cmd; import com.boydti.fawe.Fawe; import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.bukkit.adapter.v1_13_1.Spigot_v1_13_R2; @@ -37,17 +35,20 @@ import com.sk89q.worldedit.bukkit.adapter.AdapterLoadException; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplLoader; import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockCategory; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.LegacyMapper; +import io.papermc.lib.PaperLib; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Tag; @@ -55,6 +56,8 @@ import org.bukkit.block.Biome; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; @@ -75,6 +78,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarFile; import java.util.logging.Level; @@ -192,10 +196,11 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter // Now we can register events getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); + // register async tab complete, if available + if (PaperLib.isPaper()) { + getServer().getPluginManager().registerEvents(new AsyncTabCompleteListener(), this); + } - // If we are on MCPC+/Cauldron, then Forge will have already loaded - // Forge WorldEdit and there's (probably) not going to be any other - // platforms to be worried about... at the current time of writing WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); // Register 1.13 Material ids with LegacyMapper @@ -205,6 +210,8 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter legacyMapper.register(m.getId(), 0, BukkitAdapter.adapt(m).getDefaultState()); } } + + PaperLib.suggestPaper(this); } public void setupRegistries() { @@ -228,11 +235,12 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter ).toImmutableState(); BlockState defaultState = blockState.getBlockType().getAllStates().get(0); for (Map.Entry, Object> propertyObjectEntry : state.getStates().entrySet()) { - defaultState = defaultState.with((Property) propertyObjectEntry.getKey(), propertyObjectEntry.getValue()); + //noinspection unchecked + defaultState = defaultState.with((Property) propertyObjectEntry.getKey(), propertyObjectEntry.getValue()); } return defaultState; } catch (InputParseException e) { - e.printStackTrace(); + getLogger().log(Level.WARNING, "Error loading block state for " + material.getKey(), e); return blockState; } })); @@ -260,7 +268,7 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter for (Tag itemTag : Bukkit.getTags(Tag.REGISTRY_ITEMS, Material.class)) { ItemCategory.REGISTRY.register(itemTag.getKey().toString(), new ItemCategory(itemTag.getKey().toString())); } - } catch (NoSuchMethodError e) { + } catch (NoSuchMethodError ignored) { getLogger().warning("The version of Spigot/Paper you are using doesn't support Tags. The usage of tags with WorldEdit will not work until you update."); } } @@ -554,4 +562,32 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter return bukkitAdapter; } + private class AsyncTabCompleteListener implements Listener { + AsyncTabCompleteListener() { + } + + @SuppressWarnings("UnnecessaryFullyQualifiedName") + @EventHandler(ignoreCancelled = true) + public void onAsyncTabComplete(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event) { + if (!event.isCommand()) return; + + String buffer = event.getBuffer(); + final String[] parts = buffer.split(" "); + if (parts.length < 1) return; + final String label = parts[0]; + final Optional command + = WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getCommandManager().getCommand(label); + if (!command.isPresent()) return; + + CommandSuggestionEvent suggestEvent = new CommandSuggestionEvent(wrapCommandSender(event.getSender()), event.getBuffer()); + getWorldEdit().getEventBus().post(suggestEvent); + List suggestions = suggestEvent.getSuggestions(); + if (suggestions != null && !suggestions.isEmpty()) { + event.setCompletions(suggestions); + event.setHandled(true); + } + //event.setCompletions(CommandUtil.fixSuggestions(event.getBuffer(), suggestEvent.getSuggestions())); + //event.setHandled(true); + } + } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index e1d965e13..8e5bde860 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -24,6 +24,7 @@ import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -44,6 +45,21 @@ import javax.annotation.Nullable; */ public interface BukkitImplAdapter extends IBukkitAdapter { + /** + * Get the Minecraft data version for the current world data. + * + * @return the data version + */ + int getDataVersion(); + + /** + * Get a data fixer, or null if not supported + * + * @return the data fixer + */ + @Nullable + DataFixer getDataFixer(); + /** * Get the block at the given location. * @@ -104,7 +120,7 @@ public interface BukkitImplAdapter extends IBukkitAdapter { default BlockMaterial getMaterial(BlockType blockType) { return null; } - + default BlockMaterial getMaterial(BlockState blockState) { return null; } @@ -116,7 +132,7 @@ public interface BukkitImplAdapter extends IBukkitAdapter { default T fromNative(Tag foreign) { return null; } - + /** * Send the given NBT data to the player. * diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java index 8d99181d6..fb59444cd 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java @@ -52,7 +52,7 @@ public class BukkitImplLoader { "**\n" + "** When working with blocks or undoing, chests will be empty, signs\n" + "** will be blank, and so on. There will be no support for entity\n" + - "** and biome-related functions.\n" + + "** and block property-related functions.\n" + "**\n" + "** Please see http://wiki.sk89q.com/wiki/WorldEdit/Bukkit_adapters\n" + "**********************************************\n"; @@ -160,9 +160,6 @@ public class BukkitImplLoader { if (cls.isSynthetic()) continue; if (BukkitImplAdapter.class.isAssignableFrom(cls)) { return (BukkitImplAdapter) cls.newInstance(); - } else { - log.warn("Failed to load the Bukkit adapter class '" + className + - "' because it does not implement " + BukkitImplAdapter.class.getCanonicalName()); } } catch (ClassNotFoundException e) { log.warn("Failed to load the Bukkit adapter class '" + className + @@ -171,7 +168,6 @@ public class BukkitImplLoader { log.warn("Failed to load the Bukkit adapter class '" + className + "' that is not supposed to be raising this error", e); } catch (Throwable e) { - e.printStackTrace(); if (className.equals(customCandidate)) { log.warn("Failed to load the Bukkit adapter class '" + className + "'", e); } 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 b3e7adc07..d7a87ab16 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 @@ -371,6 +371,6 @@ public interface IBukkitAdapter { } default BiomeType adapt(Biome biome) { - return BiomeTypes.get(biome.name().toLowerCase()); + return BiomeTypes.register(biome.name().toLowerCase()); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java index 763b22c02..348aaa0be 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java @@ -25,7 +25,7 @@ import com.sk89q.worldedit.extension.factory.DefaultTransformParser; import com.sk89q.worldedit.extension.factory.parser.mask.DefaultMaskParser; import com.sk89q.worldedit.extension.factory.parser.pattern.DefaultPatternParser; import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -152,7 +152,7 @@ public class FaweAPI { * @param aliases The aliases to give the command (or none) */ public static void registerCommands(Object clazz, String... aliases) { - CommandManager.getInstance().registerCommands(clazz, aliases); + PlatformCommandManager.getInstance().registerCommands(clazz, aliases); } /** diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java b/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java index c1430dab8..489538bda 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java @@ -1,11 +1,24 @@ package com.boydti.fawe.command; -import com.boydti.fawe.Fawe; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.FaweAPI; -import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.BBC; -import com.boydti.fawe.jnbt.anvil.*; -import com.boydti.fawe.jnbt.anvil.filters.*; +import com.boydti.fawe.jnbt.anvil.MCAClipboard; +import com.boydti.fawe.jnbt.anvil.MCAFile; +import com.boydti.fawe.jnbt.anvil.MCAFilter; +import com.boydti.fawe.jnbt.anvil.MCAFilterCounter; +import com.boydti.fawe.jnbt.anvil.MCAQueue; +import com.boydti.fawe.jnbt.anvil.filters.DebugFixP2Roads; +import com.boydti.fawe.jnbt.anvil.filters.DeleteBiomeFilterSimple; +import com.boydti.fawe.jnbt.anvil.filters.DeleteOldFilter; +import com.boydti.fawe.jnbt.anvil.filters.DeleteUnclaimedFilter; +import com.boydti.fawe.jnbt.anvil.filters.DeleteUninhabitedFilter; +import com.boydti.fawe.jnbt.anvil.filters.PlotTrimFilter; +import com.boydti.fawe.jnbt.anvil.filters.RemapFilter; +import com.boydti.fawe.jnbt.anvil.filters.RemoveLayerFilter; +import com.boydti.fawe.jnbt.anvil.filters.SetPatternFilter; +import com.boydti.fawe.jnbt.anvil.filters.TrimAirFilter; import com.boydti.fawe.jnbt.anvil.history.IAnvilHistory; import com.boydti.fawe.jnbt.anvil.history.NullAnvilHistory; import com.boydti.fawe.object.FawePlayer; @@ -14,17 +27,16 @@ import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal4; import com.boydti.fawe.object.changeset.AnvilHistory; import com.boydti.fawe.object.clipboard.remap.ClipboardRemapper; -import com.boydti.fawe.object.mask.FaweBlockMatcher; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.SetQueue; -import com.boydti.fawe.util.StringMan; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.function.pattern.RandomPattern; import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; @@ -33,15 +45,10 @@ import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; - import java.io.IOException; import java.io.RandomAccessFile; -import java.util.*; import java.util.function.Consumer; - -import static com.google.common.base.Preconditions.checkNotNull; - @Command(aliases = {"/anvil"}, desc = "Manipulate billions of blocks: [More Info](https://github.com/boy0001/FastAsyncWorldedit/wiki/Anvil-API)") public class AnvilCommands { @@ -139,9 +146,7 @@ public class AnvilCommands { desc = "Replace all blocks in the selection with another", help = "Replace all blocks in the selection with another\n" + "The -d flag disabled wildcard data matching\n", - flags = "df", - min = 2, - max = 4 + flags = "df" ) @CommandPermissions("worldedit.anvil.replaceall") public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException { @@ -164,9 +169,7 @@ public class AnvilCommands { aliases = {"remapall"}, usage = "", help = "Remap the world between MCPE/PC values", - desc = "Remap the world between MCPE/PC values", - min = 1, - max = 1 + desc = "Remap the world between MCPE/PC values" ) @CommandPermissions("worldedit.anvil.remapall") public void remapall(Player player, String folder) throws WorldEditException { @@ -188,9 +191,7 @@ public class AnvilCommands { help = "Delete all chunks which haven't been occupied for `age-ticks` (20t = 1s) and \n" + "Have not been accessed since `file-duration` (ms) after creation and\n" + "Have not been used in the past `chunk-inactivity` (ms)" + - "The auto-save interval is the recommended value for `file-duration` and `chunk-inactivity`", - min = 2, - max = 3 + "The auto-save interval is the recommended value for `file-duration` and `chunk-inactivity`" ) @CommandPermissions("worldedit.anvil.deleteallunvisited") public void deleteAllUnvisited(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileDurationMillis) throws WorldEditException { @@ -220,7 +221,7 @@ public class AnvilCommands { } @Command( - aliases = {"deleteunclaimed"}, + name = "deleteunclaimed", usage = " [file-age=60000]", desc = "(Supports: WG, P2, GP) Delete all chunks which haven't been occupied AND claimed", help = "(Supports: WG, P2, GP) Delete all chunks which aren't claimed AND haven't been occupied for `age-ticks` (20t = 1s) and \n" + @@ -245,9 +246,7 @@ public class AnvilCommands { help = "Delete regions which haven't been accessed in a certain amount of time\n" + "You can use seconds (s), minutes (m), hours (h), days (d), weeks (w), years (y)\n" + "(months are not a unit of time)\n" + - "E.g. 8h5m12s\n", - min = 2, - max = 3 + "E.g. 8h5m12s\n" ) @CommandPermissions("worldedit.anvil.deletealloldregions") public void deleteAllOldRegions(Player player, String folder, String time) throws WorldEditException { @@ -258,7 +257,7 @@ public class AnvilCommands { } @Command( - aliases = {"trimallplots", }, + name = "trimallplots", desc = "Trim chunks in a Plot World", help = "Trim chunks in a Plot World\n" + "Unclaimed chunks will be deleted\n" + @@ -278,7 +277,7 @@ public class AnvilCommands { } @Command( - aliases = {"deletebiomechunks", }, + name = "deletebiomechunks", desc = "Delete chunks matching a specific biome" ) @CommandPermissions("worldedit.anvil.trimallair") @@ -289,7 +288,7 @@ public class AnvilCommands { } @Command( - aliases = {"trimallair", }, + name = "trimallair", desc = "Trim all air in the world" ) @CommandPermissions("worldedit.anvil.trimallair") @@ -300,7 +299,7 @@ public class AnvilCommands { } @Command( - aliases = {"debugfixroads", }, + name = "debugfixroads", desc = "debug - do not use" ) @CommandPermissions("worldedit.anvil.debugfixroads") @@ -314,9 +313,7 @@ public class AnvilCommands { aliases = {"replaceallpattern", "reap", "repallpat"}, usage = " [from-block] ", desc = "Replace all blocks in the selection with another", - flags = "dm", - min = 2, - max = 4 + flags = "dm" ) @CommandPermissions("worldedit.anvil.replaceall") public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException { @@ -346,9 +343,7 @@ public class AnvilCommands { aliases = {"countall"}, usage = " [hasSky] ", desc = "Count all blocks in a world", - flags = "d", - min = 2, - max = 3 + flags = "d" ) @CommandPermissions("worldedit.anvil.countall") public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException { 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 942971567..7db691e92 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 @@ -4,7 +4,7 @@ import com.boydti.fawe.config.Commands; import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.changeset.CFIChangeSet; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -28,12 +28,9 @@ public class CFICommand extends MethodCommands { } @Command( - aliases = {"cfi", "createfromimage"}, - usage = "", - min = 0, - max = -1, - anyFlags = true, - desc = "Start CreateFromImage" + name = "cfi", + aliases = {"cfi", "createfromimage"}, + desc = "Start CreateFromImage" ) @CommandPermissions("worldedit.anvil.cfi") public void cfi(FawePlayer fp, CommandContext context) throws CommandException, IOException { @@ -51,49 +48,49 @@ public class CFICommand extends MethodCommands { private void dispatch(FawePlayer fp, CFICommands.CFISettings settings, CommandContext context) throws CommandException { if (!settings.hasGenerator()) { - switch (context.argsLength()) { - case 0: { - String hmCmd = child.alias() + " "; - if (settings.image == null) { - hmCmd += "image"; - } else { - hmCmd = Commands.getAlias(CFICommands.class, "heightmap") + " " + settings.imageArg; - } - child.msg("What do you want to use as the base?").newline() - .text("&7[&aHeightMap&7]").cmdTip(hmCmd).text(" - A heightmap like ").text("&7[&athis&7]").linkTip("http://i.imgur.com/qCd30MR.jpg") - .newline() - .text("&7[&aEmpty&7]").cmdTip(child.alias() + " empty").text("- An empty map of a specific size") - .send(fp); - break; + if (context.argsLength() == 0) { + String hmCmd = child.alias() + " "; + if (settings.image == null) { + hmCmd += "image"; + } else { + hmCmd = + Commands.getAlias(CFICommands.class, "heightmap") + " " + settings.imageArg; } - default: { - String remaining = context.getJoinedStrings(0); - if (!dispatcher.contains(context.getString(0))) { - switch (context.argsLength()) { - case 1: { - String cmd = Commands.getAlias(CFICommands.class, "heightmap") + " " + context.getJoinedStrings(0); - dispatcher.call(cmd, context.getLocals(), new String[0]); - return; - } - case 2: { - String cmd = Commands.getAlias(CFICommands.class, "empty") + " " + context.getJoinedStrings(0); - dispatcher.call(cmd, context.getLocals(), new String[0]); - return; - } + child.msg("What do you want to use as the base?").newline() + .text("[HeightMap]").cmdTip(hmCmd).text(" - A heightmap like ") + .text("[this]").linkTip("http://i.imgur.com/qCd30MR.jpg") + .newline() + .text("[Empty]").cmdTip(child.alias() + " empty") + .text("- An empty map of a specific size") + .send(fp); + } else { + String remaining = context.getJoinedStrings(0); + if (!dispatcher.contains(context.getString(0))) { + switch (context.argsLength()) { + case 1: { + String cmd = + Commands.getAlias(CFICommands.class, "heightmap") + " " + context + .getJoinedStrings(0); + dispatcher.call(cmd, context.getLocals(), new String[0]); + return; + } + case 2: { + String cmd = + Commands.getAlias(CFICommands.class, "empty") + " " + context + .getJoinedStrings(0); + dispatcher.call(cmd, context.getLocals(), new String[0]); + return; } } - dispatcher.call(remaining, context.getLocals(), new String[0]); } + dispatcher.call(remaining, context.getLocals(), new String[0]); } } else { - switch (context.argsLength()) { - case 0: - settings.setCategory(""); - child.mainMenu(fp); - break; - default: - dispatcher.call(context.getJoinedStrings(0), context.getLocals(), new String[0]); - break; + if (context.argsLength() == 0) { + settings.setCategory(""); + child.mainMenu(fp); + } else { + dispatcher.call(context.getJoinedStrings(0), context.getLocals(), new String[0]); } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java index feff6cd22..a433e75ad 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java @@ -20,7 +20,9 @@ import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.chat.Message; import com.boydti.fawe.util.image.ImageUtil; -import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import java.util.stream.IntStream; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -72,10 +74,11 @@ import java.util.List; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; +import org.enginehub.piston.annotation.CommandContainer; import static com.boydti.fawe.util.image.ImageUtil.load; -@Command(aliases = {"/cfi"}, desc = "Create a world from images: [More Info](https://git.io/v5iDy)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class CFICommands extends MethodCommands { private final Dispatcher dispathcer; @@ -99,17 +102,14 @@ public class CFICommands extends MethodCommands { @Command( aliases = {"heightmap"}, - usage = "", desc = "Start CFI with a height map as a base" ) @CommandPermissions("worldedit.anvil.cfi") public void heightmap(FawePlayer fp, FawePrimitiveBinding.ImageUri image, @Optional("1") double yscale) throws ParameterException { if (yscale != 0) { int[] raw = ((DataBufferInt) image.load().getRaster().getDataBuffer()).getData(); - int[] table = new int[256]; - for (int i = 0; i < table.length; i++) { - table[i] = Math.min(255, (int) (i * yscale)); - } + int[] table = IntStream.range(0, 256).map(i -> Math.min(255, (int) (i * yscale))) + .toArray(); for (int i = 0; i < raw.length; i++) { int color = raw[i]; int red = table[(color >> 16) & 0xFF]; @@ -123,8 +123,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"empty"}, - usage = " ", + name = "empty", desc = "Start CFI with an empty map as a base" ) @CommandPermissions("worldedit.anvil.cfi") @@ -135,8 +134,7 @@ public class CFICommands extends MethodCommands { private String generateName() { DateFormat df = new SimpleDateFormat("dd.MM.yyyy HH.mm.ss"); - String data = df.format(new Date()); - return data; + return df.format(new Date()); } private void setup(HeightMapMCAGenerator generator, FawePlayer fp) { @@ -149,8 +147,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"brush"}, - usage = "", + name = "brush", desc = "Info about using brushes with CFI" ) @CommandPermissions("worldedit.anvil.cfi") @@ -166,23 +163,23 @@ public class CFICommands extends MethodCommands { } else { msg = msg("This is not supported with your platform/version").newline(); } - msg.text("&8< &7[&aBack&7]").cmdTip(alias()).send(fp); + msg.text("< [Back]").cmdTip(alias()).send(fp); } @Command( - aliases = {"cancel", "exit"}, - usage = "", + name = "cancel", + aliases = {"exit"}, desc = "Cancel creation" ) @CommandPermissions("worldedit.anvil.cfi") - public void cancel(FawePlayer fp) throws ParameterException, IOException { + public void cancel(FawePlayer fp) { getSettings(fp).remove(); fp.sendMessage("Cancelled!"); } @Command( - aliases = {"done", "create"}, - usage = "", + name = "done", + aliases = "create", desc = "Create the world" ) @CommandPermissions("worldedit.anvil.cfi") @@ -240,8 +237,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"column", "setcolumn"}, - usage = " [url|mask]", + name = "column", desc = "Set the floor and main block" ) @CommandPermissions("worldedit.anvil.cfi") @@ -256,8 +252,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"floor", "setfloor"}, - usage = " [url|mask]", + name = "floor", desc = "Set the floor (default: grass)" ) @CommandPermissions("worldedit.anvil.cfi") @@ -276,8 +271,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"main", "setmain"}, - usage = " [url|mask]", + name = "main", desc = "Set the main block (default: stone)" ) @CommandPermissions("worldedit.anvil.cfi") @@ -296,10 +290,10 @@ public class CFICommands extends MethodCommands { } @Command( + name = "overlay", aliases = {"overlay", "setoverlay"}, - usage = " [url|mask]", desc = "Set the overlay block", - help = "Change the block directly above the floor (default: air)\n" + + descFooter = "Change the block directly above the floor (default: air)\n" + "e.g. Tallgrass" ) @CommandPermissions("worldedit.anvil.cfi") @@ -314,9 +308,8 @@ public class CFICommands extends MethodCommands { @Command( aliases = {"smooth"}, - usage = " [image|mask]", desc = "Smooth the terrain", - help = "Smooth terrain within an image-mask, or worldedit mask\n" + + descFooter = "Smooth terrain within an image-mask, or worldedit mask\n" + " - You can use !0 as the mask to smooth everything\n" + " - This supports smoothing snow layers (set the floor to 78:7)\n" + " - A good value for radius and iterations would be 1 8." @@ -335,8 +328,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"snow"}, - usage = "[image|mask]", + name = "snow", desc = "Create some snow" ) @CommandPermissions("worldedit.anvil.cfi") @@ -351,10 +343,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"biomepriority", "palettebiomepriority", "setpalettebiomepriority"}, - usage = "[percent=50]", + name = "biomepriority", desc = "Set the biome priority", - help = "Increase or decrease biome priority when using blockBiomeColor.\n" + + descFooter = "Increase or decrease biome priority when using blockBiomeColor.\n" + "A value of 50 is the default\n" + "Above 50 will prefer to color with biomes\n" + "Below 50 will prefer to color with blocks" @@ -366,10 +357,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"paletteblocks", "colorpaletterblocks", "setcolorpaletteblocks"}, - usage = "", + name = "paletteblocks", desc = "Set the blocks used for coloring", - help = "Allow only specific blocks to be used for coloring\n" + + descFooter = "Allow only specific blocks to be used for coloring\n" + "`blocks` is a list of blocks e.g. stone,bedrock,wool\n" + "`#clipboard` will only use the blocks present in your clipboard." ) @@ -377,15 +367,15 @@ public class CFICommands extends MethodCommands { public void paletteblocks(FawePlayer fp, Player player, LocalSession session, @Optional String arg) throws ParameterException, EmptyClipboardException, InputParseException, FileNotFoundException { if (arg == null) { msg("What blocks do you want to color with?").newline() - .text("&7[&aAll&7]").cmdTip(alias() + " PaletteBlocks *").text(" - All available blocks") + .text("[All]").cmdTip(alias() + " PaletteBlocks *").text(" - All available blocks") .newline() - .text("&7[&aClipboard&7]").cmdTip(alias() + " PaletteBlocks #clipboard").text(" - The blocks in your clipboard") + .text("[Clipboard]").cmdTip(alias() + " PaletteBlocks #clipboard").text(" - The blocks in your clipboard") .newline() - .text("&7[&aList&7]").suggestTip(alias() + " PaletteBlocks stone,gravel").text(" - A comma separated list of blocks") + .text("[List]").suggestTip(alias() + " PaletteBlocks stone,gravel").text(" - A comma separated list of blocks") .newline() - .text("&7[&aComplexity&7]").cmdTip(alias() + " Complexity").text(" - Block textures within a complexity range") + .text("[Complexity]").cmdTip(alias() + " Complexity").text(" - Block textures within a complexity range") .newline() - .text("&8< &7[&aBack&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "coloring")) + .text("< [Back]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "coloring")) .send(fp); return; } @@ -444,10 +434,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"randomization", "paletterandomization"}, - usage = "", + name = "randomization", desc = "Set whether randomization is enabled", - help = "This is enabled by default, randomization will add some random variation in the blocks used to closer match the provided image.\n" + + descFooter = "This is enabled by default, randomization will add some random variation in the blocks used to closer match the provided image.\n" + "If disabled, the closest block to the color will always be used.\n" + "Randomization will allow mixing biomes when coloring with biomes" ) @@ -458,10 +447,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"complexity", "palettecomplexity"}, - usage = " ", + name = "complexity", desc = "Set the complexity for coloring", - help = "Set the complexity for coloring\n" + + descFooter = "Set the complexity for coloring\n" + "Filter out blocks to use based on their complexity, which is a measurement of how much color variation there is in the texture for that block.\n" + "Glazed terracotta is complex, and not very pleasant for terrain, whereas stone and wool are simpler textures.\n" + "Using 0 73 for the min/max would use the simplest 73% of blocks for coloring, and is a reasonable value." @@ -475,10 +463,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"schem", "schematic", "schems", "schematics", "addschems"}, - usage = "[url] ", + name = "schem", desc = "Populate schematics", - help = "Populate a schematic on the terrain\n" + + descFooter = "Populate a schematic on the terrain\n" + " - Change the mask (e.g. angle mask) to only place the schematic in specific locations.\n" + " - The rarity is a value between 0 and 100.\n" + " - The distance is the spacing between each schematic" @@ -502,10 +489,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"biome", "setbiome"}, - usage = " [image|mask]", + name = "biome", desc = "Set the biome", - help = "Set the biome in specific parts of the map.\n" + + descFooter = "Set the biome in specific parts of the map.\n" + " - If an image is used, the biome will have a chance to be set based on how white the pixel is (white #FFF = 100% chance)" + " - The whiteOnly parameter determines if only white values on the image are set" + " - If a mask is used, the biome will be set anywhere the mask applies" @@ -522,7 +508,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"caves", "addcaves"}, + name = "caves", desc = "Generate vanilla caves" ) @CommandPermissions("worldedit.anvil.cfi") @@ -533,10 +519,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"ore", "addore"}, - usage = " ", + name = "ore", desc = "Add an ore", - help = "Use a specific pattern and settings to generate ore" + descFooter = "Use a specific pattern and settings to generate ore" ) @CommandPermissions("worldedit.anvil.cfi") public void ore(FawePlayer fp, Mask mask, Pattern pattern, int size, int frequency, int rariry, int minY, int maxY) throws ParameterException, WorldEditException { @@ -546,8 +531,7 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"ores", "addores"}, - usage = "", + name = "ores", desc = "Generate the vanilla ores" ) @CommandPermissions("worldedit.anvil.cfi") @@ -558,10 +542,9 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"height", "setheight"}, - usage = "", + name = "height", desc = "Set the height", - help = "Set the terrain height either based on an image heightmap, or a numeric value." + descFooter = "Set the terrain height either based on an image heightmap, or a numeric value." ) @CommandPermissions("worldedit.anvil.cfi") public void height(FawePlayer fp, String arg) throws ParameterException, WorldEditException { @@ -576,10 +559,8 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"water", "waterid"}, - usage = "", - desc = "Change the block used for water\n" + - "e.g. Lava" + name = "water", + desc = "Change the block used for water\ne.g. Lava" ) @CommandPermissions("worldedit.anvil.cfi") public void waterId(FawePlayer fp, BlockStateHolder block) throws ParameterException, WorldEditException { @@ -592,9 +573,7 @@ public class CFICommands extends MethodCommands { @Command( aliases = {"baseid", "bedrockid"}, - usage = "", - desc = "Change the block used for the base\n" + - "e.g. Bedrock" + desc = "Change the block used for the base\ne.g. Bedrock" ) @CommandPermissions("worldedit.anvil.cfi") public void baseId(FawePlayer fp, BlockStateHolder block) throws ParameterException, WorldEditException { @@ -607,7 +586,6 @@ public class CFICommands extends MethodCommands { @Command( aliases = {"worldthickness", "width", "thickness"}, - usage = "", desc = "Set the thickness of the generated world\n" + " - A value of 0 is the default and will not modify the height" ) @@ -620,7 +598,6 @@ public class CFICommands extends MethodCommands { @Command( aliases = {"floorthickness", "floorheight", "floorwidth"}, - usage = "", desc = "Set the thickness of the top layer\n" + " - A value of 0 is the default and will only set the top block" ) @@ -659,7 +636,6 @@ public class CFICommands extends MethodCommands { @Command( aliases = {"waterheight", "sealevel", "setwaterheight"}, - usage = "", desc = "Set the level water is generated at\n" + "Set the level water is generated at\n" + " - By default water is disabled (with a value of 0)" @@ -785,14 +761,14 @@ public class CFICommands extends MethodCommands { int biomePriority = gen.getBiomePriority(); - Message msg = msg("&8>>&7 Current Settings &8<<&7").newline() - .text("&7Randomization ").text("&7[&a" + (Boolean.toString(rand).toUpperCase()) + "&7]").cmdTip(alias() + " randomization " + (!rand)) + Message msg = msg(">> Current Settings <<").newline() + .text("Randomization ").text("[" + (Boolean.toString(rand).toUpperCase()) + "]").cmdTip(alias() + " randomization " + (!rand)) .newline() - .text("&7Mask ").text("&7[&a" + mask + "&7]").cmdTip(alias() + " mask") + .text("Mask ").text("[" + mask + "]").cmdTip(alias() + " mask") .newline() - .text("&7Blocks ").text("&7[&a" + blocks + "&7]").tooltip(blockList).command(alias() + " paletteBlocks") + .text("Blocks ").text("[" + blocks + "]").tooltip(blockList).command(alias() + " paletteBlocks") .newline() - .text("&7BiomePriority ").text("&7[&a" + biomePriority + "&7]").cmdTip(alias() + " biomepriority") + .text("BiomePriority ").text("[" + biomePriority + "]").cmdTip(alias() + " biomepriority") .newline(); if (settings.image != null) { @@ -802,19 +778,19 @@ public class CFICommands extends MethodCommands { if (settings.mask != null) colorArgs.append(" " + settings.maskArg); if (!settings.whiteOnly) colorArgs.append(" -w"); - msg.text("&7Image: ") - .text("&7[&a" + settings.imageArg + "&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "image")) + msg.text("Image: ") + .text("[" + settings.imageArg + "]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "image")) .newline().newline() - .text("&cLet's Color&7: ") + .text("&cLet's Color: ") .cmdOptions(alias() + " ", colorArgs.toString(), "Biomes", "Blocks", "BlockAndBiome", "Glass") .newline(); } else { msg.newline().text("You can color a world using an image like ") - .text("&7[&aThis&7]").linkTip("http://i.imgur.com/vJYinIU.jpg").newline() + .text("[This]").linkTip("http://i.imgur.com/vJYinIU.jpg").newline() .text("&cYou MUST provide an image: ") - .text("&7[&aNone&7]").cmdTip(alias() + " " + Commands.getAlias(Command.class, "image")).newline(); + .text("[None]").cmdTip(alias() + " " + Commands.getAlias(Command.class, "image")).newline(); } - msg.text("&8< &7[&aBack&7]").cmdTip(alias()).send(fp); + msg.text("< [Back]").cmdTip(alias()).send(fp); } @Command( @@ -835,12 +811,12 @@ public class CFICommands extends MethodCommands { StringBuilder cmd = new StringBuilder(alias() + " mask "); - msg("&8>>&7 Current Settings &8<<&7").newline() - .text("&7Image Mask ").text("&7[&a" + settings.imageMaskArg + "&7]").suggestTip(cmd + "http://") + msg(">> Current Settings <<").newline() + .text("Image Mask ").text("[" + settings.imageMaskArg + "]").suggestTip(cmd + "http://") .newline() - .text("&7WorldEdit Mask ").text("&7[&a" + settings.maskArg + "&7]").suggestTip(cmd + "") + .text("WorldEdit Mask ").text("[" + settings.maskArg + "]").suggestTip(cmd + "") .newline() - .text("&8< &7[&aBack&7]").cmdTip(alias() + " " + settings.getCategory()).send(fp); + .text("< [Back]").cmdTip(alias() + " " + settings.getCategory()).send(fp); } @Command( @@ -861,10 +837,10 @@ public class CFICommands extends MethodCommands { if (pattern != null) { dispathcer.call(settings.getCategory(), context.getLocals(), new String[0]); } else { - msg("&8>>&7 Current Settings &8<<&7").newline() - .text("&7Pattern ").text("&7[&aClick Here&7]").suggestTip(cmd + " stone") + msg(">> Current Settings <<").newline() + .text("Pattern ").text("[Click Here]").suggestTip(cmd + " stone") .newline() - .text("&8< &7[&aBack&7]").cmdTip(alias() + " " + settings.getCategory()).send(fp); + .text("< [Back]").cmdTip(alias() + " " + settings.getCategory()).send(fp); } } @@ -902,9 +878,9 @@ public class CFICommands extends MethodCommands { StringBuilder cmd = new StringBuilder(alias() + " image "); if (image == null) { msg("Please provide an image:").newline() - .text("From a URL: ").text("&7[&aClick Here&7]").suggestTip(cmd + "http://") + .text("From a URL: ").text("[Click Here]").suggestTip(cmd + "http://") .newline() - .text("From a file: ").text("&7[&aClick Here&7]").suggestTip(cmd + "file://") + .text("From a file: ").text("[Click Here]").suggestTip(cmd + "file://") .send(fp); } else { if (settings.hasGenerator()) { @@ -931,7 +907,7 @@ public class CFICommands extends MethodCommands { msg("What would you like to populate?").newline() .text("(You will need to type these commands)").newline() .cmdOptions(alias() + " ", "", "Ores", "Ore", "Caves", "Schematics", "Smooth") - .newline().text("&8< &7[&aBack&7]").cmdTip(alias()) + .newline().text("< [Back]").cmdTip(alias()) .send(fp); } @@ -966,49 +942,49 @@ public class CFICommands extends MethodCommands { String waterHeight = Commands.getAlias(CFICommands.class, "waterheight"); String snow = Commands.getAlias(CFICommands.class, "snow"); - Message msg = msg("&8>>&7 Current Settings &8<<&7").newline() - .text("&7Mask ").text("&7[&a" + mask + "&7]").cmdTip(alias() + " mask") + Message msg = msg(">> Current Settings <<").newline() + .text("Mask ").text("[" + mask + "]").cmdTip(alias() + " mask") .newline() - .text("&7Pattern ").text("&7[&a" + pattern + "&7]").cmdTip(alias() + " pattern") + .text("Pattern ").text("[" + pattern + "]").cmdTip(alias() + " pattern") .newline() .newline() - .text("&8>>&7 Components &8<<&7") + .text(">> Components <<") .newline() - .text("&7[&aHeight&7]").suggestTip(alias() + " " + alias("height") + " 120").text(" - Terrain height for whole map") + .text("[Height]").suggestTip(alias() + " " + alias("height") + " 120").text(" - Terrain height for whole map") .newline() - .text("&7[&aWaterHeight&7]").suggestTip(alias() + " " + alias("waterheight") + " 60").text(" - Sea level for whole map") + .text("[WaterHeight]").suggestTip(alias() + " " + alias("waterheight") + " 60").text(" - Sea level for whole map") .newline() - .text("&7[&aFloorThickness&7]").suggestTip(alias() + " " + alias("floorthickness") + " 60").text(" - Floor thickness of entire map") + .text("[FloorThickness]").suggestTip(alias() + " " + alias("floorthickness") + " 60").text(" - Floor thickness of entire map") .newline() - .text("&7[&aWorldThickness&7]").suggestTip(alias() + " " + alias("worldthickness") + " 60").text(" - World thickness of entire map") + .text("[WorldThickness]").suggestTip(alias() + " " + alias("worldthickness") + " 60").text(" - World thickness of entire map") .newline() - .text("&7[&aSnow&7]").suggestTip(alias() + " " + alias("snow") + maskArgs).text(" - Set snow in the masked areas") + .text("[Snow]").suggestTip(alias() + " " + alias("snow") + maskArgs).text(" - Set snow in the masked areas") .newline(); if (pattern != null) { String disabled = "You must specify a pattern"; msg - .text("&7[&cWaterId&7]").tooltip(disabled).newline() - .text("&7[&cBedrockId&7]").tooltip(disabled).newline() - .text("&7[&cFloor&7]").tooltip(disabled).newline() - .text("&7[&cMain&7]").tooltip(disabled).newline() - .text("&7[&cColumn&7]").tooltip(disabled).newline() - .text("&7[&cOverlay&7]").tooltip(disabled).newline(); + .text("[&cWaterId]").tooltip(disabled).newline() + .text("[&cBedrockId]").tooltip(disabled).newline() + .text("[&cFloor]").tooltip(disabled).newline() + .text("[&cMain]").tooltip(disabled).newline() + .text("[&cColumn]").tooltip(disabled).newline() + .text("[&cOverlay]").tooltip(disabled).newline(); } else { StringBuilder compArgs = new StringBuilder(); compArgs.append(" " + settings.patternArg + maskArgs); msg - .text("&7[&aWaterId&7]").cmdTip(alias() + " waterId " + pattern).text(" - Water id for whole map").newline() - .text("&7[&aBedrockId&7]").cmdTip(alias() + " baseId " + pattern).text(" - Bedrock id for whole map").newline() - .text("&7[&aFloor&7]").cmdTip(alias() + " floor" + compArgs).text(" - Set the floor in the masked areas").newline() - .text("&7[&aMain&7]").cmdTip(alias() + " main" + compArgs).text(" - Set the main block in the masked areas").newline() - .text("&7[&aColumn&7]").cmdTip(alias() + " column" + compArgs).text(" - Set the columns in the masked areas").newline() - .text("&7[&aOverlay&7]").cmdTip(alias() + " overlay" + compArgs).text(" - Set the overlay in the masked areas").newline(); + .text("[WaterId]").cmdTip(alias() + " waterId " + pattern).text(" - Water id for whole map").newline() + .text("[BedrockId]").cmdTip(alias() + " baseId " + pattern).text(" - Bedrock id for whole map").newline() + .text("[Floor]").cmdTip(alias() + " floor" + compArgs).text(" - Set the floor in the masked areas").newline() + .text("[Main]").cmdTip(alias() + " main" + compArgs).text(" - Set the main block in the masked areas").newline() + .text("[Column]").cmdTip(alias() + " column" + compArgs).text(" - Set the columns in the masked areas").newline() + .text("[Overlay]").cmdTip(alias() + " overlay" + compArgs).text(" - Set the overlay in the masked areas").newline(); } msg.newline() - .text("&8< &7[&aBack&7]").cmdTip(alias()) + .text("< [Back]").cmdTip(alias()) .send(fp); } @@ -1160,9 +1136,9 @@ public class CFICommands extends MethodCommands { protected void mainMenu(FawePlayer fp) { msg("What do you want to do now?").newline() .cmdOptions(alias() + " ", "", "Coloring", "Component", "Populate", "Brush") - .newline().text("&3<> &7[&aView&7]").command(alias() + " " + Commands.getAlias(CFICommands.class, "download")).tooltip("View full resolution image") - .newline().text("&4>< &7[&aCancel&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "cancel")) - .newline().text("&2>> &7[&aDone&7]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "done")) + .newline().text("<> [View]").command(alias() + " " + Commands.getAlias(CFICommands.class, "download")).tooltip("View full resolution image") + .newline().text(">< [Cancel]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "cancel")) + .newline().text("&2>> [Done]").cmdTip(alias() + " " + Commands.getAlias(CFICommands.class, "done")) .send(fp); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java b/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java index e4288fa5a..98e094bcc 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java @@ -3,7 +3,7 @@ package com.boydti.fawe.config; import com.boydti.fawe.configuration.ConfigurationSection; import com.boydti.fawe.configuration.file.YamlConfiguration; import com.boydti.fawe.util.StringMan; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; @@ -31,53 +31,6 @@ public class Commands { } } - public static Command fromArgs(String[] aliases, String usage, String desc, int min, Integer max, String flags, String help, boolean queued) { - int finalMax = max == null ? -1 : max; - return new Command() { - @Override - public Class annotationType() { - return Command.class; - } - @Override - public String[] aliases() { - return aliases; - } - @Override - public String usage() { - return usage; - } - @Override - public String desc() { - return desc; - } - @Override - public int min() { - return min; - } - @Override - public int max() { - return finalMax; - } - @Override - public String flags() { - return flags; - } - @Override - public String help() { - return help; - } - @Override - public boolean anyFlags() { - return !(flags.isEmpty() || flags.matches("[a-z]+")); - } - - @Override - public boolean queued() { - return queued; - } - }; - } - public static Command translate(Class clazz, final Command command) { if (cmdConfig == null || command instanceof TranslatedCommand) { return command; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java index fb988ab7a..1b933be39 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java @@ -250,23 +250,28 @@ public abstract class MappedFaweQueue impl } } + @Override public Settings getSettings() { return settings; } + @Override public void setSettings(Settings settings) { this.settings = settings == null ? Settings.IMP : settings; } + @Override public void setWorld(String world) { this.world = world; this.weWorld = null; } + @Override public World getWEWorld() { return weWorld != null ? weWorld : (weWorld = FaweAPI.getWorld(world)); } + @Override public String getWorldName() { return world; } @@ -288,52 +293,59 @@ public abstract class MappedFaweQueue impl @Override public boolean supports(Capability capability) { - if (capability == Capability.CHANGE_TASKS) { - return true; - } - return false; + return capability == Capability.CHANGE_TASKS; } public void setSessions(ConcurrentLinkedDeque sessions) { this.sessions = sessions; } + @Override public long getModified() { return modified; } + @Override public void setModified(long modified) { this.modified = modified; } + @Override public RunnableVal2 getProgressTask() { return progressTask; } + @Override public void setProgressTask(RunnableVal2 progressTask) { this.progressTask = progressTask; } + @Override public void setChangeTask(RunnableVal2 changeTask) { this.changeTask = changeTask; } + @Override public RunnableVal2 getChangeTask() { return changeTask; } + @Override public SetQueue.QueueStage getStage() { return stage; } + @Override public void setStage(SetQueue.QueueStage stage) { this.stage = stage; } + @Override public void addNotifyTask(Runnable runnable) { this.tasks.add(runnable); } + @Override public void addTask(Runnable whenFree) { tasks.add(whenFree); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java index 172f5f67c..fccbb34e8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java @@ -1,16 +1,14 @@ package com.boydti.fawe.jnbt; import com.boydti.fawe.config.BBC; -import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.exception.FaweException; + import com.sk89q.jnbt.NBTInputStream; -import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; import java.util.HashMap; import java.util.function.BiConsumer; -import java.util.function.Function; public class NBTStreamer { private final NBTInputStream is; @@ -27,7 +25,7 @@ public class NBTStreamer { * @throws IOException */ public void readFully() throws IOException { - is.readNamedTagLazy(node -> readers.get(node)); + is.readNamedTagLazy(readers::get); is.close(); } 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 dc31b5989..047c120e7 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 @@ -122,7 +122,7 @@ public class SchematicStreamer extends NBTStreamer { ByteReader biomeReader = new ByteReader() { @Override public void run(int index, int value) { - fc.setBiome(index, BiomeTypes.get(value)); + fc.setBiome(index, BiomeTypes.register(value)); } }; NBTStreamReader initializer23 = new NBTStreamReader() { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java index faee31462..3d4934bb1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java @@ -1,11 +1,7 @@ package com.boydti.fawe.jnbt.anvil; import com.boydti.fawe.Fawe; -import com.boydti.fawe.FaweCache; import com.boydti.fawe.example.SimpleIntFaweChunk; -import com.boydti.fawe.jnbt.anvil.HeightMapMCADrawer; -import com.boydti.fawe.jnbt.anvil.MCAChunk; -import com.boydti.fawe.jnbt.anvil.MCAWriter; import com.boydti.fawe.object.*; import com.boydti.fawe.object.brush.visualization.VirtualWorld; import com.boydti.fawe.object.change.StreamChange; @@ -930,7 +926,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public BiomeType getBiomeType(int x, int z) throws FaweException.FaweChunkLoadException { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); - return BiomeTypes.get(biomes.getByte(index)); + return BiomeTypes.register(biomes.getByte(index)); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java index 2fb4ba534..ce788751b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java @@ -9,14 +9,25 @@ import com.boydti.fawe.object.number.MutableLong; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.ReflectionUtils; -import com.sk89q.jnbt.*; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; - -import java.io.DataOutput; -import java.io.DataOutputStream; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; import java.util.function.BiConsumer; public class MCAChunk extends FaweChunk { @@ -105,12 +116,9 @@ public class MCAChunk extends FaweChunk { } out.writeNamedTag("HeightMap", heightMap); out.writeNamedTagName("Sections", NBTConstants.TYPE_LIST); - nbtOut.getOutputStream().writeByte(NBTConstants.TYPE_COMPOUND); - int len = 0; - for (int[] id : ids) { - if (id != null) len++; - } - nbtOut.getOutputStream().writeInt(len); + nbtOut.writeByte(NBTConstants.TYPE_COMPOUND); + int len = (int) Arrays.stream(ids).filter(Objects::nonNull).count(); + nbtOut.writeInt(len); for (int layer = 0; layer < ids.length; layer++) { int[] idLayer = ids[layer]; if (idLayer == null) { @@ -131,8 +139,7 @@ public class MCAChunk extends FaweChunk { buffer = new byte[8192]; } FastByteArrayOutputStream buffered = new FastByteArrayOutputStream(buffer); - DataOutputStream dataOut = new DataOutputStream(buffered); - try (NBTOutputStream nbtOut = new NBTOutputStream((DataOutput) dataOut)) { + try (NBTOutputStream nbtOut = new NBTOutputStream(buffered)) { write(nbtOut); } return buffered.toByteArray(); @@ -603,14 +610,14 @@ public class MCAChunk extends FaweChunk { public BiomeType[] getBiomeArray() { BiomeType[] arr = new BiomeType[256]; for (int i = 0; i < arr.length; i++) { - arr[i] = BiomeTypes.get(biomes[i]); + arr[i] = BiomeTypes.register(biomes[i]); } return arr; } @Override public BiomeType getBiomeType(int x, int z) { - return BiomeTypes.get(biomes[(x & 15) + ((z & 15) << 4)]); + return BiomeTypes.register(biomes[(x & 15) + ((z & 15) << 4)]); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java index 1e9005880..dd9b22143 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/WritableMCAChunk.java @@ -4,7 +4,6 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MathMan; - import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTConstants; @@ -15,9 +14,6 @@ import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; - -import java.io.DataOutput; -import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -224,8 +220,7 @@ public class WritableMCAChunk extends FaweChunk { buffer = new byte[8192]; } FastByteArrayOutputStream buffered = new FastByteArrayOutputStream(buffer); - DataOutputStream dataOut = new DataOutputStream(buffered); - try (NBTOutputStream nbtOut = new NBTOutputStream((DataOutput) dataOut)) { + try (NBTOutputStream nbtOut = new NBTOutputStream(buffered)) { write(nbtOut); } return buffered.toByteArray(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/worldedit-core/src/main/java/com/boydti/fawe/object/FawePlayer.java index 5b1bf1583..73bc3768f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -24,10 +24,9 @@ import com.sk89q.worldedit.LocalSession; 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.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extension.platform.PlayerProxy; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -46,8 +45,6 @@ import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; - -import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; @@ -59,6 +56,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import org.jetbrains.annotations.NotNull; public abstract class FawePlayer extends Metadatable { @@ -152,74 +150,70 @@ public abstract class FawePlayer extends Metadatable { return cancelled; } - private void setConfirmTask(@Nullable Runnable task, CommandContext context, String command) { - if (task != null) { - Runnable newTask = () -> CommandManager.getInstance().handleCommandTask(() -> { - task.run(); - return null; - }, context.getLocals()); - setMeta("cmdConfirm", newTask); - } else { - setMeta("cmdConfirm", new CommandEvent(getPlayer(), command)); - } + private void setConfirmTask(@NotNull Runnable task, CommandContext context) { + Runnable newTask = () -> PlatformCommandManager.getInstance().handleCommandTask(() -> { + task.run(); + return null; + }, context.getLocals()); + setMeta("cmdConfirm", newTask); } - public void checkConfirmation(@Nullable Runnable task, String command, int times, int limit, CommandContext context) throws RegionOperationException { + public void checkConfirmation(@NotNull Runnable task, String command, int times, int limit, CommandContext context) throws RegionOperationException { if (command != null && !getMeta("cmdConfirmRunning", false)) { if (times > limit) { - setConfirmTask(task, context, command); + setConfirmTask(task, context); String volume = ""; throw new RegionOperationException(BBC.WORLDEDIT_CANCEL_REASON_CONFIRM.f(0, times, command, volume)); } } - if (task != null) task.run(); + task.run(); } - public void checkConfirmationRadius(@Nullable Runnable task, String command, int radius, CommandContext context) throws RegionOperationException { + public void checkConfirmationRadius(@NotNull Runnable task, String command, int radius, CommandContext context) throws RegionOperationException { if (command != null && !getMeta("cmdConfirmRunning", false)) { if (radius > 0) { if (radius > 448) { - setConfirmTask(task, context, command); + setConfirmTask(task, context); long volume = (long) (Math.PI * ((double) radius * radius)); throw new RegionOperationException(BBC.WORLDEDIT_CANCEL_REASON_CONFIRM.f(0, radius, command, NumberFormat.getNumberInstance().format(volume))); } } } - if (task != null) task.run(); + task.run(); } - public void checkConfirmationStack(@Nullable Runnable task, String command, Region region, int times, CommandContext context) throws RegionOperationException { + public void checkConfirmationStack(@NotNull Runnable task, String command, Region region, int times, CommandContext context) throws RegionOperationException { if (command != null && !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; if (area > 2 << 18) { - setConfirmTask(task, context, command); + setConfirmTask(task, context); 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.f(min, max, command, NumberFormat.getNumberInstance().format(volume))); } } } - if (task != null) task.run(); + task.run(); } - public void checkConfirmationRegion(@Nullable Runnable task, String command, Region region, CommandContext context) throws RegionOperationException { + public void checkConfirmationRegion(@NotNull Runnable task, String command, Region region, CommandContext context) throws RegionOperationException { if (command != null && !getMeta("cmdConfirmRunning", false)) { if (region != null) { BlockVector3 min = region.getMinimumPoint(); BlockVector3 max = region.getMaximumPoint(); long area = (max.getX() - min.getX()) * (max.getZ() - min.getZ() + 1); if (area > 2 << 18) { - setConfirmTask(task, context, command); + setConfirmTask(task, context); 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.f(min, max, command, NumberFormat.getNumberInstance().format(volume))); } } } - if (task != null) task.run(); + task.run(); } public synchronized boolean confirm() { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java index 855336431..5b7446848 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/FaweQueue.java @@ -175,12 +175,6 @@ public interface FaweQueue extends HasFaweQueue, Extent { fc.fillCuboid(0, 15, minY, maxY, 0, 15, combinedId); fc.optimize(); - int bcx = (current.minX) >> 4; - int bcz = (current.minZ) >> 4; - - int tcx = (current.maxX) >> 4; - int tcz = (current.maxZ) >> 4; - // [chunkx, chunkz, pos1x, pos1z, pos2x, pos2z, isedge] MainUtil.chunkTaskSync(current, new RunnableVal() { @Override public void run(int[] value) { @@ -228,6 +222,7 @@ public interface FaweQueue extends HasFaweQueue, Extent { File getSaveFolder(); + @Override default int getMaxY() { World weWorld = getWEWorld(); return weWorld == null ? 255 : weWorld.getMaxY(); @@ -452,7 +447,7 @@ public interface FaweQueue extends HasFaweQueue, Extent { try { return getCombinedId4Data(x, y, z); } catch (FaweException ignore) { - session.debug(BBC.WORLDEDIT_FAILED_LOAD_CHUNK, x >> 4, z >> 4); + BBC.WORLDEDIT_FAILED_LOAD_CHUNK.send(session.getPlayer(),x >> 4, z >> 4); return def; } catch (Throwable e) { e.printStackTrace(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/RegionWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/object/RegionWrapper.java index 8547e081e..3259c0cfc 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/RegionWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/RegionWrapper.java @@ -125,6 +125,7 @@ public class RegionWrapper extends CuboidRegion { return this.minX + "," + this.minY + "," + this.minZ + "->" + this.maxX + "," + this.maxY + "," + this.maxZ; } + @Override public boolean isGlobal() { return minX == Integer.MIN_VALUE && minZ == Integer.MIN_VALUE && maxX == Integer.MAX_VALUE && maxZ == Integer.MAX_VALUE && minY <= 0 && maxY >= 255; } 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 8a5286618..0fdb7bee8 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 @@ -13,7 +13,7 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.CommandManager; +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.expression.Expression; @@ -62,7 +62,7 @@ public class BrushSettings { } public static BrushSettings get(BrushTool tool, Player player, LocalSession session, Map settings) throws CommandException, InputParseException { - Dispatcher dispatcher = CommandManager.getInstance().getDispatcher(); + Dispatcher dispatcher = PlatformCommandManager.getInstance().getCommandManager(); Dispatcher brushDispatcher = (Dispatcher) (dispatcher.get("brush").getCallable()); if (brushDispatcher == null) { return null; 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 396ba249d..caed92d66 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 @@ -10,7 +10,7 @@ import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.CommandEvent; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; @@ -48,7 +48,7 @@ public class CommandBrush implements Brush { List cmds = StringMan.split(replaced, ';'); for (String cmd : cmds) { CommandEvent event = new CommandEvent(wePlayer, cmd); - CommandManager.getInstance().handleCommandOnCurrentThread(event); + PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); } } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java index c7edce44c..b75cc2d3f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java @@ -10,7 +10,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.CommandEvent; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; @@ -42,7 +42,7 @@ public class ScatterCommand extends ScatterBrush { List cmds = StringMan.split(replaced, ';'); for (String cmd : cmds) { CommandEvent event = new CommandEvent(wePlayer, cmd); - CommandManager.getInstance().handleCommandOnCurrentThread(event); + PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java index 5ebf33175..3e52e8f30 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java @@ -26,7 +26,6 @@ public interface HeightMap { default void applyHeightMapData(int[][] data, EditSession session, BlockVector3 pos, int size, double yscale, boolean smooth, boolean towards, boolean layers) throws MaxChangedBlocksException { BlockVector3 top = session.getMaximumPoint(); int maxY = top.getBlockY(); - int diameter = 2 * size + 1; Location min = new Location(session.getWorld(), pos.subtract(size, maxY, size).toVector3()); BlockVector3 max = pos.add(size, maxY, size); Region region = new CuboidRegion(session.getWorld(), min.toBlockPoint(), max); @@ -34,6 +33,7 @@ public interface HeightMap { if (smooth) { try { HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1)); + int diameter = 2 * size + 1; data[1] = filter.filter(data[1], diameter, diameter); } catch (Throwable e) { e.printStackTrace(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/ImmutableVirtualWorld.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/ImmutableVirtualWorld.java index 2cc85ad8d..ebae701e4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/ImmutableVirtualWorld.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/ImmutableVirtualWorld.java @@ -1,6 +1,5 @@ package com.boydti.fawe.object.brush.visualization; -import com.boydti.fawe.FaweCache; import com.boydti.fawe.example.SimpleIntFaweChunk; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; @@ -8,9 +7,9 @@ import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.util.SetQueue; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.world.biome.BiomeTypes; -import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; @@ -20,10 +19,10 @@ import com.sk89q.worldedit.regions.Region; 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.block.BlockStateHolder; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; - import java.io.File; import java.util.Collection; import java.util.Collections; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/change/MutableBiomeChange.java b/worldedit-core/src/main/java/com/boydti/fawe/object/change/MutableBiomeChange.java index 599c0fca5..277161bba 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/change/MutableBiomeChange.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/change/MutableBiomeChange.java @@ -4,7 +4,6 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.history.UndoContext; import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.math.MutableBlockVector2; -import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; public class MutableBiomeChange implements Change { @@ -26,11 +25,11 @@ public class MutableBiomeChange implements Change { @Override public void undo(UndoContext context) throws WorldEditException { - context.getExtent().setBiome(mutable, BiomeTypes.get(from)); + context.getExtent().setBiome(mutable, BiomeTypes.register(from)); } @Override public void redo(UndoContext context) throws WorldEditException { - context.getExtent().setBiome(mutable, BiomeTypes.get(to)); + context.getExtent().setBiome(mutable, BiomeTypes.register(to)); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java index 93d2f1c33..e09546f04 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java @@ -4,7 +4,11 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; import com.boydti.fawe.database.DBHandler; import com.boydti.fawe.database.RollbackDatabase; -import com.boydti.fawe.object.*; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.IntegerPair; +import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.util.MainUtil; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; @@ -13,12 +17,11 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; - -import java.io.DataOutput; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -291,7 +294,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } enttFile.getParentFile().mkdirs(); enttFile.createNewFile(); - osENTCT = new NBTOutputStream((DataOutput) getCompressedOS(new FileOutputStream(enttFile))); + osENTCT = new NBTOutputStream(getCompressedOS(new FileOutputStream(enttFile))); return osENTCT; } @@ -302,7 +305,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } entfFile.getParentFile().mkdirs(); entfFile.createNewFile(); - osENTCF = new NBTOutputStream((DataOutput) getCompressedOS(new FileOutputStream(entfFile))); + osENTCF = new NBTOutputStream(getCompressedOS(new FileOutputStream(entfFile))); return osENTCF; } @@ -313,7 +316,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } nbttFile.getParentFile().mkdirs(); nbttFile.createNewFile(); - osNBTT = new NBTOutputStream((DataOutput) getCompressedOS(new FileOutputStream(nbttFile))); + osNBTT = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbttFile))); return osNBTT; } @@ -324,7 +327,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } nbtfFile.getParentFile().mkdirs(); nbtfFile.createNewFile(); - osNBTF = new NBTOutputStream((DataOutput) getCompressedOS(new FileOutputStream(nbtfFile))); + osNBTF = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbtfFile))); return osNBTF; } @@ -343,8 +346,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!bioFile.exists()) { return null; } - FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bioFile)); - return is; + return MainUtil.getCompressedIS(new FileInputStream(bioFile)); } @Override @@ -391,8 +393,8 @@ public class DiskStorageHistory extends FaweStreamChangeSet { // skip mode gis.skipFully(1); // origin - ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); - oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); + ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read()); + oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read()); setOrigin(ox, oz); DiskStorageSummary summary = new DiskStorageSummary(ox, oz); if (!requiredRegion.isIn(ox, oz)) { @@ -432,8 +434,8 @@ public class DiskStorageHistory extends FaweStreamChangeSet { // skip mode gis.skipFully(1); // origin - ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); - oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); + ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read()); + oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read()); setOrigin(ox, oz); fis.close(); gis.close(); @@ -446,8 +448,6 @@ public class DiskStorageHistory extends FaweStreamChangeSet { public static class DiskStorageSummary { - private final int z; - private final int x; public int[] blocks; public int minX; @@ -458,8 +458,6 @@ public class DiskStorageHistory extends FaweStreamChangeSet { public DiskStorageSummary(int x, int z) { blocks = new int[BlockTypes.states.length]; - this.x = x; - this.z = z; minX = x; maxX = x; minZ = z; @@ -485,7 +483,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { for (int i = 0; i < blocks.length; i++) { if (blocks[i] != 0) { BlockState state = BlockTypes.states[i]; - map.put(state, (Integer) blocks[i]); + map.put(state, blocks[i]); } } return map; @@ -498,18 +496,14 @@ public class DiskStorageHistory extends FaweStreamChangeSet { for (Map.Entry entry : map.entrySet()) { BlockState id = entry.getKey(); int changes = entry.getValue(); - double percent = ((changes * 1000l) / count) / 10d; - newMap.put(id, (Double) percent); + double percent = (changes * 1000L / count) / 10d; + newMap.put(id, percent); } return newMap; } public int getSize() { - int count = 0; - for (int block : blocks) { - count += block; - } - return count; + return Arrays.stream(blocks).sum(); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java index 436f83a23..d169d67c7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java @@ -9,8 +9,6 @@ import com.boydti.fawe.util.MainUtil; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.worldedit.world.World; - -import java.io.DataOutput; import java.io.IOException; /** @@ -189,7 +187,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { return entCStreamZip; } entCStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE); - return entCStreamZip = new NBTOutputStream((DataOutput) getCompressedOS(entCStream)); + return entCStreamZip = new NBTOutputStream(getCompressedOS(entCStream)); } @Override @@ -198,7 +196,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { return entRStreamZip; } entRStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE); - return entRStreamZip = new NBTOutputStream((DataOutput) getCompressedOS(entRStream)); + return entRStreamZip = new NBTOutputStream(getCompressedOS(entRStream)); } @Override @@ -207,7 +205,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { return tileCStreamZip; } tileCStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE); - return tileCStreamZip = new NBTOutputStream((DataOutput) getCompressedOS(tileCStream)); + return tileCStreamZip = new NBTOutputStream(getCompressedOS(tileCStream)); } @Override @@ -216,7 +214,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { return tileRStreamZip; } tileRStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE); - return tileRStreamZip = new NBTOutputStream((DataOutput) getCompressedOS(tileRStream)); + return tileRStreamZip = new NBTOutputStream(getCompressedOS(tileRStream)); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 444fa92a8..c2ed40764 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -144,7 +144,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { return null; } int biomeId = mbb.get(HEADER_SIZE + (volume << 2) + index) & 0xFF; - return BiomeTypes.get(biomeId); + return BiomeTypes.register(biomeId); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java index b0dc34196..c3115d1c4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java @@ -1,6 +1,5 @@ package com.boydti.fawe.object.clipboard; -import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.jnbt.NBTStreamer; import com.boydti.fawe.object.IntegerTrio; @@ -9,10 +8,8 @@ import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; 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.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; @@ -122,7 +119,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard { if (!hasBiomes()) { return null; } - return BiomeTypes.get(biomes[index]); + return BiomeTypes.register(biomes[index]); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java index 5d94300bd..48a2ad169 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java @@ -41,6 +41,7 @@ public class FastWorldEditExtent extends AbstractDelegateExtent implements HasFa this.queue = queue; } + @Override public FaweQueue getQueue() { return queue; } 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 b7f86660c..7e81e7283 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 @@ -48,6 +48,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { return false; } + @Override public final boolean contains(BlockVector3 p) { return contains(p.getBlockX(), p.getBlockY(), p.getBlockZ()); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java index bee31b63d..566d3c740 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java @@ -1,5 +1,7 @@ package com.boydti.fawe.object.extent; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.worldedit.extent.AbstractDelegateExtent; @@ -7,12 +9,10 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.World; import java.io.IOException; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; - -import static com.google.common.base.Preconditions.checkNotNull; - public class ResettableExtent extends AbstractDelegateExtent implements Serializable { public ResettableExtent(Extent parent) { super(parent); @@ -41,7 +41,7 @@ public class ResettableExtent extends AbstractDelegateExtent implements Serializ return this; } - private void writeObject(java.io.ObjectOutputStream stream) throws IOException { + private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); Extent extent = getExtent(); boolean next = extent instanceof ResettableExtent; @@ -62,4 +62,4 @@ public class ResettableExtent extends AbstractDelegateExtent implements Serializ } } } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SolidPlaneMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SolidPlaneMask.java index 04baace03..a999938e6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SolidPlaneMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SolidPlaneMask.java @@ -34,13 +34,13 @@ public class SolidPlaneMask extends SolidBlockMask implements ResettableMask { originZ = vector.getBlockZ(); mode = 0; Extent extent = getExtent(); - if (!extent.getBlockType(mutable.setComponents(originX - 1, originY, originZ)).getMaterial().isAir() && !extent.getBlockType(mutable.setComponents(originX + 1, originY, originZ)).getMaterial().isAir()) { + if (!extent.getBlock(mutable.setComponents(originX - 1, originY, originZ)).getMaterial().isAir() && !extent.getBlock(mutable.setComponents(originX + 1, originY, originZ)).getMaterial().isAir()) { mode &= 1; } - if (!extent.getBlockType(mutable.setComponents(originX, originY, originZ - 1)).getMaterial().isAir() && !extent.getBlockType(mutable.setComponents(originX, originY, originZ + 1)).getMaterial().isAir()) { + if (!extent.getBlock(mutable.setComponents(originX, originY, originZ - 1)).getMaterial().isAir() && !extent.getBlock(mutable.setComponents(originX, originY, originZ + 1)).getMaterial().isAir()) { mode &= 4; } - if (!extent.getBlockType(mutable.setComponents(originX, originY - 1, originZ + 1)).getMaterial().isAir() && !extent.getBlockType(mutable.setComponents(originX, originY + 1, originZ + 1)).getMaterial().isAir()) { + if (!extent.getBlock(mutable.setComponents(originX, originY - 1, originZ + 1)).getMaterial().isAir() && !extent.getBlock(mutable.setComponents(originX, originY + 1, originZ + 1)).getMaterial().isAir()) { mode &= 2; } if (Integer.bitCount(mode) >= 3) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/AverageColorPattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/AverageColorPattern.java index bb8395a8c..ac52e3db5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/AverageColorPattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/AverageColorPattern.java @@ -4,16 +4,13 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.util.TextureHolder; import com.boydti.fawe.util.TextureUtil; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; - import java.awt.Color; import java.io.IOException; +import java.io.ObjectInputStream; public class AverageColorPattern extends AbstractExtentPattern { private transient TextureHolder holder; @@ -36,7 +33,7 @@ public class AverageColorPattern extends AbstractExtentPattern { @Override public boolean apply(Extent extent, BlockVector3 setPosition, BlockVector3 getPosition) throws WorldEditException { - BlockType blockType = extent.getBlockType(getPosition); + BlockType blockType = extent.getBlock(getPosition).getBlockType(); TextureUtil util = holder.getTextureUtil(); int currentColor = util.getColor(blockType); if (currentColor == 0) return false; @@ -46,8 +43,8 @@ public class AverageColorPattern extends AbstractExtentPattern { return extent.setBlock(setPosition, newBlock.getDefaultState()); } - private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); holder = Fawe.get().getCachedTextureUtil(true, 0, 100); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/DesaturatePattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/DesaturatePattern.java index 1647b3662..8f6544940 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/DesaturatePattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/DesaturatePattern.java @@ -4,15 +4,13 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.util.TextureHolder; import com.boydti.fawe.util.TextureUtil; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.AbstractPattern; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockType; - import java.io.IOException; +import java.io.ObjectInputStream; public class DesaturatePattern extends AbstractPattern { private transient TextureHolder holder; @@ -27,7 +25,7 @@ public class DesaturatePattern extends AbstractPattern { @Override public BaseBlock apply(BlockVector3 position) { - BlockType block = extent.getBlockType(position); + BlockType block = extent.getBlock(position).getBlockType(); TextureUtil util = holder.getTextureUtil(); int color = util.getColor(block); int r = (color >> 16) & 0xFF; @@ -44,7 +42,7 @@ public class DesaturatePattern extends AbstractPattern { @Override public boolean apply(Extent extent, BlockVector3 setPosition, BlockVector3 getPosition) throws WorldEditException { - BlockType block = extent.getBlockType(getPosition); + BlockType block = extent.getBlock(getPosition).getBlockType(); TextureUtil util = holder.getTextureUtil(); int color = util.getColor(block); int r = (color >> 16) & 0xFF; @@ -66,8 +64,8 @@ public class DesaturatePattern extends AbstractPattern { return extent.setBlock(setPosition, newBlock.getDefaultState()); } - private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); holder = Fawe.get().getCachedTextureUtil(true, 0, 100); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SaturatePattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SaturatePattern.java index 5499b1507..ac29670fa 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SaturatePattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/SaturatePattern.java @@ -4,16 +4,14 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.util.TextureHolder; import com.boydti.fawe.util.TextureUtil; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.AbstractPattern; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockType; - import java.awt.Color; import java.io.IOException; +import java.io.ObjectInputStream; public class SaturatePattern extends AbstractPattern { private transient TextureHolder holder; @@ -29,7 +27,7 @@ public class SaturatePattern extends AbstractPattern { @Override public BaseBlock apply(BlockVector3 position) { - BlockType block = extent.getBlockType(position); + BlockType block = extent.getBlock(position).getBlockType(); TextureUtil util = holder.getTextureUtil(); int currentColor = util.getColor(block); int newColor = util.multiplyColor(currentColor, color); @@ -38,7 +36,7 @@ public class SaturatePattern extends AbstractPattern { @Override public boolean apply(Extent extent, BlockVector3 setPosition, BlockVector3 getPosition) throws WorldEditException { - BlockType block = extent.getBlockType(getPosition); + BlockType block = extent.getBlock(getPosition).getBlockType(); TextureUtil util = holder.getTextureUtil(); int currentColor = util.getColor(block); if (currentColor == 0) return false; @@ -48,8 +46,8 @@ public class SaturatePattern extends AbstractPattern { return extent.setBlock(setPosition, newBlock.getDefaultState()); } - private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); holder = Fawe.get().getCachedTextureUtil(true, 0, 100); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ShadePattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ShadePattern.java index 5c8a910f2..0fe10f849 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ShadePattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ShadePattern.java @@ -1,20 +1,16 @@ package com.boydti.fawe.object.pattern; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.Fawe; import com.boydti.fawe.util.TextureUtil; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.AbstractPattern; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockType; - import java.io.IOException; - -import static com.google.common.base.Preconditions.checkNotNull; - public class ShadePattern extends AbstractPattern { private transient TextureUtil util; private final Extent extent; @@ -29,7 +25,7 @@ public class ShadePattern extends AbstractPattern { @Override public BaseBlock apply(BlockVector3 position) { - BlockType block = extent.getBlockType(position); + BlockType block = extent.getBlock(position).getBlockType(); return (darken ? util.getDarkerBlock(block) : util.getLighterBlock(block)).getDefaultState().toBaseBlock(); } @@ -37,4 +33,4 @@ public class ShadePattern extends AbstractPattern { stream.defaultReadObject(); util = Fawe.get().getCachedTextureUtil(true, 0, 100); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java index c88cca70b..aeee48c7b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/queue/IDelegateFaweQueue.java @@ -38,6 +38,7 @@ import javax.annotation.Nullable; public interface IDelegateFaweQueue extends FaweQueue { + @Override FaweQueue getQueue(); @Override 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 3abaa65cf..e9064b62b 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 @@ -88,6 +88,7 @@ public class FuzzyRegion extends AbstractRegion { setMinMax(x, y, z); } + @Override public boolean contains(int x, int y, int z) { return set.contains(x, y, z); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java index d74805895..3478d6129 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java @@ -154,8 +154,4 @@ public class FuzzyRegionSelector extends AbstractDelegateExtent implements Regio return lines; } - @Override - public List getVerticies() { - return positions; - } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/PolyhedralRegionSelector.java b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/PolyhedralRegionSelector.java index c028caa26..29094ed38 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/PolyhedralRegionSelector.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/regions/selector/PolyhedralRegionSelector.java @@ -229,8 +229,4 @@ public class PolyhedralRegionSelector implements RegionSelector, CUIRegion { } } - @Override - public List getVerticies() { - return new ArrayList<>(region.getVertices()); - } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java index 0b2ef0e2d..464465b5d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java @@ -176,7 +176,7 @@ public class Schematic { if (transform != null) { copy.setTransform(transform); } - copy.setCopyBiomes(!(clipboard instanceof BlockArrayClipboard) || ((BlockArrayClipboard) clipboard).IMP.hasBiomes()); + copy.setCopyingBiomes(!(clipboard instanceof BlockArrayClipboard) || ((BlockArrayClipboard) clipboard).IMP.hasBiomes()); if (extent instanceof EditSession) { EditSession editSession = (EditSession) extent; Mask sourceMask = editSession.getSourceMask(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java b/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java index 50a53fdef..bacb34073 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java @@ -27,12 +27,12 @@ public class Fast2DIterator implements Iterable { } public Fast2DIterator(@Nonnull Iterable iter, @Nullable HasFaweQueue editSession) { - this(iter, (FaweQueue) (editSession != null ? editSession.getQueue() : null)); + this(iter, editSession != null ? editSession.getQueue() : null); } public Fast2DIterator(@Nonnull Iterable iter, @Nullable FaweQueue faweQueue) { this.iterable = iter; - this.queue = faweQueue != null && faweQueue instanceof MappedFaweQueue ? (MappedFaweQueue) faweQueue : null; + this.queue = faweQueue instanceof MappedFaweQueue ? (MappedFaweQueue) faweQueue : null; } public Iterable getIterable() { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java index 1f3e8fc73..36f4b7bf1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java @@ -8,7 +8,6 @@ import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.IOUtil; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; - import com.github.intellectualsites.plotsquared.plot.PlotSquared; import com.github.intellectualsites.plotsquared.plot.object.Location; import com.github.intellectualsites.plotsquared.plot.object.RegionWrapper; @@ -27,7 +26,6 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicWriter; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; - import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.File; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/ReplaceAll.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/ReplaceAll.java index c9ae5e367..d894596df 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/ReplaceAll.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/plot/ReplaceAll.java @@ -20,7 +20,7 @@ import com.github.intellectualsites.plotsquared.plot.object.RunnableVal3; import com.github.intellectualsites.plotsquared.plot.object.worlds.SinglePlotArea; import com.github.intellectualsites.plotsquared.plot.util.SetupUtils; import com.sk89q.worldedit.event.platform.CommandEvent; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; @CommandDeclaration( command = "replaceall", @@ -57,7 +57,7 @@ public class ReplaceAll extends Command { FakePlayer actor = FakePlayer.getConsole(); String cmd = "/replaceallpattern " + worldName + " " + StringMan.join(args, " "); CommandEvent event = new CommandEvent(actor, cmd); - CommandManager.getInstance().handleCommandOnCurrentThread(event); + PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java b/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java index f15bb7ef0..7a75d4876 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/BrushCache.java @@ -25,7 +25,7 @@ public final class BrushCache { return item.hasNbtData() ? item.getNbtData() : null; } - private static final Object getKey(BaseItem item) { + private static Object getKey(BaseItem item) { return item.getNativeItem(); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/DocumentationPrinter.java b/worldedit-core/src/main/java/com/boydti/fawe/util/DocumentationPrinter.java index c24a30d8e..87078340c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/DocumentationPrinter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/DocumentationPrinter.java @@ -21,7 +21,7 @@ package com.boydti.fawe.util; import com.boydti.fawe.command.AnvilCommands; import com.boydti.fawe.command.CFICommands; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.NestedCommand; import com.sk89q.worldedit.command.BiomeCommands; 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 fe6e585d4..f198118d6 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 @@ -4,21 +4,27 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.Settings; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; -import com.boydti.fawe.object.*; +import com.boydti.fawe.object.FaweLimit; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.NullChangeSet; +import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; + +import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.event.extent.EditSessionEvent; 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 javax.annotation.Nonnull; import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkNotNull; +import java.util.UUID; public class EditSessionBuilder { private World world; @@ -102,8 +108,8 @@ public class EditSessionBuilder { } /** - * @param disk If it should be stored on disk - * @param uuid The uuid to store it under (if on disk) + * @param disk If it should be stored on disk + * @param uuid The uuid to store it under (if on disk) * @param compression Compression level (0-9) * @return */ @@ -180,17 +186,15 @@ public class EditSessionBuilder { return this; } - public EditSessionBuilder eventBus(@Nullable EventBus eventBus) { - this.eventBus = eventBus; - return this; - } - public EditSessionBuilder event(@Nullable EditSessionEvent event) { this.event = event; return this; } public EditSession build() { - return new EditSession(worldName, world, queue, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, eventBus, event); + if (eventBus == null) { + eventBus = WorldEdit.getInstance().getEventBus(); + } + return new EditSession(worldName, world, queue, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, eventBus, event); } } 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 84b1d975f..6bf1fdd8f 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,5 +1,7 @@ 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; @@ -12,7 +14,6 @@ import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.changeset.CPUOptimizedChangeSet; import com.boydti.fawe.object.changeset.FaweStreamChangeSet; import com.boydti.fawe.object.io.AbstractDelegateOutputStream; - import com.github.luben.zstd.ZstdInputStream; import com.github.luben.zstd.ZstdOutputStream; import com.sk89q.jnbt.CompoundTag; @@ -27,20 +28,19 @@ 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 static java.lang.System.arraycopy; -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.Graphics2D; import java.awt.image.BufferedImage; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.lang.reflect.Array; import java.net.HttpURLConnection; import java.net.MalformedURLException; @@ -77,6 +77,15 @@ 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; public class MainUtil { /* @@ -853,33 +862,4 @@ public class MainUtil { } } - public static void warnDeprecated(Class... alternatives) { - StackTraceElement[] stacktrace = new RuntimeException().getStackTrace(); - if (stacktrace.length > 1) { - for (int i = 1; i < stacktrace.length; i++) { - StackTraceElement stack = stacktrace[i]; - String s = stack.toString(); - if (s.startsWith("com.sk89q")) { - continue; - } - try { - StackTraceElement creatorElement = stacktrace[1]; - String className = creatorElement.getClassName(); - Class clazz = Class.forName(className); - String creator = clazz.getSimpleName(); - String packageName = clazz.getPackage().getName(); - - StackTraceElement deprecatedElement = stack; - String myName = Class.forName(deprecatedElement.getClassName()).getSimpleName(); - Fawe.debug("@" + creator + " used by " + myName + "." + deprecatedElement.getMethodName() + "():" + deprecatedElement.getLineNumber() + " is deprecated."); - Fawe.debug(" - Alternatives: " + StringMan.getString(alternatives)); - } catch (Throwable throwable) { - throwable.printStackTrace(); - } finally { - break; - } - } - } - } - } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/SetQueue.java b/worldedit-core/src/main/java/com/boydti/fawe/util/SetQueue.java index df7e10df5..7c1b5d6f6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/SetQueue.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/SetQueue.java @@ -171,16 +171,7 @@ public class SetQueue { e.printStackTrace(); } if (pool.getQueuedSubmissionCount() != 0 || pool.getRunningThreadCount() != 0 || pool.getQueuedTaskCount() != 0) { -// if (Fawe.get().isJava8()) - { - pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - } -// else { -// pool.shutdown(); -// pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); -// pool = new ForkJoinPool(); -// completer = new ExecutorCompletionService(pool); -// } + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); } queue.endSet(parallel); } catch (Throwable e) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/chat/Message.java b/worldedit-core/src/main/java/com/boydti/fawe/util/chat/Message.java index 46a74b7f1..d42460e4b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/chat/Message.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/chat/Message.java @@ -3,8 +3,9 @@ package com.boydti.fawe.util.chat; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FawePlayer; -import com.sk89q.worldedit.WorldEdit; + import com.sk89q.worldedit.extension.platform.Actor; + import java.util.Objects; public class Message { @@ -75,7 +76,7 @@ public class Message { } public Message command(String command) { - Fawe.get().getChatManager().command(this, (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + command); + Fawe.get().getChatManager().command(this, ("/") + command); return this; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java b/worldedit-core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java index 313bb6275..85a871ab7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java @@ -6,7 +6,7 @@ import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.StringMan; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.Link; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Description; @@ -57,7 +57,7 @@ public class UsageMessage extends Message { String prefix = !commandString.isEmpty() ? commandString + " " : ""; List list = new ArrayList<>(dispatcher.getCommands()); - list.sort(new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN)); + list.sort(new PrimaryAliasComparator(PlatformCommandManager.COMMAND_CLEAN_PATTERN)); for (CommandMapping mapping : list) { boolean perm = locals == null || mapping.getCallable().testPermission(locals); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/web/SchemSync.java b/worldedit-core/src/main/java/com/boydti/fawe/web/SchemSync.java index 67a97478d..7ef61dde1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/web/SchemSync.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/web/SchemSync.java @@ -87,8 +87,9 @@ public class SchemSync implements Runnable { UtilityCommands.allFiles(dir.listFiles(), true, new Consumer() { @Override public void accept(File file) { - String path = dir.toURI().relativize(file.toURI()).getPath(); try { + String path = dir.toURI() + .relativize(file.toURI()).getPath(); out.writeUTF(path); } catch (IOException e) { e.printStackTrace(); 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 index 92e7b0f94..02faf6642 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TargetBlock; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockTypes; @@ -120,6 +121,10 @@ public class PlayerWrapper extends AbstractPlayerActor { parent.printError(msg); } + @Override public void print(Component component) { + parent.print(component); + } + @Override public String[] getGroups() { return parent.getGroups(); diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java b/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java index b0e873c0d..6b5776619 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java @@ -181,6 +181,18 @@ public class CompoundTagBuilder { return put(key, new StringTag(value)); } + /** + * Remove the given key from the compound tag. Does nothing if the key doesn't exist. + * + * @param key the key + * @return this object + */ + public CompoundTagBuilder remove(String key) { + checkNotNull(key); + entries.remove(key); + return this; + } + /** * Put all the entries from the given map into this map. * diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedCompoundTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedCompoundTag.java index 6ce8b20f1..0ef0aa680 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedCompoundTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedCompoundTag.java @@ -1,10 +1,9 @@ package com.sk89q.jnbt; -import java.io.DataInputStream; import java.io.IOException; -import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import net.jpountz.lz4.LZ4BlockInputStream; public abstract class CompressedCompoundTag extends CompoundTag { private T in; @@ -20,7 +19,7 @@ public abstract class CompressedCompoundTag extends CompoundTag { return super.getValue(); } - public abstract DataInputStream adapt(T src) throws IOException; + public abstract LZ4BlockInputStream adapt(T src) throws IOException; public T getSource() { return in; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java index c5f43e4d6..8aa4b74b8 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java @@ -2,15 +2,12 @@ package com.sk89q.jnbt; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.object.io.FastByteArraysInputStream; -import com.boydti.fawe.object.io.PGZIPOutputStream; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicWriter; import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; -import java.io.DataInputStream; import java.io.IOException; -import java.util.zip.GZIPInputStream; public class CompressedSchematicTag extends CompressedCompoundTag { public CompressedSchematicTag(Clipboard holder) { @@ -18,7 +15,7 @@ public class CompressedSchematicTag extends CompressedCompoundTag { } @Override - public DataInputStream adapt(Clipboard src) throws IOException { + public LZ4BlockInputStream adapt(Clipboard src) throws IOException { FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream(); try (LZ4BlockOutputStream lz4out = new LZ4BlockOutputStream(blocksOut)) { NBTOutputStream nbtOut = new NBTOutputStream(lz4out); @@ -27,6 +24,6 @@ public class CompressedSchematicTag extends CompressedCompoundTag { throw new RuntimeException(e); } FastByteArraysInputStream in = new FastByteArraysInputStream(blocksOut.toByteArrays()); - return new DataInputStream(new LZ4BlockInputStream(in)); + return new LZ4BlockInputStream(in); } } diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/IntArrayTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/IntArrayTag.java index 7c5e49603..04ec07be8 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/IntArrayTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/IntArrayTag.java @@ -21,6 +21,8 @@ package com.sk89q.jnbt; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Locale; + /** * The {@code TAG_Int_Array} tag. */ @@ -48,7 +50,7 @@ public final class IntArrayTag extends Tag { public String toString() { StringBuilder hex = new StringBuilder(); for (int b : value) { - String hexDigits = Integer.toHexString(b).toUpperCase(); + String hexDigits = Integer.toHexString(b).toUpperCase(Locale.ROOT); if (hexDigits.length() == 1) { hex.append("0"); } diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/LongArrayTag.java b/worldedit-core/src/main/java/com/sk89q/jnbt/LongArrayTag.java index 30dad0cc3..1771ed9a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/LongArrayTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/LongArrayTag.java @@ -21,6 +21,8 @@ package com.sk89q.jnbt; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Locale; + /** * The {@code TAG_Long_Array} tag. */ @@ -48,7 +50,7 @@ public class LongArrayTag extends Tag { public String toString() { StringBuilder hex = new StringBuilder(); for (long b : value) { - String hexDigits = Long.toHexString(b).toUpperCase(); + String hexDigits = Long.toHexString(b).toUpperCase(Locale.ROOT); if (hexDigits.length() == 1) { hex.append("0"); } diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java index f7541d72d..d1cd534ef 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java @@ -152,39 +152,21 @@ public final class NBTInputStream implements Closeable { if (reader instanceof NBTStreamer.ByteReader) { NBTStreamer.ByteReader byteReader = (NBTStreamer.ByteReader) reader; int i = 0; - if (is instanceof InputStream) { - DataInputStream dis = is; - if (length > 1024) { - if (buf == null) { - buf = new byte[1024]; - } - int left = length; - for (; left > 1024; left -= 1024) { - dis.readFully(buf); - for (byte b : buf) { - byteReader.run(i++, b & 0xFF); - } + DataInputStream dis = is; + if (length > 1024) { + if (buf == null) { + buf = new byte[1024]; + } + int left = length; + for (; left > 1024; left -= 1024) { + dis.readFully(buf); + for (byte b : buf) { + byteReader.run(i++, b & 0xFF); } } - for (; i < length; i++) { - byteReader.run(i, dis.read()); - } - } else { - if (length > 1024) { - if (buf == null) { - buf = new byte[1024]; - } - int left = length; - for (; left > 1024; left -= 1024) { - is.readFully(buf); - for (byte b : buf) { - byteReader.run(i++, b & 0xFF); - } - } - } - for (; i < length; i++) { - byteReader.run(i, is.readByte() & 0xFF); - } + } + for (; i < length; i++) { + byteReader.run(i, dis.read()); } } else if (reader instanceof NBTStreamer.LazyReader) { reader.accept(length, is); @@ -410,88 +392,6 @@ public final class NBTInputStream implements Closeable { } } - public Object readDataPayload(int type, int depth) throws IOException { - switch (type) { - case NBTConstants.TYPE_END: - if (depth == 0) { - throw new IOException( - "TAG_End found without a TAG_Compound/TAG_List tag preceding it."); - } else { - return null; - } - case NBTConstants.TYPE_BYTE: - return is.readByte(); - case NBTConstants.TYPE_SHORT: - return is.readShort(); - case NBTConstants.TYPE_INT: - return is.readInt(); - case NBTConstants.TYPE_LONG: - return is.readLong(); - case NBTConstants.TYPE_FLOAT: - return is.readFloat(); - case NBTConstants.TYPE_DOUBLE: - return is.readDouble(); - case NBTConstants.TYPE_BYTE_ARRAY: - int length = is.readInt(); - byte[] bytes = new byte[length]; - is.readFully(bytes); - return bytes; - case NBTConstants.TYPE_STRING: - length = is.readShort(); - bytes = new byte[length]; - is.readFully(bytes); - return new String(bytes, NBTConstants.CHARSET); - case NBTConstants.TYPE_LIST: - int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } - length = is.readInt(); - ArrayList list = new ArrayList<>(); - for (int i = 0; i < length; ++i) { - Object obj = readDataPayload(childType, depth + 1); - if (obj == null) { - throw new IOException("TAG_End not permitted in a list."); - } - list.add(obj); - } - - return list; - case NBTConstants.TYPE_COMPOUND: - Map map = new HashMap<>(); - while (true) { - int newType = is.readByte(); - String name = readNamedTagName(newType); - Object data = readDataPayload(newType, depth + 1); - if (data == null) { - break; - } else { - map.put(name, data); - } - } - - return map; - case NBTConstants.TYPE_INT_ARRAY: { - length = is.readInt(); - int[] data = new int[length]; - for (int i = 0; i < length; i++) { - data[i] = is.readInt(); - } - return data; - } - case NBTConstants.TYPE_LONG_ARRAY: { - length = is.readInt(); - long[] data = new long[length]; - for (int i = 0; i < length; i++) { - data[i] = is.readLong(); - } - return data; - } - default: - throw new IOException("Invalid tag type: " + type + "."); - } - } - /** * Reads the payload of a tag given the type. * @@ -560,22 +460,20 @@ public final class NBTInputStream implements Closeable { } return new CompoundTag(tagMap); - case NBTConstants.TYPE_INT_ARRAY: { + case NBTConstants.TYPE_INT_ARRAY: length = is.readInt(); int[] data = new int[length]; for (int i = 0; i < length; i++) { data[i] = is.readInt(); } return new IntArrayTag(data); - } - case NBTConstants.TYPE_LONG_ARRAY: { + case NBTConstants.TYPE_LONG_ARRAY: length = is.readInt(); long[] longData = new long[length]; for (int i = 0; i < length; i++) { longData[i] = is.readLong(); } return new LongArrayTag(longData); - } default: throw new IOException("Invalid tag type: " + type + "."); } @@ -583,13 +481,7 @@ public final class NBTInputStream implements Closeable { @Override public void close() throws IOException { - if (is instanceof AutoCloseable) { - try { - ((AutoCloseable) is).close(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } + is.close(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java index 6b30e85c1..96c3b6de5 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java @@ -19,8 +19,6 @@ package com.sk89q.jnbt; -import com.boydti.fawe.object.io.LittleEndianOutputStream; - import static com.google.common.base.Preconditions.checkNotNull; import java.io.Closeable; @@ -45,7 +43,7 @@ public final class NBTOutputStream extends OutputStream implements Closeable, Da /** * The output stream. */ - private DataOutput os; + private final DataOutputStream os; /** * Creates a new {@code NBTOutputStream}, which will write data to the @@ -60,23 +58,10 @@ public final class NBTOutputStream extends OutputStream implements Closeable, Da this.os = new DataOutputStream(os); } - public NBTOutputStream(DataOutput os) throws IOException { - this.os = os; - } - public DataOutput getOutputStream() { return os; } - /** - * Use a little endian output stream - */ - public void setLittleEndian() { - if (!(os instanceof LittleEndianOutputStream)) { - this.os = new LittleEndianOutputStream((OutputStream) os); - } - } - /** * Writes a tag. * @@ -159,7 +144,7 @@ public final class NBTOutputStream extends OutputStream implements Closeable, Da writeNamedEmptyList(name, NBTConstants.TYPE_COMPOUND); } - public void writeNamedEmptyList(String name, int type) throws IOException { + private void writeNamedEmptyList(String name, int type) throws IOException { writeNamedTagName(name, NBTConstants.TYPE_LIST); os.writeByte(type); os.writeInt(0); @@ -420,7 +405,7 @@ public final class NBTOutputStream extends OutputStream implements Closeable, Da @Override public void close() throws IOException { - if (os instanceof Closeable) ((Closeable) os).close(); + os.close(); } @Override @@ -500,6 +485,6 @@ public final class NBTOutputStream extends OutputStream implements Closeable, Da */ @Override public void flush() throws IOException { - if (os instanceof Flushable) ((Flushable) os).flush(); + ((Flushable) os).flush(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/CommandsManager.java b/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/CommandsManager.java index f8af6a8c2..e5153a1b2 100644 --- a/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/CommandsManager.java +++ b/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/CommandsManager.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -60,6 +61,7 @@ import java.util.Set; * @param command sender class */ @SuppressWarnings("ProtectedField") +@Deprecated public abstract class CommandsManager { protected static final Logger logger = @@ -253,7 +255,7 @@ public abstract class CommandsManager { * @return true if the command exists */ public boolean hasCommand(String command) { - return commands.get(null).containsKey(command.toLowerCase()); + return commands.get(null).containsKey(command.toLowerCase(Locale.ROOT)); } /** @@ -433,7 +435,7 @@ public abstract class CommandsManager { String cmdName = args[level]; Map map = commands.get(parent); - Method method = map.get(cmdName.toLowerCase()); + Method method = map.get(cmdName.toLowerCase(Locale.ROOT)); if (method == null) { if (parent == null) { // Root diff --git a/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/package-info.java b/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/package-info.java new file mode 100644 index 000000000..7fe54c4ae --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/package-info.java @@ -0,0 +1,24 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +/** + * This package contains the old command system. It is no longer in use. Please switch + * to Piston, Intake, ACF, or similar systems. + */ +package com.sk89q.minecraft.util.commands; diff --git a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java index 32386a34a..dd4bf4b29 100644 --- a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java @@ -310,7 +310,7 @@ public final class StringUtil { } public static List parseListInQuotes(String[] input, char delimiter, char quoteOpen, char quoteClose, boolean appendLeftover) { - List parsableBlocks = new ArrayList<>(); + List parsableBlocks = new ArrayList<>(); StringBuilder buffer = new StringBuilder(); for (String split : input) { if (split.indexOf(quoteOpen) != -1 && split.indexOf(quoteClose) == -1) { 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 a49f1ddf2..c4e26b5fe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -19,6 +19,12 @@ package com.sk89q.worldedit; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.regions.Regions.asFlatRegion; +import static com.sk89q.worldedit.regions.Regions.maximumBlockY; +import static com.sk89q.worldedit.regions.Regions.minimumBlockY; + import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; @@ -26,7 +32,6 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.example.MappedFaweQueue; import com.boydti.fawe.jnbt.anvil.MCAQueue; import com.boydti.fawe.jnbt.anvil.MCAWorld; -import com.boydti.fawe.logging.LoggingChangeSet; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; @@ -44,7 +49,17 @@ import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; import com.boydti.fawe.object.collection.LocalBlockVectorSet; import com.boydti.fawe.object.exception.FaweException; -import com.boydti.fawe.object.extent.*; +import com.boydti.fawe.object.extent.FastWorldEditExtent; +import com.boydti.fawe.object.extent.FaweRegionExtent; +import com.boydti.fawe.object.extent.HeightBoundExtent; +import com.boydti.fawe.object.extent.MultiRegionExtent; +import com.boydti.fawe.object.extent.NullExtent; +import com.boydti.fawe.object.extent.ProcessedWEExtent; +import com.boydti.fawe.object.extent.ResettableExtent; +import com.boydti.fawe.object.extent.SingleRegionExtent; +import com.boydti.fawe.object.extent.SlowExtent; +import com.boydti.fawe.object.extent.SourceMaskExtent; +import com.boydti.fawe.object.extent.StripNBTExtent; import com.boydti.fawe.object.function.SurfaceRegionFunction; import com.boydti.fawe.object.mask.ResettableMask; import com.boydti.fawe.object.pattern.ExistingPattern; @@ -58,9 +73,6 @@ import com.boydti.fawe.util.Perm; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.wrappers.WorldWrapper; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Supplier; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.blocks.BaseItemStack; @@ -81,7 +93,16 @@ import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.block.Naturalizer; import com.sk89q.worldedit.function.generator.ForestGenerator; import com.sk89q.worldedit.function.generator.GardenPatchGenerator; -import com.sk89q.worldedit.function.mask.*; +import com.sk89q.worldedit.function.mask.BlockMaskBuilder; +import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.BoundedHeightMask; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +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.NoiseFilter2D; +import com.sk89q.worldedit.function.mask.RegionMask; import com.sk89q.worldedit.function.operation.ChangeSetExecutor; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operation; @@ -122,15 +143,13 @@ import com.sk89q.worldedit.regions.EllipsoidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Regions; -import static com.sk89q.worldedit.regions.Regions.asFlatRegion; -import static com.sk89q.worldedit.regions.Regions.maximumBlockY; -import static com.sk89q.worldedit.regions.Regions.minimumBlockY; import com.sk89q.worldedit.regions.shape.ArbitraryBiomeShape; import com.sk89q.worldedit.regions.shape.ArbitraryShape; import com.sk89q.worldedit.regions.shape.RegionShape; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.world.SimpleWorld; @@ -144,11 +163,7 @@ 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.weather.WeatherType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -156,6 +171,11 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An {@link Extent} that handles history, {@link BlockBag}s, change limits, @@ -229,20 +249,17 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, public static final UUID CONSOLE = UUID.fromString("1-1-3-3-7"); @Deprecated - public EditSession(@Nonnull World world, @Nullable FaweQueue queue, @Nullable FawePlayer player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable RegionWrapper[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @Nullable EventBus bus, @Nullable EditSessionEvent event) { + public EditSession(@Nonnull World world, @Nullable FaweQueue queue, @Nullable FawePlayer player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable RegionWrapper[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @NotNull EventBus bus, @Nullable EditSessionEvent event) { this(null, world, queue, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, bus, event); } - public EditSession(@Nullable String worldName, @Nullable World world, @Nullable FaweQueue queue, @Nullable FawePlayer player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable Region[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @Nullable EventBus bus, @Nullable EditSessionEvent event) { + public EditSession(@Nullable String worldName, @Nullable World world, @Nullable FaweQueue queue, @Nullable FawePlayer player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable Region[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @NotNull EventBus bus, @Nullable EditSessionEvent event) { super(world); this.worldName = worldName == null ? world == null ? queue == null ? "" : queue.getWorldName() : world.getName() : worldName; if (world == null && this.worldName != null) world = FaweAPI.getWorld(this.worldName); this.world = world; - if (bus == null) { - bus = WorldEdit.getInstance().getEventBus(); - } if (event == null) { event = new EditSessionEvent(world, player == null ? null : (player.getPlayer()), -1, null); } @@ -344,13 +361,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, 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); } @@ -416,7 +427,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, public void resetLimit() { this.limit.set(this.originalLimit); - ExtentTraverser find = new ExtentTraverser(extent).find(ProcessedWEExtent.class); + ExtentTraverser find = new ExtentTraverser<>(extent).find(ProcessedWEExtent.class); if (find != null && find.get() != null) { find.get().setLimit(this.limit); } @@ -455,7 +466,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return FaweRegionExtent (may be null) */ public FaweRegionExtent getRegionExtent() { - ExtentTraverser traverser = new ExtentTraverser(this.extent).find(FaweRegionExtent.class); + ExtentTraverser traverser = new ExtentTraverser<>(this.extent).find(FaweRegionExtent.class); return traverser == null ? null : traverser.get(); } @@ -467,6 +478,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return bypassHistory; } + @Override public Extent getExtent() { return extent; } @@ -532,27 +544,17 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } } - /** - * Send a debug message to the Actor responsible for this EditSession (or Console) - * - * @param message - * @param args - */ - public void debug(BBC message, Object... args) { - message.send(player, args); - } - /** * Get the FaweQueue this EditSession uses to queue the changes
* - Note: All implementation queues for FAWE are instances of NMSMappedFaweQueue * * @return */ + @Override public FaweQueue getQueue() { return queue; } - @Deprecated private AbstractDelegateExtent wrapExtent(Extent extent, EventBus eventBus, EditSessionEvent event, Stage stage) { event = event.clone(stage); event.setExtent(extent); @@ -739,7 +741,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return mask, may be null */ public Mask getMask() { - ExtentTraverser maskingExtent = new ExtentTraverser(this.extent).find(MaskingExtent.class); + ExtentTraverser maskingExtent = new ExtentTraverser<>(this.extent).find(MaskingExtent.class); return maskingExtent != null ? maskingExtent.get().getMask() : null; } @@ -749,18 +751,18 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return mask, may be null */ public Mask getSourceMask() { - ExtentTraverser maskingExtent = new ExtentTraverser(this.extent).find(SourceMaskExtent.class); + ExtentTraverser maskingExtent = new ExtentTraverser<>(this.extent).find(SourceMaskExtent.class); return maskingExtent != null ? maskingExtent.get().getMask() : null; } public void addTransform(ResettableExtent transform) { wrapped = true; if (transform == null) { - ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class); + ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class); AbstractDelegateExtent next = extent; while (traverser != null && traverser.get() instanceof ResettableExtent) { traverser = traverser.next(); - next = traverser.get(); + next = (AbstractDelegateExtent) traverser.get(); } this.extent = next; return; @@ -770,7 +772,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } public @Nullable ResettableExtent getTransform() { - ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class); + ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class); if (traverser != null) { return (ResettableExtent) traverser.get(); } @@ -788,7 +790,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } else { new MaskTraverser(mask).reset(this); } - ExtentTraverser maskingExtent = new ExtentTraverser(this.extent).find(SourceMaskExtent.class); + ExtentTraverser maskingExtent = new ExtentTraverser<>(this.extent).find(SourceMaskExtent.class); if (maskingExtent != null && maskingExtent.get() != null) { Mask oldMask = maskingExtent.get().getMask(); if (oldMask instanceof ResettableMask) { @@ -845,7 +847,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return the survival simulation extent */ public SurvivalModeExtent getSurvivalExtent() { - ExtentTraverser survivalExtent = new ExtentTraverser(this.extent).find(SurvivalModeExtent.class); + ExtentTraverser survivalExtent = new ExtentTraverser<>(this.extent).find(SurvivalModeExtent.class); if (survivalExtent != null) { return survivalExtent.get(); } else { @@ -878,7 +880,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, if (history == null) { return; } - ExtentTraverser traverseHistory = new ExtentTraverser(this.extent).find(HistoryExtent.class); + ExtentTraverser traverseHistory = new ExtentTraverser<>(this.extent).find(HistoryExtent.class); if (disableHistory) { if (traverseHistory != null && traverseHistory.exists()) { ExtentTraverser beforeHistory = traverseHistory.previous(); @@ -890,7 +892,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } } } else if (traverseHistory == null || !traverseHistory.exists()) { - ExtentTraverser traverseBypass = new ExtentTraverser(this.extent).find(bypassHistory); + ExtentTraverser traverseBypass = new ExtentTraverser<>(this.extent).find(bypassHistory); if (traverseBypass != null) { ExtentTraverser beforeHistory = traverseBypass.previous(); beforeHistory.setNext(history); @@ -939,19 +941,19 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * * @return a map of missing blocks */ - public Map popMissingBlocks() { + public Map popMissingBlocks() { BlockBag bag = getBlockBag(); if (bag != null) { bag.flushChanges(); - Map missingBlocks; + Map missingBlocks; ChangeSet changeSet = getChangeSet(); if (changeSet instanceof BlockBagChangeSet) { missingBlocks = ((BlockBagChangeSet) changeSet).popMissing(); } else { - ExtentTraverser find = new ExtentTraverser(extent).find(BlockBagExtent.class); + ExtentTraverser find = new ExtentTraverser<>(extent).find(BlockBagExtent.class); if (find != null && find.get() != null) { missingBlocks = find.get().popMissing(); } else { @@ -1065,6 +1067,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ()); } + @Override public BlockState getLazyBlock(int x, int y, int z) { return extent.getLazyBlock(x, y, z); } @@ -1083,18 +1086,6 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return extent.getFullBlock(position); } - /** - * Get a block type at the given position. - * - * @param position the position - * @return the block type - * @deprecated Use {@link #getLazyBlock(BlockVector3)} or {@link #getBlock(BlockVector3)} - */ - @Deprecated - public BlockType getBlockType(final BlockVector3 position) { - return getBlockType(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - } - /** * Returns the highest solid 'terrain' block. * @@ -1104,6 +1095,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @param maxY maximal height * @return height of highest block found or 'minY' */ + @Override public int getHighestTerrainBlock(int x, int z, int minY, int maxY) { for (int y = maxY; y >= minY; --y) { if (getBlock(x, y, z).getBlockType().getMaterial().isMovementBlocker()) { @@ -1123,6 +1115,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @param filter a mask of blocks to consider, or null to consider any solid (movement-blocking) block * @return height of highest block found or 'minY' */ + @Override public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) { for (int y = maxY; y >= minY; --y) { if (filter.test(mutablebv.setComponents(x, y, z))) { @@ -1209,6 +1202,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } + @Override public > boolean setBlock(int x, int y, int z, B block) { this.changes++; try { @@ -1301,7 +1295,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, @Override @Nullable - public Entity createEntity(com.sk89q.worldedit.util.Location location, final BaseEntity entity) { + public Entity createEntity(Location location, final BaseEntity entity) { return this.extent.createEntity(location, entity); } @@ -1472,12 +1466,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } if (searchIDs.size() == 1) { final BlockType id = searchIDs.iterator().next(); - RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { - @Override - public boolean apply(BlockVector3 position) throws WorldEditException { - return getBlockType(position) == id; - } - }, this); + RegionVisitor visitor = new RegionVisitor(region, position -> getBlock(position).getBlockType() == id, this); Operations.completeBlindly(visitor); return visitor.getAffected(); } @@ -1489,23 +1478,14 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } public int countBlock(final Region region, final boolean[] ids) { - RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { - @Override - public boolean apply(BlockVector3 position) throws WorldEditException { - return ids[getBlockType(position).getInternalId()]; - } - }, this); + RegionVisitor visitor = new RegionVisitor(region, + position -> ids[getBlock(position).getInternalId()], this); Operations.completeBlindly(visitor); return visitor.getAffected(); } public int countBlock(final Region region, final Mask mask) { - RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { - @Override - public boolean apply(BlockVector3 position) throws WorldEditException { - return mask.test(position); - } - }, this); + RegionVisitor visitor = new RegionVisitor(region, mask::test, this); Operations.completeBlindly(visitor); return visitor.getAffected(); } @@ -1519,12 +1499,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, */ public int countBlocks(Region region, Set searchBlocks) { Mask mask = new BlockMaskBuilder().addBlocks(searchBlocks).build(extent); - RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { - @Override - public boolean apply(BlockVector3 position) throws WorldEditException { - return mask.test(position); - } - }, this); + RegionVisitor visitor = new RegionVisitor(region, mask::test, this); Operations.completeBlindly(visitor); return visitor.getAffected(); } @@ -1534,30 +1509,27 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, final int startPerformY = region.getMinimumPoint().getBlockY(); final int startCheckY = fullHeight ? 0 : startPerformY; final int endY = region.getMaximumPoint().getBlockY(); - RegionVisitor visitor = new RegionVisitor(flat, new RegionFunction() { - @Override - public boolean apply(BlockVector3 pos) throws WorldEditException { - int x = pos.getBlockX(); - int z = pos.getBlockZ(); - int freeSpot = startCheckY; - for (int y = startCheckY; y <= endY; y++) { - if (y < startPerformY) { - if (!getBlockType(x, y, z).getMaterial().isAir()) { - freeSpot = y + 1; - } - continue; - } - BlockType block = getBlockType(x, y, z); - if (!block.getMaterial().isAir()) { - if (freeSpot != y) { - setBlock(x, freeSpot, z, block); - setBlock(x, y, z, replace); - } - freeSpot++; + RegionVisitor visitor = new RegionVisitor(flat, pos -> { + int x = pos.getBlockX(); + int z = pos.getBlockZ(); + int freeSpot = startCheckY; + for (int y = startCheckY; y <= endY; y++) { + if (y < startPerformY) { + if (!getBlockType(x, y, z).getMaterial().isAir()) { + freeSpot = y + 1; } + continue; + } + BlockType block = getBlockType(x, y, z); + if (!block.getMaterial().isAir()) { + if (freeSpot != y) { + setBlock(x, freeSpot, z, block); + setBlock(x, y, z, replace); + } + freeSpot++; } - return true; } + return true; }, this); Operations.completeBlindly(visitor); return this.changes; @@ -1574,7 +1546,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int fillDirection(final BlockVector3 origin, final Pattern pattern, final double radius, final int depth, BlockVector3 direction) { + public int fillDirection(final BlockVector3 origin, final Pattern pattern, final double radius, final int depth, BlockVector3 direction) throws MaxChangedBlocksException { checkNotNull(origin); checkNotNull(pattern); checkArgument(radius >= 0, "radius >= 0"); @@ -1727,12 +1699,11 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, FaweRegionExtent regionExtent = getRegionExtent(); if (!(region instanceof CuboidRegion)) return false; if (regionExtent != null) { - if (!(region instanceof CuboidRegion)) return false; BlockVector3 pos1 = region.getMinimumPoint(); BlockVector3 pos2 = region.getMaximumPoint(); boolean contains = false; for (Region current : regionExtent.getRegions()) { - if (current.contains((int) pos1.getX(), pos1.getBlockY(), pos1.getBlockZ()) && current.contains(pos2.getBlockX(), pos2.getBlockY(), pos2.getBlockZ())) { + if (current.contains(pos1.getX(), pos1.getBlockY(), pos1.getBlockZ()) && current.contains(pos2.getBlockX(), pos2.getBlockY(), pos2.getBlockZ())) { contains = true; break; } @@ -1745,8 +1716,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return false; if (getChangeTask() != getChangeSet()) return false; if (!Masks.isNull(getMask()) || !Masks.isNull(getSourceMask())) return false; - if (getBlockBag() != null) return false; - return true; + return getBlockBag() == null; } public boolean hasExtraExtents() { @@ -1772,7 +1742,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, getWorld(), // Causes clamping of Y range position.add(adjustment.multiply(-1)), position.add(adjustment)); - Pattern pattern = (BlockTypes.AIR.getDefaultState()); + Pattern pattern = BlockTypes.AIR.getDefaultState(); return replaceBlocks(region, mask, pattern); } @@ -1787,7 +1757,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, public > int setBlocks(Region region, B block) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(block); - boolean hasNbt = block instanceof BaseBlock && ((BaseBlock)block).hasNbtData(); + boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); if (canBypassAll(region, false, true) && !hasNbt) { return changes = queue.setBlocks((CuboidRegion) region, block.getInternalId()); @@ -1845,7 +1815,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public > int replaceBlocks(Region region, Set filter, B replacement) throws MaxChangedBlocksException { - return replaceBlocks(region, filter, (replacement)); + return replaceBlocks(region, filter, replacement); } /** @@ -1917,7 +1887,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public > int makeCuboidFaces(Region region, B block) throws MaxChangedBlocksException { - return makeCuboidFaces(region, (block)); + return makeCuboidFaces(region, block); } /** @@ -1969,7 +1939,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public > int makeCuboidWalls(Region region, B block) throws MaxChangedBlocksException { - return makeCuboidWalls(region, (block)); + return makeCuboidWalls(region, block); } /** @@ -2094,7 +2064,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, BlockVector3 to = region.getMinimumPoint(); ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to); copy.setCopyingEntities(copyEntities); - copy.setCopyBiomes(copyBiomes); + copy.setCopyingBiomes(copyBiomes); copy.setRepetitions(count); copy.setTransform(new AffineTransform().translate(dir.multiply(size))); Mask sourceMask = getSourceMask(); @@ -2140,8 +2110,8 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to); if (replacement == null) replacement = BlockTypes.AIR.getDefaultState(); - final BlockReplace remove = replacement instanceof ExistingPattern ? null : new BlockReplace(this, replacement); - copy.setCopyBiomes(copyBiomes); + BlockReplace remove = replacement instanceof ExistingPattern ? null : new BlockReplace(this, replacement); + copy.setCopyingBiomes(copyBiomes); copy.setCopyingEntities(copyEntities); copy.setSourceFunction(remove); // Remove copy.setRemovingEntities(true); @@ -2170,7 +2140,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return number of blocks moved * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int moveCuboidRegion(Region region, BlockVector3 dir, int distance, boolean copyAir, Pattern replacement) { + public int moveCuboidRegion(Region region, BlockVector3 dir, int distance, boolean copyAir, Pattern replacement) throws MaxChangedBlocksException { return moveRegion(region, dir, distance, copyAir, true, false, replacement); } @@ -2214,7 +2184,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, if (waterlogged) { replace = new BlockReplace(this, new WaterloggedRemover(this)); } else { - replace = new BlockReplace(this, (BlockTypes.AIR.getDefaultState())); + replace = new BlockReplace(this, BlockTypes.AIR.getDefaultState()); } RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this); @@ -2256,7 +2226,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, blockMask ); - BlockReplace replace = new BlockReplace(this, (fluid.getDefaultState())); + BlockReplace replace = new BlockReplace(this, fluid.getDefaultState()); NonRisingVisitor visitor = new NonRisingVisitor(mask, replace); // Around the origin in a 3x3 block @@ -2298,15 +2268,15 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return number of blocks changed * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int makeCylinder(BlockVector3 pos, Pattern block, double radiusX, double radiusZ, int height, boolean filled) throws MaxChangedBlocksException{ + public int makeCylinder(BlockVector3 pos, Pattern block, double radiusX, double radiusZ, int height, boolean filled) throws MaxChangedBlocksException { return makeCylinder(pos, block, radiusX, radiusZ, height, 0, filled); } - public int makeHollowCylinder(BlockVector3 pos, final Pattern block, double radiusX, double radiusZ, int height, double thickness) { + public int makeHollowCylinder(BlockVector3 pos, final Pattern block, double radiusX, double radiusZ, int height, double thickness) throws MaxChangedBlocksException { return makeCylinder(pos, block, radiusX, radiusZ, height, thickness, false); } - private int makeCylinder(BlockVector3 pos, final Pattern block, double radiusX, double radiusZ, int height, double thickness, final boolean filled) { + private int makeCylinder(BlockVector3 pos, Pattern block, double radiusX, double radiusZ, int height, double thickness, boolean filled) throws MaxChangedBlocksException { int affected = 0; radiusX += 0.5; @@ -2322,12 +2292,12 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, if (posv.getBlockY() < 0) { posv.mutY(0); - } else if (((posv.getBlockY() + height) - 1) > maxY) { - height = (maxY - posv.getBlockY()) + 1; + } else if (posv.getBlockY() + height - 1 > maxY) { + height = maxY - posv.getBlockY() + 1; } - final double invRadiusX = 1 / (radiusX); - final double invRadiusZ = 1 / (radiusZ); + final double invRadiusX = 1 / radiusX; + final double invRadiusZ = 1 / radiusZ; int px = posv.getBlockX(); int py = posv.getBlockY(); @@ -2336,7 +2306,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, final int ceilRadiusX = (int) Math.ceil(radiusX); final int ceilRadiusZ = (int) Math.ceil(radiusZ); - double dx, dxz, dz; + double distanceSq; double nextXn = 0; if (thickness != 0) { @@ -2350,15 +2320,13 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, nextMinXn = (x + 1) * minInvRadiusX; double nextZn = 0; double nextMinZn = 0; - dx = xn * xn; forZ: for (int z = 0; z <= ceilRadiusZ; ++z) { final double zn = nextZn; double dz2 = nextMinZn * nextMinZn; nextZn = (z + 1) * invRadiusZ; nextMinZn = (z + 1) * minInvRadiusZ; - dz = zn * zn; - dxz = dx + dz; - if (dxz > 1) { + distanceSq = lengthSq(xn, zn); + if (distanceSq > 1) { if (z == 0) { break forX; } @@ -2381,14 +2349,14 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, forX: for (int x = 0; x <= ceilRadiusX; ++x) { final double xn = nextXn; nextXn = (x + 1) * invRadiusX; + double dx = xn * xn; double nextZn = 0; - dx = xn * xn; forZ: for (int z = 0; z <= ceilRadiusZ; ++z) { final double zn = nextZn; nextZn = (z + 1) * invRadiusZ; - dz = zn * zn; - dxz = dx + dz; - if (dxz > 1) { + double dz = zn * zn; + distanceSq = lengthSq(xn,zn); + if (distanceSq > 1) { if (z == 0) { break forX; } @@ -2414,7 +2382,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return this.changes; } - public int makeCircle(BlockVector3 pos, final Pattern block, double radiusX, double radiusY, double radiusZ, boolean filled, Vector3 normal) { + public int makeCircle(BlockVector3 pos, final Pattern block, double radiusX, double radiusY, double radiusZ, boolean filled, Vector3 normal) throws MaxChangedBlocksException { radiusX += 0.5; radiusY += 0.5; radiusZ += 0.5; @@ -2549,20 +2517,18 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, double nextXn = invRadiusX; forX: for (int x = 0; x <= ceilRadiusX; ++x) { final double xn = nextXn; - double dx = xn * xn; nextXn = (x + 1) * invRadiusX; double nextZn = invRadiusZ; + double dx = xn * xn; forZ: for (int z = 0; z <= ceilRadiusZ; ++z) { final double zn = nextZn; double dz = zn * zn; - double dxz = dx + dz; nextZn = (z + 1) * invRadiusZ; double nextYn = invRadiusY; forY: for (int y = 0; y <= ceilRadiusY; ++y) { final double yn = nextYn; - double dy = yn * yn; - double dxyz = dxz + dy; + double dxyz = lengthSq(zn, yn, zn); nextYn = (y + 1) * invRadiusY; if (dxyz > 1) { @@ -2576,7 +2542,8 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } if (!filled) { - if (nextXn * nextXn + dy + dz <= 1 && nextYn * nextYn + dx + dz <= 1 && nextZn * nextZn + dx + dy <= 1) { + double dy = yn * yn; + if (lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1) { continue; } } @@ -2763,11 +2730,13 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int green(BlockVector3 position, double radius, final boolean onlyNormalDirt) + public int green(BlockVector3 position, double radius, boolean onlyNormalDirt) throws MaxChangedBlocksException { + int affected = 0; final double radiusSq = radius * radius; final int ox = position.getBlockX(); + final int oy = position.getBlockY(); final int oz = position.getBlockZ(); final BlockState grass = BlockTypes.GRASS_BLOCK.getDefaultState(); @@ -2776,27 +2745,25 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, for (int x = ox - ceilRadius; x <= ox + ceilRadius; ++x) { int dx = x - ox; int dx2 = dx * dx; - for (int z = oz - ceilRadius; z <= (oz + ceilRadius); ++z) { + for (int z = oz - ceilRadius; z <= oz + ceilRadius; ++z) { int dz = z - oz; int dz2 = dz * dz; if (dx2 + dz2 > radiusSq) { continue; } - loop: + for (int y = maxY; y >= 1; --y) { - BlockType block = getBlockType(x, y, z); - switch (block.getInternalId()) { - case BlockID.COARSE_DIRT: - if (onlyNormalDirt) break loop; - case BlockID.DIRT: - this.setBlock(x, y, z, BlockTypes.GRASS_BLOCK.getDefaultState()); - break loop; - case BlockID.WATER: - case BlockID.LAVA: - default: - if (block.getMaterial().isMovementBlocker()) { - break loop; - } + final BlockState block = getBlock(x, y, z); + + if (block.getBlockType() == BlockTypes.DIRT || + (!onlyNormalDirt && block.getBlockType() == BlockTypes.COARSE_DIRT)) { + this.setBlock(x, y, z, grass); + + break; + } else if (block.getBlockType() == BlockTypes.WATER || block.getBlockType() == BlockTypes.LAVA) { + break; + } else if (block.getBlockType().getMaterial().isMovementBlocker()) { + break; } } } @@ -2813,11 +2780,11 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @return number of patches created * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int makePumpkinPatches(final BlockVector3 position, final int apothem) { + public int makePumpkinPatches(BlockVector3 position, int apothem) throws MaxChangedBlocksException { return makePumpkinPatches(position, apothem, 0.02); } - public int makePumpkinPatches(final BlockVector3 position, final int apothem, double density) { + public int makePumpkinPatches(BlockVector3 position, int apothem, double density) throws MaxChangedBlocksException { // We want to generate pumpkins GardenPatchGenerator generator = new GardenPatchGenerator(this); generator.setPlant(GardenPatchGenerator.getPumpkinPattern()); @@ -2846,7 +2813,6 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int makeForest(BlockVector3 basePosition, int size, double density, TreeGenerator.TreeType treeType) throws MaxChangedBlocksException { - try { for (int x = basePosition.getBlockX() - size; x <= (basePosition.getBlockX() + size); ++x) { for (int z = basePosition.getBlockZ() - size; z <= (basePosition.getBlockZ() + size); ++z) { // Don't want to be in the ground @@ -2873,7 +2839,6 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } } } - } catch (MaxChangedBlocksException ignore) {} return this.changes; } @@ -2928,7 +2893,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } } else { for (final BlockVector3 pt : region) { - BlockType type = getBlockType(pt); + BlockType type = getBlock(pt).getBlockType(); counter[type.getInternalId()]++; } } @@ -3100,6 +3065,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, final RValue x = expression.getVariable("x", false).optimize(); final RValue y = expression.getVariable("y", false).optimize(); final RValue z = expression.getVariable("z", false).optimize(); + final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero); expression.setEnvironment(environment); final Vector3 zero2 = zero.add(0.5, 0.5, 0.5); @@ -3142,7 +3108,6 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException { - try { final Set outside = new LocalBlockVectorSet(); final BlockVector3 min = region.getMinimumPoint(); @@ -3201,12 +3166,12 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } } this.changes++; + try { pattern.apply(this.extent, position, position); + } catch (WorldEditException e) { + e.printStackTrace(); } - } catch (WorldEditException e) { - throw new RuntimeException(e); } - return changes; } @@ -3276,12 +3241,12 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } Set newVset; if (flat) { - newVset = this.getStretched(vset, radius); + newVset = getStretched(vset, radius); if (!filled) { newVset = this.getOutline(newVset); } } else { - newVset = this.getBallooned(vset, radius); + newVset = getBallooned(vset, radius); if (!filled) { newVset = this.getHollowed(newVset); } @@ -3324,16 +3289,20 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, interpol.setNodes(nodes); double splinelength = interpol.arcLength(0, 1); for (double loop = 0; loop <= 1; loop += 1D / splinelength / quality) { - final BlockVector3 tipv = interpol.getPosition(loop).toBlockPoint(); + BlockVector3 tipv = interpol.getPosition(loop).toBlockPoint(); if (radius == 0) { - pattern.apply(this, tipv, tipv); + try { + pattern.apply(this, tipv, tipv); + } catch (WorldEditException e) { + e.printStackTrace(); + } } else { vset.add(tipv); } } Set newVset; if (radius != 0) { - newVset = this.getBallooned(vset, radius); + newVset = getBallooned(vset, radius); if (!filled) { newVset = this.getHollowed(newVset); } @@ -3342,7 +3311,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return changes; } - private Set getBallooned(Set vset, double radius) { + private static Set getBallooned(Set vset, double radius) { if (radius < 1) { return vset; } @@ -3352,9 +3321,9 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, for (BlockVector3 v : vset) { int tipx = v.getBlockX(), tipy = v.getBlockY(), tipz = v.getBlockZ(); - for (int loopx = tipx - ceilrad; loopx <= (tipx + ceilrad); loopx++) { - for (int loopy = tipy - ceilrad; loopy <= (tipy + ceilrad); loopy++) { - for (int loopz = tipz - ceilrad; loopz <= (tipz + ceilrad); loopz++) { + for (int loopx = tipx - ceilrad; loopx <= tipx + ceilrad; loopx++) { + for (int loopy = tipy - ceilrad; loopy <= tipy + ceilrad; loopy++) { + for (int loopz = tipz - ceilrad; loopz <= tipz + ceilrad; loopz++) { if (MathMan.hypot(loopx - tipx, loopy - tipy, loopz - tipz) <= radius) { returnset.add(loopx, loopy, loopz); } @@ -3365,7 +3334,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return returnset; } - public Set getStretched(final Set vset, final double radius) { + public static Set getStretched(Set vset, double radius) { if (radius < 1) { return vset; } @@ -3373,8 +3342,8 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, final int ceilrad = (int) Math.ceil(radius); for (final BlockVector3 v : vset) { final int tipx = v.getBlockX(), tipy = v.getBlockY(), tipz = v.getBlockZ(); - for (int loopx = tipx - ceilrad; loopx <= (tipx + ceilrad); loopx++) { - for (int loopz = tipz - ceilrad; loopz <= (tipz + ceilrad); loopz++) { + for (int loopx = tipx - ceilrad; loopx <= tipx + ceilrad; loopx++) { + for (int loopz = tipz - ceilrad; loopz <= tipz + ceilrad; loopz++) { if (MathMan.hypot(loopx - tipx, 0, loopz - tipz) <= radius) { returnset.add(loopx, v.getBlockY(), loopz); } @@ -3384,7 +3353,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return returnset; } - public Set getOutline(final Set vset) { + public Set getOutline(Set vset) { final LocalBlockVectorSet returnset = new LocalBlockVectorSet(); final LocalBlockVectorSet newset = new LocalBlockVectorSet(); newset.addAll(vset); @@ -3400,7 +3369,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return returnset; } - public Set getHollowed(final Set vset) { + public Set getHollowed(Set vset) { final Set returnset = new LocalBlockVectorSet(); final LocalBlockVectorSet newset = new LocalBlockVectorSet(); newset.addAll(vset); @@ -3421,6 +3390,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, private void recurseHollow(Region region, BlockVector3 origin, Set outside) { final LocalBlockVectorSet queue = new LocalBlockVectorSet(); queue.add(origin); + while (!queue.isEmpty()) { final BlockVector3 current = queue.getIndex(0); queue.remove(current); @@ -3518,6 +3488,10 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return worldName; } + @Override public @org.jetbrains.annotations.Nullable Path getStoragePath() { + return null; + } + @Override public int getBlockLightLevel(BlockVector3 position) { return queue.getEmmittedLight(position.getBlockX(), position.getBlockY(), position.getBlockZ()); @@ -3666,12 +3640,6 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, return false; } -// public void dropItem(BlockVector3 position, BaseItemStack item) { -// if (getWorld() != null) { -// getWorld().dropItem(position, item); -// } -// } - @Override public void simulateBlockMine(BlockVector3 position) { TaskManager.IMP.sync((Supplier) () -> { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java index 007290e7a..f4aea5dfc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalConfiguration.java @@ -20,13 +20,13 @@ package com.sk89q.worldedit; import com.google.common.collect.Lists; +import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extent.NullExtent; import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.function.mask.BlockMaskBuilder; import com.sk89q.worldedit.util.logging.LogFormat; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; import com.sk89q.worldedit.world.snapshot.SnapshotRepository; @@ -59,11 +59,10 @@ public abstract class LocalConfiguration { public boolean logCommands = false; public String logFile = ""; public String logFormat = LogFormat.DEFAULT_FORMAT; - public boolean registerHelp = true; // what is the point of this, it's not even used + public boolean registerHelp = true; // unused public String wandItem = "minecraft:wooden_axe"; public boolean superPickaxeDrop = true; public boolean superPickaxeManyDrop = true; - public boolean noDoubleSlash = false; public boolean useInventory = false; public boolean useInventoryOverride = false; public boolean useInventoryCreativeOverride = false; @@ -76,7 +75,7 @@ public abstract class LocalConfiguration { public Set allowedDataCycleBlocks = new HashSet<>(); public String saveDir = "schematics"; public String scriptsDir = "craftscripts"; - public boolean showHelpInfo = true; + public boolean showHelpInfo = true; // unused public int butcherDefaultRadius = -1; public int butcherMaxRadius = -1; public boolean allowSymlinks = false; @@ -158,7 +157,11 @@ public abstract class LocalConfiguration { if (disallowedBlocksMask == null) { BlockMaskBuilder builder = new BlockMaskBuilder(); for (String blockRegex : disallowedBlocks) { - builder.addRegex(blockRegex); + try { + builder.addRegex(blockRegex); + } catch (InputParseException e) { + e.printStackTrace(); + } } disallowedBlocksMask = builder.build(new NullExtent()); } 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 e46ea7b44..633b60ee9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweInputStream; @@ -39,8 +41,6 @@ import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TextureHolder; import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.wrappers.WorldWrapper; - -import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Options; import com.sk89q.jchronic.utils.Span; @@ -71,19 +71,19 @@ import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.regions.selector.RegionSelectorType; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.HandSide; 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.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.snapshot.Snapshot; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.time.ZoneId; import java.util.Calendar; import java.util.Collections; import java.util.LinkedList; @@ -93,17 +93,19 @@ import java.util.Map; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * Stores session information. */ public class LocalSession implements TextureHolder { - public transient static int MAX_HISTORY_SIZE = 15; + public static transient int MAX_HISTORY_SIZE = 15; // Non-session related fields private transient LocalConfiguration config; - private transient final AtomicBoolean dirty = new AtomicBoolean(); + private final transient AtomicBoolean dirty = new AtomicBoolean(); private transient int failedCuiAttempts = 0; // Session related @@ -127,7 +129,6 @@ public class LocalSession implements TextureHolder { }); private transient volatile Integer historyNegativeIndex; private transient ClipboardHolder clipboard; - private transient boolean toolControl = true; private transient boolean superPickaxe = false; private transient BlockTool pickaxeMode = new SinglePickaxe(); private transient boolean hasTool = false; @@ -143,18 +144,21 @@ public class LocalSession implements TextureHolder { private transient Mask sourceMask; private transient TextureUtil texture; private transient ResettableExtent transform = null; - private transient TimeZone timezone = TimeZone.getDefault(); + private transient ZoneId timezone = ZoneId.systemDefault(); private transient World currentWorld; private transient UUID uuid; private transient volatile long historySize = 0; private transient VirtualWorld virtual; private transient BlockVector3 cuiTemporaryBlock; + private transient List> lastDistribution; // Saved properties private String lastScript; private RegionSelectorType defaultSelector; private boolean useServerCUI = false; // Save this to not annoy players. + private String wandItem; + private String navWandItem; /** * Construct the object. @@ -193,11 +197,6 @@ public class LocalSession implements TextureHolder { } } - /** - * @param uuid - * @param world - * @return If any loading occured - */ public boolean loadSessionHistoryFromDisk(UUID uuid, World world) { if (world == null || uuid == null) { return false; @@ -344,7 +343,7 @@ public class LocalSession implements TextureHolder { * * @return the timezone */ - public TimeZone getTimeZone() { + public ZoneId getTimeZone() { return timezone; } @@ -353,7 +352,7 @@ public class LocalSession implements TextureHolder { * * @param timezone the user's timezone */ - public void setTimezone(TimeZone timezone) { + public void setTimezone(ZoneId timezone) { checkNotNull(timezone); this.timezone = timezone; } @@ -409,7 +408,6 @@ public class LocalSession implements TextureHolder { } loadSessionHistoryFromDisk(player.getUniqueId(), world); if (changeSet instanceof FaweChangeSet) { - int size = getHistoryNegativeIndex(); ListIterator iter = history.listIterator(); int i = 0; int cutoffIndex = history.size() - getHistoryNegativeIndex(); @@ -454,9 +452,8 @@ public class LocalSession implements TextureHolder { return; } // Don't store anything if no changes were made - if (editSession.size() == 0) { - return; - } + if (editSession.size() == 0) return; + FaweChangeSet changeSet = (FaweChangeSet) editSession.getChangeSet(); if (changeSet.isEmpty()) { return; @@ -468,7 +465,6 @@ public class LocalSession implements TextureHolder { } // Destroy any sessions after this undo point if (append) { - int size = getHistoryNegativeIndex(); ListIterator iter = history.listIterator(); int i = 0; int cutoffIndex = history.size() - getHistoryNegativeIndex(); @@ -518,7 +514,7 @@ public class LocalSession implements TextureHolder { loadSessionHistoryFromDisk(player.getUniqueId(), fp.getWorldForEditing()); if (getHistoryNegativeIndex() < history.size()) { FaweChangeSet changeSet = getChangeSet(history.get(getHistoryIndex())); - EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld()) + try (EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld()) .allowedRegionsEverywhere() .checkMemory(false) .changeSetNull() @@ -526,11 +522,12 @@ public class LocalSession implements TextureHolder { .limitUnprocessed(fp) .player(fp) .blockBag(getBlockBag(player)) - .build(); - newEditSession.setBlocks(changeSet, ChangeSetExecutor.Type.UNDO); - setDirty(); - historyNegativeIndex++; - return newEditSession; + .build()) { + newEditSession.setBlocks(changeSet, ChangeSetExecutor.Type.UNDO); + setDirty(); + historyNegativeIndex++; + return newEditSession; + } } else { int size = history.size(); if (getHistoryNegativeIndex() != size) { @@ -556,7 +553,7 @@ public class LocalSession implements TextureHolder { setDirty(); historyNegativeIndex--; FaweChangeSet changeSet = getChangeSet(history.get(getHistoryIndex())); - EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld()) + try (EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld()) .allowedRegionsEverywhere() .checkMemory(false) .changeSetNull() @@ -564,9 +561,10 @@ public class LocalSession implements TextureHolder { .limitUnprocessed(fp) .player(fp) .blockBag(getBlockBag(player)) - .build(); - newEditSession.setBlocks(changeSet, ChangeSetExecutor.Type.REDO); - return newEditSession; + .build()) { + newEditSession.setBlocks(changeSet, ChangeSetExecutor.Type.REDO); + return newEditSession; + } } return null; @@ -707,27 +705,6 @@ public class LocalSession implements TextureHolder { return world; } - /** - * Get the world selection. - * - * @return the current selection - */ - public Region getWorldSelection() throws IncompleteRegionException { - return getSelection(getSelectionWorld()); - } - - /** - * This is an alias for {@link #getSelection(World)}. - * It enables CraftScripts to get a world selection as it is - * not possible to use getSelection which have two default - * implementations. - * - * @return Get the selection region in the world. - */ - public Region getWorldSelection(World world) throws IncompleteRegionException { - return getSelection(world); - } - /** * Gets the clipboard. * @@ -783,21 +760,20 @@ public class LocalSession implements TextureHolder { } /** - * See if tool control is enabled. - * - * @return true if enabled + * @return true always - see deprecation notice + * @deprecated The wand is now a tool that can be bound/unbound. */ + @Deprecated public boolean isToolControlEnabled() { - return toolControl; + return true; } /** - * Change tool control setting. - * - * @param toolControl true to enable tool control + * @param toolControl unused - see deprecation notice + * @deprecated The wand is now a tool that can be bound/unbound. */ + @Deprecated public void setToolControl(boolean toolControl) { - this.toolControl = toolControl; } /** @@ -962,15 +938,12 @@ public class LocalSession implements TextureHolder { @Nullable public Tool getTool(Player player) { - if (!Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES && !hasTool) { - return null; - } BaseItem item = player.getItemInHand(HandSide.MAIN_HAND); return getTool(item, player); } public Tool getTool(BaseItem item, Player player) { - if (Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES && item.getNativeItem() != null) { + if (item.getNativeItem() != null) { BrushTool tool = BrushCache.getTool(player, this, item); if (tool != null) return tool; } @@ -987,7 +960,7 @@ public class LocalSession implements TextureHolder { * @throws InvalidToolBindException if the item can't be bound to that item */ public BrushTool getBrushTool(ItemType item) throws InvalidToolBindException { - return getBrushTool(item.getDefaultState(), null, true); + return getBrushTool(item, null, true); } public BrushTool getBrushTool(Player player) throws InvalidToolBindException { @@ -995,17 +968,19 @@ public class LocalSession implements TextureHolder { } public BrushTool getBrushTool(Player player, boolean create) throws InvalidToolBindException { - BaseItem item = player.getItemInHand(HandSide.MAIN_HAND); + ItemType item = player.getItemInHand(HandSide.MAIN_HAND).getType(); return getBrushTool(item, player, create); } + public BrushTool getBrushTool(ItemType item, boolean create) throws InvalidToolBindException { + return getBrushTool(item, null, create); + } - - public BrushTool getBrushTool(BaseItem item, Player player, boolean create) throws InvalidToolBindException { - Tool tool = getTool(item, player); + public BrushTool getBrushTool(ItemType item, Player player, boolean create) throws InvalidToolBindException { + Tool tool = getTool(item.getDefaultState(), player); if (!(tool instanceof BrushTool)) { if (create) { tool = new BrushTool(); - setTool(item, tool, player); + setTool(item.getDefaultState(), tool, player); } else { return null; } @@ -1032,7 +1007,7 @@ public class LocalSession implements TextureHolder { setTool(item.getDefaultState(), tool, null); } - public void setTool(@Nullable Tool tool, Player player) throws InvalidToolBindException { + public void setTool(Player player, @Nullable Tool tool) throws InvalidToolBindException { BaseItemStack item = player.getItemInHand(HandSide.MAIN_HAND); setTool(item, tool, player); } @@ -1047,7 +1022,7 @@ public class LocalSession implements TextureHolder { throw new InvalidToolBindException(type, "Already used for the navigation wand"); } Tool previous; - if (player != null && (tool instanceof BrushTool || tool == null) && Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES && item.getNativeItem() != null) { + if (player != null && (tool instanceof BrushTool || tool == null) && item.getNativeItem() != null) { previous = BrushCache.getCachedTool(item); BrushCache.setTool(item, (BrushTool) tool); if (tool != null) { @@ -1068,7 +1043,7 @@ public class LocalSession implements TextureHolder { } } } - if (previous != null && player != null && previous instanceof BrushTool) { + if (player != null && previous instanceof BrushTool) { BrushTool brushTool = (BrushTool) previous; brushTool.clear(player); } @@ -1259,9 +1234,9 @@ public class LocalSession implements TextureHolder { * * @param text the message */ - public void handleCUIInitializationMessage(String text) { + public void handleCUIInitializationMessage(String text, Actor actor) { checkNotNull(text); - if (this.failedCuiAttempts > 3) { + if (this.hasCUISupport || this.failedCuiAttempts > 3) { return; } @@ -1271,13 +1246,18 @@ public class LocalSession implements TextureHolder { this.failedCuiAttempts ++; return; } - setCUISupport(true); + + int version; try { - setCUIVersion(Integer.parseInt(split[1])); + version = Integer.parseInt(split[1]); } catch (NumberFormatException e) { WorldEdit.logger.warn("Error while reading CUI init message: " + e.getMessage()); this.failedCuiAttempts ++; + return; } + setCUISupport(true); + setCUIVersion(version); + dispatchCUISelection(actor); } } @@ -1327,9 +1307,10 @@ public class LocalSession implements TextureHolder { public Calendar detectDate(String input) { checkNotNull(input); - Time.setTimeZone(getTimeZone()); + TimeZone tz = TimeZone.getTimeZone(getTimeZone()); + Time.setTimeZone(tz); Options opt = new com.sk89q.jchronic.Options(); - opt.setNow(Calendar.getInstance(getTimeZone())); + opt.setNow(Calendar.getInstance(tz)); Span date = Chronic.parse(input, opt); if (date == null) { return null; @@ -1349,16 +1330,13 @@ public class LocalSession implements TextureHolder { BlockBag blockBag = getBlockBag(player); - World world = player.getWorld(); - boolean isPlayer = player.isPlayer(); - EditSessionBuilder builder = new EditSessionBuilder(world); - if (player.isPlayer()) builder.player(FawePlayer.wrap(player)); - builder.blockBag(blockBag); - builder.fastmode(fastMode); - - EditSession editSession = builder.build(); - + // Create an edit session + EditSession editSession = WorldEdit.getInstance().getEditSessionFactory() + .getEditSession(player.isPlayer() ? player.getWorld() : null, + getBlockChangeLimit(), blockBag, player); Request.request().setEditSession(editSession); + + editSession.setFastMode(fastMode); if (mask != null) { editSession.setMask(mask); } @@ -1368,6 +1346,7 @@ public class LocalSession implements TextureHolder { if (transform != null) { editSession.addTransform(transform); } + return editSession; } @@ -1465,6 +1444,38 @@ public class LocalSession implements TextureHolder { return tmp; } + /** + * Get the preferred wand item for this user, or {@code null} to use the default + * @return item id of wand item, or {@code null} + */ + public String getWandItem() { + return wandItem; + } + + /** + * Get the preferred navigation wand item for this user, or {@code null} to use the default + * @return item id of nav wand item, or {@code null} + */ + public String getNavWandItem() { + return navWandItem; + } + + /** + * Get the last block distribution stored in this session. + * + * @return block distribution or {@code null} + */ + public List> getLastDistribution() { + return lastDistribution == null ? null : Collections.unmodifiableList(lastDistribution); + } + + /** + * Store a block distribution in this session. + */ + public void setLastDistribution(List> dist) { + lastDistribution = dist; + } + public ResettableExtent getTransform() { return transform; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java index 49c323d17..a5b7b6c56 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java @@ -32,6 +32,7 @@ public class UnknownDirectionException extends WorldEditException { * @param dir the input that was tried */ public UnknownDirectionException(String dir) { + super("Unknown direction: " + dir); this.dir = dir; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java b/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java index 18e3581b8..cf437a194 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -19,9 +19,13 @@ package com.sk89q.worldedit; -import com.boydti.fawe.config.BBC; +import static com.sk89q.worldedit.event.platform.Interaction.HIT; +import static com.sk89q.worldedit.event.platform.Interaction.OPEN; + import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.BlockInteractEvent; @@ -48,6 +52,7 @@ import com.sk89q.worldedit.scripting.RhinoCraftScriptEngine; import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.concurrency.EvenMoreExecutors; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.util.io.file.FileSelectionAbortedException; import com.sk89q.worldedit.util.io.file.FilenameException; @@ -60,11 +65,6 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.registry.BundledBlockData; import com.sk89q.worldedit.world.registry.BundledItemData; import com.sk89q.worldedit.world.registry.LegacyMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import javax.script.ScriptException; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; @@ -76,10 +76,12 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; - -import static com.sk89q.worldedit.event.platform.Interaction.HIT; -import static com.sk89q.worldedit.event.platform.Interaction.OPEN; +import javax.annotation.Nullable; +import javax.script.ScriptException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The entry point and container for a working implementation of WorldEdit. @@ -105,6 +107,7 @@ public final class WorldEdit { private final PlatformManager platformManager = new PlatformManager(this); private final EditSessionFactory editSessionFactory = new EditSessionFactory.EditSessionFactoryImpl(eventBus); private final SessionManager sessions = new SessionManager(this); + private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20)); private final Supervisor supervisor = new SimpleSupervisor(); private final BlockFactory blockFactory = new BlockFactory(this); @@ -155,7 +158,7 @@ public final class WorldEdit { } /** - * Get the supervisor. + * Get the supervisor. Internal, not for API use. * * @return the supervisor */ @@ -163,6 +166,15 @@ public final class WorldEdit { return supervisor; } + /** + * Get the executor service. Internal, not for API use. + * + * @return the executor service + */ + public ListeningExecutorService getExecutorService() { + return executorService; + } + /** * Get the block factory from which new {@link BlockStateHolder}s can be * constructed. @@ -307,6 +319,17 @@ public final class WorldEdit { if (exts.size() != 1) { exts = exts.subList(0, 1); } + } else { + int dot = filename.lastIndexOf('.'); + if (dot < 0 || dot == filename.length() - 1) { + String currentExt = filename.substring(dot + 1); + if (exts.contains(currentExt) && checkFilename(filename)) { + File f = new File(dir, filename); + if (f.exists()) { + return f; + } + } + } } File result = null; for (Iterator iter = exts.iterator(); iter.hasNext() && (result == null || (!isSave && !result.exists()));) { @@ -321,7 +344,7 @@ public final class WorldEdit { private File getSafeFileWithExtension(File dir, String filename, String extension) { if (extension != null) { int dot = filename.lastIndexOf('.'); - if (dot < 0 || !filename.substring(dot).equalsIgnoreCase(extension)) { + if (dot < 0 || dot == filename.length() - 1 || !filename.substring(dot + 1).equalsIgnoreCase(extension)) { filename += "." + extension; } } @@ -396,16 +419,15 @@ public final class WorldEdit { } /** - * Get the direction vector for a player's direction. May return - * null if a direction could not be found. + * Get the direction vector for a player's direction. * * @param player the player * @param dirStr the direction string * @return a direction vector - * @throws UnknownDirectionException thrown if the direction is not known + * @throws UnknownDirectionException thrown if the direction is not known, or a relative direction is used with null player */ - public BlockVector3 getDirection(Player player, String dirStr) throws UnknownDirectionException { - dirStr = dirStr.toLowerCase(); + public BlockVector3 getDirection(@Nullable Player player, String dirStr) throws UnknownDirectionException { + dirStr = dirStr.toLowerCase(Locale.ROOT); final Direction dir = getPlayerDirection(player, dirStr); @@ -417,16 +439,15 @@ public final class WorldEdit { } /** - * Get the direction vector for a player's direction. May return - * null if a direction could not be found. + * Get the direction vector for a player's direction. * * @param player the player * @param dirStr the direction string * @return a direction vector - * @throws UnknownDirectionException thrown if the direction is not known + * @throws UnknownDirectionException thrown if the direction is not known, or a relative direction is used with null player */ - public BlockVector3 getDiagonalDirection(Player player, String dirStr) throws UnknownDirectionException { - dirStr = dirStr.toLowerCase(); + public BlockVector3 getDiagonalDirection(@Nullable Player player, String dirStr) throws UnknownDirectionException { + dirStr = dirStr.toLowerCase(Locale.ROOT); final Direction dir = getPlayerDirection(player, dirStr); @@ -438,15 +459,14 @@ public final class WorldEdit { } /** - * Get the direction vector for a player's direction. May return - * null if a direction could not be found. + * Get the direction vector for a player's direction. * * @param player the player * @param dirStr the direction string * @return a direction enum value - * @throws UnknownDirectionException thrown if the direction is not known + * @throws UnknownDirectionException thrown if the direction is not known, or a relative direction is used with null player */ - private Direction getPlayerDirection(Player player, String dirStr) throws UnknownDirectionException { + private Direction getPlayerDirection(@Nullable Player player, String dirStr) throws UnknownDirectionException { final Direction dir; switch (dirStr.charAt(0)) { @@ -490,19 +510,19 @@ public final class WorldEdit { case 'm': // me case 'f': // forward - dir = player.getCardinalDirection(0); + dir = getDirectionRelative(player, 0); break; case 'b': // back - dir = player.getCardinalDirection(180); + dir = getDirectionRelative(player, 180); break; case 'l': // left - dir = player.getCardinalDirection(-90); + dir = getDirectionRelative(player, -90); break; case 'r': // right - dir = player.getCardinalDirection(90); + dir = getDirectionRelative(player, 90); break; default: @@ -511,6 +531,13 @@ public final class WorldEdit { return dir; } + private Direction getDirectionRelative(Player player, int yawOffset) throws UnknownDirectionException { + if (player != null) { + return player.getCardinalDirection(yawOffset); + } + throw new UnknownDirectionException("Only a player can use relative directions"); + } + /** * Flush a block bag's changes to a player. * @@ -650,9 +677,9 @@ public final class WorldEdit { try { engine = new RhinoCraftScriptEngine(); - } catch (NoClassDefFoundError e) { + } catch (NoClassDefFoundError ignored) { player.printError("Failed to find an installed script engine."); - player.printError("Please see http://wiki.sk89q.com/wiki/WorldEdit/Installation"); + player.printError("Please see https://worldedit.readthedocs.io/en/latest/usage/other/craftscripts/"); return; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java index c4dc6dac8..3401af293 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java @@ -19,16 +19,13 @@ package com.sk89q.worldedit.blocks; +import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.world.NbtValued; import com.sk89q.worldedit.world.item.ItemType; -import com.sk89q.worldedit.world.item.ItemTypes; import javax.annotation.Nullable; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Represents an item, without an amount value. See {@link BaseItemStack} * for an instance with stack amount information. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java new file mode 100644 index 000000000..67bb630bd --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java @@ -0,0 +1,144 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.command.factory.ItemUseFactory; +import com.sk89q.worldedit.command.factory.ReplaceFactory; +import com.sk89q.worldedit.command.factory.TreeGeneratorFactory; +import com.sk89q.worldedit.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.Contextual; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.factory.Apply; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; +import com.sk89q.worldedit.regions.factory.RegionFactory; +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.util.formatting.text.format.TextColor; +import com.sk89q.worldedit.util.formatting.text.format.TextDecoration; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.CommandManagerService; +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.inject.Key; +import org.enginehub.piston.part.CommandArgument; +import org.enginehub.piston.part.SubCommandPart; + +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; +import static org.enginehub.piston.part.CommandParts.arg; + +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class ApplyBrushCommands { + + private static final CommandArgument REGION_FACTORY = arg(TranslatableComponent.of("shape"), TextComponent.of("The shape of the region")) + .defaultsTo(ImmutableList.of()) + .ofTypes(ImmutableList.of(Key.of(RegionFactory.class))) + .build(); + + private static final CommandArgument RADIUS = arg(TranslatableComponent.of("radius"), TextComponent.of("The size of the brush")) + .defaultsTo(ImmutableList.of("5")) + .ofTypes(ImmutableList.of(Key.of(double.class))) + .build(); + + public static void register(CommandManagerService service, CommandManager commandManager, CommandRegistrationHandler registration) { + commandManager.register("apply", builder -> { + builder.description(TextComponent.of("Apply brush, apply a function to every block")); + builder.action(org.enginehub.piston.Command.Action.NULL_ACTION); + + CommandManager manager = service.newCommandManager(); + registration.register( + manager, + ApplyBrushCommandsRegistration.builder(), + new ApplyBrushCommands() + ); + + builder.condition(new PermissionCondition(ImmutableSet.of("worldedit.brush.apply"))); + + builder.addParts(REGION_FACTORY, RADIUS); + builder.addPart(SubCommandPart.builder(TranslatableComponent.of("type"), TextComponent.of("Type of brush to use")) + .withCommands(manager.getAllCommands().collect(Collectors.toList())) + .required() + .build()); + }); + } + + private void setApplyBrush(CommandParameters parameters, Player player, LocalSession localSession, + Contextual generatorFactory) throws WorldEditException { + double radius = requireNonNull(RADIUS.value(parameters).asSingle(double.class)); + RegionFactory regionFactory = REGION_FACTORY.value(parameters).asSingle(RegionFactory.class); + BrushCommands.setOperationBasedBrush(player, localSession, radius, + new Apply(generatorFactory), regionFactory, "worldedit.brush.apply"); + } + + @Command( + name = "forest", + desc = "Plant trees" + ) + public void forest(CommandParameters parameters, + Player player, LocalSession localSession, + @Arg(desc = "The type of tree to plant") + TreeGenerator.TreeType type) throws WorldEditException { + setApplyBrush(parameters, player, localSession, new TreeGeneratorFactory(type)); + } + + @Command( + name = "item", + desc = "Use an item" + ) + @CommandPermissions("worldedit.brush.item") + public void item(CommandParameters parameters, + Player player, LocalSession localSession, + @Arg(desc = "The type of item to use") + BaseItem item, + @Arg(desc = "The direction in which the item will be applied", def = "up") + @Direction(includeDiagonals = true) + com.sk89q.worldedit.util.Direction direction) throws WorldEditException { + player.print(TextComponent.builder().append("WARNING: ", TextColor.RED, TextDecoration.BOLD) + .append("This brush simulates item usages. Its effects may not work on all platforms, may not be undo-able," + + " and may cause strange interactions with other mods/plugins. Use at your own risk.").build()); + setApplyBrush(parameters, player, localSession, new ItemUseFactory(item, direction)); + } + + @Command( + name = "set", + desc = "Place a block" + ) + public void set(CommandParameters parameters, + Player player, LocalSession localSession, + @Arg(desc = "The pattern of blocks to use") + Pattern pattern) throws WorldEditException { + setApplyBrush(parameters, player, localSession, new ReplaceFactory(pattern)); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index 449e86676..06a693df9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -19,25 +19,23 @@ package com.sk89q.worldedit.command; -import com.boydti.fawe.config.BBC; -import com.boydti.fawe.config.Commands; -import com.boydti.fawe.object.visitor.Fast2DIterator; -import com.boydti.fawe.util.chat.Message; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.visitor.Fast2DIterator; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; 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.Logging; +import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.function.FlatRegionFunction; import com.sk89q.worldedit.function.FlatRegionMaskingFilter; -import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.biome.BiomeReplace; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask2D; @@ -45,106 +43,84 @@ import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Regions; -import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.command.binding.Switch; +import com.sk89q.worldedit.util.formatting.component.PaginationBox; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeData; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.registry.BiomeRegistry; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +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.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; /** * Implements biome-related commands such as "/biomelist". */ -@Command(aliases = {}, desc = "Change, list and inspect biomes") -public class BiomeCommands extends MethodCommands { +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class BiomeCommands { /** * Create a new instance. - * - * @param worldEdit reference to WorldEdit */ - public BiomeCommands(WorldEdit worldEdit) { - super(worldEdit); - } - - private BiomeRegistry getBiomeRegistry() { - return worldEdit.getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); + public BiomeCommands() { } @Command( - aliases = { "biomelist", "biomels" }, - usage = "[page]", - desc = "Gets all biomes available.", - max = 1 + name = "biomelist", + aliases = { "biomels" }, + desc = "Gets all biomes available." ) @CommandPermissions("worldedit.biome.list") - public void biomeList(Player player, CommandContext args) throws WorldEditException { - int page; - int offset; - int count = 0; + public void biomeList(Actor actor, + @ArgFlag(name = 'p', desc = "Page number.", def = "1") + int page) { + WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> { + BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); - if (args.argsLength() == 0 || (page = args.getInteger(0)) < 2) { - page = 1; - offset = 0; - } else { - offset = (page - 1) * 19; - } - - BiomeRegistry biomeRegistry = getBiomeRegistry(); - Collection biomes = BiomeTypes.values(); - int totalPages = biomes.size() / 19 + 1; - Message msg = BBC.BIOME_LIST_HEADER.m(page, totalPages); - String setBiome = Commands.getAlias(BiomeCommands.class, "/setbiome"); - for (BiomeType biome : biomes) { - if (offset > 0) { - offset--; - } else { - BiomeData data = biomeRegistry.getData(biome); - if (data != null) { - msg.newline().text(data.getName()).cmdTip(setBiome + " " + data.getName()); - } else { - msg.newline().text("").cmdTip(setBiome + " " + biome.getId()); - } - if (++count == 18) { - break; - } - } - } - msg.newline().paginate(getCommand().aliases()[0], page, totalPages); - msg.send(player); + PaginationBox paginationBox = PaginationBox.fromStrings("Available Biomes", "/biomelist -p %page%", + BiomeType.REGISTRY.values().stream() + .map(biomeType -> { + String id = biomeType.getId(); + final BiomeData data = biomeRegistry.getData(biomeType); + if (data != null) { + String name = data.getName(); + return id + " (" + name + ")"; + } else { + return id; + } + }) + .collect(Collectors.toList())); + return paginationBox.create(page); + }, null); } @Command( - aliases = { "biomeinfo" }, - flags = "pt", + name = "biomeinfo", desc = "Get the biome of the targeted block.", - help = - "Get the biome of the block.\n" + - "By default use all the blocks contained in your selection.\n" + - "-t use the block you are looking at.\n" + - "-p use the block you are currently in", - max = 0 + descFooter = "By default, uses all blocks in your selection." ) @CommandPermissions("worldedit.biome.info") - public void biomeInfo(Player player, LocalSession session, final EditSession editSession, CommandContext args) throws WorldEditException { - BiomeRegistry biomeRegistry = getBiomeRegistry(); - Collection values = BiomeTypes.values(); - final int[] biomes = new int[values.size()]; + public void biomeInfo(Player player, LocalSession session, EditSession editSession, + @Switch(name = 't', desc = "Use the block you are looking at.") + boolean useLineOfSight, + @Switch(name = 'p', desc = "Use the block you are currently in.") + boolean usePosition) throws WorldEditException { + BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); + Set biomes = new HashSet<>(); + String qualifier; - int size = 0; - if (args.hasFlag('t')) { + if (useLineOfSight) { Location blockPosition = player.getBlockTrace(300); if (blockPosition == null) { BBC.NO_BLOCK.send(player); @@ -152,68 +128,56 @@ public class BiomeCommands extends MethodCommands { } BiomeType biome = player.getWorld().getBiome(blockPosition.toBlockPoint().toBlockVector2()); - biomes[biome.getInternalId()]++; - size = 1; - } else if (args.hasFlag('p')) { + biomes.add(biome); + + qualifier = "at line of sight point"; + } else if (usePosition) { BiomeType biome = player.getWorld().getBiome(player.getLocation().toBlockPoint().toBlockVector2()); - biomes[biome.getInternalId()]++; - size = 1; + biomes.add(biome); + + qualifier = "at your position"; } else { World world = player.getWorld(); Region region = session.getSelection(world); if (region instanceof FlatRegion) { for (BlockVector2 pt : new Fast2DIterator(((FlatRegion) region).asFlatRegion(), editSession)) { - biomes[editSession.getBiome(pt).getInternalId()]++; - size++; + biomes.add(world.getBiome(pt)); } } else { - RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { - @Override - public boolean apply(BlockVector3 position) throws WorldEditException { - biomes[editSession.getBiome(position.toBlockVector2()).getInternalId()]++; - return true; - } + RegionVisitor visitor = new RegionVisitor(region, position -> { + biomes.add(world.getBiome(position.toBlockVector2())); + return true; }, editSession); Operations.completeBlindly(visitor); - size += visitor.getAffected(); } + + qualifier = "in your selection"; } BBC.BIOME_LIST_HEADER.send(player, 1, 1); - - List> distribution = new ArrayList<>(); - for (int i = 0; i < biomes.length; i++) { - int count = biomes[i]; - if (count != 0) { - distribution.add(new Countable<>(BiomeTypes.get(i), count)); + player.print(biomes.size() != 1 ? "Biomes " + qualifier + ":" : "Biome " + qualifier + ":"); + for (BiomeType biome : biomes) { + BiomeData data = biomeRegistry.getData(biome); + if (data != null) { + player.print(" " + data.getName()); + } else { + player.print(" "); } } - Collections.sort(distribution); - for (Countable c : distribution) { - BiomeData data = biomeRegistry.getData(c.getID()); - String str = String.format("%-7s (%.3f%%) %s #%d", - String.valueOf(c.getAmount()), - c.getAmount() / (double) size * 100, - data == null ? "Unknown" : data.getName(), - c.getID().getInternalId()); - player.print(str); - } } @Command( - aliases = { "/setbiome" }, - usage = "", - flags = "p", - desc = "Sets the biome of the player's current block or region.", - help = - "Set the biome of the region.\n" + - "By default use all the blocks contained in your selection.\n" + - "-p use the block you are currently in" + name = "/setbiome", + desc = "Sets the biome of your current block or region.", + descFooter = "By default, uses all the blocks in your selection" ) @Logging(REGION) @CommandPermissions("worldedit.biome.set") - public void setBiome(Player player, LocalSession session, EditSession editSession, BiomeType target, @Switch('p') boolean atPosition) throws WorldEditException { + public void setBiome(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "Biome type.") BiomeType target, + @Switch(name = 'p', desc = "Use your current position") + boolean atPosition) throws WorldEditException { World world = player.getWorld(); Region region; Mask mask = editSession.getMask(); @@ -233,8 +197,9 @@ public class BiomeCommands extends MethodCommands { Operations.completeLegacy(visitor); BBC.BIOME_CHANGED.send(player, visitor.getAffected()); - if (!player.hasPermission("fawe.tips")) + if (!player.hasPermission("fawe.tips")) { BBC.TIP_BIOME_PATTERN.or(BBC.TIP_BIOME_MASK).send(player); + } } } 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 21081b8e0..2ad9bbb1e 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 @@ -19,13 +19,40 @@ package com.sk89q.worldedit.command; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.Fawe; import com.boydti.fawe.command.FawePrimitiveBinding; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.brush.*; +import com.boydti.fawe.object.brush.BlendBall; +import com.boydti.fawe.object.brush.BlobBrush; +import com.boydti.fawe.object.brush.BrushSettings; +import com.boydti.fawe.object.brush.CatenaryBrush; +import com.boydti.fawe.object.brush.CircleBrush; +import com.boydti.fawe.object.brush.CommandBrush; +import com.boydti.fawe.object.brush.CopyPastaBrush; +import com.boydti.fawe.object.brush.ErodeBrush; +import com.boydti.fawe.object.brush.FallingSphere; +import com.boydti.fawe.object.brush.FlattenBrush; +import com.boydti.fawe.object.brush.HeightBrush; +import com.boydti.fawe.object.brush.ImageBrush; +import com.boydti.fawe.object.brush.LayerBrush; +import com.boydti.fawe.object.brush.LineBrush; +import com.boydti.fawe.object.brush.PopulateSchem; +import com.boydti.fawe.object.brush.RaiseBrush; +import com.boydti.fawe.object.brush.RecurseBrush; +import com.boydti.fawe.object.brush.ScatterBrush; +import com.boydti.fawe.object.brush.ScatterCommand; +import com.boydti.fawe.object.brush.ScatterOverlayBrush; +import com.boydti.fawe.object.brush.ShatterBrush; +import com.boydti.fawe.object.brush.SplatterBrush; +import com.boydti.fawe.object.brush.SplineBrush; +import com.boydti.fawe.object.brush.StencilBrush; +import com.boydti.fawe.object.brush.SurfaceSphereBrush; +import com.boydti.fawe.object.brush.SurfaceSpline; import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap; import com.boydti.fawe.object.brush.sweep.SweepBrush; import com.boydti.fawe.object.clipboard.MultiClipboardHolder; @@ -33,9 +60,6 @@ import com.boydti.fawe.object.mask.IdMask; import com.boydti.fawe.util.ColorUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.image.ImageUtil; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Step; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EmptyClipboardException; @@ -43,6 +67,7 @@ import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.command.tool.brush.ButcherBrush; import com.sk89q.worldedit.command.tool.brush.ClipboardBrush; @@ -52,11 +77,13 @@ import com.sk89q.worldedit.command.tool.brush.HollowCylinderBrush; import com.sk89q.worldedit.command.tool.brush.HollowSphereBrush; import com.sk89q.worldedit.command.tool.brush.SmoothBrush; import com.sk89q.worldedit.command.tool.brush.SphereBrush; +import com.sk89q.worldedit.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; @@ -66,89 +93,209 @@ import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.HandSide; +import com.sk89q.worldedit.util.command.CallableProcessor; +import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.InvalidUsageException; -import com.sk89q.worldedit.util.command.binding.Range; -import com.sk89q.worldedit.util.command.binding.Switch; -import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.util.command.parametric.AParametricCallable; import com.sk89q.worldedit.util.command.parametric.ParameterException; -import com.sk89q.worldedit.world.block.*; import com.sk89q.worldedit.world.block.BlockStateHolder; - -import java.awt.*; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import java.awt.Color; import java.awt.image.BufferedImage; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.List; +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.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; /** * Commands to set brush shape. */ -@Command(aliases = {"brush", "br", "tool"}, - desc = "Commands to build and draw from far away. [More Info](https://git.io/vSPYf)" -) -public class BrushCommands extends BrushProcessor { +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class BrushCommands { + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ public BrushCommands(WorldEdit worldEdit) { - super(worldEdit); + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( aliases = {"blendball", "bb", "blend"}, - usage = "[radius=5]", desc = "Smooths and blends terrain", help = "Smooths and blends terrain\n" + - "Pic: https://i.imgur.com/cNUQUkj.png -> https://i.imgur.com/hFOFsNf.png", - min = 0, - max = 1 + "Pic: https://i.imgur.com/cNUQUkj.png -> https://i.imgur.com/hFOFsNf.png" ) @CommandPermissions("worldedit.brush.blendball") - public BrushSettings blendBallBrush(Player player, LocalSession session, @Optional("5") Expression radius, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, new BlendBall()).setSize(radius); + public BrushSettings blendBallBrush(Player player, LocalSession session, + @Arg(desc = "The radius to sample for blending", def = "5") + double radius, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new BlendBall(); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( - aliases = {"erode", "e"}, - usage = "[radius=5]", - desc = "Erodes terrain", - help = "Erodes terrain", - min = 0, - max = 1 + name = "erode", + desc = "Erodes terrain" ) @CommandPermissions("worldedit.brush.erode") - public BrushSettings erodeBrush(Player player, LocalSession session, @Optional("5") Expression radius, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, new ErodeBrush()).setSize(radius); + public BrushSettings erodeBrush(Player player, LocalSession session, + @Arg(desc = "The radius for eroding", def = "5") + double radius, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new ErodeBrush(); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( - aliases = {"pull"}, - usage = "[radius=5]", - desc = "Pull terrain towards you", - help = "Pull terrain towards you", - min = 0, - max = 1 + name = "pull", + desc = "Pull terrain towards you" ) @CommandPermissions("worldedit.brush.pull") - public BrushSettings pullBrush(Player player, LocalSession session, @Optional("5") Expression radius, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, new RaiseBrush()).setSize(radius); + public BrushSettings pullBrush(Player player, LocalSession session, + @Arg(desc = "The radius to sample for blending", def = "5") + double radius, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new RaiseBrush(); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( - aliases = {"circle"}, - usage = " [radius=5]", - desc = "Creates a circle which revolves around your facing direction", - help = "Creates a circle which revolves around your facing direction.\n" + - "Note: Decrease brush radius, and enabled visualization to assist with placement mid-air", - min = 1, - max = 2 + name = "circle", + desc = "Creates a circle which revolves around your facing direction" ) @CommandPermissions("worldedit.brush.sphere") - public BrushSettings circleBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, new CircleBrush(player)).setSize(radius).setFill(fill); + public BrushSettings circleBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, + @Arg(desc = "The radius to sample for blending", def = "5") + double radius, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new CircleBrush(player); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } @Command( @@ -157,18 +304,42 @@ public class BrushCommands extends BrushProcessor { desc = "Set all connected blocks", help = "Set all connected blocks\n" + "The -d flag Will apply in depth first order\n" + - "Note: Set a mask to recurse along specific blocks", - min = 0, - max = 3 + "Note: Set a mask to recurse along specific blocks" ) @CommandPermissions("worldedit.brush.recursive") - public BrushSettings recursiveBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, @Optional("5") Expression radius, @Switch('d') boolean depthFirst, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, - new RecurseBrush(depthFirst)) - .setSize(radius) - .setFill(fill) - .setMask(new IdMask(editSession)); + public BrushSettings recursiveBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, + @Arg(desc = "The radius to sample for blending", def = "5") + double radius, @Switch('d') boolean depthFirst, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new RecurseBrush(depthFirst); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill).setMask(new IdMask(editSession)); } @Command( @@ -176,21 +347,45 @@ public class BrushCommands extends BrushProcessor { usage = " [radius=0]", flags = "hsf", desc = "Create lines", - help = - "Create lines.\n" + - "The -h flag creates only a shell\n" + - "The -s flag selects the clicked point after drawing\n" + - "The -f flag creates a flat line", - min = 1, - max = 2 + help = "Create lines.\n" + + "The -h flag creates only a shell\n" + + "The -s flag selects the clicked point after drawing\n" + + "The -f flag creates a flat line" ) @CommandPermissions("worldedit.brush.line") - public BrushSettings lineBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("0") Expression radius, @Switch('h') boolean shell, @Switch('s') boolean select, @Switch('f') boolean flat, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, - new LineBrush(shell, select, flat)) - .setSize(radius) - .setFill(fill); + public BrushSettings lineBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, + @Arg(desc = "The radius to sample for blending", def = "0") + double radius, @Switch('h') boolean shell, @Switch('s') boolean select, @Switch('f') boolean flat, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new LineBrush(shell, select, flat); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } @Command( @@ -201,18 +396,43 @@ public class BrushCommands extends BrushProcessor { "Insufficient brush radius, or clicking the the wrong spot will result in undesired shapes. The shapes must be simple lines or loops.\n" + "Pic1: http://i.imgur.com/CeRYAoV.jpg -> http://i.imgur.com/jtM0jA4.png\n" + "Pic2: http://i.imgur.com/bUeyc72.png -> http://i.imgur.com/tg6MkcF.png" + - "Tutorial: https://www.planetminecraft.com/blog/fawe-tutorial/", - min = 0, - max = 2 + "Tutorial: https://www.planetminecraft.com/blog/fawe-tutorial/" ) @CommandPermissions("worldedit.brush.spline") - public BrushSettings splineBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("25") Expression radius, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + public BrushSettings splineBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, + @Arg(desc = "The radius to sample for blending", def = "25") + double radius, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); player.print(BBC.BRUSH_SPLINE.f(radius)); - return set(session, context, - new SplineBrush(player, session)) - .setSize(radius) - .setFill(fill); + Brush brush = new SplineBrush(player, session); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } // Adapted from: https://github.com/Rafessor/VaeronTools @@ -221,14 +441,41 @@ public class BrushCommands extends BrushProcessor { usage = "[copies=-1]", desc = "Sweep your clipboard content along a curve", help = "Sweeps your clipboard content along a curve.\n" + - "Define a curve by selecting the individual points with a brush\n" + - "Set [copies] to a value > 0 if you want to have your selection pasted a limited amount of times equally spaced on the curve", - max = 1 + "Define a curve by selecting the individual points with a brush\n" + + "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 BrushSettings sweepBrush(Player player, LocalSession session, EditSession editSession, @Optional("-1") int copies, CommandContext context) throws WorldEditException { player.print(BBC.BRUSH_SPLINE.s()); - return set(session, context, new SweepBrush(copies)); + Brush brush = new SweepBrush(copies); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush); } @Command( @@ -239,17 +486,42 @@ public class BrushCommands extends BrushProcessor { "The lengthFactor controls how long the line is\n" + "The -h flag creates only a shell\n" + "The -s flag selects the clicked point after drawing\n" + - "The -d flag sags the catenary toward the facing direction\n", - min = 1, - max = 3 + "The -d flag sags the catenary toward the facing direction\n" ) @CommandPermissions("worldedit.brush.spline") - public BrushSettings catenaryBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("1.2") @Range(min=1) double lengthFactor, @Optional("0") Expression radius, @Switch('h') boolean shell, @Switch('s') boolean select, @Switch('d') boolean facingDirection, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, - new CatenaryBrush(shell, select, facingDirection, lengthFactor)) - .setSize(radius) - .setFill(fill); + public BrushSettings catenaryBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("1.2") @Range(min=1) double lengthFactor, + @Arg(desc = "The radius to sample for blending", def = "0") + double radius, @Switch('h') boolean shell, @Switch('s') boolean select, @Switch('d') boolean facingDirection, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new CatenaryBrush(shell, select, facingDirection, lengthFactor); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } @Command( @@ -257,65 +529,112 @@ public class BrushCommands extends BrushProcessor { usage = " [size=0] [tension=0] [bias=0] [continuity=0] [quality=10]", desc = "Draws a spline (curved line) on the surface", help = "Create a spline on the surface\n" + - "Video: https://www.youtube.com/watch?v=zSN-2jJxXlM", - min = 0, - max = 6 + "Video: https://www.youtube.com/watch?v=zSN-2jJxXlM" ) @CommandPermissions("worldedit.brush.surfacespline") // 0, 0, 0, 10, 0, - public BrushSettings surfaceSpline(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("0") Expression radius, @Optional("0") double tension, @Optional("0") double bias, @Optional("0") double continuity, @Optional("10") double quality, CommandContext context) throws WorldEditException { + public BrushSettings surfaceSpline(Player player, EditSession editSession, LocalSession session, Pattern fill, + @Arg(desc = "The radius to sample for blending", def = "0") + double radius, @Optional("0") double tension, @Optional("0") double bias, @Optional("0") double continuity, @Optional("10") double quality, CommandContext context) throws WorldEditException { player.print(BBC.BRUSH_SPLINE.f(radius)); - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, - new SurfaceSpline(tension, bias, continuity, quality)) - .setSize(radius) - .setFill(fill); + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new SurfaceSpline(tension, bias, continuity, quality); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } @Command( aliases = {"rock", "blob"}, usage = " [radius=10] [roundness=100] [frequency=30] [amplitude=50]", flags = "h", - desc = "Creates a distorted sphere", - min = 1, - max = 5 + desc = "Creates a distorted sphere" ) @CommandPermissions("worldedit.brush.rock") public BrushSettings blobBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("10") Vector3 radius, @Optional("100") double sphericity, @Optional("30") double frequency, @Optional("50") double amplitude, CommandContext context) throws WorldEditException { double max = MathMan.max(radius.getX(), radius.getY(), radius.getZ()); - getWorldEdit().checkMaxBrushRadius(max); + worldEdit.checkMaxBrushRadius(max); Brush brush = new BlobBrush(radius.divide(max), frequency / 100, amplitude / 100, sphericity / 100); - return set(session, context, - brush) - .setSize(max) - .setFill(fill); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(max).setFill(fill); } @Command( - aliases = {"sphere", "s"}, - usage = " [radius=2]", - flags = "hf", - desc = "Creates a sphere", - help = - "Creates a sphere.\n" + - "The -h flag creates hollow spheres instead." + - "The -f flag creates falling spheres.", - min = 1, - max = 2 + name = "sphere", + aliases = { "s" }, + desc = "Choose the sphere brush" ) @CommandPermissions("worldedit.brush.sphere") - public BrushSettings sphereBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("2") @Range(min=0) Expression radius, @Switch('h') boolean hollow, @Switch('f') boolean falling, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + public BrushSettings sphereBrush(Player player, LocalSession session, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern, + @Arg(desc = "The radius of the sphere", def = "2") + double radius, + @Switch(name = 'h', desc = "Create hollow spheres instead") + boolean hollow, + @Switch(name = 'f', desc = "Create falling spheres instead") + boolean falling, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); Brush brush; if (hollow) { brush = new HollowSphereBrush(); } else { if (fill instanceof BlockStateHolder) { BlockType type = ((BlockStateHolder) fill).getBlockType(); - switch (type.getInternalId()) { - case BlockID.SAND: - case BlockID.GRAVEL: - BBC.BRUSH_TRY_OTHER.send(player); - falling = true; + int internalId = type.getInternalId(); + if (type == BlockTypes.SAND || type == BlockTypes.GRAVEL) { + BBC.BRUSH_TRY_OTHER.send(player); + falling = true; } } if (falling) { @@ -325,46 +644,92 @@ public class BrushCommands extends BrushProcessor { } } - return set(session, context, - brush) - .setSize(radius) - .setFill(fill); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(pattern); } @Command( aliases = {"shatter", "partition", "split"}, usage = " [radius=10] [count=10]", desc = "Creates random lines to break the terrain into pieces", - help = - "Creates uneven lines separating terrain into multiple pieces\n" + - "Pic: https://i.imgur.com/2xKsZf2.png", - min = 1, - max = -1 + help = "Creates uneven lines separating terrain into multiple pieces\n" + + "Pic: https://i.imgur.com/2xKsZf2.png" ) @CommandPermissions("worldedit.brush.shatter") - public BrushSettings shatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("10") Expression radius, @Optional("10") int count, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, - new ShatterBrush(count)) - .setSize(radius) - .setFill(fill) - .setMask(new ExistingBlockMask(editSession)); + public BrushSettings shatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, + @Arg(desc = "The radius to sample for blending", def = "10") + double radius, + @Arg(desc = "Lines", def = "10") int count, CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new ShatterBrush(count); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill).setMask(new ExistingBlockMask(editSession)); } @Command( aliases = {"stencil"}, usage = " [radius=5] [file|#clipboard|imgur=null] [rotation=360] [yscale=1.0]", desc = "Use a height map to paint a surface", - help = - "Use a height map to paint any surface.\n" + - "The -w flag will only apply at maximum saturation\n" + - "The -r flag will apply random rotation", - min = 1, - max = -1 + help = "Use a height map to paint any surface.\n" + + "The -w flag will only apply at maximum saturation\n" + + "The -r flag will apply random rotation", + min = 1 ) @CommandPermissions("worldedit.brush.stencil") public BrushSettings stencilBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite, @Switch('r') boolean randomRotate, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException { - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); InputStream stream = getHeightmapStream(image); HeightBrush brush; try { @@ -375,10 +740,34 @@ public class BrushCommands extends BrushProcessor { if (randomRotate) { brush.setRandomRotate(true); } - return set(session, context, - brush) - .setSize(radius) - .setFill(fill); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == ((Brush) brush).getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } @Command( @@ -386,17 +775,15 @@ public class BrushCommands extends BrushProcessor { usage = " [yscale=1]", desc = "Use a height map to paint a surface", flags = "a", - help = - "Use a height map to paint any surface.\n" + - "The -a flag will use image alpha\n" + - "The -f blends the image with the existing terrain", - min = 1, - max = -1 + help = "Use a height map to paint any surface.\n" + + "The -a flag will use image alpha\n" + + "The -f blends the image with the existing terrain", + min = 1 ) @CommandPermissions("worldedit.brush.stencil") public BrushSettings imageBrush(Player player, EditSession editSession, LocalSession session, @Optional("5") Expression radius, FawePrimitiveBinding.ImageUri imageUri, @Optional("1") @Range(min=Double.MIN_NORMAL) final double yscale, @Switch('a') boolean alpha, @Switch('f') boolean fadeOut, CommandContext context) throws WorldEditException, IOException, ParameterException { BufferedImage image = imageUri.load(); - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); if (yscale != 1) { ImageUtil.scaleAlpha(image, yscale); alpha = true; @@ -406,69 +793,136 @@ public class BrushCommands extends BrushProcessor { alpha = true; } ImageBrush brush = new ImageBrush(image, session, alpha); - return set(session, context, - brush) - .setSize(radius); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == ((Brush) brush).getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( aliases = {"surface", "surf"}, usage = " [radius=5]", desc = "Use a height map to paint a surface", - help = - "Use a height map to paint any surface.\n" + - "The -w flag will only apply at maximum saturation\n" + - "The -r flag will apply random rotation", - min = 1, - max = -1 + help = "Use a height map to paint any surface.\n" + + "The -w flag will only apply at maximum saturation\n" + + "The -r flag will apply random rotation", + min = 1 ) @CommandPermissions("worldedit.brush.surface") public BrushSettings surfaceBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, new SurfaceSphereBrush()).setFill(fill).setSize(radius); + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new SurfaceSphereBrush(); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setFill(fill).setSize(radius); } @Command( - aliases = {"scatter", "scat"}, + name = "scatter", usage = " [radius=5] [points=5] [distance=1]", desc = "Scatter a pattern on a surface", - help = - "Set a number of blocks randomly on a surface each a certain distance apart.\n" + - " The -o flag will overlay the block\n" + - "Video: https://youtu.be/RPZIaTbqoZw?t=34s", - flags = "o", - min = 1, - max = 4 + help = "Set a number of blocks randomly on a surface each a certain distance apart.\n" + + " The -o flag will overlay the block\n" + + "Video: https://youtu.be/RPZIaTbqoZw?t=34s", ) @CommandPermissions("worldedit.brush.scatter") public BrushSettings scatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, @Optional("5") double points, @Optional("1") double distance, @Switch('o') boolean overlay, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); Brush brush; if (overlay) { brush = new ScatterOverlayBrush((int) points, (int) distance); } else { brush = new ScatterBrush((int) points, (int) distance); } - return set(session, context, - brush) - .setSize(radius) - .setFill(fill); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } @Command( aliases = {"populateschematic", "populateschem", "popschem", "pschem", "ps"}, usage = " [radius=30] [points=5]", desc = "Scatter a schematic on a surface", - help = - "Chooses the scatter schematic brush.\n" + - "The -r flag will apply random rotation", - flags = "r", - min = 2, - max = 4 + help = "Chooses the scatter schematic brush.\n" + + "The -r flag will apply random rotation" ) @CommandPermissions("worldedit.brush.populateschematic") public BrushSettings scatterSchemBrush(Player player, EditSession editSession, LocalSession session, Mask mask, String clipboard, @Optional("30") Expression radius, @Optional("50") double density, @Switch('r') boolean rotate, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); try { @@ -483,27 +937,51 @@ public class BrushCommands extends BrushProcessor { return null; } - return set(session, context, - new PopulateSchem(mask, holders, (int) density, rotate)) - .setSize(radius); + Brush brush = new PopulateSchem(mask, holders, (int) density, rotate); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } catch (IOException e) { throw new RuntimeException(e); } } @Command( - aliases = {"layer"}, + name = "layer", usage = " [color| ...]", desc = "Replaces terrain with a layer.", help = "Replaces terrain with a layer.\n" + - "Example: /br layer 5 95:1 95:2 35:15 - Places several layers on a surface\n" + - "Pic: https://i.imgur.com/XV0vYoX.png", - min = 0, - max = 999 + "Example: /br layer 5 95:1 95:2 35:15 - Places several layers on a surface\n" + + "Pic: https://i.imgur.com/XV0vYoX.png" ) @CommandPermissions("worldedit.brush.layer") public BrushSettings surfaceLayer(Player player, EditSession editSession, LocalSession session, Expression radius, CommandContext args, CommandContext context) throws WorldEditException, InvalidUsageException { - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); ParserContext parserContext = new ParserContext(); parserContext.setActor(player); parserContext.setWorld(player.getWorld()); @@ -522,32 +1000,80 @@ public class BrushCommands extends BrushProcessor { } catch (IllegalArgumentException ignore) { for (int i = 1; i < args.argsLength(); i++) { String arg = args.getString(i); - blocks.add(getWorldEdit().getBlockFactory().parseFromInput(arg, parserContext)); + blocks.add(worldEdit.getBlockFactory().parseFromInput(arg, parserContext)); } } - return set(session, context, - new LayerBrush(blocks.toArray(new BlockStateHolder[blocks.size()]))) - .setSize(radius); + Brush brush = new LayerBrush(blocks.toArray(new BlockStateHolder[0])); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args1 = (String) locals.get("arguments"); + if (args1 != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args1.substring(args1.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( - aliases = {"splatter", "splat"}, - usage = " [radius=5] [seeds=1] [recursion=5] [solid=true]", + name = "splatter", desc = "Splatter a pattern on a surface", help = "Sets a bunch of blocks randomly on a surface.\n" + - "Pic: https://i.imgur.com/hMD29oO.png\n" + - "Example: /br splatter stone,dirt 30 15\n" + - "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.", - min = 1, - max = 5 + "Pic: https://i.imgur.com/hMD29oO.png\n" + + "Example: /br splatter stone,dirt 30 15\n" + + "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 BrushSettings splatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, @Optional("1") double points, @Optional("5") double recursion, @Optional("true") boolean solid, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, - new SplatterBrush((int) points, (int) recursion, solid)) - .setSize(radius) - .setFill(fill); + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new SplatterBrush((int) points, (int) recursion, solid); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius).setFill(fill); } @Command( @@ -558,134 +1084,328 @@ public class BrushCommands extends BrushProcessor { "Run commands at random points on a surface\n" + " - The scatter radius is the min distance between each point\n" + " - Your selection will be expanded to the specified size around each point\n" + - " - Placeholders: {x}, {y}, {z}, {world}, {size}", - min = 1, - max = -1 + " - Placeholders: {x}, {y}, {z}, {world}, {size}" ) @CommandPermissions("worldedit.brush.scattercommand") public BrushSettings scatterCommandBrush(Player player, EditSession editSession, LocalSession session, Expression radius, double points, double distance, CommandContext args, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - return set(session, context, - new ScatterCommand((int) points, (int) distance, args.getJoinedStrings(3))) - .setSize(radius); + worldEdit.checkMaxBrushRadius(radius); + Brush brush = new ScatterCommand((int) points, (int) distance, args.getJoinedStrings(3)); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args1 = (String) locals.get("arguments"); + if (args1 != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args1.substring(args1.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( - aliases = {"cylinder", "cyl", "c", "disk", "disc"}, - usage = " [radius=2] [height=1]", - flags = "h", - desc = "Creates a cylinder", - help = - "Creates a cylinder.\n" + - "The -h flag creates hollow cylinders instead.", - min = 1, - max = 3 + name = "cylinder", + aliases = { "cyl", "c" }, + desc = "Choose the cylinder brush" ) @CommandPermissions("worldedit.brush.cylinder") - public BrushSettings cylinderBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, - @Optional("2") Expression radius, @Optional("1") int height, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); - getWorldEdit().checkMaxBrushRadius(height); + public BrushSettings cylinderBrush(Player player, LocalSession session, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern, + @Arg(desc = "The radius of the cylinder", def = "2") + double radius, + @Arg(desc = "The height of the cylinder", def = "1") + int height, + @Switch(name = 'h', desc = "Create hollow cylinders instead") + boolean hollow, + CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(height); BrushSettings settings; if (hollow) { - settings = set(session, context, new HollowCylinderBrush(height)); + Brush brush = new HollowCylinderBrush(height); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + settings = bs.setBrush(brush); } else { - settings = set(session, context, new CylinderBrush(height)); + Brush brush = new CylinderBrush(height); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + settings = bs.setBrush(brush); } - settings.setSize(radius) - .setFill(fill); + settings.setSize(radius).setFill(pattern); return settings; } @Command( - aliases = {"clipboard"}, - usage = "", + name = "clipboard", + aliases = { "copy" }, desc = "Choose the clipboard brush (Recommended: `/br copypaste`)", - help = - "Chooses the clipboard brush.\n" + - "The -a flag makes it not paste air.\n" + - "Without the -p flag, the paste will appear centered at the target location. " + - "With the flag, then the paste will appear relative to where you had " + - "stood relative to the copied area when you copied it." + help = "Chooses the clipboard brush.\n" + + "The -a flag makes it not paste air.\n" + + "Without the -p flag, the paste will appear centered at the target location. " + + "With the flag, then the paste will appear relative to where you had " + + "stood relative to the copied area when you copied it." ) @CommandPermissions("worldedit.brush.clipboard") - public BrushSettings clipboardBrush(Player player, LocalSession session, @Switch('a') boolean ignoreAir, @Switch('p') boolean usingOrigin, CommandContext context) throws WorldEditException { + public BrushSettings clipboardBrush(Player player, LocalSession session, + @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") + boolean usingOrigin, + @Switch(name = 'e', desc = "Paste entities if available") + boolean pasteEntities, + @Switch(name = 'b', desc = "Paste biomes if available") + boolean pasteBiomes, + @ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard", def = "") + Mask sourceMask, + CommandContext context) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); Clipboard clipboard = holder.getClipboard(); BlockVector3 size = clipboard.getDimensions(); - getWorldEdit().checkMaxBrushRadius(size.getBlockX()); - getWorldEdit().checkMaxBrushRadius(size.getBlockY()); - getWorldEdit().checkMaxBrushRadius(size.getBlockZ()); - return set(session, context, new ClipboardBrush(holder, ignoreAir, usingOrigin)); + worldEdit.checkMaxBrushRadius(size.getBlockX()); + worldEdit.checkMaxBrushRadius(size.getBlockY()); + worldEdit.checkMaxBrushRadius(size.getBlockZ()); + Brush brush = new ClipboardBrush(holder, ignoreAir, usingOrigin); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush); } @Command( - aliases = {"smooth"}, - usage = "[size=2] [iterations=4]", - flags = "n", - desc = "Smooths terrain (Recommended: `/br blendball`)", - help = - "Chooses the terrain softener brush.\n" + - "The -n flag makes it only consider naturally occurring blocks.", - min = 0, - max = 2 + name = "smooth", + desc = "Choose the terrain softener brush", + descFooter = "Example: '/brush smooth 2 4 grass_block,dirt,stone'" ) @CommandPermissions("worldedit.brush.smooth") public BrushSettings smoothBrush(Player player, LocalSession session, EditSession editSession, - @Optional("2") Expression radius, @Optional("4") int iterations, @Optional Mask mask, CommandContext context) throws WorldEditException { + @Arg(desc = "The radius to sample for softening", def = "2") + double radius, + @Arg(desc = "The number of iterations to perform", def = "4") + int iterations, + @Arg(desc = "The mask of blocks to use for the heightmap", def = "") + Mask mask, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); FawePlayer fp = FawePlayer.wrap(player); FaweLimit limit = Settings.IMP.getLimit(fp); iterations = Math.min(limit.MAX_ITERATIONS, iterations); - return set(session, context, - new SmoothBrush(iterations, mask)) - .setSize(radius); + BrushTool tool = session.getBrushTool(player, false); + Brush brush = new SmoothBrush(iterations, mask); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( - aliases = {"ex", "extinguish"}, - usage = "[radius=5]", - desc = "Shortcut fire extinguisher brush", - min = 0, - max = 1 + name = "extinguish", + aliases = { "ex" }, + desc = "Shortcut fire extinguisher brush" ) @CommandPermissions("worldedit.brush.ex") - public BrushSettings extinguishBrush(Player player, LocalSession session, EditSession editSession, @Optional("5") Expression radius, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + public BrushSettings extinguishBrush(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius to extinguish", def = "5") + double radius, + CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + BrushTool tool = session.getBrushTool(player, false); Pattern fill = BlockTypes.AIR.getDefaultState(); - return set(session, context, - new SphereBrush()) - .setSize(radius) - .setFill(fill) - .setMask(new SingleBlockTypeMask(editSession, BlockTypes.FIRE)); + Brush brush = new SphereBrush(); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush) + .setSize(radius).setFill(fill).setMask(new SingleBlockTypeMask(editSession, BlockTypes.FIRE)); } @Command( - aliases = {"gravity", "grav"}, - usage = "[radius=5]", - flags = "h", - desc = "Gravity brush", - help = - "This brush simulates the affect of gravity.\n" + - "The -h flag makes it affect blocks starting at the world's max y, " + - "instead of the clicked block's y + radius.", - min = 0, - max = 1 + name = "gravity", + aliases = { "grav" }, + desc = "Gravity brush, simulates the effect of gravity" ) @CommandPermissions("worldedit.brush.gravity") - public BrushSettings gravityBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Switch('h') boolean fromMaxY, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + public BrushSettings gravityBrush(Player player, LocalSession session, + @Arg(desc = "The radius to apply gravity in", def = "5") + double radius, + @Switch(name = 'h', desc = "Affect blocks starting at max Y, rather than the target location Y + radius") + boolean fromMaxY, + CommandContext context) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); - return set(session, context, - new GravityBrush(fromMaxY)) - .setSize(radius); + BrushTool tool = session.getBrushTool(player, false); + Brush brush = new GravityBrush(fromMaxY); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( @@ -693,15 +1413,12 @@ public class BrushCommands extends BrushProcessor { usage = "[radius=5] [file|#clipboard|imgur=null] [rotation=0] [yscale=1.00]", flags = "h", desc = "Raise or lower terrain using a heightmap", - help = - "This brush raises and lowers land.\n" + - " - The `-r` flag enables random off-axis rotation\n" + - " - The `-l` flag will work on snow layers\n" + - " - The `-s` flag disables smoothing\n" + - "Note: Use a negative yscale to reduce height\n" + - "Snow Pic: https://i.imgur.com/Hrzn0I4.png", - min = 1, - max = 4 + help = "This brush raises and lowers land.\n" + + " - The `-r` flag enables random off-axis rotation\n" + + " - The `-l` flag will work on snow layers\n" + + " - The `-s` flag disables smoothing\n" + + "Note: Use a negative yscale to reduce height\n" + + "Snow Pic: https://i.imgur.com/Hrzn0I4.png" ) @CommandPermissions("worldedit.brush.height") public BrushSettings heightBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException { @@ -713,13 +1430,10 @@ public class BrushCommands extends BrushProcessor { usage = "[radius=5] [file|#clipboard|imgur=null] [rotation=0] [yscale=1.00]", flags = "h", desc = "Cliff brush", - help = - "This brush flattens terrain and creates cliffs.\n" + - " - The `-r` flag enables random off-axis rotation\n" + - " - The `-l` flag will work on snow layers\n" + - " - The `-s` flag disables smoothing", - min = 1, - max = 4 + help = "This brush flattens terrain and creates cliffs.\n" + + " - The `-r` flag enables random off-axis rotation\n" + + " - The `-l` flag will work on snow layers\n" + + " - The `-s` flag disables smoothing" ) @CommandPermissions("worldedit.brush.height") public BrushSettings cliffBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException { @@ -731,12 +1445,10 @@ public class BrushCommands extends BrushProcessor { usage = "[radius=5] [file|#clipboard|imgur=null] [rotation=0] [yscale=1.00]", flags = "h", help = "Flatten brush flattens terrain\n" + - " - The `-r` flag enables random off-axis rotation\n" + - " - The `-l` flag will work on snow layers\n" + - " - The `-s` flag disables smoothing", - desc = "This brush raises or lowers land towards the clicked point", - min = 1, - max = 4 + " - The `-r` flag enables random off-axis rotation\n" + + " - The `-l` flag will work on snow layers\n" + + " - The `-s` flag disables smoothing", + desc = "This brush raises or lowers land towards the clicked point" ) @CommandPermissions("worldedit.brush.height") public BrushSettings flattenBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException { @@ -744,7 +1456,7 @@ public class BrushCommands extends BrushProcessor { } private BrushSettings terrainBrush(Player player, LocalSession session, Expression radius, String image, int rotation, double yscale, boolean flat, boolean randomRotate, boolean layers, boolean smooth, ScalableHeightMap.Shape shape, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException { - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); InputStream stream = getHeightmapStream(image); HeightBrush brush; if (flat) { @@ -763,9 +1475,34 @@ public class BrushCommands extends BrushProcessor { if (randomRotate) { brush.setRandomRotate(true); } - return set(session, context, - brush) - .setSize(radius); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == ((Brush) brush).getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } private InputStream getHeightmapStream(String filename) throws FileNotFoundException, ParameterException { @@ -783,68 +1520,117 @@ public class BrushCommands extends BrushProcessor { usage = "[depth=5]", desc = "Copy Paste brush", help = "Left click the base of an object to copy.\n" + - "Right click to paste\n" + - "The -r flag Will apply random rotation on paste\n" + - "The -a flag Will apply auto view based rotation on paste\n" + - "Note: Works well with the clipboard scroll action\n" + - "Video: https://www.youtube.com/watch?v=RPZIaTbqoZw", - min = 0, - max = 1 + "Right click to paste\n" + + "The -r flag Will apply random rotation on paste\n" + + "The -a flag Will apply auto view based rotation on paste\n" + + "Note: Works well with the clipboard scroll action\n" + + "Video: https://www.youtube.com/watch?v=RPZIaTbqoZw" ) @CommandPermissions("worldedit.brush.copy") public BrushSettings copy(Player player, LocalSession session, @Optional("5") Expression radius, @Switch('r') boolean randomRotate, @Switch('a') boolean autoRotate, CommandContext context) throws WorldEditException { - getWorldEdit().checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(radius); player.print(BBC.BRUSH_COPY.f(radius)); - return set(session, context, - new CopyPastaBrush(player, session, randomRotate, autoRotate)) - .setSize(radius); + Brush brush = new CopyPastaBrush(player, session, randomRotate, autoRotate); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args = (String) locals.get("arguments"); + if (args != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( aliases = {"command", "cmd"}, usage = " [cmd1;cmd2...]", desc = "Command brush", - help = - "Run the commands at the clicked position.\n" + - " - Your selection will be expanded to the specified size around each point\n" + - " - Placeholders: {x}, {y}, {z}, {world}, {size}", - - min = 2, - max = 99 + help = "Run the commands at the clicked position.\n" + + " - Your selection will be expanded to the specified size around each point\n" + + " - Placeholders: {x}, {y}, {z}, {world}, {size}" ) @CommandPermissions("worldedit.brush.command") public BrushSettings command(Player player, LocalSession session, Expression radius, CommandContext args, CommandContext context) throws WorldEditException { String cmd = args.getJoinedStrings(1); - return set(session, context, - new CommandBrush(cmd)) - .setSize(radius); + Brush brush = new CommandBrush(cmd); + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool = session.getBrushTool(player, false); + if (tool != null) { + BrushSettings currentContext = tool.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args1 = (String) locals.get("arguments"); + if (args1 != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args1.substring(args1.indexOf(' ') + 1)); + } + return bs.setBrush(brush).setSize(radius); } @Command( - aliases = {"butcher", "kill"}, - usage = "[radius=5]", - flags = "plangbtfr", - desc = "Butcher brush", - help = "Kills nearby mobs within the specified radius.\n" + - "Flags:\n" + - " -p also kills pets.\n" + - " -n also kills NPCs.\n" + - " -g also kills Golems.\n" + - " -a also kills animals.\n" + - " -b also kills ambient mobs.\n" + - " -t also kills mobs with name tags.\n" + - " -f compounds all previous flags.\n" + - " -r also destroys armor stands.\n" + - " -l currently does nothing.", - min = 0, - max = 1 + name = "butcher", + aliases = { "kill" }, + desc = "Butcher brush, kills mobs within a radius" ) @CommandPermissions("worldedit.brush.butcher") - public BrushSettings butcherBrush(Player player, LocalSession session, CommandContext args, CommandContext context) throws WorldEditException { - LocalConfiguration config = getWorldEdit().getConfiguration(); + public BrushSettings butcherBrush(Player player, LocalSession session, CommandContext args, + @Arg(desc = "Radius to kill mobs in", def = "5") + double radius, + @Switch(name = 'p', desc = "Also kill pets") + boolean killPets, + @Switch(name = 'n', desc = "Also kill NPCs") + boolean killNpcs, + @Switch(name = 'g', desc = "Also kill golems") + boolean killGolems, + @Switch(name = 'a', desc = "Also kill animals") + boolean killAnimals, + @Switch(name = 'b', desc = "Also kill ambient mobs") + boolean killAmbient, + @Switch(name = 't', desc = "Also kill mobs with name tags") + boolean killWithName, + @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 { + LocalConfiguration config = worldEdit.getConfiguration(); - double radius = args.argsLength() > 0 ? args.getDouble(0) : 5; double maxRadius = config.maxBrushRadius; // hmmmm not horribly worried about this because -1 is still rather efficient, // the problem arises when butcherMaxRadius is some really high number but not infinite @@ -852,18 +1638,68 @@ public class BrushCommands extends BrushProcessor { if (player.hasPermission("worldedit.butcher")) { maxRadius = Math.max(config.maxBrushRadius, config.butcherMaxRadius); } - if (radius > maxRadius && maxRadius != -1) { + if (radius > maxRadius) { BBC.TOOL_RADIUS_ERROR.send(player, maxRadius); return null; } CreatureButcher flags = new CreatureButcher(player); - flags.fromCommand(args); + flags.or(CreatureButcher.Flags.FRIENDLY , killFriendly); // No permission check here. Flags will instead be filtered by the subsequent calls. + flags.or(CreatureButcher.Flags.PETS , killPets, "worldedit.butcher.pets"); + flags.or(CreatureButcher.Flags.NPCS , killNpcs, "worldedit.butcher.npcs"); + flags.or(CreatureButcher.Flags.GOLEMS , killGolems, "worldedit.butcher.golems"); + flags.or(CreatureButcher.Flags.ANIMALS , killAnimals, "worldedit.butcher.animals"); + flags.or(CreatureButcher.Flags.AMBIENT , killAmbient, "worldedit.butcher.ambient"); + flags.or(CreatureButcher.Flags.TAGGED , killWithName, "worldedit.butcher.tagged"); + flags.or(CreatureButcher.Flags.ARMOR_STAND , killArmorStands, "worldedit.butcher.armorstands"); - return set(session, context, - new ButcherBrush(flags)) - .setSize(radius); + BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); + tool.setSize(radius); + ButcherBrush brush = new ButcherBrush(flags); + tool.setBrush(brush, "worldedit.brush.butcher"); + + CommandLocals locals = context.getLocals(); + BrushSettings bs = new BrushSettings(); + + BrushTool tool1 = session.getBrushTool(player, false); + if (tool1 != null) { + BrushSettings currentContext = tool1.getContext(); + if (currentContext != null) { + Brush currentBrush = currentContext.getBrush(); + if (currentBrush != null && currentBrush.getClass() == ((Brush) brush).getClass()) { + bs = currentContext; + } + } + } + + CommandCallable callable = locals.get(CommandCallable.class); + String[] perms; + if (callable instanceof AParametricCallable) { + perms = ((AParametricCallable) callable).getPermissions(); + } else { + perms = getPermissions(); + } + bs.addPermissions(perms); + + String args1 = (String) locals.get("arguments"); + if (args1 != null) { + bs.addSetting(BrushSettings.SettingType.BRUSH, args1.substring(args1.indexOf(' ') + 1)); + } + return bs.setBrush(brush); } + @Override + 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); + if (tool != null) { + tool.setPrimary(settings); + tool.setSecondary(settings); + BBC.BRUSH_EQUIPPED.send(actor, ((String) locals.get("arguments")).split(" ")[1]); + } + return null; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java index 6cd61bce0..0878b6a71 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushOptionsCommands.java @@ -11,30 +11,27 @@ import com.boydti.fawe.object.extent.ResettableExtent; import com.boydti.fawe.object.io.PGZIPOutputStream; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; -import com.sk89q.minecraft.util.commands.Command; + +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; - -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.CommandManager; +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.util.HandSide; import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; -import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -163,16 +160,14 @@ public class BrushOptionsCommands extends MethodCommands { max = 0 ) public void none(Player player, LocalSession session, CommandContext args) throws WorldEditException { - session.setTool(null, player); + session.setTool(player, null); BBC.TOOL_NONE.send(player); } @Command( aliases = {"/", ","}, usage = "[on|off]", - desc = "Toggle the super pickaxe function", - min = 0, - max = 1 + desc = "Toggle the super pickaxe function" ) @CommandPermissions("worldedit.superpickaxe") public void togglePickaxe(Player player, LocalSession session, CommandContext args) throws WorldEditException { @@ -210,8 +205,8 @@ public class BrushOptionsCommands extends MethodCommands { session.setTool(item, null, player); String cmd = "brush " + args.getJoinedStrings(0); CommandEvent event = new CommandEvent(player, cmd); - CommandManager.getInstance().handleCommandOnCurrentThread(event); - BrushTool newTool = session.getBrushTool(item, player, false); + PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); + BrushTool newTool = session.getBrushTool(item.getType(), player, false); if (newTool != null && tool != null) { newTool.setSecondary(tool.getSecondary()); } @@ -231,8 +226,8 @@ public class BrushOptionsCommands extends MethodCommands { session.setTool(item, null, player); String cmd = "brush " + args.getJoinedStrings(0); CommandEvent event = new CommandEvent(player, cmd); - CommandManager.getInstance().handleCommandOnCurrentThread(event); - BrushTool newTool = session.getBrushTool(item, player, false); + PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); + BrushTool newTool = session.getBrushTool(item.getType(), player, false); if (newTool != null && tool != null) { newTool.setPrimary(tool.getPrimary()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushProcessor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushProcessor.java deleted file mode 100644 index 3504bc352..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushProcessor.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.sk89q.worldedit.command; - -import com.boydti.fawe.config.BBC; -import com.boydti.fawe.object.brush.BrushSettings; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandLocals; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.command.tool.BrushTool; -import com.sk89q.worldedit.command.tool.InvalidToolBindException; -import com.sk89q.worldedit.command.tool.brush.Brush; -import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.command.CallableProcessor; -import com.sk89q.worldedit.util.command.CommandCallable; -import com.sk89q.worldedit.util.command.parametric.AParametricCallable; - -public class BrushProcessor extends MethodCommands implements CallableProcessor { - private final WorldEdit worldEdit; - - public BrushProcessor(WorldEdit worldEdit) { - this.worldEdit = worldEdit; - } - - public WorldEdit getWorldEdit() { - return worldEdit; - } - - @Override - public BrushSettings process(CommandLocals locals, BrushSettings settings) throws WorldEditException { - Actor actor = locals.get(Actor.class); - LocalSession session = worldEdit.getSessionManager().get(actor); - session.setTool(null, (Player) actor); - BrushTool tool = session.getBrushTool((Player) actor); - if (tool != null) { - tool.setPrimary(settings); - tool.setSecondary(settings); - BBC.BRUSH_EQUIPPED.send(actor, ((String) locals.get("arguments")).split(" ")[1]); - } - return null; - } - - public BrushSettings set(LocalSession session, CommandContext context, Brush brush) throws InvalidToolBindException { - CommandLocals locals = context.getLocals(); - Actor actor = locals.get(Actor.class); - BrushSettings bs = new BrushSettings(); - - BrushTool tool = session.getBrushTool((Player) actor, false); - if (tool != null) { - BrushSettings currentContext = tool.getContext(); - if (currentContext != null) { - Brush currentBrush = currentContext.getBrush(); - if (currentBrush != null && currentBrush.getClass() == brush.getClass()) { - bs = currentContext; - } - } - } - - CommandCallable callable = locals.get(CommandCallable.class); - String[] perms; - if (callable != null && callable instanceof AParametricCallable) { - perms = ((AParametricCallable) callable).getPermissions(); - } else { - perms = getPermissions(); - } - bs.addPermissions(perms); - - if (locals != null) { - String args = (String) locals.get("arguments"); - if (args != null) { - bs.addSetting(BrushSettings.SettingType.BRUSH, args.substring(args.indexOf(' ') + 1)); - } - } - return bs.setBrush(brush); - } -} \ No newline at end of file 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 1741d64c8..79eadbb7c 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 @@ -19,36 +19,47 @@ package com.sk89q.worldedit.command; -import com.boydti.fawe.config.BBC; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalConfiguration; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; +import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME; + +import com.google.gson.JsonIOException; import com.sk89q.worldedit.LocalSession; 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.Logging; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.internal.anvil.ChunkDeleter; +import com.sk89q.worldedit.internal.anvil.ChunkDeletionInfo; import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.MathUtils; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.component.PaginationBox; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.world.storage.LegacyChunkStore; import com.sk89q.worldedit.world.storage.McRegionChunkStore; - -import java.io.FileOutputStream; +import java.io.File; import java.io.IOException; -import java.io.OutputStreamWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Set; - - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import java.util.stream.Collectors; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.ArgFlag; +import org.enginehub.piston.exception.StopExecutionException; /** * Commands for working with chunks. */ -@Command(aliases = {}, desc = "[legacy] Inspect chunks: [More Info](http://wiki.sk89q.com/wiki/WorldEdit/Chunk_tools)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class ChunkCommands { private final WorldEdit worldEdit; @@ -59,131 +70,101 @@ public class ChunkCommands { } @Command( - aliases = {"chunkinfo"}, - usage = "", - desc = "Get information about the chunk that you are inside", - min = 0, - max = 0 + name = "chunkinfo", + desc = "Get information about the chunk you're inside" ) @CommandPermissions("worldedit.chunkinfo") - public void chunkInfo(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void chunkInfo(Player player) { Location pos = player.getBlockIn(); int chunkX = (int) Math.floor(pos.getBlockX() / 16.0); int chunkZ = (int) Math.floor(pos.getBlockZ() / 16.0); - String folder1 = Integer.toString(MathUtils.divisorMod(chunkX, 64), 36); - String folder2 = Integer.toString(MathUtils.divisorMod(chunkZ, 64), 36); - String filename = "c." + Integer.toString(chunkX, 36) - + "." + Integer.toString(chunkZ, 36) + ".dat"; - + final BlockVector2 chunkPos = BlockVector2.at(chunkX, chunkZ); player.print("Chunk: " + chunkX + ", " + chunkZ); - player.print("Old format: " + folder1 + "/" + folder2 + "/" + filename); - player.print("McRegion: region/" + McRegionChunkStore.getFilename( - BlockVector2.at(chunkX, chunkZ))); + player.print("Old format: " + LegacyChunkStore.getFilename(chunkPos)); + player.print("McRegion: region/" + McRegionChunkStore.getFilename(chunkPos)); } @Command( - aliases = {"listchunks"}, - usage = "", - desc = "List chunks that your selection includes", - min = 0, - max = 0 + name = "listchunks", + desc = "List chunks that your selection includes" ) @CommandPermissions("worldedit.listchunks") - public void listChunks(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public void listChunks(Player player, LocalSession session, + @ArgFlag(name = 'p', desc = "Page number.", def = "1") int page) throws WorldEditException { Set chunks = session.getSelection(player.getWorld()).getChunks(); - for (BlockVector2 chunk : chunks) { - player.print(LegacyChunkStore.getFilename(chunk)); - } + PaginationBox paginationBox = PaginationBox.fromStrings("Selected Chunks", "/listchunks -p %page%", + chunks.stream().map(LegacyChunkStore::getFilename).collect(Collectors.toList())); + player.print(paginationBox.create(page)); } @Command( - aliases = {"delchunks"}, - usage = "", - desc = "Deprecated, use anvil commands", - min = 0, - max = 0 + name = "delchunks", + desc = "Delete chunks that your selection includes" ) @CommandPermissions("worldedit.delchunks") @Logging(REGION) - public void deleteChunks(Player player, LocalSession session) throws WorldEditException { - player.print("Note that this command does not yet support the mcregion format."); - LocalConfiguration config = worldEdit.getConfiguration(); - - Set chunks = session.getSelection(player.getWorld()).getChunks(); - FileOutputStream out = null; - - if (config.shellSaveType == null) { - player.printError("Shell script type must be configured: 'bat' or 'bash' expected."); - } else if (config.shellSaveType.equalsIgnoreCase("bat")) { - try { - out = new FileOutputStream("worldedit-delchunks.bat"); - OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8"); - writer.write("@ECHO off\r\n"); - writer.write("ECHO This batch file was generated by FAWE.\r\n"); - writer.write("ECHO It contains a list of chunks that were in the selected region\r\n"); - writer.write("ECHO at the time that the /delchunks command was used. Run this file\r\n"); - writer.write("ECHO in order to delete the chunk files listed in this file.\r\n"); - writer.write("ECHO.\r\n"); - writer.write("PAUSE\r\n"); - - for (BlockVector2 chunk : chunks) { - String filename = LegacyChunkStore.getFilename(chunk); - writer.write("ECHO " + filename + "\r\n"); - writer.write("DEL \"world/" + filename + "\"\r\n"); - } - - writer.write("ECHO Complete.\r\n"); - writer.write("PAUSE\r\n"); - writer.close(); - player.print("worldedit-delchunks.bat written. Run it when no one is near the region."); - } catch (IOException e) { - player.printError("Error occurred: " + e.getMessage()); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException ignored) { - } - } - } - } else if (config.shellSaveType.equalsIgnoreCase("bash")) { - try { - out = new FileOutputStream("worldedit-delchunks.sh"); - OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8"); - writer.write("#!/bin/bash\n"); - writer.write("echo This shell file was generated by FAWE.\n"); - writer.write("echo It contains a list of chunks that were in the selected region\n"); - writer.write("echo at the time that the /delchunks command was used. Run this file\n"); - writer.write("echo in order to delete the chunk files listed in this file.\n"); - writer.write("echo\n"); - writer.write("read -p \"Press any key to continue...\"\n"); - - for (BlockVector2 chunk : chunks) { - String filename = LegacyChunkStore.getFilename(chunk); - writer.write("echo " + filename + "\n"); - writer.write("rm \"world/" + filename + "\"\n"); - } - - writer.write("echo Complete.\n"); - writer.write("read -p \"Press any key to continue...\"\n"); - writer.close(); - player.print("worldedit-delchunks.sh written. Run it when no one is near the region."); - player.print("You will have to chmod it to be executable."); - } catch (IOException e) { - player.printError("Error occurred: " + e.getMessage()); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException ignored) { - } - } - } - } else { - player.printError("Shell script type must be configured: 'bat' or 'bash' expected."); + public void deleteChunks(Player player, LocalSession session, + @ArgFlag(name = 'o', desc = "Only delete chunks older than the specified time.", def = "") + ZonedDateTime beforeTime) throws WorldEditException { + Path worldDir = player.getWorld().getStoragePath(); + if (worldDir == null) { + throw new StopExecutionException(TextComponent.of("Couldn't find world folder for this world.")); } + + File chunkFile = worldEdit.getWorkingDirectoryFile(DELCHUNKS_FILE_NAME); + Path chunkPath = chunkFile.toPath(); + ChunkDeletionInfo currentInfo = null; + if (Files.exists(chunkPath)) { + try { + currentInfo = ChunkDeleter.readInfo(chunkFile.toPath()); + } catch (IOException e) { + throw new StopExecutionException(TextComponent.of("Error reading existing chunk file.")); + } + } + if (currentInfo == null) { + currentInfo = new ChunkDeletionInfo(); + currentInfo.batches = new ArrayList<>(); + } + + ChunkDeletionInfo.ChunkBatch newBatch = new ChunkDeletionInfo.ChunkBatch(); + newBatch.worldPath = worldDir.toAbsolutePath().normalize().toString(); + newBatch.backup = true; + final Region selection = session.getSelection(player.getWorld()); + if (selection instanceof CuboidRegion) { + newBatch.minChunk = BlockVector2.at(selection.getMinimumPoint().getBlockX() >> 4, selection.getMinimumPoint().getBlockZ() >> 4); + newBatch.maxChunk = BlockVector2.at(selection.getMaximumPoint().getBlockX() >> 4, selection.getMaximumPoint().getBlockZ() >> 4); + } else { + // this has a possibility to OOM for very large selections still + Set chunks = selection.getChunks(); + newBatch.chunks = new ArrayList<>(chunks); + } + if (beforeTime != null) { + newBatch.deletionPredicates = new ArrayList<>(); + ChunkDeletionInfo.DeletionPredicate timePred = new ChunkDeletionInfo.DeletionPredicate(); + timePred.property = "modification"; + timePred.comparison = "<"; + timePred.value = String.valueOf((int) beforeTime.toOffsetDateTime().toEpochSecond()); + newBatch.deletionPredicates.add(timePred); + } + currentInfo.batches.add(newBatch); + + try { + ChunkDeleter.writeInfo(currentInfo, chunkPath); + } catch (IOException | JsonIOException e) { + throw new StopExecutionException(TextComponent.of("Failed to write chunk list: " + e.getMessage())); + } + + player.print(String.format("%d chunk(s) have been marked for deletion the next time the server starts.", + newBatch.getChunkCount())); + if (currentInfo.batches.size() > 1) { + player.printDebug(String.format("%d chunks total marked for deletion. (May have overlaps).", + currentInfo.batches.stream().mapToInt(ChunkDeletionInfo.ChunkBatch::getChunkCount).sum())); + } + player.print(TextComponent.of("You can mark more chunks for deletion, or to stop now, run: ", TextColor.LIGHT_PURPLE) + .append(TextComponent.of("/stop", TextColor.AQUA) + .clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, "/stop")))); } } 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 050908187..976a8a043 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 @@ -36,16 +36,18 @@ import com.boydti.fawe.util.ImgurUtility; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MaskTraverser; -import com.sk89q.minecraft.util.commands.Command; +import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; 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.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.command.util.Logging; +import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.extent.PasteEvent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; @@ -71,8 +73,11 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.util.command.binding.Switch; -import com.sk89q.worldedit.util.command.parametric.Optional; +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.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; import java.io.File; import java.io.IOException; @@ -86,16 +91,11 @@ import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; - - /** * Clipboard commands. */ -@Command(aliases = {}, desc = "Related commands to copy and pasting blocks: [More Info](https://goo.gl/z2ScQR)") -public class ClipboardCommands extends MethodCommands { +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class ClipboardCommands { /** * Create a new instance. @@ -107,79 +107,36 @@ public class ClipboardCommands extends MethodCommands { checkNotNull(worldEdit); } - @Command( - aliases = { "/lazycopy" }, - flags = "em", - desc = "Lazily copy the selection to the clipboard", - help = "Lazily copy the selection to the clipboard\n" + - "Flags:\n" + - " -e skips copying entities\n" + - " -m sets a source mask so that excluded blocks become air\n" + - " -b copies biomes\n" + - "WARNING: Pasting entities cannot yet be undone!", - max = 0 - ) - @CommandPermissions("worldedit.clipboard.lazycopy") - public void lazyCopy(Player player, LocalSession session, EditSession editSession, - @Selection final Region region, @Switch('e') boolean skipEntities, - @Switch('m') Mask mask, @Switch('b') boolean copyBiomes) throws WorldEditException { - BlockVector3 min = region.getMinimumPoint(); - BlockVector3 max = region.getMaximumPoint(); - long volume = (((long) max.getX() - (long) min.getX() + 1) * ((long) max.getY() - (long) min.getY() + 1) * ((long) max.getZ() - (long) min.getZ() + 1)); - FaweLimit limit = FawePlayer.wrap(player).getLimit(); - if (volume >= limit.MAX_CHECKS) { - throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); - } - session.setClipboard(null); - final BlockVector3 origin = region.getMinimumPoint(); - final int mx = origin.getBlockX(); - final int my = origin.getBlockY(); - final int mz = origin.getBlockZ(); - ReadOnlyClipboard lazyClipboard = ReadOnlyClipboard.of(editSession, region, !skipEntities, copyBiomes); - - BlockArrayClipboard clipboard = new BlockArrayClipboard(region, lazyClipboard); - clipboard.setOrigin(session.getPlacementPosition(player)); - session.setClipboard(new ClipboardHolder(clipboard)); - BBC.COMMAND_COPY.send(player, region.getArea()); - if (!player.hasPermission("fawe.tips")) - BBC.TIP_PASTE.or(BBC.TIP_LAZYCOPY, BBC.TIP_DOWNLOAD, BBC.TIP_ROTATE, BBC.TIP_COPYPASTE, BBC.TIP_REPLACE_MARKER, BBC.TIP_COPY_PATTERN).send(player); - } - @Command( - aliases = { "/copy", "/c" }, - flags = "em", - desc = "Copy the selection to the clipboard", - help = "Copy the selection to the clipboard\n" + - "Flags:\n" + - " -e skips copying entities\n" + - " -m sets a source mask so that excluded blocks become air\n" + - " -b copies biomes\n" + - "WARNING: Pasting entities cannot yet be undone!", - min = 0, - max = 0 + name = "/copy", + desc = "Copy the selection to the clipboard" ) @CommandPermissions("worldedit.clipboard.copy") public void copy(FawePlayer fp, Player player, LocalSession session, EditSession editSession, - @Selection Region region, @Switch('e') boolean skipEntities, - @Switch('m') Mask mask, CommandContext context, @Switch('b') boolean copyBiomes) throws WorldEditException { - BlockVector3 min = region.getMinimumPoint(); - BlockVector3 max = region.getMaximumPoint(); + @Selection Region region, + @Switch(name = 'e', desc = "Also copy entities") + boolean copyEntities, + @Switch(name = 'b', desc = "Also copy biomes") + boolean copyBiomes, + @ArgFlag(name = 'm', desc = "Set the exclude mask, matching blocks become air", def = "") + Mask mask, CommandContext context) throws WorldEditException { + BlockVector3 min = region.getMinimumPoint(); + BlockVector3 max = region.getMaximumPoint(); long volume = (((long) max.getX() - (long) min.getX() + 1) * ((long) max.getY() - (long) min.getY() + 1) * ((long) max.getZ() - (long) min.getZ() + 1)); FaweLimit limit = FawePlayer.wrap(player).getLimit(); if (volume >= limit.MAX_CHECKS) { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); } - BlockVector3 pos = session.getPlacementPosition(player); fp.checkConfirmationRegion(() -> { session.setClipboard(null); BlockArrayClipboard clipboard = new BlockArrayClipboard(region, player.getUniqueId()); session.setClipboard(new ClipboardHolder(clipboard)); - clipboard.setOrigin(pos); + clipboard.setOrigin(session.getPlacementPosition(player)); ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); - copy.setCopyingEntities(!skipEntities); - copy.setCopyBiomes(copyBiomes); + copy.setCopyingEntities(copyEntities); + copy.setCopyingBiomes(copyBiomes); Mask sourceMask = editSession.getSourceMask(); if (sourceMask != null) { new MaskTraverser(sourceMask).reset(editSession); @@ -198,23 +155,52 @@ public class ClipboardCommands extends MethodCommands { } @Command( - aliases = {"/lazycut"}, - flags = "em", - desc = "Lazily cut the selection to the clipboard", - help = "Lazily cut the selection to the clipboard\n" + - "Flags:\n" + - " -e skips entity copy\n" + - " -m sets a source mask so that excluded blocks become air\n" + - " -b copies biomes\n" + - "WARNING: Pasting entities cannot yet be undone!", - max = 0 + name = "/lazycopy", + desc = "Lazily copy the selection to the clipboard" + ) + @CommandPermissions("worldedit.clipboard.lazycopy") + public void lazyCopy(Player player, LocalSession session, EditSession editSession, + @Selection Region region, + @Switch(name = 'e', desc = "Also copy entities") + boolean copyEntities, + @ArgFlag(name = 'm', desc = "Set the exclude mask, matching blocks become air", def = "") + Mask mask, + @Switch(name = 'b', desc = "Also copy biomes") + boolean copyBiomes) throws WorldEditException { + BlockVector3 min = region.getMinimumPoint(); + BlockVector3 max = region.getMaximumPoint(); + long volume = (((long) max.getX() - (long) min.getX() + 1) * ((long) max.getY() - (long) min.getY() + 1) * ((long) max.getZ() - (long) min.getZ() + 1)); + FaweLimit limit = FawePlayer.wrap(player).getLimit(); + if (volume >= limit.MAX_CHECKS) { + throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); + } + session.setClipboard(null); + ReadOnlyClipboard lazyClipboard = ReadOnlyClipboard.of(editSession, region, copyEntities, copyBiomes); + + BlockArrayClipboard clipboard = new BlockArrayClipboard(region, lazyClipboard); + clipboard.setOrigin(session.getPlacementPosition(player)); + session.setClipboard(new ClipboardHolder(clipboard)); + BBC.COMMAND_COPY.send(player, region.getArea()); + if (!player.hasPermission("fawe.tips")) { + BBC.TIP_PASTE.or(BBC.TIP_LAZYCOPY, BBC.TIP_DOWNLOAD, BBC.TIP_ROTATE, BBC.TIP_COPYPASTE, BBC.TIP_REPLACE_MARKER, BBC.TIP_COPY_PATTERN).send(player); + } + } + + @Command( + name = "/lazycut", + desc = "Lazily cut the selection to the clipboard" ) @CommandPermissions("worldedit.clipboard.lazycut") public void lazyCut(Player player, LocalSession session, EditSession editSession, - @Selection final Region region, @Switch('e') boolean skipEntities, - @Switch('m') Mask mask, @Switch('b') boolean copyBiomes) throws WorldEditException { - BlockVector3 min = region.getMinimumPoint(); - BlockVector3 max = region.getMaximumPoint(); + @Selection final Region region, + @Switch(name = 'e', desc = "Also copy entities") + boolean copyEntities, + @ArgFlag(name = 'm', desc = "Set the exclude mask, matching blocks become air", def = "") + Mask mask, + @Switch(name = 'b', desc = "Also copy biomes") + boolean copyBiomes) throws WorldEditException { + BlockVector3 min = region.getMinimumPoint(); + BlockVector3 max = region.getMaximumPoint(); long volume = (((long) max.getX() - (long) min.getX() + 1) * ((long) max.getY() - (long) min.getY() + 1) * ((long) max.getZ() - (long) min.getZ() + 1)); FaweLimit limit = FawePlayer.wrap(player).getLimit(); if (volume >= limit.MAX_CHECKS) { @@ -224,11 +210,7 @@ public class ClipboardCommands extends MethodCommands { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES); } session.setClipboard(null); - final BlockVector3 origin = region.getMinimumPoint(); - final int mx = origin.getBlockX(); - final int my = origin.getBlockY(); - final int mz = origin.getBlockZ(); - ReadOnlyClipboard lazyClipboard = new WorldCutClipboard(editSession, region, !skipEntities, copyBiomes); + ReadOnlyClipboard lazyClipboard = new WorldCutClipboard(editSession, region, copyEntities, copyBiomes); BlockArrayClipboard clipboard = new BlockArrayClipboard(region, lazyClipboard); clipboard.setOrigin(session.getPlacementPosition(player)); session.setClipboard(new ClipboardHolder(clipboard)); @@ -236,25 +218,25 @@ public class ClipboardCommands extends MethodCommands { } @Command( - aliases = { "/cut" }, - flags = "em", - usage = "[leave-id]", + name = "/cut", desc = "Cut the selection to the clipboard", - help = "Copy the selection to the clipboard\n" + - "Flags:\n" + - " -e skips entity copy\n" + - " -m sets a source mask so that excluded blocks become air\n" + - " -b copies biomes\n" + - "WARNING: Cutting and pasting entities cannot yet be undone!", - max = 1 + descFooter = "WARNING: Cutting and pasting entities cannot be undone!" ) @CommandPermissions("worldedit.clipboard.cut") @Logging(REGION) public void cut(FawePlayer fp, Player player, LocalSession session, EditSession editSession, - @Selection Region region, @Optional("air") Pattern leavePattern, @Switch('e') boolean skipEntities, - @Switch('m') Mask mask, @Switch('b') boolean copyBiomes, CommandContext context) throws WorldEditException { - BlockVector3 min = region.getMinimumPoint(); - BlockVector3 max = region.getMaximumPoint(); + @Selection Region region, + @Arg(desc = "Pattern to leave in place of the selection", def = "air") + Pattern leavePattern, + @Switch(name = 'e', desc = "Also cut entities") + boolean copyEntities, + @Switch(name = 'b', desc = "Also copy biomes, source biomes are unaffected") + boolean copyBiomes, + @ArgFlag(name = 'm', desc = "Set the exclude mask, matching blocks become air", def = "") + Mask mask, + CommandContext context) throws WorldEditException { + BlockVector3 min = region.getMinimumPoint(); + BlockVector3 max = region.getMaximumPoint(); long volume = (((long) max.getX() - (long) min.getX() + 1) * ((long) max.getY() - (long) min.getY() + 1) * ((long) max.getZ() - (long) min.getZ() + 1)); FaweLimit limit = FawePlayer.wrap(player).getLimit(); if (volume >= limit.MAX_CHECKS) { @@ -263,16 +245,15 @@ public class ClipboardCommands extends MethodCommands { if (volume >= limit.MAX_CHANGES) { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES); } - BlockVector3 pos = session.getPlacementPosition(player); fp.checkConfirmationRegion(() -> { session.setClipboard(null); BlockArrayClipboard clipboard = new BlockArrayClipboard(region, player.getUniqueId()); - clipboard.setOrigin(pos); + clipboard.setOrigin(session.getPlacementPosition(player)); ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); copy.setSourceFunction(new BlockReplace(editSession, leavePattern)); - copy.setCopyingEntities(!skipEntities); + copy.setCopyingEntities(copyEntities); copy.setRemovingEntities(true); - copy.setCopyBiomes(copyBiomes); + copy.setCopyingBiomes(copyBiomes); Mask sourceMask = editSession.getSourceMask(); if (sourceMask != null) { new MaskTraverser(sourceMask).reset(editSession); @@ -286,18 +267,20 @@ public class ClipboardCommands extends MethodCommands { session.setClipboard(new ClipboardHolder(clipboard)); BBC.COMMAND_CUT_SLOW.send(player, region.getArea()); - if (!player.hasPermission("fawe.tips")) BBC.TIP_LAZYCUT.send(player); + if (!player.hasPermission("fawe.tips")) { + BBC.TIP_LAZYCUT.send(player); + } }, getArguments(context), region, context); } @Command( - aliases = {"download"}, + name = "download", desc = "Downloads your clipboard through the configured web interface" ) @Deprecated @CommandPermissions({"worldedit.clipboard.download"}) - public void download(final Player player, final LocalSession session, @Optional("schem") final String formatName) throws CommandException, WorldEditException { + public void download(final Player player, final LocalSession session, @Optional("schem") final String formatName) throws WorldEditException { final ClipboardFormat format = ClipboardFormats.findByAlias(formatName); if (format == null) { BBC.CLIPBOARD_INVALID_FORMAT.send(player, formatName); @@ -334,7 +317,9 @@ public class ClipboardCommands extends MethodCommands { try (ZipOutputStream zos = new ZipOutputStream(out)) { for (File file : files) { String fileName = file.getName(); - if (MainUtil.isInSubDirectory(working, file)) fileName = working.toURI().relativize(file.toURI()).getPath(); + if (MainUtil.isInSubDirectory(working, file)) { + fileName = working.toURI().relativize(file.toURI()).getPath(); + } ZipEntry ze = new ZipEntry(fileName); zos.putNextEntry(ze); Files.copy(file.toPath(), zos); @@ -376,32 +361,28 @@ public class ClipboardCommands extends MethodCommands { } url = FaweAPI.upload(target, format); } - if (url == null) { - BBC.GENERATING_LINK_FAILED.send(player); - } else { - String urlText = url.toString(); - if (Settings.IMP.WEB.SHORTEN_URLS) { - try { - urlText = MainUtil.getText("https://empcraft.com/s/?" + URLEncoder.encode(url.toString(), "UTF-8")); - } catch (IOException e) { - e.printStackTrace(); - } - } - BBC.DOWNLOAD_LINK.send(player, urlText); - } - } + if (url == null) { + BBC.GENERATING_LINK_FAILED.send(player); + } else { + String urlText = url.toString(); + if (Settings.IMP.WEB.SHORTEN_URLS) { + try { + urlText = MainUtil.getText("https://empcraft.com/s/?" + URLEncoder.encode(url.toString(), "UTF-8")); + } catch (IOException e) { + e.printStackTrace(); + } + } + BBC.DOWNLOAD_LINK.send(player, urlText); + } + } } @Command( - aliases = {"asset", "createasset", "makeasset"}, - usage = "[category]", - desc = "Create an asset", - help = "Saves your clipboard to the asset web interface", - min = 1, - max = 1 + name = "asset", + desc = "Saves your clipboard to the asset web interface", ) @CommandPermissions({"worldedit.clipboard.asset"}) - public void asset(final Player player, final LocalSession session, String category) throws CommandException, WorldEditException { + public void asset(final Player player, final LocalSession session, String category) throws WorldEditException { final ClipboardFormat format = BuiltInClipboardFormat.MCEDIT_SCHEMATIC; ClipboardHolder holder = session.getClipboard(); Clipboard clipboard = holder.getClipboard(); @@ -429,35 +410,25 @@ public class ClipboardCommands extends MethodCommands { } } - @Deprecated - public void paste(Player player, LocalSession session, EditSession editSession, - @Switch('a') boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, - @Switch('s') boolean selectPasted) throws WorldEditException { - this.paste(player, session, editSession, ignoreAirBlocks, false, false, atOrigin, selectPasted); - } - @Command( - aliases = { "/paste" }, - usage = "", - flags = "saobe", - desc = "Paste the clipboard's contents", - help = - "Pastes the clipboard's contents.\n" + - "Flags:\n" + - " -a skips air blocks\n" + - " -b skips pasting biomes\n" + - " -e skips pasting entities\n" + - " -o pastes at the original position\n" + - " -s selects the region after pasting", - min = 0, - max = 0 + name = "/paste", + desc = "Paste the clipboard's contents" ) @CommandPermissions("worldedit.clipboard.paste") @Logging(PLACEMENT) public void paste(Player player, LocalSession session, EditSession editSession, - @Switch('a') boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, - @Switch('b') boolean ignoreBiomes, @Switch('e') boolean ignoreEntities, - @Switch('s') boolean selectPasted) throws WorldEditException { + @Switch(name = 'a', desc = "Skip air blocks") + boolean ignoreAirBlocks, + @Switch(name = 'o', desc = "Paste at the original position") + boolean atOrigin, + @Switch(name = 's', desc = "Select the region after pasting") + boolean selectPasted, + @Switch(name = 'e', desc = "Paste entities if available") + boolean pasteEntities, + @Switch(name = 'b', desc = "Paste biomes if available") + boolean pasteBiomes, + @ArgFlag(name = 'm', desc = "Only paste blocks matching this mask", def = "") + Mask sourceMask) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); if (holder.getTransform().isIdentity() && editSession.getSourceMask() == null) { @@ -473,8 +444,9 @@ public class ClipboardCommands extends MethodCommands { .createPaste(editSession) .to(to) .ignoreAirBlocks(ignoreAirBlocks) - .ignoreBiomes(ignoreBiomes) - .ignoreEntities(ignoreEntities) + .copyBiomes(pasteBiomes) + .copyEntities(pasteEntities) + .maskSource(sourceMask) .build(); Operations.completeLegacy(operation); @@ -488,39 +460,36 @@ public class ClipboardCommands extends MethodCommands { selector.explainRegionAdjust(player, session); } BBC.COMMAND_PASTE.send(player, to); - if (!player.hasPermission("fawe.tips")) + if (!player.hasPermission("fawe.tips")) { BBC.TIP_COPYPASTE.or(BBC.TIP_SOURCE_MASK, BBC.TIP_REPLACE_MARKER).send(player, to); + } } private void checkPaste(Player player, EditSession editSession, BlockVector3 to, ClipboardHolder holder, Clipboard clipboard) { URI uri = null; - if (holder instanceof URIClipboardHolder) uri = ((URIClipboardHolder) holder).getURI(clipboard); + if (holder instanceof URIClipboardHolder) { + uri = ((URIClipboardHolder) holder).getURI(clipboard); + } PasteEvent event = new PasteEvent(player, clipboard, uri, editSession, to); worldEdit.getEventBus().post(event); - if (event.isCancelled()) throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MANUAL); + if (event.isCancelled()) { + throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MANUAL); + } } @Command( - aliases = {"/place"}, - usage = "", - flags = "sao", + name = "/place", desc = "Place the clipboard's contents without applying transformations (e.g. rotate)", - help = - "Places the clipboard's contents without applying transformations (e.g. rotate).\n" + - "Flags:\n" + - " -a skips air blocks\n" + - " -o pastes at the original position\n" + - " -s selects the region after pasting", - min = 0, - max = 0 ) - - // Skips all transforms @CommandPermissions("worldedit.clipboard.place") @Logging(PLACEMENT) public void place(Player player, LocalSession session, final EditSession editSession, - @Switch('a') final boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, - @Switch('s') boolean selectPasted) throws WorldEditException { + @Switch(name = 'a', desc = "Skip air blocks") + boolean ignoreAirBlocks, + @Switch(name = 'o', desc = "Paste at the original position") + boolean atOrigin, + @Switch(name = 's', desc = "Select the region after pasting") + boolean selectPasted) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); final Clipboard clipboard = holder.getClipboard(); final BlockVector3 origin = clipboard.getOrigin(); @@ -532,8 +501,8 @@ public class ClipboardCommands extends MethodCommands { Region region = clipboard.getRegion().clone(); if (selectPasted) { - BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); - BlockVector3 realTo = to.add(holder.getTransform().apply(clipboardOffset.toVector3()).toBlockPoint()); + BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); + BlockVector3 realTo = to.add(holder.getTransform().apply(clipboardOffset.toVector3()).toBlockPoint()); BlockVector3 max = realTo.add(holder.getTransform().apply(region.getMaximumPoint().subtract(region.getMinimumPoint()).toVector3()).toBlockPoint()); RegionSelector selector = new CuboidRegionSelector(player.getWorld(), realTo, max); session.setRegionSelector(player.getWorld(), selector); @@ -548,38 +517,46 @@ public class ClipboardCommands extends MethodCommands { } @Command( - aliases = { "/rotate" }, - usage = " [] []", + name = "/rotate", desc = "Rotate the contents of the clipboard", - help = "Non-destructively rotate the contents of the clipboard.\n" + - "Angles are provided in degrees and a positive angle will result in a clockwise rotation. " + - "Multiple rotations can be stacked. Interpolation is not performed so angles should be a multiple of 90 degrees.\n" + descFooter = "Non-destructively rotate the contents of the clipboard.\n" + + "Angles are provided in degrees and a positive angle will result in a clockwise rotation. " + + "Multiple rotations can be stacked. Interpolation is not performed so angles should be a multiple of 90 degrees.\n" ) @CommandPermissions("worldedit.clipboard.rotate") - public void rotate(Player player, LocalSession session, Double yRotate, @Optional Double xRotate, @Optional Double zRotate) throws WorldEditException { + public void rotate(Player player, LocalSession session, + @Arg(desc = "Amount to rotate on the y-axis") + double yRotate, + @Arg(desc = "Amount to rotate on the x-axis", def = "0") + double xRotate, + @Arg(desc = "Amount to rotate on the z-axis", def = "0") + double zRotate) throws WorldEditException { + if (Math.abs(yRotate % 90) > 0.001 || + Math.abs(xRotate % 90) > 0.001 || + Math.abs(zRotate % 90) > 0.001) { + player.printDebug("Note: Interpolation is not yet supported, so angles that are multiples of 90 is recommended."); + } + ClipboardHolder holder = session.getClipboard(); AffineTransform transform = new AffineTransform(); - transform = transform.rotateY(-(yRotate != null ? yRotate : 0)); - transform = transform.rotateX(-(xRotate != null ? xRotate : 0)); - transform = transform.rotateZ(-(zRotate != null ? zRotate : 0)); + transform = transform.rotateY(-yRotate); + transform = transform.rotateX(-xRotate); + transform = transform.rotateZ(-zRotate); holder.setTransform(holder.getTransform().combine(transform)); BBC.COMMAND_ROTATE.send(player); - if (!player.hasPermission("fawe.tips")) + if (!player.hasPermission("fawe.tips")) { BBC.TIP_FLIP.or(BBC.TIP_DEFORM, BBC.TIP_TRANSFORM).send(player); + } } @Command( - aliases = { "/flip" }, - usage = "[]", - desc = "Flip the contents of the clipboard", - help = - "Flips the contents of the clipboard across the point from which the copy was made.\n", - min = 0, - max = 1 + name = "/flip", + desc = "Flip the contents of the clipboard across the origin" ) @CommandPermissions("worldedit.clipboard.flip") public void flip(Player player, LocalSession session, - @Optional(Direction.AIM) @Direction BlockVector3 direction) throws WorldEditException { + @Arg(desc = "The direction to flip, defaults to look direction.", def = Direction.AIM) + @Direction BlockVector3 direction) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); AffineTransform transform = new AffineTransform(); transform = transform.scale(direction.abs().multiply(-2).add(1, 1, 1).toVector3()); @@ -588,11 +565,8 @@ public class ClipboardCommands extends MethodCommands { } @Command( - aliases = { "clearclipboard" }, - usage = "", - desc = "Clear your clipboard", - min = 0, - max = 0 + name = "clearclipboard", + desc = "Clear your clipboard" ) @CommandPermissions("worldedit.clipboard.clear") public void clearClipboard(Player player, LocalSession session) throws WorldEditException { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ExpandCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ExpandCommands.java new file mode 100644 index 000000000..6f34e83f2 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ExpandCommands.java @@ -0,0 +1,152 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command; + +import com.google.common.collect.ImmutableSet; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.command.util.Logging; +import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.MultiDirection; +import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import org.enginehub.piston.Command; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.CommandManagerService; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.part.SubCommandPart; + +import java.util.List; + +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; +import static com.sk89q.worldedit.internal.command.CommandUtil.requireIV; + +/** + * Extracted from {@link SelectionCommands} to allow importing of {@link Command}. + */ +@CommandContainer +public class ExpandCommands { + + public static void register(CommandRegistrationHandler registration, + CommandManager commandManager, + CommandManagerService commandManagerService) { + // Collect the general expand command + CommandManager collect = commandManagerService.newCommandManager(); + + registration.register( + collect, + ExpandCommandsRegistration.builder(), + new ExpandCommands() + ); + + Command expandBaseCommand = collect.getCommand("/expand") + .orElseThrow(() -> new IllegalStateException("No /expand command")); + + commandManager.register("/expand", command -> { + command.condition(new PermissionCondition(ImmutableSet.of("worldedit.selection.expand"))); + + command.addPart(SubCommandPart.builder( + TranslatableComponent.of("vert"), + TextComponent.of("Vertical expansion sub-command") + ) + .withCommands(ImmutableSet.of(createVertCommand(commandManager))) + .optional() + .build()); + + command.addParts(expandBaseCommand.getParts()); + command.action(expandBaseCommand.getAction()); + command.description(expandBaseCommand.getDescription()); + }); + } + + private static Command createVertCommand(CommandManager commandManager) { + return commandManager.newCommand("vert") + .description(TextComponent.of("Vertically expand the selection to world limits.")) + .action(parameters -> { + expandVert( + requireIV(Key.of(LocalSession.class), "localSession", parameters), + requireIV(Key.of(Player.class), "localSession", parameters) + ); + return 1; + }) + .build(); + } + + private static void expandVert(LocalSession session, Player player) throws IncompleteRegionException { + Region region = session.getSelection(player.getWorld()); + try { + int oldSize = region.getArea(); + region.expand( + BlockVector3.at(0, (player.getWorld().getMaxY() + 1), 0), + BlockVector3.at(0, -(player.getWorld().getMaxY() + 1), 0)); + session.getRegionSelector(player.getWorld()).learnChanges(); + int newSize = region.getArea(); + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + player.print("Region expanded " + (newSize - oldSize) + + " blocks [top-to-bottom]."); + } catch (RegionOperationException e) { + player.printError(e.getMessage()); + } + } + + @org.enginehub.piston.annotation.Command( + name = "/expand", + desc = "Expand the selection area" + ) + @Logging(REGION) + public void expand(Player player, LocalSession session, + @Arg(desc = "Amount to expand the selection by, can be `vert` to expand to the whole vertical column") + int amount, + @Arg(desc = "Amount to expand the selection by in the other direction", def = "0") + int reverseAmount, + @Arg(desc = "Direction to expand", def = Direction.AIM) + @MultiDirection + List direction) throws WorldEditException { + Region region = session.getSelection(player.getWorld()); + int oldSize = region.getArea(); + + if (reverseAmount == 0) { + for (BlockVector3 dir : direction) { + region.expand(dir.multiply(amount)); + } + } else { + for (BlockVector3 dir : direction) { + region.expand(dir.multiply(amount), dir.multiply(-reverseAmount)); + } + } + + session.getRegionSelector(player.getWorld()).learnChanges(); + int newSize = region.getArea(); + + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + + player.print("Region expanded " + (newSize - oldSize) + " block(s)."); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java index 5e2bdd9b8..d21acec38 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java @@ -33,12 +33,9 @@ import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; - -import static com.google.common.base.Preconditions.checkNotNull; - /** * Helper class to 'bake' a transform into a clipboard. - *

+ * *

This class needs a better name and may need to be made more generic.

* * @see Clipboard @@ -52,7 +49,7 @@ public class FlattenedClipboardTransform { /** * Create a new instance. * - * @param original the original clipboard + * @param original the original clipboard * @param transform the transform */ private FlattenedClipboardTransform(Clipboard original, Transform transform) { @@ -94,10 +91,10 @@ public class FlattenedClipboardTransform { Vector3 newMinimum = corners[0]; Vector3 newMaximum = corners[0]; + for (int i = 1; i < corners.length; i++) { - Vector3 cbv = corners[i]; - newMinimum = newMinimum.getMinimum(cbv); - newMaximum = newMaximum.getMaximum(cbv); + newMinimum = newMinimum.getMinimum(corners[i]); + newMaximum = newMaximum.getMaximum(corners[i]); } // After transformation, the points may not really sit on a block, @@ -119,13 +116,16 @@ public class FlattenedClipboardTransform { if (transform != null && !transform.isIdentity()) extent = new BlockTransformExtent(original, transform); ForwardExtentCopy copy = new ForwardExtentCopy(extent, original.getRegion(), original.getOrigin(), target, original.getOrigin()); copy.setTransform(transform); + if (original.hasBiomes()) { + copy.setCopyingBiomes(true); + } return copy; } /** * Create a new instance to bake the transform with. * - * @param original the original clipboard + * @param original the original clipboard * @param transform the transform * @return a builder */ @@ -133,5 +133,4 @@ public class FlattenedClipboardTransform { return new FlattenedClipboardTransform(original, transform); } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java new file mode 100644 index 000000000..38d92a34e --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -0,0 +1,284 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command; + +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.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.input.DisallowedUsageException; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.util.formatting.component.PaginationBox; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.world.item.ItemType; +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.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * General WorldEdit commands. + */ +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class GeneralCommands { + + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public GeneralCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; + } + + @Command( + name = "/limit", + desc = "Modify block change limit" + ) + @CommandPermissions("worldedit.limit") + public void limit(Player player, LocalSession session, + @Arg(desc = "The limit to set", def = "") + Integer limit) { + + LocalConfiguration config = worldEdit.getConfiguration(); + boolean mayDisable = player.hasPermission("worldedit.limit.unrestricted"); + + limit = limit == null ? config.defaultChangeLimit : Math.max(-1, limit); + if (!mayDisable && config.maxChangeLimit > -1) { + if (limit > config.maxChangeLimit) { + player.printError("Your maximum allowable limit is " + config.maxChangeLimit + "."); + return; + } + } + + session.setBlockChangeLimit(limit); + player.print("Block change limit set to " + limit + "." + + (limit == config.defaultChangeLimit ? "" : " (Use //limit to go back to the default.)")); + } + + @Command( + name = "/timeout", + desc = "Modify evaluation timeout time." + ) + @CommandPermissions("worldedit.timeout") + public void timeout(Player player, LocalSession session, + @Arg(desc = "The timeout time to set", def = "") + Integer limit) { + LocalConfiguration config = worldEdit.getConfiguration(); + boolean mayDisable = player.hasPermission("worldedit.timeout.unrestricted"); + + limit = limit == null ? config.calculationTimeout : Math.max(-1, limit); + if (!mayDisable && config.maxCalculationTimeout > -1) { + if (limit > config.maxCalculationTimeout) { + player.printError("Your maximum allowable timeout is " + config.maxCalculationTimeout + " ms."); + return; + } + } + + session.setTimeout(limit); + player.print("Timeout time set to " + limit + " ms." + + (limit == config.calculationTimeout ? "" : " (Use //timeout to go back to the default.)")); + } + + @Command( + name = "/fast", + desc = "Toggle fast mode" + ) + @CommandPermissions("worldedit.fast") + public void fast(Player player, LocalSession session, + @Arg(desc = "The new fast mode state", def = "") + Boolean fastMode) { + boolean hasFastMode = session.hasFastMode(); + if (fastMode != null && fastMode == hasFastMode) { + player.printError("Fast mode already " + (fastMode ? "enabled" : "disabled") + "."); + return; + } + + if (hasFastMode) { + session.setFastMode(false); + player.print("Fast mode disabled."); + } else { + session.setFastMode(true); + player.print("Fast mode enabled. Lighting in the affected chunks may be wrong and/or you may need to rejoin to see changes."); + } + } + + @Command( + name = "/reorder", + desc = "Sets the reorder mode of WorldEdit" + ) + @CommandPermissions("worldedit.reorder") + public void reorderMode(Player player, LocalSession session, + @Arg(desc = "The reorder mode", def = "") + EditSession.ReorderMode reorderMode) { + if (reorderMode == null) { + player.print("The reorder mode is " + session.getReorderMode().getDisplayName()); + } else { + session.setReorderMode(reorderMode); + player.print("The reorder mode is now " + session.getReorderMode().getDisplayName()); + } + } + + @Command( + name = "/drawsel", + desc = "Toggle drawing the current selection" + ) + @CommandPermissions("worldedit.drawsel") + public void drawSelection(Player player, LocalSession session, + @Arg(desc = "The new draw selection state", def = "") + Boolean drawSelection) throws WorldEditException { + if (!WorldEdit.getInstance().getConfiguration().serverSideCUI) { + throw new DisallowedUsageException("This functionality is disabled in the configuration!"); + } + boolean useServerCui = session.shouldUseServerCUI(); + if (drawSelection != null && drawSelection == useServerCui) { + player.printError("Server CUI already " + (useServerCui ? "enabled" : "disabled") + "."); + return; + } + if (useServerCui) { + session.setUseServerCUI(false); + session.updateServerCUI(player); + player.print("Server CUI disabled."); + } else { + session.setUseServerCUI(true); + session.updateServerCUI(player); + player.print("Server CUI enabled. This only supports cuboid regions, with a maximum size of 32x32x32."); + } + } + + @Command( + name = "gmask", + aliases = {"/gmask"}, + desc = "Set the global mask" + ) + @CommandPermissions("worldedit.global-mask") + public void gmask(Player player, LocalSession session, + @Arg(desc = "The mask to set", def = "") + Mask mask) { + if (mask == null) { + session.setMask(null); + player.print("Global mask disabled."); + } else { + session.setMask(mask); + player.print("Global mask set."); + } + } + + @Command( + name = "toggleplace", + aliases = {"/toggleplace"}, + desc = "Switch between your position and pos1 for placement" + ) + public void togglePlace(Player player, LocalSession session) { + if (session.togglePlacementPosition()) { + player.print("Now placing at pos #1."); + } else { + player.print("Now placing at the block you stand in."); + } + } + + @Command( + name = "searchitem", + aliases = {"/searchitem", "/l", "/search"}, + desc = "Search for an item" + ) + @CommandPermissions("worldedit.searchitem") + public void searchItem(Actor actor, + @Switch(name = 'b', desc = "Only search for blocks") + boolean blocksOnly, + @Switch(name = 'i', desc = "Only search for items") + boolean itemsOnly, + @ArgFlag(name = 'p', desc = "Page of results to return", def = "1") + int page, + @Arg(desc = "Search query", variable = true) + List query) { + String search = String.join(" ", query); + if (search.length() <= 2) { + actor.printError("Enter a longer search string (len > 2)."); + return; + } + if (blocksOnly && itemsOnly) { + actor.printError("You cannot use both the 'b' and 'i' flags simultaneously."); + return; + } + + WorldEditAsyncCommandBuilder.createAndSendMessage(actor, new ItemSearcher(search, blocksOnly, itemsOnly, page), + "(Please wait... searching items.)"); + } + + private static class ItemSearcher implements Callable { + private final boolean blocksOnly; + private final boolean itemsOnly; + private final String search; + private final int page; + + ItemSearcher(String search, boolean blocksOnly, boolean itemsOnly, int page) { + this.blocksOnly = blocksOnly; + this.itemsOnly = itemsOnly; + this.search = search; + this.page = page; + } + + @Override + public Component call() throws Exception { + String command = "/searchitem " + (blocksOnly ? "-b " : "") + (itemsOnly ? "-i " : "") + "-p %page% " + search; + Map results = new TreeMap<>(); + String idMatch = search.replace(' ', '_'); + String nameMatch = search.toLowerCase(Locale.ROOT); + for (ItemType searchType : ItemType.REGISTRY) { + if (blocksOnly && !searchType.hasBlockType()) { + continue; + } + + if (itemsOnly && searchType.hasBlockType()) { + continue; + } + final String id = searchType.getId(); + String name = searchType.getName(); + final boolean hasName = !name.equals(id); + name = name.toLowerCase(Locale.ROOT); + if (id.contains(idMatch) || (hasName && name.contains(nameMatch))) { + results.put(id, name + (hasName ? " (" + id + ")" : "")); + } + } + List list = new ArrayList<>(results.values()); + return PaginationBox.fromStrings("Search results for '" + search + "'", command, list).create(page); + } + } +} 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 3845a5ac5..445a76e37 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 @@ -27,21 +27,27 @@ import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.image.ImageUtil; -import com.sk89q.minecraft.util.commands.Command; + +import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; 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.Logging; +import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; +import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; +import static com.sk89q.worldedit.command.util.Logging.LogMode.POSITION; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.RegionVisitor; +import com.sk89q.worldedit.internal.annotation.Radii; import com.sk89q.worldedit.internal.annotation.Selection; +import static com.sk89q.worldedit.internal.command.CommandUtil.checkCommandArgument; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -49,29 +55,26 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator.TreeType; -import com.sk89q.worldedit.util.command.binding.Range; -import com.sk89q.worldedit.util.command.binding.Switch; -import com.sk89q.worldedit.util.command.binding.Text; -import com.sk89q.worldedit.util.command.parametric.Optional; -import com.sk89q.worldedit.util.command.parametric.ParameterException; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockType; +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; -import java.awt.*; +import java.util.List; +import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.URL; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.ALL; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; - - /** * Commands for the generation of shapes and other objects. */ -@Command(aliases = {}, desc = "Create structures and features: [More Info](https://goo.gl/KuLFRW)") -public class GenerationCommands extends MethodCommands { +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class GenerationCommands { + + private final WorldEdit worldEdit; /** * Create a new instance. @@ -79,18 +82,28 @@ public class GenerationCommands extends MethodCommands { * @param worldEdit reference to WorldEdit */ public GenerationCommands(WorldEdit worldEdit) { - super(worldEdit); + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( - aliases = {"/caves"}, + name = "/caves", usage = "[size=8] [freq=40] [rarity=7] [minY=8] [maxY=127] [sysFreq=1] [sysRarity=25] [pocketRarity=0] [pocketMin=0] [pocketMax=3]", - desc = "Generates caves", - help = "Generates a cave network" + desc = "Generates a cave network" ) @CommandPermissions("worldedit.generation.caves") @Logging(PLACEMENT) - public void caves(FawePlayer fp, LocalSession session, EditSession editSession, @Selection Region region, @Optional("8") int size, @Optional("40") int frequency, @Optional("7") int rarity, @Optional("8") int minY, @Optional("127") int maxY, @Optional("1") int systemFrequency, @Optional("25") int individualRarity, @Optional("0") int pocketChance, @Optional("0") int pocketMin, @Optional("3") int pocketMax, CommandContext context) throws WorldEditException { + public void caves(FawePlayer fp, LocalSession session, EditSession editSession, @Selection Region region, + @Arg(desc = "TODO", def = "8") int size, + @Arg(desc = "TODO", def = "40") int frequency, + @Arg(desc = "TODO", def = "7") int rarity, + @Arg(desc = "TODO", def = "8") int minY, + @Arg(desc = "TODO", def = "127") int maxY, + @Arg(desc = "TODO", def = "1") int systemFrequency, + @Arg(desc = "TODO", def = "25") int individualRarity, + @Arg(desc = "TODO", def = "0") int pocketChance, + @Arg(desc = "TODO", def = "0") int pocketMin, + @Arg(desc = "TODO", def = "3") int pocketMax, CommandContext context) throws WorldEditException { fp.checkConfirmationRegion(() -> { CavesGen gen = new CavesGen(size, frequency, rarity, minY, maxY, systemFrequency, individualRarity, pocketChance, pocketMin, pocketMax); editSession.generate(region, gen); @@ -98,14 +111,10 @@ public class GenerationCommands extends MethodCommands { }, getArguments(context), region, context); } - // public void addOre(Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { @Command( - aliases = {"/ores"}, - desc = "Generates ores", - help = "Generates ores", - min = 1, - max = 1 + name = "/ores", + desc = "Generates ores" ) @CommandPermissions("worldedit.generation.ore") @Logging(PLACEMENT) @@ -119,59 +128,49 @@ public class GenerationCommands extends MethodCommands { @Command( aliases = {"/image", "/img"}, desc = "Generate an image", - usage = " [randomize=true] [complexity=100] [dimensions=100,100]", - min = 1, - max = 4 + usage = " [randomize=true] [complexity=100] [dimensions=100,100]" ) @CommandPermissions("worldedit.generation.image") @Logging(PLACEMENT) - public void image(Player player, LocalSession session, EditSession editSession, String arg, @Optional("true") boolean randomize, @Optional("100") int threshold, @Optional BlockVector2 dimensions) throws WorldEditException, IOException { + public void image(Player player, LocalSession session, EditSession editSession, String arg, @Optional("true") boolean randomize, + @Arg(desc = "TODO", def = "100") int threshold, @Optional BlockVector2 dimensions) throws WorldEditException, IOException { TextureUtil tu = Fawe.get().getCachedTextureUtil(randomize, 0, threshold); URL url = new URL(arg); if (!url.getHost().equalsIgnoreCase("i.imgur.com") && !url.getHost().equalsIgnoreCase("empcraft.com")) { throw new IOException("Only i.imgur.com or empcraft.com/ui links are allowed!"); } - FawePlayer fp = FawePlayer.wrap(player); BufferedImage image = MainUtil.readImage(url); if (dimensions != null) { image = ImageUtil.getScaledInstance(image, dimensions.getBlockX(), dimensions.getBlockZ(), RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); } -// MutableBlockVector3 pos1 = new MutableBlockVector3(player.getLocation().toBlockPoint()); -// MutableBlockVector3 pos2 = new MutableBlockVector3(pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1)); BlockVector3 pos1 = player.getLocation().toBlockPoint(); BlockVector3 pos2 = pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1); CuboidRegion region = new CuboidRegion(pos1, pos2); int[] count = new int[1]; final BufferedImage finalImage = image; - RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { - @Override - public boolean apply(BlockVector3 pos) throws WorldEditException { - try { - int x = pos.getBlockX() - pos1.getBlockX(); - int z = pos.getBlockZ() - pos1.getBlockZ(); - int color = finalImage.getRGB(x, z); - BlockType block = tu.getNearestBlock(color); - count[0]++; - if (block != null) return editSession.setBlock(pos, block.getDefaultState()); - return false; - } catch (Throwable e) { - e.printStackTrace(); - } + RegionVisitor visitor = new RegionVisitor(region, pos -> { + try { + int x = pos.getBlockX() - pos1.getBlockX(); + int z = pos.getBlockZ() - pos1.getBlockZ(); + int color = finalImage.getRGB(x, z); + BlockType block = tu.getNearestBlock(color); + count[0]++; + if (block != null) return editSession.setBlock(pos, block.getDefaultState()); return false; + } catch (Throwable e) { + e.printStackTrace(); } + return false; }, editSession); Operations.completeBlindly(visitor); BBC.VISITOR_BLOCK.send(player, editSession.getBlockChangeCount()); } @Command( - aliases = {"/ore"}, + name = "/ore", usage = " ", - desc = "Generates ores", - help = "Generates ores", - min = 7, - max = 7 + desc = "Generates ores" ) @CommandPermissions("worldedit.generation.ore") @Logging(PLACEMENT) @@ -183,20 +182,24 @@ public class GenerationCommands extends MethodCommands { } @Command( - aliases = { "/hcyl" }, + name = "/hcyl", usage = " [,] [height]", desc = "Generates a hollow cylinder.", help = "Generates a hollow cylinder.\n" + "By specifying 2 radii, separated by a comma,\n" + "you can generate elliptical cylinders.\n" + - "The 1st radius is north/south, the 2nd radius is east/west.", - min = 2, - max = 4 + "The 1st radius is north/south, the 2nd radius is east/west." ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) - public void hcyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, BlockVector2 radius, @Optional("1") int height, @Range(min = 1) @Optional("1") double thickness, CommandContext context) throws WorldEditException { + public void hcyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The pattern of blocks to generate") + Pattern pattern, + BlockVector2 radius, + @Arg(desc = "The height of the cylinder", def = "1") + int height, + @Range(min = 1) @Optional("1") double thickness, CommandContext context) throws WorldEditException { double max = MathMan.max(radius.getBlockX(), radius.getBlockZ()); worldEdit.checkMaxRadius(max); BlockVector3 pos = session.getPlacementPosition(player); @@ -207,21 +210,24 @@ public class GenerationCommands extends MethodCommands { } @Command( - aliases = { "/cyl" }, - usage = " [,] [height]", - flags = "h", + name = "/cyl", desc = "Generates a cylinder.", help = "Generates a cylinder.\n" + "By specifying 2 radii, separated by a comma,\n" + "you can generate elliptical cylinders.\n" + - "The 1st radius is north/south, the 2nd radius is east/west.", - min = 2, - max = 3 + "The 1st radius is north/south, the 2nd radius is east/west." ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) - public void cyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, BlockVector2 radius, @Optional("1") int height, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException { + public void cyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The pattern of blocks to generate") + Pattern pattern, + BlockVector2 radius, + @Arg(desc = "The height of the cylinder", def = "1") + int height, + @Switch(name = 'h', desc = "Make a hollow cylinder") + boolean hollow, CommandContext context) throws WorldEditException { double max = Math.max(radius.getBlockX(), radius.getBlockZ()); worldEdit.checkMaxRadius(max); BlockVector3 pos = session.getPlacementPosition(player); @@ -232,104 +238,151 @@ public class GenerationCommands extends MethodCommands { } @Command( - aliases = { "/hsphere" }, + name = "/hsphere", usage = " [,,] [raised?]", desc = "Generates a hollow sphere.", help = "Generates a hollow sphere.\n" + "By specifying 3 radii, separated by commas,\n" + "you can generate an ellipsoid. The order of the ellipsoid radii\n" + - "is north/south, up/down, east/west.", - min = 2, - max = 3 + "is north/south, up/down, east/west." ) @CommandPermissions("worldedit.generation.sphere") @Logging(PLACEMENT) - public void hsphere(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, BlockVector3 radius, @Optional("false") boolean raised, CommandContext context) throws WorldEditException { - sphere(fp, player, session, editSession, pattern, radius, raised, true, context); + public void hsphere(FawePlayer fp, Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The pattern of blocks to generate") + Pattern pattern, + @Arg(desc = "The radii of the sphere. Order is N/S, U/D, E/W") + @Radii(3) + List radii, + @Switch(name = 'r', desc = "Raise the bottom of the sphere to the placement position") + boolean raised, + CommandContext context) throws WorldEditException { + sphere(fp, player, session, editSession, pattern, radii, raised, true, context); } @Command( - aliases = { "/sphere" }, - usage = " [,,] [raised?]", - flags = "h", + name = "/sphere", desc = "Generates a filled sphere.", help = "Generates a filled sphere.\n" + "By specifying 3 radii, separated by commas,\n" + "you can generate an ellipsoid. The order of the ellipsoid radii\n" + - "is north/south, up/down, east/west.", - min = 2, - max = 3 + "is north/south, up/down, east/west." ) @CommandPermissions("worldedit.generation.sphere") @Logging(PLACEMENT) - public void sphere(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, BlockVector3 radius, @Optional("false") boolean raised, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException { - double max = MathMan.max(radius.getBlockX(), radius.getBlockY(), radius.getBlockZ()); - worldEdit.checkMaxRadius(max); + public int sphere(FawePlayer fp, Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The pattern of blocks to generate") + Pattern pattern, + @Arg(desc = "The radii of the sphere. Order is N/S, U/D, E/W") + @Radii(3) + List radii, + @Switch(name = 'r', desc = "Raise the bottom of the sphere to the placement position") + boolean raised, + @Switch(name = 'h', desc = "Make a hollow sphere") + boolean hollow) throws WorldEditException { + final double radiusX, radiusY, radiusZ; + switch (radii.size()) { + case 1: + radiusX = radiusY = radiusZ = Math.max(1, radii.get(0)); + break; + + case 3: + radiusX = Math.max(1, radii.get(0)); + radiusY = Math.max(1, radii.get(1)); + radiusZ = Math.max(1, radii.get(2)); + break; + + default: + player.printError("You must either specify 1 or 3 radius values."); + return 0; + } + + worldEdit.checkMaxRadius(radiusX); + worldEdit.checkMaxRadius(radiusY); + worldEdit.checkMaxRadius(radiusZ); + BlockVector3 pos = session.getPlacementPosition(player); - BlockVector3 finalPos = raised ? pos.add(0, radius.getY(), 0) : pos; + BlockVector3 finalPos; + if (raised) { + finalPos = pos.add(0, (int) radiusY, 0); + } else { + finalPos = pos; + } fp.checkConfirmationRadius(() -> { - int affected = editSession.makeSphere(finalPos, pattern, radius.getX(), radius.getY(), radius.getZ(), !hollow); + int affected = editSession.makeSphere(finalPos, pattern, radiusX, radiusY, radiusZ, !hollow); player.findFreePosition(); BBC.VISITOR_BLOCK.send(fp, affected); }, getArguments(context), (int) max, context); } @Command( - aliases = { "forestgen" }, - usage = "[size] [type] [density]", - desc = "Generate a forest", - min = 0, - max = 3 + name = "forestgen", + desc = "Generate a forest" ) @CommandPermissions("worldedit.generation.forest") @Logging(POSITION) - public void forestGen(Player player, LocalSession session, EditSession editSession, @Optional("10") int size, - @Optional("tree") TreeType type, @Optional("5") @Range(min = 0, max = 100) double density) throws WorldEditException { + public int forestGen(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The size of the forest, in blocks", def = "10") + int size, + @Arg(desc = "The type of forest", def = "tree") + TreeType type, + @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"); density = density / 100; int affected = editSession.makeForest(session.getPlacementPosition(player), size, density, type); player.print(affected + " trees created."); + return affected; } @Command( - aliases = { "pumpkins" }, - usage = "[size=10] [density=0.02]", - desc = "Generate pumpkin patches", - min = 0, - max = 2 + name = "pumpkins", + desc = "Generate pumpkin patches" ) @CommandPermissions("worldedit.generation.pumpkins") @Logging(POSITION) - public void pumpkins(Player player, LocalSession session, EditSession editSession, @Optional("10") int apothem, @Optional("0.02") double density) throws WorldEditException, ParameterException { + public int pumpkins(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The size of the patch", def = "10") + int size, + @Arg(desc = "//TODO", def = "10") + int apothem, + @Arg(desc = "//TODO ", def = "0.02") + double density) throws WorldEditException { int affected = editSession.makePumpkinPatches(session.getPlacementPosition(player), apothem, density); BBC.COMMAND_PUMPKIN.send(player, affected); + return affected; } @Command( - aliases = { "/hpyramid" }, - usage = " ", - desc = "Generate a hollow pyramid", - min = 2, - max = 2 + name = "/hpyramid", + desc = "Generate a hollow pyramid" ) @CommandPermissions("worldedit.generation.pyramid") @Logging(PLACEMENT) - public void hollowPyramid(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, @Range(min = 1) int size, CommandContext context) throws WorldEditException { + public void hollowPyramid(FawePlayer fp, Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern, + @Arg(desc = "The size of the pyramid") + int size, CommandContext context) throws WorldEditException { pyramid(fp, player, session, editSession, pattern, size, true, context); } @Command( - aliases = { "/pyramid" }, - usage = " ", - flags = "h", - desc = "Generate a filled pyramid", - min = 2, - max = 2 + name = "/pyramid", + desc = "Generate a filled pyramid" ) @CommandPermissions("worldedit.generation.pyramid") @Logging(PLACEMENT) - public void pyramid(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, @Range(min = 1) int size, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException { + public void pyramid(FawePlayer fp, Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern, + @Arg(desc = "The size of the pyramid") + int size, + @Switch(name = 'h', desc = "Make a hollow pyramid") + boolean hollow, + CommandContext context) throws WorldEditException { BlockVector3 pos = session.getPlacementPosition(player); worldEdit.checkMaxRadius(size); fp.checkConfirmationRadius(() -> { @@ -339,36 +392,28 @@ public class GenerationCommands extends MethodCommands { }, getArguments(context), size, context); } - @Command( - aliases = { "/generate", "/gen", "/g" }, - usage = " ", + name = "/generate", + aliases = { "/gen", "/g" }, desc = "Generates a shape according to a formula.", - help = - "Generates a shape according to a formula that is expected to\n" + - "return positive numbers (true) if the point is inside the shape\n" + - "Optionally set type/data to the desired block.\n" + - "Flags:\n" + - " -h to generate a hollow shape\n" + - " -r to use raw minecraft coordinates\n" + - " -o is like -r, except offset from placement.\n" + - " -c is like -r, except offset selection center.\n" + - "If neither -r nor -o is given, the selection is mapped to -1..1\n" + - "See also tinyurl.com/wesyntax.", - flags = "hroc", - min = 2, - max = -1 + descFooter = "See also https://tinyurl.com/wesyntax." ) @CommandPermissions("worldedit.generation.shape") @Logging(ALL) - public void generate(FawePlayer fp, Player player, LocalSession session, EditSession editSession, - @Selection Region region, - Pattern pattern, - @Text String expression, - @Switch('h') boolean hollow, - @Switch('r') boolean useRawCoords, - @Switch('o') boolean offset, - @Switch('c') boolean offsetCenter, CommandContext context) throws WorldEditException { + public int generate(FawePlayer fp, Player player, LocalSession session, EditSession editSession, + @Selection Region region, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern, + @Arg(desc = "Expression to test block placement locations and set block type", variable = true) + List expression, + @Switch(name = 'h', desc = "Generate a hollow shape") + boolean hollow, + @Switch(name = 'r', desc = "Use the game's coordinate origin") + boolean useRawCoords, + @Switch(name = 'o', desc = "Use the placement's coordinate origin") + boolean offset, + @Switch(name = 'c', desc = "Use the selection's center as origin") + boolean offsetCenter) throws WorldEditException { final Vector3 zero; Vector3 unit; @@ -399,47 +444,42 @@ public class GenerationCommands extends MethodCommands { final Vector3 unit1 = unit; + final int affected = 0; fp.checkConfirmationRegion(() -> { try { - final int affected = editSession.makeShape(region, zero, unit1, pattern, expression, hollow); + affected = editSession.makeShape(region, zero, unit1, pattern, String.join(" ", expression), hollow, session.getTimeout()); player.findFreePosition(); BBC.VISITOR_BLOCK.send(fp, affected); } catch (ExpressionException e) { - fp.sendMessage(e.getMessage()); + player.printError(e.getMessage()); + return 0; } }, getArguments(context), region, context); + return affected; } @Command( - aliases = { "/generatebiome", "/genbiome", "/gb" }, - usage = " ", + name = "/generatebiome", + aliases = { "/genbiome", "/gb" }, desc = "Sets biome according to a formula.", - help = - "Generates a shape according to a formula that is expected to\n" + - "return positive numbers (true) if the point is inside the shape\n" + - "Sets the biome of blocks in that shape.\n" + - "Flags:\n" + - " -h to generate a hollow shape\n" + - " -r to use raw minecraft coordinates\n" + - " -o is like -r, except offset from placement.\n" + - " -c is like -r, except offset selection center.\n" + - "If neither -r nor -o is given, the selection is mapped to -1..1\n" + - "See also tinyurl.com/wesyntax.", - flags = "hroc", - min = 2, - max = -1 + descFooter = "See also https://tinyurl.com/wesyntax." ) @CommandPermissions("worldedit.generation.shape.biome") @Logging(ALL) - public void generateBiome(FawePlayer fp, Player player, LocalSession session, EditSession editSession, - @Selection Region region, - BiomeType target, - @Text String expression, - @Switch('h') boolean hollow, - @Switch('r') boolean useRawCoords, - @Switch('o') boolean offset, - @Switch('c') boolean offsetCenter, - CommandContext context) throws WorldEditException { + public int generateBiome(Player player, LocalSession session, EditSession editSession, + @Selection Region region, + @Arg(desc = "The biome type to set") + BiomeType target, + @Arg(desc = "Expression to test block placement locations and set biome type", variable = true) + List expression, + @Switch(name = 'h', desc = "Generate a hollow shape") + boolean hollow, + @Switch(name = 'r', desc = "Use the game's coordinate origin") + boolean useRawCoords, + @Switch(name = 'o', desc = "Use the placement's coordinate origin") + boolean offset, + @Switch(name = 'c', desc = "Use the selection's center as origin") + boolean offsetCenter) throws WorldEditException { final Vector3 zero; Vector3 unit; @@ -475,7 +515,7 @@ public class GenerationCommands extends MethodCommands { player.findFreePosition(); BBC.VISITOR_FLAT.send(fp, affected); } catch (ExpressionException e) { - fp.toWorldEditPlayer().printError(e.getMessage()); + player.printError(e.getMessage()); } }, getArguments(context), region, context); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java index 12f777b70..633928390 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java @@ -2,16 +2,29 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; import com.boydti.fawe.util.StringMan; + import com.google.common.base.Joiner; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.platform.CommandManager; -import com.sk89q.worldedit.util.command.*; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; +import com.sk89q.worldedit.util.command.CommandCallable; +import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.DelegateCallable; +import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.util.command.PrimaryAliasComparator; import com.sk89q.worldedit.util.command.parametric.AParametricCallable; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public abstract class HelpBuilder implements Runnable { private final CommandCallable callable; @@ -45,7 +58,6 @@ public abstract class HelpBuilder implements Runnable { } catch (Exception ignored) { } - boolean isRootLevel = true; List visited = new ArrayList<>(); // Create the message @@ -61,10 +73,10 @@ public abstract class HelpBuilder implements Runnable { Map> grouped = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (CommandMapping mapping : aliases) { CommandCallable c = mapping.getCallable(); - String group; if (c instanceof DelegateCallable) { c = ((DelegateCallable) c).getParent(); } + String group; if (c instanceof AParametricCallable) { Command command = ((AParametricCallable) c).getCommand(); if (command != null && command.aliases().length != 0) { @@ -94,6 +106,7 @@ public abstract class HelpBuilder implements Runnable { Map mappings = effectiveLength == 1 ? grouped.get(cat) : null; if (mappings == null) { // Drill down to the command + boolean isRootLevel = true; for (int i = 0; i < effectiveLength; i++) { String command = args.getString(i); @@ -142,12 +155,11 @@ public abstract class HelpBuilder implements Runnable { found.add(closest); displayFailure(BBC.HELP_SUGGEST.f(arg, StringMan.join(found, ", "))); return; - } else { - String msg = String.format("The sub-command '%s' under '%s' could not be found.", - command, Joiner.on(" ").join(visited)); - displayFailure(msg); - return; } + String msg = String.format("The sub-command '%s' under '%s' could not be found.", + command, Joiner.on(" ").join(visited)); + displayFailure(msg); + return; } visited.add(args.getString(i)); isRootLevel = false; @@ -160,7 +172,7 @@ public abstract class HelpBuilder implements Runnable { } if (!(callable instanceof Dispatcher)) { // TODO interactive box - String cmd = (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + Joiner.on(" ").join(visited); + String cmd = "/" + Joiner.on(" ").join(visited); displayUsage(callable, cmd); return; } @@ -181,7 +193,7 @@ public abstract class HelpBuilder implements Runnable { return; } } - aliases.sort(new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN)); + aliases.sort(new PrimaryAliasComparator(PlatformCommandManager.COMMAND_CLEAN_PATTERN)); // Calculate pagination int offset = perPage * Math.max(0, page); @@ -194,15 +206,12 @@ public abstract class HelpBuilder implements Runnable { int end = Math.min(offset + perPage, aliases.size()); List subAliases = aliases.subList(offset, end); List subPrefixes = prefixes.subList(offset, end); - Map commandMap = new LinkedHashMap<>(); - for (int i = 0; i < subAliases.size(); i++) { - commandMap.put(subAliases.get(i), subPrefixes.get(i)); - } + Map commandMap = IntStream.range(0, subAliases.size()).boxed().collect(Collectors.toMap(subAliases::get, subPrefixes::get, (a, b) -> b, LinkedHashMap::new)); String visitedString = Joiner.on(" ").join(visited); displayCommands(commandMap, visitedString, page, pageTotal, effectiveLength); } } else { - String cmd = (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + Joiner.on(" ").join(visited); + String cmd = "/" + Joiner.on(" ").join(visited); displayUsage(callable, cmd); } } catch (Throwable e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java index e1d44e054..bc8c9d0bb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java @@ -27,17 +27,18 @@ import com.boydti.fawe.database.DBHandler; import com.boydti.fawe.database.RollbackDatabase; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +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.entity.Player; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -49,11 +50,14 @@ import com.sk89q.worldedit.world.World; import java.io.File; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; /** * Commands to undo, redo, and clear history. */ -@Command(aliases = {}, desc = "Commands to undo, redo, and clear history: [More Info](http://wiki.sk89q.com/wiki/WorldEdit/Features#History)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class HistoryCommands extends MethodCommands { /** @@ -66,13 +70,12 @@ public class HistoryCommands extends MethodCommands { } @Command( + name = "fawerollback", aliases = {"/frb", "frb", "fawerollback", "/fawerollback", "/rollback"}, usage = " ", desc = "Undo a specific edit. " + - " - The time uses s, m, h, d, y.\n" + - " - Import from disk: /frb #import", - min = 1, - max = 3 + " - The time uses s, m, h, d, y.\n" + + " - Import from disk: /frb #import" ) @CommandPermissions("worldedit.history.rollback") public void faweRollback(final Player player, LocalSession session, final String user, @Optional("0") @Range(min = 0) int radius, @Optional("0") String time, @Switch('r') boolean restore) throws WorldEditException { @@ -80,74 +83,71 @@ public class HistoryCommands extends MethodCommands { BBC.SETTING_DISABLE.send(player, "history.use-database (Import with /frb #import )"); return; } - switch (user.charAt(0)) { - case '#': { - if (user.equals("#import")) { - if (!player.hasPermission("fawe.rollback.import")) { - BBC.NO_PERM.send(player, "fawe.rollback.import"); - return; + if (user.charAt(0) == '#') { + if (user.equals("#import")) { + if (!player.hasPermission("fawe.rollback.import")) { + BBC.NO_PERM.send(player, "fawe.rollback.import"); + return; + } + File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY); + if (!folder.exists()) { + return; + } + for (File worldFolder : folder.listFiles()) { + if (!worldFolder.isDirectory()) { + continue; } - File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY); - if (!folder.exists()) { - return; - } - for (File worldFolder : folder.listFiles()) { - if (!worldFolder.isDirectory()) { - continue; - } - String worldName = worldFolder.getName(); - World world = FaweAPI.getWorld(worldName); - if (world != null) { - for (File userFolder : worldFolder.listFiles()) { - if (!userFolder.isDirectory()) { - continue; - } - String userUUID = userFolder.getName(); - try { - UUID uuid = UUID.fromString(userUUID); - for (File historyFile : userFolder.listFiles()) { - String name = historyFile.getName(); - if (!name.endsWith(".bd")) { - continue; - } - RollbackOptimizedHistory rollback = new RollbackOptimizedHistory(world, uuid, Integer.parseInt(name.substring(0, name.length() - 3))); - DiskStorageHistory.DiskStorageSummary summary = rollback.summarize(RegionWrapper.GLOBAL(), false); - if (summary != null) { - rollback.setDimensions(BlockVector3.at(summary.minX, 0, summary.minZ), BlockVector3.at(summary.maxX, 255, summary.maxZ)); - rollback.setTime(historyFile.lastModified()); - RollbackDatabase db = DBHandler.IMP.getDatabase(world); - db.logEdit(rollback); - player.print("Logging: " + historyFile); - } + String worldName = worldFolder.getName(); + World world = FaweAPI.getWorld(worldName); + if (world != null) { + for (File userFolder : worldFolder.listFiles()) { + if (!userFolder.isDirectory()) { + continue; + } + String userUUID = userFolder.getName(); + try { + UUID uuid = UUID.fromString(userUUID); + for (File historyFile : userFolder.listFiles()) { + String name = historyFile.getName(); + if (!name.endsWith(".bd")) { + continue; + } + RollbackOptimizedHistory rollback = new RollbackOptimizedHistory(world, uuid, Integer.parseInt(name.substring(0, name.length() - 3))); + DiskStorageHistory.DiskStorageSummary summary = rollback.summarize(RegionWrapper.GLOBAL(), false); + if (summary != null) { + rollback.setDimensions(BlockVector3.at(summary.minX, 0, summary.minZ), BlockVector3.at(summary.maxX, 255, summary.maxZ)); + rollback.setTime(historyFile.lastModified()); + RollbackDatabase db = DBHandler.IMP.getDatabase(world); + db.logEdit(rollback); + player.print("Logging: " + historyFile); } - } catch (IllegalArgumentException e) { - e.printStackTrace(); - continue; } + } catch (IllegalArgumentException e) { + e.printStackTrace(); } } } - player.print("Done import!"); - return; - } - String toParse = user.substring(1); - if (!MathMan.isInteger(toParse)) { - BBC.COMMAND_SYNTAX.send(player, "/frb #"); - return; - } - int index = Integer.parseInt(toParse); - final World world = player.getWorld(); - UUID uuid = player.getUniqueId(); - DiskStorageHistory file = new DiskStorageHistory(world, uuid, index); - if (file.getBDFile().exists()) { - if (restore) file.redo(FawePlayer.wrap(player)); - else file.undo(FawePlayer.wrap(player)); - BBC.ROLLBACK_ELEMENT.send(player, world.getName() + "/" + user + "-" + index); - } else { - BBC.TOOL_INSPECT_INFO_FOOTER.send(player, 0); } + player.print("Done import!"); return; } + String toParse = user.substring(1); + if (!MathMan.isInteger(toParse)) { + BBC.COMMAND_SYNTAX.send(player, "/frb #"); + return; + } + int index = Integer.parseInt(toParse); + final World world = player.getWorld(); + UUID uuid = player.getUniqueId(); + DiskStorageHistory file = new DiskStorageHistory(world, uuid, index); + if (file.getBDFile().exists()) { + if (restore) file.redo(FawePlayer.wrap(player)); + else file.undo(FawePlayer.wrap(player)); + BBC.ROLLBACK_ELEMENT.send(player, world.getName() + "/" + user + "-" + index); + } else { + BBC.TOOL_INSPECT_INFO_FOOTER.send(player, 0); + } + return; } UUID other = Fawe.imp().getUUID(user); if (other == null) { @@ -168,15 +168,12 @@ public class HistoryCommands extends MethodCommands { Location origin = player.getLocation(); BlockVector3 bot = origin.toBlockPoint().subtract(radius, radius, radius); bot = bot.withY(Math.max(0, bot.getY())); -// bot.mutY(Math.max(0, bot.getY())); BlockVector3 top = origin.toBlockPoint().add(radius, radius, radius); bot = bot.withY(Math.min(255, top.getY())); -// top.mutY(Math.min(255, top.getY())); RollbackDatabase database = DBHandler.IMP.getDatabase(world); final AtomicInteger count = new AtomicInteger(); final FawePlayer fp = FawePlayer.wrap(player); - final FaweQueue finalQueue; Region[] allowedRegions = fp.getCurrentRegions(FaweMaskManager.MaskType.OWNER); if (allowedRegions == null) { BBC.NO_REGION.send(fp); @@ -200,13 +197,12 @@ public class HistoryCommands extends MethodCommands { } @Command( - aliases = {"/fawerestore", "/frestore"}, + name = "fawerestore", + alias = {"/fawerestore", "/frestore"}, usage = " ", desc = "Redo a specific edit. " + - " - The time uses s, m, h, d, y.\n" + - " - Import from disk: /frb #import", - min = 1, - max = 3 + " - The time uses s, m, h, d, y.\n" + + " - Import from disk: /frb #import" ) @CommandPermissions("worldedit.history.rollback") public void restore(final Player player, LocalSession session, final String user, @Optional("0") @Range(min = 0) int radius, @Optional("0") String time) throws WorldEditException { @@ -214,30 +210,35 @@ public class HistoryCommands extends MethodCommands { } @Command( - aliases = {"/undo", "undo"}, - usage = "[times] [player]", - desc = "Undoes the last action", - min = 0, - max = 2 + name = "undo", + aliases = { "/undo" }, + desc = "Undoes the last action (from history)" ) - @CommandPermissions("worldedit.history.undo") - public void undo(Player player, LocalSession session, CommandContext context) throws WorldEditException { + @CommandPermissions({"worldedit.history.undo", "worldedit.history.undo.self"}) + public void undo(Player player, LocalSession session, + @Arg(desc = "Number of undoes to perform", def = "1") + int times, + @Arg(name = "player", desc = "Undo this player's operations", def = "") + String playerName, + CommandContext context) throws WorldEditException { if (session.hasFastMode()) { BBC.COMMAND_UNDO_DISABLED.send(player); return; } - int times = Math.max(1, context.getInteger(0, 1)); + times = Math.max(1, times); + LocalSession undoSession = session; + int finalTimes = times; FawePlayer.wrap(player).checkConfirmation(() -> { + player.checkPermission("worldedit.history.undo.other"); EditSession undone = null; int i = 0; - for (; i < times; ++i) { + for (; i < finalTimes; ++i) { if (context.argsLength() < 2) { - undone = session.undo(session.getBlockBag(player), player); + undone = undoSession.undo(undoSession.getBlockBag(player), player); } else { - player.checkPermission("worldedit.history.undo.other"); - LocalSession sess = worldEdit.getSessionManager().findByName(context.getString(1)); + LocalSession sess = worldEdit.getSessionManager().findByName(playerName); if (sess == null) { - BBC.COMMAND_HISTORY_OTHER_ERROR.send(player, context.getString(1)); + BBC.COMMAND_HISTORY_OTHER_ERROR.send(player, playerName); break; } undone = sess.undo(session.getBlockBag(player), player); @@ -256,54 +257,52 @@ public class HistoryCommands extends MethodCommands { } @Command( - aliases = {"/redo", "redo"}, - usage = "[times] [player]", - desc = "Redoes the last action (from history)", - min = 0, - max = 2 + name = "redo", + aliases = { "/redo" }, + desc = "Redoes the last action (from history)" ) - @CommandPermissions("worldedit.history.redo") - public void redo(Player player, LocalSession session, CommandContext args) throws WorldEditException { - int times = Math.max(1, args.getInteger(0, 1)); - - EditSession redone = null; - int i = 0; - for (; i < times; ++i) { - if (args.argsLength() < 2) { - redone = session.redo(session.getBlockBag(player), player); - } else { - player.checkPermission("worldedit.history.redo.other"); - LocalSession sess = worldEdit.getSessionManager().findByName(args.getString(1)); - if (sess == null) { - BBC.COMMAND_HISTORY_OTHER_ERROR.send(player, args.getString(1)); - break; - } - redone = sess.redo(session.getBlockBag(player), player); - if (redone == null) break; + @CommandPermissions({"worldedit.history.redo", "worldedit.history.redo.self"}) + public void redo(Player player, LocalSession session, + @Arg(desc = "Number of redoes to perform", def = "1") + int times, + @Arg(name = "player", desc = "Redo this player's operations", def = "") + String playerName) throws WorldEditException { + times = Math.max(1, times); + LocalSession redoSession = session; + if (playerName != null) { + player.checkPermission("worldedit.history.redo.other"); + redoSession = worldEdit.getSessionManager().findByName(playerName); + if (redoSession == null) { + player.printError("Unable to find session for " + playerName); + return; } } - if (redone == null) i--; - if (i > 0) { - BBC.COMMAND_REDO_SUCCESS.send(player, i == 1 ? "" : " x" + i); - worldEdit.flushBlockBag(player, redone); + int timesRedone = 0; + for (int i = 0; i < times; ++i) { + EditSession redone = redoSession.redo(redoSession.getBlockBag(player), player); + if (redone != null) { + timesRedone++; + worldEdit.flushBlockBag(player, redone); + } else { + break; + } } - if (redone == null) { + if (timesRedone > 0) { + player.print("Redid " + timesRedone + " available edits."); + } else { BBC.COMMAND_REDO_ERROR.send(player); } } @Command( - aliases = {"/clearhistory", "clearhistory"}, - usage = "", - desc = "Clear your history", - min = 0, - max = 0 + name = "clearhistory", + aliases = { "/clearhistory" }, + desc = "Clear your history" ) @CommandPermissions("worldedit.history.clear") - public void clearHistory(Player player, LocalSession session) throws WorldEditException { + public void clearHistory(Player player, LocalSession session) { session.clearHistory(); BBC.COMMAND_HISTORY_CLEAR.send(player); } - } 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 84138e9e5..7775c69ac 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,7 +1,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.object.mask.*; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/MethodCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/MethodCommands.java index 389c2ec2c..97b2849ed 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/MethodCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/MethodCommands.java @@ -1,7 +1,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.Commands; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -97,4 +97,4 @@ public class MethodCommands { } return new String[0]; } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java index c00cd587b..8f36b0114 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java @@ -20,28 +20,26 @@ package com.sk89q.worldedit.command; import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; +import static com.sk89q.worldedit.command.util.Logging.LogMode.POSITION; -import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; -import com.boydti.fawe.util.MathMan; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalSession; 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.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.command.parametric.Optional; -import com.sk89q.worldedit.world.World; +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; /** * Commands for moving the player around. */ -@Command(aliases = {}, desc = "Commands for moving the player around: [More Info](https://goo.gl/uQTUiT)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class NavigationCommands { private final WorldEdit worldEdit; @@ -57,11 +55,9 @@ public class NavigationCommands { } @Command( - aliases = { "unstuck", "!" }, - usage = "", - desc = "Escape from being stuck inside a block", - min = 0, - max = 0 + name = "unstuck", + aliases = { "!" }, + desc = "Escape from being stuck inside a block" ) @CommandPermissions("worldedit.navigation.unstuck") public void unstuck(Player player) throws WorldEditException { @@ -70,18 +66,18 @@ public class NavigationCommands { } @Command( - aliases = {"ascend", "asc"}, - usage = "[# of levels]", - desc = "Go up a floor", - min = 0, - max = 1 + name = "ascend", + aliases = { "asc" }, + desc = "Go up a floor" ) @CommandPermissions("worldedit.navigation.ascend") - public void ascend(Player player, @Optional("1") int levelsToAscend) throws WorldEditException { + public void ascend(Player player, + @Arg(desc = "# of levels to ascend", def = "1") + int levels) throws WorldEditException { int ascentLevels = 0; while (player.ascendLevel()) { ++ascentLevels; - if (levelsToAscend == ascentLevels) { + if (levels == ascentLevels) { break; } } @@ -97,48 +93,46 @@ public class NavigationCommands { } @Command( - aliases = {"descend", "desc"}, - usage = "[# of floors]", - desc = "Go down a floor", - min = 0, - max = 1 + name = "descend", + aliases = { "desc" }, + desc = "Go down a floor" ) @CommandPermissions("worldedit.navigation.descend") - public void descend(Player player, @Optional("1") int levelsToDescend) throws WorldEditException { + public void descend(Player player, + @Arg(desc = "# of levels to descend", def = "1") + int levels) throws WorldEditException { int descentLevels = 0; while (player.descendLevel()) { ++descentLevels; - if (levelsToDescend == descentLevels) { + if (levels == descentLevels) { break; } } if (descentLevels == 0) { BBC.DESCEND_FAIL.send(player); + } else if (descentLevels == 1) { + BBC.DESCEND_SINGULAR.send(player); } else { - if (descentLevels == 1) { - BBC.DESCEND_SINGULAR.send(player); - } else { - BBC.DESCEND_PLURAL.send(player, descentLevels); - } + BBC.DESCEND_PLURAL.send(player, descentLevels); } } @Command( - aliases = {"ceil"}, - usage = "[clearance]", - desc = "Go to the celing", - flags = "fg", - min = 0, - max = 1 + name = "ceil", + desc = "Go to the ceiling" ) @CommandPermissions("worldedit.navigation.ceiling") @Logging(POSITION) - public void ceiling(Player player, CommandContext args) throws WorldEditException { + public void ceiling(Player player, + @Arg(desc = "# of blocks to leave above you", def = "0") + int clearance, + @Switch(name = 'f', desc = "Force using flight to keep you still") + boolean forceFlight, + @Switch(name = 'g', desc = "Force using glass to keep you still") + boolean forceGlass) throws WorldEditException { + clearance = Math.max(0, clearance); - final int clearance = args.argsLength() > 0 ? - Math.max(0, args.getInteger(0)) : 0; - - final boolean alwaysGlass = getAlwaysGlass(args); + boolean alwaysGlass = getAlwaysGlass(forceFlight, forceGlass); if (player.ascendToCeiling(clearance, alwaysGlass)) { BBC.WHOOSH.send(player); } else { @@ -147,11 +141,8 @@ public class NavigationCommands { } @Command( - aliases = {"thru"}, - usage = "", - desc = "Passthrough walls", - min = 0, - max = 0 + name = "thru", + desc = "Pass through walls" ) @CommandPermissions("worldedit.navigation.thru.command") public void thru(Player player) throws WorldEditException { @@ -163,33 +154,16 @@ public class NavigationCommands { } @Command( - aliases = {"jumpto", "j"}, - usage = "[world,x,y,z]", - desc = "Teleport to a location\n" + - "Flags:" + - " -f forces the specified position to be used", - flags = "f", - min = 0, - max = 1 + name = "jumpto", + aliases = { "j" }, + desc = "Teleport to a location" ) @CommandPermissions("worldedit.navigation.jumpto.command") - public void jumpTo(Player player, LocalSession session, CommandContext args) throws WorldEditException { - Location pos; - if (args.argsLength() == 1) { - String arg = args.getString(0); - String[] split = arg.split(","); - World world = FaweAPI.getWorld(split[0]); - if (world != null && split.length == 4 && MathMan.isInteger(split[1]) && MathMan.isInteger(split[2]) && MathMan.isInteger(split[3])) { - pos = new Location(world, Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3])); - } else { - BBC.SELECTOR_INVALID_COORDINATES.send(player, args.getString(0)); - return; - } - } else { - pos = player.getSolidBlockTrace(300); - } + public void jumpTo(Player player) throws WorldEditException { + + Location pos = player.getSolidBlockTrace(300); if (pos != null) { - if(args.hasFlag('f')) player.setPosition(pos); else player.findFreePosition(pos); + player.findFreePosition(pos); BBC.POOF.send(player); } else { BBC.NO_BLOCK.send(player); @@ -197,19 +171,19 @@ public class NavigationCommands { } @Command( - aliases = {"up"}, - usage = "", - desc = "Go upwards some distance", - flags = "fg", - min = 1, - max = 1 + name = "up", + desc = "Go upwards some distance" ) @CommandPermissions("worldedit.navigation.up") @Logging(POSITION) - public void up(Player player, CommandContext args) throws WorldEditException { - final int distance = args.getInteger(0); - - final boolean alwaysGlass = getAlwaysGlass(args); + public void up(Player player, + @Arg(desc = "Distance to go upwards") + int distance, + @Switch(name = 'f', desc = "Force using flight to keep you still") + boolean forceFlight, + @Switch(name = 'g', desc = "Force using glass to keep you still") + boolean forceGlass) throws WorldEditException { + boolean alwaysGlass = getAlwaysGlass(forceFlight, forceGlass); if (player.ascendUpwards(distance, alwaysGlass)) { BBC.WHOOSH.send(player); } else { @@ -220,17 +194,13 @@ public class NavigationCommands { /** * Helper function for /up and /ceil. * - * @param args The {@link CommandContext} to extract the flags from. + * @param forceFlight if flight should be used, rather than the default config option + * @param forceGlass if glass should always be placed, rather than the default config option * @return true, if glass should always be put under the player */ - private boolean getAlwaysGlass(CommandContext args) { + private boolean getAlwaysGlass(boolean forceFlight, boolean forceGlass) { final LocalConfiguration config = worldEdit.getConfiguration(); - final boolean forceFlight = args.hasFlag('f'); - final boolean forceGlass = args.hasFlag('g'); - return forceGlass || (config.navigationUseGlass && !forceFlight); } - - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/OptionsCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/OptionsCommands.java index 8572c984c..31dcdefb5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/OptionsCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/OptionsCommands.java @@ -12,7 +12,7 @@ import com.boydti.fawe.util.TextureUtil; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.Sets; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.EditSession; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java new file mode 100644 index 000000000..cc0689630 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java @@ -0,0 +1,150 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.command.factory.ItemUseFactory; +import com.sk89q.worldedit.command.factory.ReplaceFactory; +import com.sk89q.worldedit.command.factory.TreeGeneratorFactory; +import com.sk89q.worldedit.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.Contextual; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.factory.Paint; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; +import com.sk89q.worldedit.regions.factory.RegionFactory; +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.util.formatting.text.format.TextColor; +import com.sk89q.worldedit.util.formatting.text.format.TextDecoration; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.CommandManagerService; +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.inject.Key; +import org.enginehub.piston.part.CommandArgument; +import org.enginehub.piston.part.SubCommandPart; + +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; +import static org.enginehub.piston.part.CommandParts.arg; + +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class PaintBrushCommands { + + private static final CommandArgument REGION_FACTORY = arg(TranslatableComponent.of("shape"), TextComponent.of("The shape of the region")) + .defaultsTo(ImmutableList.of()) + .ofTypes(ImmutableList.of(Key.of(RegionFactory.class))) + .build(); + + private static final CommandArgument RADIUS = arg(TranslatableComponent.of("radius"), TextComponent.of("The size of the brush")) + .defaultsTo(ImmutableList.of("5")) + .ofTypes(ImmutableList.of(Key.of(double.class))) + .build(); + + private static final CommandArgument DENSITY = arg(TranslatableComponent.of("density"), TextComponent.of("The density of the brush")) + .defaultsTo(ImmutableList.of("20")) + .ofTypes(ImmutableList.of(Key.of(double.class))) + .build(); + + public static void register(CommandManagerService service, CommandManager commandManager, CommandRegistrationHandler registration) { + commandManager.register("paint", builder -> { + builder.description(TextComponent.of("Paint brush, apply a function to a surface")); + builder.action(org.enginehub.piston.Command.Action.NULL_ACTION); + + CommandManager manager = service.newCommandManager(); + registration.register( + manager, + PaintBrushCommandsRegistration.builder(), + new PaintBrushCommands() + ); + + builder.condition(new PermissionCondition(ImmutableSet.of("worldedit.brush.paint"))); + + builder.addParts(REGION_FACTORY, RADIUS, DENSITY); + builder.addPart(SubCommandPart.builder(TranslatableComponent.of("type"), TextComponent.of("Type of brush to use")) + .withCommands(manager.getAllCommands().collect(Collectors.toList())) + .required() + .build()); + }); + } + + private void setPaintBrush(CommandParameters parameters, Player player, LocalSession localSession, + Contextual generatorFactory) throws WorldEditException { + double radius = requireNonNull(RADIUS.value(parameters).asSingle(double.class)); + double density = requireNonNull(DENSITY.value(parameters).asSingle(double.class)) / 100; + RegionFactory regionFactory = REGION_FACTORY.value(parameters).asSingle(RegionFactory.class); + BrushCommands.setOperationBasedBrush(player, localSession, radius, + new Paint(generatorFactory, density), regionFactory, "worldedit.brush.paint"); + } + + @Command( + name = "forest", + desc = "Plant trees" + ) + public void forest(CommandParameters parameters, + Player player, LocalSession localSession, + @Arg(desc = "The type of tree to plant") + TreeGenerator.TreeType type) throws WorldEditException { + setPaintBrush(parameters, player, localSession, new TreeGeneratorFactory(type)); + } + + @Command( + name = "item", + desc = "Use an item" + ) + @CommandPermissions("worldedit.brush.item") + public void item(CommandParameters parameters, + Player player, LocalSession localSession, + @Arg(desc = "The type of item to use") + BaseItem item, + @Arg(desc = "The direction in which the item will be applied", def = "up") + @Direction(includeDiagonals = true) + com.sk89q.worldedit.util.Direction direction) throws WorldEditException { + player.print(TextComponent.builder().append("WARNING: ", TextColor.RED, TextDecoration.BOLD) + .append("This brush simulates item usages. Its effects may not work on all platforms, may not be undo-able," + + " and may cause strange interactions with other mods/plugins. Use at your own risk.").build()); + setPaintBrush(parameters, player, localSession, new ItemUseFactory(item, direction)); + } + + @Command( + name = "set", + desc = "Place a block" + ) + public void set(CommandParameters parameters, + Player player, LocalSession localSession, + @Arg(desc = "The pattern of blocks to use") + Pattern pattern) throws WorldEditException { + setPaintBrush(parameters, player, localSession, new ReplaceFactory(pattern)); + } + +} 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 4d222a1d5..a90b518fa 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 @@ -8,7 +8,7 @@ import com.boydti.fawe.object.pattern.*; import com.boydti.fawe.object.random.SimplexRandom; import com.boydti.fawe.util.ColorUtil; import com.boydti.fawe.util.TextureUtil; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.worldedit.*; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.InputParseException; @@ -47,7 +47,7 @@ public class PatternCommands extends MethodCommands { public PatternCommands(WorldEdit worldEdit) { super(worldEdit); } - + @Command( aliases = {"#existing", "#*", "*", ".*"}, desc = "Use the block that is already there", 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 bd96cd32d..e2a86d391 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -19,6 +19,16 @@ package com.sk89q.worldedit.command; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.ALL; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import static com.sk89q.worldedit.command.util.Logging.LogMode.ORIENTATION_REGION; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; +import static com.sk89q.worldedit.internal.command.CommandUtil.checkCommandArgument; +import static com.sk89q.worldedit.regions.Regions.asFlatRegion; +import static com.sk89q.worldedit.regions.Regions.maximumBlockY; +import static com.sk89q.worldedit.regions.Regions.minimumBlockY; + import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; import com.boydti.fawe.example.NMSMappedFaweQueue; @@ -28,20 +38,14 @@ import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.visitor.Fast2DIterator; import com.boydti.fawe.util.MathMan; - -import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.ALL; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.ORIENTATION_REGION; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; 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.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.function.GroundFunction; @@ -67,31 +71,27 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.Regions; -import static com.sk89q.worldedit.regions.Regions.asFlatRegion; -import static com.sk89q.worldedit.regions.Regions.maximumBlockY; -import static com.sk89q.worldedit.regions.Regions.minimumBlockY; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator.TreeType; -import com.sk89q.worldedit.util.command.binding.Range; -import com.sk89q.worldedit.util.command.binding.Switch; -import com.sk89q.worldedit.util.command.binding.Text; -import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.Biomes; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.registry.BiomeRegistry; - import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +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; /** * Commands that operate on regions. */ -@Command(aliases = {}, desc = "Commands that operate on regions: [More Info](http://wiki.sk89q.com/wiki/WorldEdit/Region_operations)") -public class RegionCommands extends MethodCommands { +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class RegionCommands { private final WorldEdit worldEdit; @@ -101,16 +101,13 @@ public class RegionCommands extends MethodCommands { * @param worldEdit reference to WorldEdit */ public RegionCommands(WorldEdit worldEdit) { - super(worldEdit); checkNotNull(worldEdit); this.worldEdit = worldEdit; } @Command( - aliases = {"/fixlighting"}, - desc = "Get the light at a position", - min = 0, - max = 0 + name = "/fixlighting", + desc = "Get the light at a position" ) @CommandPermissions("worldedit.light.fix") public void fixlighting(Player player) throws WorldEditException { @@ -127,10 +124,8 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = {"/getlighting"}, - desc = "Get the light at a position", - min = 0, - max = 0 + name = "/getlighting", + desc = "Get the light at a position" ) @CommandPermissions("worldedit.light.fix") public void getlighting(Player player) throws WorldEditException { @@ -141,10 +136,8 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = {"/removelight", "/removelighting"}, - desc = "Removing lighting in a selection", - min = 0, - max = 0 + name = "/removelighting", + desc = "Removing lighting in a selection" ) @CommandPermissions("worldedit.light.remove") public void removelighting(Player player) { @@ -160,7 +153,7 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = {"/nbtinfo", "/nbt"}, + name = "/nbtinfo", desc = "View nbt info for a block" ) @CommandPermissions("worldedit.nbtinfo") @@ -179,10 +172,8 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = {"/setblocklight", "/setlight"}, - desc = "Set block lighting in a selection", - min = 1, - max = 1 + name = "/setblocklight", + desc = "Set block lighting in a selection" ) @CommandPermissions("worldedit.light.set") public void setlighting(Player player, @Selection Region region, @Range(min = 0, max = 15) int value) { @@ -200,10 +191,8 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = {"/setskylight"}, - desc = "Set sky lighting in a selection", - min = 1, - max = 1 + name = "/setskylight" + desc = "Set sky lighting in a selection" ) @CommandPermissions("worldedit.light.set") public void setskylighting(Player player, @Selection Region region, @Range(min = 0, max = 15) int value) { @@ -221,30 +210,25 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/line" }, - usage = " [thickness]", - desc = "Draws a line segment between cuboid selection corners", - help = - "Draws a line segment between cuboid selection corners.\n" + - "Can only be used with cuboid selections.\n" + - "Flags:\n" + - " -h generates only a shell", - flags = "h", - min = 1, - max = 2 + name = "/line", + desc = "Draws a line segment between cuboid selection corners", + descFooter = "Can only be used with a cuboid selection" ) @CommandPermissions("worldedit.region.line") @Logging(REGION) - public void line(Player player, EditSession editSession, - @Selection Region region, - Pattern pattern, - @Optional("0") @Range(min = 0) int thickness, - @Switch('h') boolean shell) throws WorldEditException { - + public int line(Player player, EditSession editSession, + @Selection Region region, + @Arg(desc = "The pattern of blocks to place") + Pattern pattern, + @Arg(desc = "The thickness of the line", def = "0") + int thickness, + @Switch(name = 'h', desc = "Generate only a shell") + boolean shell) throws WorldEditException { if (!(region instanceof CuboidRegion)) { player.printError("//line only works with cuboid selections"); - return; + return 0; } + checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); CuboidRegion cuboidregion = (CuboidRegion) region; BlockVector3 pos1 = cuboidregion.getPos1(); @@ -252,34 +236,30 @@ public class RegionCommands extends MethodCommands { int blocksChanged = editSession.drawLine(pattern, pos1, pos2, thickness, !shell); BBC.VISITOR_BLOCK.send(player, blocksChanged); + return blocksChanged; } @Command( - aliases = {"/curve", "/spline"}, - usage = " [thickness]", - desc = "Draws a spline through selected points", - help = - "Draws a spline through selected points.\n" + - "Can only be used with convex polyhedral selections.\n" + - "Flags:\n" + - " -h generates only a shell", - flags = "h", - min = 1, - max = 2 + name = "/curve", + desc = "Draws a spline through selected points", + descFooter = "Can only be used with a convex polyhedral selection" ) @CommandPermissions("worldedit.region.curve") @Logging(REGION) public void curve(FawePlayer player, EditSession editSession, @Selection Region region, - Pattern pattern, - @Optional("0") @Range(min = 0) int thickness, - @Switch('h') boolean shell, + @Arg(desc = "The pattern of blocks to place") + Pattern pattern, + @Arg(desc = "The thickness of the curve", def = "0") + int thickness, + @Switch(name = 'h', desc = "Generate only a shell") + boolean shell, CommandContext context) throws WorldEditException { if (!(region instanceof ConvexPolyhedralRegion)) { - player.sendMessage("//curve only works with convex polyhedral selections"); + player.printError("//curve only works with convex polyhedral selections"); return; } - worldEdit.checkMaxRadius(thickness); + checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); player.checkConfirmationRegion(() -> { ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region; @@ -292,12 +272,9 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/replace", "/re", "/rep" }, - usage = "[from-mask] ", - desc = "Replace all blocks in the selection with another", - flags = "f", - min = 1, - max = 2 + name = "/replace", + aliases = { "/re", "/rep" }, + desc = "Replace all blocks in the selection with another" ) @CommandPermissions("worldedit.region.replace") @Logging(REGION) @@ -337,15 +314,14 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/overlay" }, - usage = "", - desc = "Set a block on top of blocks in the region", - min = 1, - max = 1 + name = "/overlay", + desc = "Set a block on top of blocks in the region" ) @CommandPermissions("worldedit.region.overlay") @Logging(REGION) - public void overlay(FawePlayer player, EditSession editSession, @Selection Region region, Pattern pattern, CommandContext context) throws WorldEditException { + public void overlay(FawePlayer player, EditSession editSession, @Selection Region region, + @Arg(desc = "The pattern of blocks to overlay") + Pattern pattern, CommandContext context) throws WorldEditException { player.checkConfirmationRegion(() -> { int affected = editSession.overlayCuboidBlocks(region, pattern); BBC.VISITOR_BLOCK.send(player, affected); @@ -363,13 +339,8 @@ public class RegionCommands extends MethodCommands { @Logging(REGION) public void lay(FawePlayer player, EditSession editSession, @Selection Region region, Pattern pattern, CommandContext context) throws WorldEditException { player.checkConfirmationRegion(() -> { - BlockVector3 min = region.getMinimumPoint(); - BlockVector3 max = region.getMaximumPoint(); + BlockVector3 max = region.getMaximumPoint(); int maxY = max.getBlockY(); - int width = region.getWidth(); - int height = region.getLength(); - int bx = min.getBlockX(); - int bz = min.getBlockZ(); Iterable flat = Regions.asFlatRegion(region).asFlatRegion(); Iterator iter = new Fast2DIterator(flat, editSession).iterator(); int y = 0; @@ -386,12 +357,10 @@ public class RegionCommands extends MethodCommands { }, getArguments(context), region, context); } - @Command( - aliases = { "/center", "/middle" }, - usage = "", - desc = "Set the center block(s)", - min = 1, - max = 1 + @org.enginehub.piston.annotation.Command( + name = "/center", + aliases = { "/middle" }, + desc = "Set the center block(s)" ) @Logging(REGION) @CommandPermissions("worldedit.region.center") @@ -401,11 +370,8 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/naturalize" }, - usage = "", - desc = "3 layers of dirt on top then rock below", - min = 0, - max = 0 + name = "/naturalize", + desc = "3 layers of dirt on top then rock below" ) @CommandPermissions("worldedit.region.naturalize") @Logging(REGION) @@ -417,11 +383,8 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/walls" }, - usage = "", - desc = "Build the four sides of the selection", - min = 1, - max = 1 + name = "/walls", + desc = "Build the four sides of the selection" ) @CommandPermissions("worldedit.region.walls") @Logging(REGION) @@ -433,11 +396,9 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/faces", "/outline" }, - usage = "", - desc = "Build the walls, ceiling, and floor of a selection", - min = 1, - max = 1 + name = "/faces", + aliases = { "/outline" }, + desc = "Build the walls, ceiling, and floor of a selection" ) @CommandPermissions("worldedit.region.faces") @Logging(REGION) @@ -449,15 +410,9 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/smooth" }, - usage = "[iterations] [filter]", + name = "/smooth", desc = "Smooth the elevation in the selection", - help = - "Smooths the elevation in the selection.\n" + - "Optionally, restricts the height map to a set of blocks specified with mask syntax.\n" + - "For example, '//smooth 1 grass_block,dirt,stone' would only smooth natural surface terrain.", - min = 0, - max = 2 + descFooter = "Example: '//smooth 1 grass_block,dirt,stone' would only smooth natural surface terrain." ) @CommandPermissions("worldedit.region.smooth") @Logging(REGION) @@ -524,20 +479,27 @@ public class RegionCommands extends MethodCommands { "The -e ignores entities\n" + "The -a ignores air blocks.\n" + "Optionally fills the old location with .", - min = 0, max = 3 ) @CommandPermissions("worldedit.region.move") @Logging(ORIENTATION_REGION) public void move(FawePlayer player, LocalSession session, EditSession editSession, @Selection Region region, - @Optional("1") @Range(min = 1) int count, - @Optional(Direction.AIM) @Direction(includeDiagonals = true) BlockVector3 direction, - @Optional("air") Pattern replace, - @Switch('b') boolean copyBiomes, + @Arg(desc = "# of blocks to move", def = "1") + int count, + @Arg(desc = "The direction to move", def = Direction.AIM) + @Direction(includeDiagonals = true) + BlockVector3 direction, + @Arg(desc = "The pattern of blocks to leave", def = "air") + Pattern replace, + @Switch(name = 's', desc = "Shift the selection to the target location") + boolean moveSelection, + @Switch(name = 'a', desc = "Ignore air blocks") + boolean ignoreAirBlocks + @Switch('b') boolean copyBiomes, + @Switch('e') boolean skipEntities, @Switch('a') boolean skipAir, - @Switch('s') boolean moveSelection, CommandContext context) throws WorldEditException { player.checkConfirmationRegion(() -> { int affected = editSession.moveRegion(region, direction, count, !skipAir, !skipEntities, copyBiomes, replace); @@ -549,7 +511,7 @@ public class RegionCommands extends MethodCommands { session.getRegionSelector(player.getWorld()).learnChanges(); session.getRegionSelector(player.getWorld()).explainRegionAdjust(player.getPlayer(), session); } catch (RegionOperationException e) { - player.sendMessage(e.getMessage()); + player.printError(e.getMessage()); } } @@ -562,10 +524,8 @@ public class RegionCommands extends MethodCommands { usage = "[replace]", flags = "m", desc = "Have the blocks in the selection fall", - help = - "Make the blocks in the selection fall\n" + - "The -m flag will only fall within the vertical selection.", - min = 0, + help = "Make the blocks in the selection fall\n" + + "The -m flag will only fall within the vertical selection.", max = 2 ) @CommandPermissions("worldedit.region.fall") @@ -582,28 +542,24 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/stack" }, - usage = "[count] [direction]", - flags = "sam", - desc = "Repeat the contents of the selection", - help = - "Repeats the contents of the selection.\n" + - "Flags:\n" + - " -s shifts the selection to the last stacked copy\n" + - " -a skips air blocks", - min = 0, - max = 2 + name = "/stack", + desc = "Repeat the contents of the selection" ) @CommandPermissions("worldedit.region.stack") @Logging(ORIENTATION_REGION) public void stack(FawePlayer player, EditSession editSession, LocalSession session, @Selection Region region, - @Optional("1") @Range(min = 1) int count, - @Optional(Direction.AIM) @Direction(includeDiagonals = true) BlockVector3 direction, - @Switch('s') boolean moveSelection, - @Switch('b') boolean copyBiomes, - @Switch('e') boolean skipEntities, - @Switch('a') boolean ignoreAirBlocks, @Switch('m') Mask sourceMask, CommandContext context) throws WorldEditException { + @Arg(desc = "# of copies to stack", def = "1") + int count, + @Arg(desc = "The direction to stack", def = Direction.AIM) + @Direction(includeDiagonals = true) + BlockVector3 direction, + @Switch(name = 's', desc = "Shift the selection to the last stacked copy") + boolean moveSelection, + @Switch(name = 'b', desc = "//TODO") boolean copyBiomes, + @Switch(name = 'e', desc = "//TODO") boolean skipEntities, + @Switch(name = 'a', desc = "Ignore air blocks") + boolean ignoreAirBlocks, @Switch('m') Mask sourceMask, CommandContext context) throws WorldEditException { player.checkConfirmationStack(() -> { if (sourceMask != null) { editSession.addSourceMask(sourceMask); @@ -637,17 +593,18 @@ public class RegionCommands extends MethodCommands { "to modify the variables x, y and z to point to a new block\n" + "to fetch. See also tinyurl.com/wesyntax.", flags = "ro", - min = 1, - max = -1 + min = 1 ) @CommandPermissions("worldedit.region.deform") @Logging(ALL) public void deform(FawePlayer fp, Player player, LocalSession session, EditSession editSession, @Selection Region region, - @Text String expression, - @Switch('r') boolean useRawCoords, - @Switch('o') boolean offset, - CommandContext context) throws WorldEditException { + @Arg(desc = "The expression to use", variable = true) + List expression, + @Switch(name = 'r', desc = "Use the game's coordinate origin") + boolean useRawCoords, + @Switch(name = 'o', desc = "Use the selection's center as origin") + boolean offset) throws WorldEditException { final Vector3 zero; Vector3 unit; @@ -676,7 +633,7 @@ public class RegionCommands extends MethodCommands { player.findFreePosition(); BBC.VISITOR_BLOCK.send(fp, affected); } catch (ExpressionException e) { - fp.sendMessage(e.getMessage()); + player.printError(e.getMessage()); } }, getArguments(context), region, context); } @@ -689,7 +646,6 @@ public class RegionCommands extends MethodCommands { "Regenerates the contents of the current selection.\n" + "This command might affect things outside the selection,\n" + "if they are within the same chunk.", - min = 0, max = 2 ) @CommandPermissions("worldedit.regen") @@ -697,7 +653,6 @@ public class RegionCommands extends MethodCommands { public void regenerateChunk(FawePlayer player, LocalSession session, EditSession editSession, @Selection Region region, CommandContext context) throws WorldEditException { player.checkConfirmationRegion(() -> { Mask mask = session.getMask(); - Mask sourceMask = session.getSourceMask(); session.setMask((Mask) null); session.setSourceMask((Mask) null); BiomeType biome = null; @@ -730,19 +685,19 @@ public class RegionCommands extends MethodCommands { aliases = { "/hollow" }, usage = "[[ ]]", desc = "Hollows out the object contained in this selection", - help = - "Hollows out the object contained in this selection.\n" + + help = "Hollows out the object contained in this selection.\n" + "Optionally fills the hollowed out part with the given block.\n" + "Thickness is measured in manhattan distance.", - min = 0, max = 2 ) @CommandPermissions("worldedit.region.hollow") @Logging(REGION) public void hollow(FawePlayer player, EditSession editSession, @Selection Region region, - @Optional("0") @Range(min = 0) int thickness, - @Optional("air") Pattern pattern, + @Arg(desc = "Thickness of the shell to leave", def = "0") + int thickness, + @Arg(desc = "The pattern of blocks to replace the hollowed area with", def = "air") + Pattern pattern, CommandContext context) throws WorldEditException { player.checkConfirmationRegion(() -> { int affected = editSession.hollowOutRegion(region, thickness, pattern); @@ -751,30 +706,29 @@ public class RegionCommands extends MethodCommands { } @Command( - aliases = { "/forest" }, - usage = "[type] [density]", - desc = "Make a forest within the region", - min = 0, - max = 2 + name = "/forest", + desc = "Make a forest within the region" ) @CommandPermissions("worldedit.region.forest") @Logging(REGION) - public void forest(Player player, EditSession editSession, @Selection Region region, @Optional("tree") TreeType type, - @Optional("5") @Range(min = 0, max = 100) double density) throws WorldEditException { + public void forest(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The type of tree to place", def = "tree") + TreeType type, + @Arg(desc = "The density of the forest", def = "5") + double density) throws WorldEditException { int affected = editSession.makeForest(region, density / 100, type); BBC.COMMAND_TREE.send(player, affected); } @Command( - aliases = { "/flora" }, - usage = "[density]", - desc = "Make flora within the region", - min = 0, - max = 1 + name = "/flora", + desc = "Make flora within the region" ) @CommandPermissions("worldedit.region.flora") @Logging(REGION) - public void flora(FawePlayer player, EditSession editSession, @Selection Region region, @Optional("10") @Range(min = 0, max = 100) double density, CommandContext context) throws WorldEditException { + public void flora(FawePlayer player, EditSession editSession, @Selection Region region, + @Arg(desc = "The density of the forest", def = "5") + double density, CommandContext context) throws WorldEditException { player.checkConfirmationRegion(() -> { FloraGenerator generator = new FloraGenerator(editSession); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 13f766d03..9be934618 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.command; +import static com.boydti.fawe.util.ReflectionUtils.as; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Commands; import com.boydti.fawe.config.Settings; @@ -28,79 +31,101 @@ import com.boydti.fawe.object.clipboard.MultiClipboardHolder; import com.boydti.fawe.object.clipboard.URIClipboardHolder; import com.boydti.fawe.object.clipboard.remap.ClipboardRemapper; import com.boydti.fawe.object.schematic.StructureFormat; -//import com.boydti.fawe.object.schematic.visualizer.SchemVis; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.chat.Message; -import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; 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.entity.Player; import com.sk89q.worldedit.event.extent.PlayerSaveClipboardEvent; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.util.command.binding.Switch; -import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.util.formatting.component.ErrorFormat; +import com.sk89q.worldedit.util.formatting.component.PaginationBox; +import com.sk89q.worldedit.util.formatting.component.SchematicPaginationBox; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.io.Closer; import com.sk89q.worldedit.util.io.file.FilenameException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.file.Files; -import java.util.*; -import java.util.concurrent.atomic.LongAdder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Callable; import java.util.regex.Pattern; +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.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; +import org.enginehub.piston.exception.StopExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import static com.boydti.fawe.util.ReflectionUtils.as; +//import com.boydti.fawe.object.schematic.visualizer.SchemVis; /** * Commands that work with schematic files. */ -@Command(aliases = {"schematic", "schem", "/schematic", "/schem", "clipboard", "/clipboard"}, desc = "Commands that work with schematic files") -public class SchematicCommands extends MethodCommands { +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class SchematicCommands { - /** - * 9 schematics per page fits in the MC chat window. - */ - private static final int SCHEMATICS_PER_PAGE = 9; private static final Logger log = LoggerFactory.getLogger(SchematicCommands.class); + private final WorldEdit worldEdit; /** * Create a new instance. * * @param worldEdit reference to WorldEdit */ - public SchematicCommands(final WorldEdit worldEdit) { - super(worldEdit); + public SchematicCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( - aliases = {"loadall"}, - usage = "[] ", - help = "Load multiple clipboards\n" + - "The -r flag will apply random rotation", - desc = "Load multiple clipboards (paste will randomly choose one)" + name = "loadall", + desc = "Load multiple clipboards (paste will randomly choose one)", + descFooter = "Load multiple clipboards (paste will randomly choose one)\n" + + "The -r flag will apply random rotation" ) @Deprecated @CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.load.web", "worldedit.schematic.load.asset"}) - public void loadall(final Player player, final LocalSession session, @Optional("schematic") final String formatName, final String filename, @Switch('r') boolean randomRotate) throws FilenameException { + public void loadall(final Player player, final LocalSession session, + @Arg(desc = "Format name.", def = "schematic") + final String formatName, + @Arg(desc = "File name.") + final String filename, + @Switch(name = 'r', desc = "Apply random rotation") + boolean randomRotate) throws FilenameException { final ClipboardFormat format = ClipboardFormats.findByAlias(formatName); if (format == null) { BBC.CLIPBOARD_INVALID_FORMAT.send(player, formatName); @@ -118,9 +143,8 @@ public class SchematicCommands extends MethodCommands { } @Command( - aliases = {"clear"}, - desc = "Clear your clipboard", - max = 0 + name = "clear", + desc = "Clear your clipboard" ) @CommandPermissions({"worldedit.clipboard.clear", "worldedit.schematic.clear"}) public void clear(Player player, LocalSession session) throws WorldEditException { @@ -129,11 +153,8 @@ public class SchematicCommands extends MethodCommands { } @Command( - aliases = {"unload"}, - usage = "[file]", - desc = "Remove a clipboard from your multi-clipboard", - min = 1, - max = 1 + name = "unload", + desc = "Remove a clipboard from your multi-clipboard" ) @CommandPermissions({"worldedit.clipboard.clear", "worldedit.schematic.clear"}) public void unload(Player player, LocalSession session, String fileName) throws WorldEditException { @@ -166,19 +187,13 @@ public class SchematicCommands extends MethodCommands { } @Command( - aliases = {"remap"}, - help = "Remap a clipboard between MCPE/PC values", + name = "remap", desc = "Remap a clipboard between MCPE/PC values" ) @Deprecated @CommandPermissions({"worldedit.schematic.remap"}) public void remap(final Player player, final LocalSession session) throws WorldEditException { - ClipboardRemapper remapper; - if (false) { - remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE); - } else { - remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PE, ClipboardRemapper.RemapPlatform.PC); - } + ClipboardRemapper remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PE, ClipboardRemapper.RemapPlatform.PC); for (Clipboard clip : session.getClipboard().getClipboards()) { remapper.apply(clip); @@ -187,13 +202,15 @@ public class SchematicCommands extends MethodCommands { } @Command( - aliases = {"load"}, - usage = "[] ", - desc = "Load a schematic into your clipboard" + name = "load", + desc = "Load a schematic into your clipboard" ) - @Deprecated @CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.load.asset", "worldedit.schematic.load.web", "worldedit.schematic.load.other"}) - public void load(Player player, LocalSession session, @Optional() String formatName, String filename) throws FilenameException { + public void load(Player player, LocalSession session, + @Arg(desc = "File name.") + String filename, + @Arg(desc = "Format name.", def = "sponge") + String formatName) throws FilenameException { LocalConfiguration config = worldEdit.getConfiguration(); ClipboardFormat format = formatName == null ? null : ClipboardFormats.findByAlias(formatName); InputStream in = null; @@ -236,7 +253,7 @@ public class SchematicCommands extends MethodCommands { return; } if (format == null && filename.matches(".*\\.[\\w].*")) { - String extension = filename.substring(filename.lastIndexOf('.') + 1, filename.length()); + String extension = filename.substring(filename.lastIndexOf('.') + 1); format = ClipboardFormats.findByExtension(extension); } f = MainUtil.resolve(dir, filename, format, false); @@ -248,7 +265,7 @@ public class SchematicCommands extends MethodCommands { } } if (f == null || !f.exists() || !MainUtil.isInSubDirectory(working, f)) { - player.printError("Schematic " + filename + " does not exist! (" + ((f != null) && f.exists()) + "|" + f + "|" + (f != null && !MainUtil.isInSubDirectory(working, f)) + ")"); + player.printError("Schematic " + filename + " does not exist! (" + (f != null && f.exists()) + "|" + f + "|" + (f != null && !MainUtil.isInSubDirectory(working, f)) + ")"); return; } if (format == null) { @@ -279,33 +296,58 @@ public class SchematicCommands extends MethodCommands { } @Command( - aliases = {"save"}, - usage = "[format] ", - desc = "Save a schematic into your clipboard", - help = "The default format for 1.13 is schem" + name = "save", + desc = "Save a schematic into your clipboard" ) - @Deprecated @CommandPermissions({"worldedit.clipboard.save", "worldedit.schematic.save", "worldedit.schematic.save.other"}) - public void save(Player player, LocalSession session, @Optional("schem") String formatName, String filename, @Switch('g') boolean global, @Switch('f') boolean allowOverwrite) throws CommandException, WorldEditException { + public void save(Player player, LocalSession session, + @Arg(desc = "File name.") + String filename, + @Arg(desc = "Format name.", def = "sponge") + String formatName, + @Switch(name = 'f', desc = "Overwrite an existing file.") + boolean allowOverwrite, + @Switch(name = 'g', desc = "//TODO") + boolean global + ) throws WorldEditException { LocalConfiguration config = worldEdit.getConfiguration(); + + File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); + + if (!global && Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS) { + dir = new File(dir, player.getUniqueId().toString()); + } + ClipboardFormat format = ClipboardFormats.findByAlias(formatName); if (format == null) { player.printError("Unknown schematic format: " + formatName); return; } - File working = worldEdit.getWorkingDirectoryFile(config.saveDir); - File dir = !global && Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working; + if (filename.contains("../")) { if (!player.hasPermission("worldedit.schematic.save.other")) { BBC.NO_PERM.send(player, "worldedit.schematic.save.other"); return; } if (filename.startsWith("../")) { - dir = working; + dir = worldEdit.getWorkingDirectoryFile(config.saveDir); filename = filename.substring(3); } } - File f = worldEdit.getSafeSaveFile(player, dir, filename, format.getPrimaryFileExtension(), format.getPrimaryFileExtension()); + + File f = worldEdit.getSafeSaveFile(player, dir, filename, format.getPrimaryFileExtension()); + + boolean overwrite = f.exists(); + if (overwrite) { + if (!player.hasPermission("worldedit.schematic.delete")) { + throw new StopExecutionException(TextComponent.of("That schematic already exists!")); + } + if (!allowOverwrite) { + player.printError("That schematic already exists. Use the -f flag to overwrite it."); + return; + } + } + if (f.getName().replaceAll("." + format.getPrimaryFileExtension(), "").isEmpty()) { File directory = f.getParentFile(); if (directory.exists()) { @@ -315,16 +357,13 @@ public class SchematicCommands extends MethodCommands { f = new File(directory, "1." + format.getPrimaryFileExtension()); } } - final File parent = f.getParentFile(); - if ((parent != null) && !parent.exists()) { + + // Create parent directories + File parent = f.getParentFile(); + if (parent != null && !parent.exists()) { if (!parent.mkdirs()) { - try { - Files.createDirectories(parent.toPath()); - } catch (IOException e) { - e.printStackTrace(); - log.info("Could not create folder for schematics!"); - return; - } + throw new StopExecutionException(TextComponent.of( + "Could not create folder for schematics!")); } } try { @@ -333,8 +372,10 @@ public class SchematicCommands extends MethodCommands { } else if (!allowOverwrite) { BBC.SCHEMATIC_MOVE_EXISTS.send(player, f.getName()); } + + ClipboardHolder holder = session.getClipboard(); + try (FileOutputStream fos = new FileOutputStream(f)) { - ClipboardHolder holder = session.getClipboard(); Clipboard clipboard = holder.getClipboard(); Transform transform = holder.getTransform(); Clipboard target; @@ -350,7 +391,9 @@ public class SchematicCommands extends MethodCommands { } URI uri = null; - if (holder instanceof URIClipboardHolder) uri = ((URIClipboardHolder) holder).getURI(clipboard); + if (holder instanceof URIClipboardHolder) { + uri = ((URIClipboardHolder) holder).getURI(clipboard); + } if (new PlayerSaveClipboardEvent(player, clipboard, uri, f.toURI()).call()) { try (ClipboardWriter writer = format.getWriter(fos)) { if (writer instanceof StructureFormat) { @@ -376,12 +419,9 @@ public class SchematicCommands extends MethodCommands { } @Command( - aliases = {"move", "m"}, - usage = "", - desc = "Move your loaded schematic", - help = "Move your currently loaded schematics", - min = 1, - max = 1 + name = "move", + aliases = {"m"}, + desc = "Move your loaded schematic" ) @CommandPermissions({"worldedit.schematic.move", "worldedit.schematic.move.other"}) public void move(Player player, LocalSession session, String directory) throws WorldEditException, IOException { @@ -429,55 +469,53 @@ public class SchematicCommands extends MethodCommands { } @Command( - aliases = {"delete", "d"}, - usage = "", - desc = "Delete a saved schematic", - help = "Delete a schematic from the schematic list", - min = 1, - max = 1 + name = "delete", + aliases = {"d"}, + desc = "Delete a saved schematic" ) @CommandPermissions({"worldedit.schematic.delete", "worldedit.schematic.delete.other"}) - public void delete(final Player player, final LocalSession session, final CommandContext args) throws WorldEditException, IOException { - final LocalConfiguration config = this.worldEdit.getConfiguration(); - final File working = this.worldEdit.getWorkingDirectoryFile(config.saveDir); - final File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working; + public void delete(Actor actor, LocalSession session, + @Arg(desc = "File name.") + String filename) throws WorldEditException, IOException { + LocalConfiguration config = worldEdit.getConfiguration(); + File working = worldEdit.getWorkingDirectoryFile(config.saveDir); + File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, actor.getUniqueId().toString()) : working; List files = new ArrayList<>(); - final String filename = args.getString(0); - if (filename.equalsIgnoreCase("*")) { files.addAll(getFiles(session.getClipboard())); } else { File f = MainUtil.resolveRelative(new File(dir, filename)); files.add(f); } + if (files.isEmpty()) { - BBC.SCHEMATIC_NONE.send(player); + BBC.SCHEMATIC_NONE.send(actor); return; } for (File f : files) { if (!MainUtil.isInSubDirectory(working, f) || !f.exists()) { - player.printError("Schematic " + filename + " does not exist! (" + f.exists() + "|" + f + "|" + (!MainUtil.isInSubDirectory(working, f)) + ")"); + actor.printError("Schematic " + filename + " does not exist! (" + f.exists() + "|" + f + "|" + (!MainUtil.isInSubDirectory(working, f)) + ")"); continue; } - if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && !MainUtil.isInSubDirectory(dir, f) && !player.hasPermission("worldedit.schematic.delete.other")) { - BBC.NO_PERM.send(player, "worldedit.schematic.delete.other"); + if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && !MainUtil.isInSubDirectory(dir, f) && !actor.hasPermission("worldedit.schematic.delete.other")) { + BBC.NO_PERM.send(actor, "worldedit.schematic.delete.other"); continue; } - if (!delete(f)) { - player.printError("Deletion of " + filename + " failed! Maybe it is read-only."); + if (!f.delete()) { + actor.printError("Deletion of " + filename + " failed! Maybe it is read-only."); continue; } - BBC.FILE_DELETED.send(player, filename); + BBC.FILE_DELETED.send(actor, filename); } } private List getFiles(ClipboardHolder clipboard) { - List files = new ArrayList<>(); Collection uris = Collections.emptyList(); if (clipboard instanceof URIClipboardHolder) { uris = ((URIClipboardHolder) clipboard).getURIs(); } + List files = new ArrayList<>(); for (URI uri : uris) { File file = new File(uri); if (file.exists()) files.add(file); @@ -485,40 +523,29 @@ public class SchematicCommands extends MethodCommands { return files; } - private boolean delete(File file) { - if (file.delete()) { - new File(file.getParentFile(), "." + file.getName() + ".cached").delete(); - return true; - } - return false; - } - @Command( - aliases = {"formats", "listformats", "f"}, - desc = "List available formats", - max = 0 + name = "formats", + aliases = {"listformats", "f"}, + desc = "List available formats" ) @CommandPermissions("worldedit.schematic.formats") - public void formats(final Actor actor) throws WorldEditException { - BBC.SCHEMATIC_FORMAT.send(actor); - Message m = new Message(BBC.SCHEMATIC_FORMAT).newline(); - String baseCmd = Commands.getAlias(SchematicCommands.class, "schematic") + " " + Commands.getAlias(SchematicCommands.class, "save"); + public void formats(Actor actor) { + actor.print("Available clipboard formats (Name: Lookup names)"); + StringBuilder builder; boolean first = true; - for (final ClipboardFormat format : ClipboardFormats.getAll()) { - StringBuilder builder = new StringBuilder(); + for (ClipboardFormat format : ClipboardFormats.getAll()) { + builder = new StringBuilder(); builder.append(format.getName()).append(": "); - for (final String lookupName : format.getAliases()) { + for (String lookupName : format.getAliases()) { if (!first) { builder.append(", "); } builder.append(lookupName); first = false; } - String cmd = baseCmd + " " + format.getName() + " "; - m.text(builder).suggestTip(cmd).newline(); first = true; + actor.print(builder.toString()); } - m.send(actor); } @@ -587,25 +614,21 @@ public class SchematicCommands extends MethodCommands { */ @Command( - aliases = {"list", "ls", "all"}, - desc = "List saved schematics", - usage = "[global|mine|] [page=1]", - flags = "dnp", - help = "List all schematics in the schematics directory\n" + - " -p prints the requested page\n" + - " -f restricts by format\n" + name = "list", + aliases = {"all", "ls"}, + desc = "List saved schematics", + descFooter = "Note: Format is not fully verified until loading." ) @CommandPermissions("worldedit.schematic.list") - public void list(FawePlayer fp, Actor actor, CommandContext args, @Switch('p') @Optional("1") int page, @Switch('f') String formatName) throws WorldEditException { - if (args.argsLength() == 0) { - BBC.COMMAND_SYNTAX.send(fp, getCommand().usage()); - return; - } + public void list(FawePlayer fp, Actor actor, CommandContext args, + @ArgFlag(name = 'p', desc = "Page to view.", def = "1") + int page, + @Switch(name = 'f', desc = "Restricts by format.") + String formatName) throws WorldEditException { LocalConfiguration config = worldEdit.getConfiguration(); - String prefix = config.noDoubleSlash ? "" : "/"; File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); - String schemCmd = prefix + Commands.getAlias(SchematicCommands.class, "schematic"); + String schemCmd = "/" + Commands.getAlias(SchematicCommands.class, "schematic"); String loadSingle = schemCmd + " " + Commands.getAlias(SchematicCommands.class, "load"); String loadMulti = schemCmd + " " + Commands.getAlias(SchematicCommands.class, "loadall"); String unload = schemCmd + " " + Commands.getAlias(SchematicCommands.class, "unload"); @@ -623,7 +646,6 @@ public class SchematicCommands extends MethodCommands { boolean loaded = multi != null && multi.contains(uri); String name = relFilePath; - String color; String uriStr = uri.toString(); if (uriStr.startsWith("file:/")) { File file = new File(uri.getPath()); @@ -635,38 +657,163 @@ public class SchematicCommands extends MethodCommands { } catch (IOException ignore) {} if (file.isDirectory()) { isDir = true; - color = "&6"; - } else { - color = "&a"; - if (name.indexOf('.') != -1) name = name.substring(0, name.lastIndexOf('.')); + } else if (name.indexOf('.') != -1) { + name = name.substring(0, name.lastIndexOf('.')); } - } else if (uriStr.startsWith("http://") || uriStr.startsWith("https://")) { - // url - color = "&9"; - } else { - color = "&7"; - } + } // url - msg.text("&8 - "); + msg.text(" - "); if (msg.supportsInteraction()) { if (loaded) { - msg.text("&7[&c-&7]").command(unload + " " + relFilePath).tooltip("Unload"); + msg.text("[-]").command(unload + " " + relFilePath).tooltip("Unload"); } else { - msg.text("&7[&a+&7]").command(loadMulti + " " + relFilePath).tooltip("Add to clipboard"); + msg.text("[+]").command(loadMulti + " " + relFilePath).tooltip("Add to clipboard"); } - if (!isDir) msg.text("&7[&cX&7]").suggest("/" + delete + " " + relFilePath).tooltip("Delete"); - else if (hasShow) msg.text("&7[&3O&7]").command(showCmd + " " + args.getJoinedStrings(0) + " " + relFilePath).tooltip("Show"); - msg.text(color + name); + if (!isDir) msg.text("[X]").suggest("/" + delete + " " + relFilePath).tooltip("Delete"); + else if (hasShow) msg.text("[O]").command(showCmd + " " + args.getJoinedStrings(0) + " " + relFilePath).tooltip("Show"); + msg.text(name); if (isDir) { msg.command(list + " " + relFilePath).tooltip("List"); } else { msg.command(loadSingle + " " + relFilePath).tooltip("Load"); } } else { - msg.text(color).text(name); + msg.text(name); } } }); } + + private static class SchematicLoadTask implements Callable { + private final Player player; + private final File file; + private final ClipboardFormat format; + + SchematicLoadTask(Player player, File file, ClipboardFormat format) { + this.player = player; + this.file = file; + this.format = format; + } + + @Override + public ClipboardHolder call() throws Exception { + try (Closer closer = Closer.create()) { + FileInputStream fis = closer.register(new FileInputStream(file)); + BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); + ClipboardReader reader = closer.register(format.getReader(bis)); + + Clipboard clipboard = reader.read(); + log.info(player.getName() + " loaded " + file.getCanonicalPath()); + return new ClipboardHolder(clipboard); + } + } + } + + private static class SchematicSaveTask implements Callable { + private final Player player; + private final File file; + private final ClipboardFormat format; + private final ClipboardHolder holder; + private final boolean overwrite; + + SchematicSaveTask(Player player, File file, ClipboardFormat format, ClipboardHolder holder, boolean overwrite) { + this.player = player; + this.file = file; + this.format = format; + this.holder = holder; + this.overwrite = overwrite; + } + + @Override + public Void call() throws Exception { + Clipboard clipboard = holder.getClipboard(); + Transform transform = holder.getTransform(); + Clipboard target; + + // If we have a transform, bake it into the copy + if (transform.isIdentity()) { + target = clipboard; + } else { + FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform); + target = new BlockArrayClipboard(result.getTransformedRegion()); + target.setOrigin(clipboard.getOrigin()); + Operations.completeLegacy(result.copyTo(target)); + } + + try (Closer closer = Closer.create()) { + FileOutputStream fos = closer.register(new FileOutputStream(file)); + BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos)); + ClipboardWriter writer = closer.register(format.getWriter(bos)); + writer.write(target); + + log.info(player.getName() + " saved " + file.getCanonicalPath() + (overwrite ? " (overwriting previous file)" : "")); + } + return null; + } + } + + private static class SchematicListTask implements Callable { + private final String prefix; + private final int sortType; + private final int page; + private final File rootDir; + private final String pageCommand; + + SchematicListTask(String prefix, int sortType, int page, String pageCommand) { + this.prefix = prefix; + this.sortType = sortType; + this.page = page; + this.rootDir = WorldEdit.getInstance().getWorkingDirectoryFile(prefix); + this.pageCommand = pageCommand; + } + + @Override + public Component call() throws Exception { + List fileList = allFiles(rootDir); + + if (fileList == null || fileList.isEmpty()) { + return ErrorFormat.wrap("No schematics found."); + } + + File[] files = new File[fileList.size()]; + fileList.toArray(files); + // cleanup file list + Arrays.sort(files, (f1, f2) -> { + // http://stackoverflow.com/questions/203030/best-way-to-list-files-in-java-sorted-by-date-modified + int res; + if (sortType == 0) { // use name by default + int p = f1.getParent().compareTo(f2.getParent()); + if (p == 0) { // same parent, compare names + res = f1.getName().compareTo(f2.getName()); + } else { // different parent, sort by that + res = p; + } + } else { + res = Long.compare(f1.lastModified(), f2.lastModified()); // use date if there is a flag + if (sortType == 1) res = -res; // flip date for newest first instead of oldest first + } + return res; + }); + + PaginationBox paginationBox = new SchematicPaginationBox(prefix, files, pageCommand); + return paginationBox.create(page); + } + } + + private static List allFiles(File root) { + File[] files = root.listFiles(); + if (files == null) return null; + List fileList = new ArrayList<>(); + for (File f : files) { + if (f.isDirectory()) { + List subFiles = allFiles(f); + if (subFiles == null) continue; // empty subdir + fileList.addAll(subFiles); + } else { + fileList.add(f); + } + } + return fileList; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java index 15af47b97..ce4410ca9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java @@ -19,39 +19,48 @@ package com.sk89q.worldedit.command; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; + import com.boydti.fawe.config.BBC; import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper; -import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.LocalSession; 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.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.scripting.CraftScriptContext; import com.sk89q.worldedit.scripting.CraftScriptEngine; import com.sk89q.worldedit.scripting.RhinoCraftScriptEngine; -import com.sk89q.worldedit.session.request.Request; -import org.mozilla.javascript.NativeJavaObject; - -import javax.annotation.Nullable; -import javax.script.ScriptException; -import java.io.*; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import javax.script.ScriptException; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; +import org.mozilla.javascript.NativeJavaObject; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.ALL; /** * Commands related to scripting. */ -@Command(aliases = {}, desc = "Run craftscripts: [More Info](https://goo.gl/dHDxLG)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class ScriptingCommands { private final WorldEdit worldEdit; @@ -61,21 +70,18 @@ public class ScriptingCommands { * * @param worldEdit reference to WorldEdit */ - public ScriptingCommands(final WorldEdit worldEdit) { + public ScriptingCommands(WorldEdit worldEdit) { checkNotNull(worldEdit); this.worldEdit = worldEdit; } @Command( - aliases = {"setupdispatcher"}, - desc = "", - usage = "", - min = 1, - max = 1 + name = "setupdispatcher", + desc = "" ) @CommandPermissions("fawe.setupdispatcher") public void setupdispatcher(Player player, LocalSession session, final CommandContext args) throws WorldEditException { - CommandManager.getInstance().setupDispatcher(); + PlatformCommandManager.getInstance().setupDispatcher(); } public static T runScript(Player player, File f, String[] args) throws WorldEditException { @@ -112,7 +118,7 @@ public class ScriptingCommands { byte[] data = new byte[in.available()]; in.readFully(data); in.close(); - script = new String(data, 0, data.length, "utf-8"); + script = new String(data, 0, data.length, StandardCharsets.UTF_8); } catch (IOException e) { actor.printError("Script read error: " + e.getMessage()); return null; @@ -157,9 +163,7 @@ public class ScriptingCommands { e.printStackTrace(); actor.printError("Failed to execute:"); actor.printRaw(e.getMessage()); - } catch (NumberFormatException e) { - throw e; - } catch (WorldEditException e) { + } catch (NumberFormatException | WorldEditException e) { throw e; } catch (Throwable e) { actor.printError("Failed to execute (see console):"); @@ -172,22 +176,25 @@ public class ScriptingCommands { return (T) result; } - @Command(aliases = {"cs"}, usage = " [args...]", desc = "Execute a CraftScript", min = 1, max = -1) + @Command( + name = "cs", + desc = "Execute a CraftScript" + ) @CommandPermissions("worldedit.scripting.execute") @Logging(ALL) - public void execute(final Player player, final LocalSession session, final CommandContext args) throws WorldEditException { + public void execute(Player player, LocalSession session, CommandContext args) throws WorldEditException { final String[] scriptArgs = args.getSlice(1); - final String name = args.getString(0); + final String filename = args.getString(0); - if (!player.hasPermission("worldedit.scripting.execute." + name)) { + if (!player.hasPermission("worldedit.scripting.execute." + filename)) { BBC.SCRIPTING_NO_PERM.send(player); return; } - session.setLastScript(name); + session.setLastScript(filename); - final File dir = this.worldEdit.getWorkingDirectoryFile(this.worldEdit.getConfiguration().scriptsDir); - final File f = this.worldEdit.getSafeOpenFile(player, dir, name, "js", "js"); + File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().scriptsDir); + File f = worldEdit.getSafeOpenFile(player, dir, filename, "js", "js"); try { new RhinoCraftScriptEngine(); } catch (NoClassDefFoundError e) { @@ -200,10 +207,15 @@ public class ScriptingCommands { runScript(LocationMaskedPlayerWrapper.unwrap(player), f, scriptArgs); } - @Command(aliases = {".s"}, usage = "[args...]", desc = "Execute last CraftScript", min = 0, max = -1) + @Command( + name = ".s", + desc = "Execute last CraftScript" + ) @CommandPermissions("worldedit.scripting.execute") @Logging(ALL) - public void executeLast(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void executeLast(Player player, LocalSession session, + @Arg(desc = "Arguments to the CraftScript", def = "", variable = true) + List args) throws WorldEditException { String lastScript = session.getLastScript(); @@ -217,17 +229,10 @@ public class ScriptingCommands { return; } - final String[] scriptArgs = args.getSlice(0); + File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().scriptsDir); + File f = worldEdit.getSafeOpenFile(player, dir, lastScript, "js", "js"); - final File dir = this.worldEdit.getWorkingDirectoryFile(this.worldEdit.getConfiguration().scriptsDir); - final File f = this.worldEdit.getSafeOpenFile(player, dir, lastScript, "js", "js"); - - try { - this.worldEdit.runScript(LocationMaskedPlayerWrapper.unwrap(player), f, scriptArgs); - } catch (final WorldEditException ex) { - BBC.SCRIPTING_ERROR.send(player); - } + worldEdit.runScript(player, f, Stream.concat(Stream.of(lastScript), args.stream()) + .toArray(String[]::new)); } - - } 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 bb1e3e37f..6893ca7f6 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 @@ -19,27 +19,31 @@ package com.sk89q.worldedit.command; +import static com.sk89q.worldedit.command.util.Logging.LogMode.POSITION; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; + import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.clipboard.URIClipboardHolder; import com.boydti.fawe.object.mask.IdMask; import com.boydti.fawe.object.regions.selector.FuzzyRegionSelector; import com.boydti.fawe.object.regions.selector.PolyhedralRegionSelector; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.command.argument.SelectorChoice; +import com.sk89q.worldedit.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.MultiDirection; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -55,28 +59,29 @@ import com.sk89q.worldedit.regions.selector.RegionSelectorType; import com.sk89q.worldedit.regions.selector.SphereRegionSelector; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Countable; -import com.sk89q.worldedit.util.command.binding.Switch; -import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; -import com.sk89q.worldedit.util.formatting.Style; -import com.sk89q.worldedit.util.formatting.StyledFragment; import com.sk89q.worldedit.util.formatting.component.CommandListBox; +import com.sk89q.worldedit.util.formatting.component.SubtleFormat; +import com.sk89q.worldedit.util.formatting.component.TextComponentProducer; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.storage.ChunkStore; - import java.io.File; import java.net.URI; -import java.util.ArrayList; import java.util.List; import java.util.Optional; - -import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import java.util.stream.Stream; +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; /** * Selection commands. */ -@Command(aliases = {}, desc = "Change your selection points, mode or view info about your selection: [More Info](http://wiki.sk89q.com/wiki/WorldEdit/Selection)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class SelectionCommands { private final WorldEdit we; @@ -86,26 +91,17 @@ public class SelectionCommands { } @Command( - aliases = {"/pos1", "posa", "/1"}, - usage = "[coordinates]", - desc = "Set position 1", - min = 0, - max = 1 + name = "/pos1", + desc = "Set position 1" ) @Logging(POSITION) @CommandPermissions("worldedit.selection.pos") - public void pos1(Player player, LocalSession session, CommandContext args) throws WorldEditException { - + public void pos1(Player player, LocalSession session, + @Arg(desc = "Coordinates to set position 1 to", def = "") + BlockVector3 coordinates) throws WorldEditException { BlockVector3 pos; - - if (args.argsLength() == 1) { - if (args.getString(0).matches("-?\\d+,-?\\d+,-?\\d+")) { - String[] coords = args.getString(0).split(","); - pos = BlockVector3.at(Integer.parseInt(coords[0]), Integer.parseInt(coords[1]), Integer.parseInt(coords[2])); - } else { - BBC.SELECTOR_INVALID_COORDINATES.send(player, args.getString(0)); - return; - } + if (coordinates != null) { + pos = coordinates; } else { pos = player.getBlockIn().toBlockPoint(); } @@ -115,31 +111,22 @@ public class SelectionCommands { return; } - session.getRegionSelector(player.getWorld()).explainPrimarySelection(player, session, pos); + session.getRegionSelector(player.getWorld()) + .explainPrimarySelection(player, session, pos); } @Command( - aliases = {"/pos2", "posb", "/2"}, - usage = "[coordinates]", - desc = "Set position 2", - min = 0, - max = 1 + name = "/pos2", + desc = "Set position 2" ) @Logging(POSITION) @CommandPermissions("worldedit.selection.pos") - public void pos2(Player player, LocalSession session, CommandContext args) throws WorldEditException { - + public void pos2(Player player, LocalSession session, + @Arg(desc = "Coordinates to set position 2 to", def = "") + BlockVector3 coordinates) throws WorldEditException { BlockVector3 pos; - if (args.argsLength() == 1) { - if (args.getString(0).matches("-?\\d+,-?\\d+,-?\\d+")) { - String[] coords = args.getString(0).split(","); - pos = BlockVector3.at(Integer.parseInt(coords[0]), - Integer.parseInt(coords[1]), - Integer.parseInt(coords[2])); - } else { - BBC.SELECTOR_INVALID_COORDINATES.send(player, args.getString(0)); - return; - } + if (coordinates != null) { + pos = coordinates; } else { pos = player.getBlockIn().toBlockPoint(); } @@ -154,15 +141,13 @@ public class SelectionCommands { } @Command( - aliases = {"/hpos1"}, - usage = "", - desc = "Set position 1 to targeted block", - min = 0, - max = 0 + name = "/hpos1", + desc = "Set position 1 to targeted block" ) @CommandPermissions("worldedit.selection.hpos") - public void hpos1(Player player, LocalSession session, CommandContext args) throws WorldEditException { - BlockVector3 pos = player.getBlockTrace(300).toBlockPoint(); + public void hpos1(Player player, LocalSession session) throws WorldEditException { + + BlockVector3 pos = player.getBlockTrace(300).toBlockPoint(); if (pos != null) { if (!session.getRegionSelector(player.getWorld()).selectPrimary(pos, ActorSelectorLimits.forActor(player))) { @@ -178,15 +163,13 @@ public class SelectionCommands { } @Command( - aliases = {"/hpos2"}, - usage = "", - desc = "Set position 2 to targeted block", - min = 0, - max = 0 + name = "/hpos2", + desc = "Set position 2 to targeted block" ) @CommandPermissions("worldedit.selection.hpos") - public void hpos2(Player player, LocalSession session, CommandContext args) throws WorldEditException { - BlockVector3 pos = player.getBlockTrace(300).toBlockPoint(); + public void hpos2(Player player, LocalSession session) throws WorldEditException { + + BlockVector3 pos = player.getBlockTrace(300).toBlockPoint(); if (pos != null) { if (!session.getRegionSelector(player.getWorld()).selectSecondary(pos, ActorSelectorLimits.forActor(player))) { @@ -202,28 +185,22 @@ public class SelectionCommands { } @Command( - aliases = {"/chunk"}, - usage = "[x,z coordinates]", - flags = "sc", - desc = "Set the selection to your current chunk.", - help = - "Set the selection to the chunk you are currently in.\n" + - "With the -s flag, your current selection is expanded\n" + - "to encompass all chunks that are part of it.\n\n" + - "Specifying coordinates will use those instead of your\n" + - "current position. Use -c to specify chunk coordinates,\n" + - "otherwise full coordinates will be implied.\n" + - "(for example, the coordinates 5,5 are the same as -c 0,0)", - min = 0, - max = 1 + name = "/chunk", + desc = "Set the selection to your current chunk." ) @Logging(POSITION) @CommandPermissions("worldedit.selection.chunk") - public void chunk(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void chunk(Player player, LocalSession session, + @Arg(desc = "The chunk to select", def = "") + BlockVector2 coordinates, + @Switch(name = 's', desc = "Expand your selection to encompass all chunks that are part of it") + boolean expandSelection, + @Switch(name = 'c', desc = "Use chunk coordinates instead of block coordinates") + boolean useChunkCoordinates) throws WorldEditException { final BlockVector3 min; final BlockVector3 max; final World world = player.getWorld(); - if (args.hasFlag('s')) { + if (expandSelection) { Region region = session.getSelection(world); final BlockVector2 min2D = ChunkStore.toChunk(region.getMinimumPoint()); @@ -235,16 +212,11 @@ public class SelectionCommands { BBC.SELECTION_CHUNKS.send(player, min2D.getBlockX() + ", " + min2D.getBlockZ(), max2D.getBlockX() + ", " + max2D.getBlockZ()); } else { final BlockVector2 min2D; - if (args.argsLength() == 1) { + if (coordinates != null) { // coords specified - String[] coords = args.getString(0).split(","); - if (coords.length != 2) { - BBC.SELECTOR_INVALID_COORDINATES.send(player, args.getString(0)); - } - int x = Integer.parseInt(coords[0]); - int z = Integer.parseInt(coords[1]); - BlockVector2 pos = BlockVector2.at(x, z); - min2D = (args.hasFlag('c')) ? pos : ChunkStore.toChunk(pos.toBlockVector3()); + min2D = useChunkCoordinates + ? coordinates + : ChunkStore.toChunk(coordinates.toBlockVector3()); } else { // use player loc min2D = ChunkStore.toChunk(player.getBlockIn().toBlockPoint()); @@ -271,14 +243,12 @@ public class SelectionCommands { } @Command( - aliases = {"/wand", "/w"}, - usage = "", - desc = "Get the wand object", - min = 0, - max = 0 + name = "/wand", + desc = "Get the wand object" ) @CommandPermissions("worldedit.wand") - public void wand(Player player) throws WorldEditException { + public void wand(Player player, LocalSession session, + @Switch(name = 'n', desc = "Get a navigation wand") boolean navWand) throws WorldEditException { player.giveItem(new BaseItemStack(ItemTypes.parse(we.getConfiguration().wandItem), 1)); BBC.SELECTION_WAND.send(player); if (!player.hasPermission("fawe.tips")) @@ -286,176 +256,46 @@ public class SelectionCommands { } @Command( - aliases = {"toggleeditwand"}, - usage = "", - desc = "Toggle functionality of the edit wand", - min = 0, - max = 0 + name = "toggleeditwand", + desc = "Remind the user that the wand is now a tool and can be unbound with /none." ) @CommandPermissions("worldedit.wand.toggle") - public void toggleWand(Player player, LocalSession session, CommandContext args) throws WorldEditException { - - session.setToolControl(!session.isToolControlEnabled()); - - if (session.isToolControlEnabled()) { - BBC.SELECTION_WAND_ENABLE.m().send(player); - } else { - BBC.SELECTION_WAND_DISABLE.send(player); - } + public void toggleWand(Player player) { + player.print(TextComponent.of("The selection wand is now a normal tool. You can disable it with ") + .append(TextComponent.of("/none", TextColor.AQUA).clickEvent( + ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "/none"))) + .append(TextComponent.of(" and rebind it to any item with ")) + .append(TextComponent.of("//selwand", TextColor.AQUA).clickEvent( + ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "//selwand"))) + .append(TextComponent.of(" or get a new wand with ")) + .append(TextComponent.of("//wand", TextColor.AQUA).clickEvent( + ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "//wand")))); } @Command( - aliases = {"/expand"}, - usage = " [reverse-amount] ", - desc = "Expand the selection area", - min = 1, - max = 3 - ) - @Logging(REGION) - @CommandPermissions("worldedit.selection.expand") - public void expand(Player player, LocalSession session, CommandContext args) throws WorldEditException { - - // Special syntax (//expand vert) to expand the selection between - // sky and bedrock. - if (args.getString(0).equalsIgnoreCase("vert") || args.getString(0).equalsIgnoreCase("vertical")) { - Region region = session.getSelection(player.getWorld()); - try { - int oldSize = region.getArea(); - region.expand( - BlockVector3.at(0, (player.getWorld().getMaxY() + 1), 0), - BlockVector3.at(0, -(player.getWorld().getMaxY() + 1), 0)); - session.getRegionSelector(player.getWorld()).learnChanges(); - int newSize = region.getArea(); - session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); - BBC.SELECTION_EXPAND_VERT.send(player, (newSize - oldSize)); - } catch (RegionOperationException e) { - player.printError(e.getMessage()); - } - - return; - } - List dirs = new ArrayList<>(); - int change = args.getInteger(0); - int reverseChange = 0; - - switch (args.argsLength()) { - case 2: - // Either a reverse amount or a direction - try { - reverseChange = args.getInteger(1); - dirs.add(we.getDirection(player, "me")); - } catch (NumberFormatException e) { - if (args.getString(1).contains(",")) { - String[] split = args.getString(1).split(","); - for (String s : split) { - dirs.add(we.getDirection(player, s.toLowerCase())); - } - } else { - dirs.add(we.getDirection(player, args.getString(1).toLowerCase())); - } - } - break; - - case 3: - // Both reverse amount and direction - reverseChange = args.getInteger(1); - if (args.getString(2).contains(",")) { - String[] split = args.getString(2).split(","); - for (String s : split) { - dirs.add(we.getDirection(player, s.toLowerCase())); - } - } else { - dirs.add(we.getDirection(player, args.getString(2).toLowerCase())); - } - break; - - default: - dirs.add(we.getDirection(player, "me")); - break; - - } - - Region region = session.getSelection(player.getWorld()); - int oldSize = region.getArea(); - - if (reverseChange == 0) { - for (BlockVector3 dir : dirs) { - region.expand(dir.multiply(change)); - } - } else { - for (BlockVector3 dir : dirs) { - region.expand(dir.multiply(change), dir.multiply(-reverseChange)); - } - } - - session.getRegionSelector(player.getWorld()).learnChanges(); - int newSize = region.getArea(); - - session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); - BBC.SELECTION_EXPAND.send(player, (newSize - oldSize)); - } - - @Command( - aliases = {"/contract"}, - usage = " [reverse-amount] [direction]", - desc = "Contract the selection area", - min = 1, - max = 3 + name = "/contract", + desc = "Contract the selection area" ) @Logging(REGION) @CommandPermissions("worldedit.selection.contract") - public void contract(Player player, LocalSession session, CommandContext args) throws WorldEditException { - - List dirs = new ArrayList<>(); - int change = args.getInteger(0); - int reverseChange = 0; - - switch (args.argsLength()) { - case 2: - // Either a reverse amount or a direction - try { - reverseChange = args.getInteger(1); - dirs.add(we.getDirection(player, "me")); - } catch (NumberFormatException e) { - if (args.getString(1).contains(",")) { - String[] split = args.getString(1).split(","); - for (String s : split) { - dirs.add(we.getDirection(player, s.toLowerCase())); - } - } else { - dirs.add(we.getDirection(player, args.getString(1).toLowerCase())); - } - } - break; - - case 3: - // Both reverse amount and direction - reverseChange = args.getInteger(1); - if (args.getString(2).contains(",")) { - String[] split = args.getString(2).split(","); - for (String s : split) { - dirs.add(we.getDirection(player, s.toLowerCase())); - } - } else { - dirs.add(we.getDirection(player, args.getString(2).toLowerCase())); - } - break; - - default: - dirs.add(we.getDirection(player, "me")); - break; - } - + public void contract(Player player, LocalSession session, + @Arg(desc = "Amount to contract the selection by") + int amount, + @Arg(desc = "Amount to contract the selection by in the other direction", def = "0") + int reverseAmount, + @Arg(desc = "Direction to contract", def = Direction.AIM) + @MultiDirection + List direction) throws WorldEditException { try { Region region = session.getSelection(player.getWorld()); int oldSize = region.getArea(); - if (reverseChange == 0) { - for (BlockVector3 dir : dirs) { - region.contract(dir.multiply(change)); + if (reverseAmount == 0) { + for (BlockVector3 dir : direction) { + region.contract(dir.multiply(amount)); } } else { - for (BlockVector3 dir : dirs) { - region.contract(dir.multiply(change), dir.multiply(-reverseChange)); + for (BlockVector3 dir : direction) { + region.contract(dir.multiply(amount), dir.multiply(-reverseAmount)); } } session.getRegionSelector(player.getWorld()).learnChanges(); @@ -463,6 +303,7 @@ public class SelectionCommands { session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + BBC.SELECTION_CONTRACT.send(player, (oldSize - newSize)); } catch (RegionOperationException e) { player.printError(e.getMessage()); @@ -470,40 +311,28 @@ public class SelectionCommands { } @Command( - aliases = {"/shift"}, - usage = " [direction]", - desc = "Shift the selection area", - min = 1, - max = 2 + name = "/shift", + desc = "Shift the selection area" ) @Logging(REGION) @CommandPermissions("worldedit.selection.shift") - public void shift(Player player, LocalSession session, CommandContext args) throws WorldEditException { - - List dirs = new ArrayList<>(); - int change = args.getInteger(0); - if (args.argsLength() == 2) { - if (args.getString(1).contains(",")) { - for (String s : args.getString(1).split(",")) { - dirs.add(we.getDirection(player, s.toLowerCase())); - } - } else { - dirs.add(we.getDirection(player, args.getString(1).toLowerCase())); - } - } else { - dirs.add(we.getDirection(player, "me")); - } - + public void shift(Player player, LocalSession session, + @Arg(desc = "Amount to shift the selection by") + int amount, + @Arg(desc = "Direction to contract", def = Direction.AIM) + @MultiDirection + List direction) throws WorldEditException { try { Region region = session.getSelection(player.getWorld()); - for (BlockVector3 dir : dirs) { - region.shift(dir.multiply(change)); + for (BlockVector3 dir : direction) { + region.shift(dir.multiply(amount)); } session.getRegionSelector(player.getWorld()).learnChanges(); session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + BBC.SELECTION_SHIFT.send(player); } catch (RegionOperationException e) { player.printError(e.getMessage()); @@ -511,81 +340,73 @@ public class SelectionCommands { } @Command( - aliases = {"/outset"}, - usage = "", - desc = "Outset the selection area", - help = - "Expands the selection by the given amount in all directions.\n" + - "Flags:\n" + - " -h only expand horizontally\n" + - " -v only expand vertically\n", - flags = "hv", - min = 1, - max = 1 + name = "/outset", + desc = "Outset the selection area" ) @Logging(REGION) @CommandPermissions("worldedit.selection.outset") - public void outset(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void outset(Player player, LocalSession session, + @Arg(desc = "Amount to expand the selection by in all directions") + int amount, + @Switch(name = 'h', desc = "Only expand horizontally") + boolean onlyHorizontal, + @Switch(name = 'v', desc = "Only expand vertically") + boolean onlyVertical) throws WorldEditException { Region region = session.getSelection(player.getWorld()); - region.expand(getChangesForEachDir(args)); + region.expand(getChangesForEachDir(amount, onlyHorizontal, onlyVertical)); session.getRegionSelector(player.getWorld()).learnChanges(); session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); BBC.SELECTION_OUTSET.send(player); } @Command( - aliases = {"/inset"}, - usage = "", - desc = "Inset the selection area", - help = - "Contracts the selection by the given amount in all directions.\n" + - "Flags:\n" + - " -h only contract horizontally\n" + - " -v only contract vertically\n", - flags = "hv", - min = 1, - max = 1 + name = "/inset", + desc = "Inset the selection area" ) @Logging(REGION) @CommandPermissions("worldedit.selection.inset") - public void inset(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void inset(Player player, LocalSession session, + @Arg(desc = "Amount to contract the selection by in all directions") + int amount, + @Switch(name = 'h', desc = "Only contract horizontally") + boolean onlyHorizontal, + @Switch(name = 'v', desc = "Only contract vertically") + boolean onlyVertical) throws WorldEditException { Region region = session.getSelection(player.getWorld()); - region.contract(getChangesForEachDir(args)); + region.contract(getChangesForEachDir(amount, onlyHorizontal, onlyVertical)); session.getRegionSelector(player.getWorld()).learnChanges(); session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); BBC.SELECTION_INSET.send(player); } - private BlockVector3[] getChangesForEachDir(CommandContext args) { - List changes = new ArrayList<>(6); - int change = args.getInteger(0); + private BlockVector3[] getChangesForEachDir(int amount, boolean onlyHorizontal, boolean onlyVertical) { + Stream.Builder changes = Stream.builder(); - if (!args.hasFlag('h')) { - changes.add((BlockVector3.at(0, 1, 0)).multiply(change)); - changes.add((BlockVector3.at(0, -1, 0)).multiply(change)); + if (!onlyHorizontal) { + changes.add(BlockVector3.UNIT_Y); + changes.add(BlockVector3.UNIT_MINUS_Y); } - if (!args.hasFlag('v')) { - changes.add((BlockVector3.at(1, 0, 0)).multiply(change)); - changes.add((BlockVector3.at(-1, 0, 0)).multiply(change)); - changes.add((BlockVector3.at(0, 0, 1)).multiply(change)); - changes.add((BlockVector3.at(0, 0, -1)).multiply(change)); + if (!onlyVertical) { + changes.add(BlockVector3.UNIT_X); + changes.add(BlockVector3.UNIT_MINUS_X); + changes.add(BlockVector3.UNIT_Z); + changes.add(BlockVector3.UNIT_MINUS_Z); } - return changes.toArray(new BlockVector3[0]); + return changes.build().map(v -> v.multiply(amount)).toArray(BlockVector3[]::new); } @Command( - aliases = {"/size"}, - flags = "c", - usage = "", - desc = "Get information about the selection", - min = 0, - max = 0 + name = "/size", + desc = "Get information about the selection" ) @CommandPermissions("worldedit.selection.size") - public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - if (args.hasFlag('c')) { + public void size(Player player, LocalSession session, + @Switch(name = 'c', desc = "Get clipboard info instead") + boolean clipboardInfo) throws WorldEditException { + Region region; + if (clipboardInfo) { ClipboardHolder root = session.getClipboard(); // Clipboard clipboard = holder.getClipboard(); int index = 0; @@ -603,8 +424,10 @@ public class SelectionCommands { name = Integer.toString(index); } - Region region = clipboard.getRegion(); - BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(BlockVector3.ONE); + region = clipboard.getRegion(); + BlockVector3 size = region.getMaximumPoint() + .subtract(region.getMinimumPoint()). + add(1, 1, 1); BlockVector3 origin = clipboard.getOrigin(); String sizeStr = size.getBlockX() + "*" + size.getBlockY() + "*" + size.getBlockZ(); @@ -617,30 +440,10 @@ public class SelectionCommands { index++; } - - - -// player.print("Cuboid dimensions (max - min): " + size); -// player.print("Offset: " + origin); -// player.print("Cuboid distance: " + size.distance(Vector.ONE)); -// player.print("# of blocks: " + (int) (size.getX() * size.getY() * size.getZ())); -//======= -// public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { -// if (args.hasFlag('c')) { -// ClipboardHolder holder = session.getClipboard(); -// Clipboard clipboard = holder.getClipboard(); -// Region region = clipboard.getRegion(); -// BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()); -// BlockVector3 origin = clipboard.getOrigin(); -// -// player.print("Cuboid dimensions (max - min): " + size); -// player.print("Offset: " + origin); -// player.print("Cuboid distance: " + size.distance(BlockVector3.ONE)); -// player.print("# of blocks: " + (int) (size.getX() * size.getY() * size.getZ())); return; } - Region region = session.getSelection(player.getWorld()); + region = session.getSelection(player.getWorld()); BlockVector3 size = region.getMaximumPoint() .subtract(region.getMinimumPoint()) .add(1, 1, 1); @@ -660,57 +463,53 @@ public class SelectionCommands { @Command( - aliases = {"/count"}, - usage = "", - desc = "Counts the number of a certain type of block", - flags = "d", - min = 1, - max = 1 + name = "/count", + desc = "Counts the number of a certain type of block" ) @CommandPermissions("worldedit.analysis.count") - public void count(Player player, LocalSession session, EditSession editSession, Mask mask) throws WorldEditException { + public void count(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The mask of blocks to match") + Mask mask) throws WorldEditException { int count = editSession.countBlock(session.getSelection(player.getWorld()), mask); - BBC.SELECTION_COUNT.send(player, count); + player.print("Counted: " + count); } @Command( - aliases = {"/distr"}, - usage = "", - desc = "Get the distribution of blocks in the selection", - help = - "Gets the distribution of blocks in the selection.\n" + - "The -c flag gets the distribution of your clipboard.", - flags = "c", - min = 0, - max = 0 + name = "/distr", + desc = "Get the distribution of blocks in the selection" ) @CommandPermissions("worldedit.analysis.distr") - public void distr(Player player, LocalSession session, EditSession editSession, @Switch('c') boolean useClipboard, @Switch('d') boolean useData) throws WorldEditException, CommandException { - int size; - List distributionData; + public void distr(Player player, LocalSession session, EditSession editSession, + @Switch(name = 'c', desc = "Get the distribution of the clipboard instead") + boolean clipboardDistr, + @Switch(name = 'd', desc = "Separate blocks by state") + boolean separateStates) throws WorldEditException { + List distribution; Region region; - if (useClipboard) { + if (clipboardDistr) { // TODO multi clipboard distribution - Clipboard clipboard = session.getClipboard().getClipboard(); + Clipboard clipboard = session.getClipboard().getClipboard(); // throws if missing region = clipboard.getRegion(); editSession.setExtent(new AbstractDelegateExtent(clipboard)); } else { region = session.getSelection(player.getWorld()); } - if (useData) - distributionData = (List) editSession.getBlockDistributionWithData(region); + if (separateStates) + distribution = (List) editSession.getBlockDistributionWithData(region); else - distributionData = (List) editSession.getBlockDistribution(region); - size = session.getSelection(player.getWorld()).getArea(); + distribution = (List) editSession.getBlockDistribution(region); - if (distributionData.size() <= 0) { + if (distribution.isEmpty()) { // *Should* always be false player.printError("No blocks counted."); return; } + + // note: doing things like region.getArea is inaccurate for non-cuboids. + int size = session.getSelection(player.getWorld()).getArea(); BBC.SELECTION_DISTR.send(player, size); - for (Countable c : distributionData) { + for (Countable c : distribution) { String name = c.getID().toString(); String str = String.format("%-7s (%.3f%%) %s", String.valueOf(c.getAmount()), @@ -721,16 +520,17 @@ public class SelectionCommands { } @Command( - aliases = {"/sel", ";", "/desel", "/deselect"}, - flags = "d", - usage = "[cuboid|extend|poly|polyhedron|ellipsoid|sphere|cyl|convex|fuzzy]", - desc = "Choose a region selector", - min = 0, - max = 1 + name = "/sel", + aliases = { ";", "/desel", "/deselect" }, + desc = "Choose a region selector" ) - public void select(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public void select(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "Selector to switch to", def = "") + SelectorChoice selector, + @Switch(name = 'd', desc = "Set default selector") + boolean setDefaultSelector) throws WorldEditException { final World world = player.getWorld(); - if (args.argsLength() == 0) { + if (selector == null) { session.getRegionSelector(world).clear(); session.dispatchCUISelection(player); BBC.SELECTION_CLEARED.send(player); @@ -740,79 +540,93 @@ public class SelectionCommands { final String typeName = args.getString(0); final RegionSelector oldSelector = session.getRegionSelector(world); - final RegionSelector selector; - if (typeName.equalsIgnoreCase("cuboid")) { - selector = new CuboidRegionSelector(oldSelector); - player.print(BBC.SEL_CUBOID.s()); - } else if (typeName.equalsIgnoreCase("extend")) { - selector = new ExtendingCuboidRegionSelector(oldSelector); - player.print(BBC.SEL_CUBOID_EXTEND.s()); - } else if (typeName.equalsIgnoreCase("poly")) { - selector = new Polygonal2DRegionSelector(oldSelector); - player.print(BBC.SEL_2D_POLYGON.s()); - Optional limit = ActorSelectorLimits.forActor(player).getPolygonVertexLimit(); - limit.ifPresent(integer -> player.print(BBC.SEL_MAX.f(integer))); - player.print(BBC.SEL_LIST.s()); - } else if (typeName.equalsIgnoreCase("ellipsoid")) { - selector = new EllipsoidRegionSelector(oldSelector); - player.print(BBC.SEL_ELLIPSIOD.s()); - } else if (typeName.equalsIgnoreCase("sphere")) { - selector = new SphereRegionSelector(oldSelector); - player.print(BBC.SEL_SPHERE.s()); - } else if (typeName.equalsIgnoreCase("cyl")) { - selector = new CylinderRegionSelector(oldSelector); - player.print(BBC.SEL_CYLINDRICAL.s()); - } else if (typeName.equalsIgnoreCase("convex") || typeName.equalsIgnoreCase("hull")) { - selector = new ConvexPolyhedralRegionSelector(oldSelector); - player.print(BBC.SEL_CONVEX_POLYHEDRAL.s()); - Optional limit = ActorSelectorLimits.forActor(player).getPolyhedronVertexLimit(); - limit.ifPresent(integer -> player.print(BBC.SEL_MAX.f(integer))); - player.print(BBC.SEL_LIST.s()); - } else if (typeName.equalsIgnoreCase("polyhedral") || typeName.equalsIgnoreCase("polyhedron")) { - selector = new PolyhedralRegionSelector(player.getWorld()); - player.print(BBC.SEL_CONVEX_POLYHEDRAL.s()); - Optional limit = ActorSelectorLimits.forActor(player).getPolyhedronVertexLimit(); - limit.ifPresent(integer -> player.print(BBC.SEL_MAX.f(integer))); - player.print(BBC.SEL_LIST.s()); - } else if (typeName.startsWith("fuzzy") || typeName.startsWith("magic")) { - Mask mask; - if (typeName.length() > 6) { - ParserContext parserContext = new ParserContext(); - parserContext.setActor(player); - parserContext.setWorld(player.getWorld()); - parserContext.setSession(session); - parserContext.setExtent(editSession); - mask = we.getMaskFactory().parseFromInput(typeName.substring(6), parserContext); - } else { - mask = new IdMask(editSession); + final RegionSelector newSelector; + switch (selector) { + case CUBOID: + newSelector = new CuboidRegionSelector(oldSelector); + player.print(BBC.SEL_CUBOID.s()); + break; + case EXTEND: + newSelector = new ExtendingCuboidRegionSelector(oldSelector); + player.print(BBC.SEL_CUBOID_EXTEND.s()); + break; + case POLY: { + newSelector = new Polygonal2DRegionSelector(oldSelector); + player.print(BBC.SEL_2D_POLYGON.s()); + Optional limit = ActorSelectorLimits.forActor(player).getPolygonVertexLimit(); + limit.ifPresent(integer -> player.print(BBC.SEL_MAX.f(integer))); + break; } - selector = new FuzzyRegionSelector(player, editSession, mask); - player.print(BBC.SEL_FUZZY.f()); - player.print(BBC.SEL_LIST.f()); - } else { - CommandListBox box = new CommandListBox("Selection modes"); - StyledFragment contents = box.getContents(); - StyledFragment tip = contents.createFragment(Style.RED); - tip.append(BBC.SEL_MODES.s()).newLine(); + case ELLIPSOID: + newSelector = new EllipsoidRegionSelector(oldSelector); + player.print(BBC.SEL_ELLIPSIOD.s()); + break; + case SPHERE: + newSelector = new SphereRegionSelector(oldSelector); + player.print(BBC.SEL_SPHERE.s()); + break; + case CYL: + newSelector = new CylinderRegionSelector(oldSelector); + player.print(BBC.SEL_CYLINDRICAL.s()); + break; + case CONVEX: + case HULL: + case POLYHEDRON: { + newSelector = new ConvexPolyhedralRegionSelector(oldSelector); + player.print(BBC.SEL_CONVEX_POLYHEDRAL.s()); + Optional limit = ActorSelectorLimits.forActor(player).getPolyhedronVertexLimit(); + limit.ifPresent(integer -> player.print(BBC.SEL_MAX.f(integer))); + break; + } + case POLYHEDRAL: + newSelector = new PolyhedralRegionSelector(player.getWorld()); + player.print(BBC.SEL_CONVEX_POLYHEDRAL.s()); + Optional limit = ActorSelectorLimits.forActor(player).getPolyhedronVertexLimit(); + limit.ifPresent(integer -> player.print(BBC.SEL_MAX.f(integer))); + player.print(BBC.SEL_LIST.s()); + break; + case FUZZY: + case MAGIC: + Mask mask; + if (typeName.length() > 6) { + ParserContext parserContext = new ParserContext(); + parserContext.setActor(player); + parserContext.setWorld(player.getWorld()); + parserContext.setSession(session); + parserContext.setExtent(editSession); + mask = we.getMaskFactory().parseFromInput(typeName.substring(6), parserContext); + } else { + mask = new IdMask(world); + } + newSelector = new FuzzyRegionSelector(player, editSession, mask); + player.print(BBC.SEL_FUZZY.f()); + player.print(BBC.SEL_LIST.f()); + break; + case LIST: + default: + CommandListBox box = new CommandListBox("Selection modes", null); + box.setHidingHelp(true); + TextComponentProducer contents = box.getContents(); + contents.append(SubtleFormat.wrap("Select one of the modes below:")).newline(); - box.appendCommand("//sel cuboid", "Select two corners of a cuboid"); - box.appendCommand("//sel extend", "Fast cuboid selection mode"); - box.appendCommand("//sel poly", "Select a 2D polygon with height"); - box.appendCommand("//sel ellipsoid", "Select an ellipsoid"); - box.appendCommand("//sel sphere", "Select a sphere"); - box.appendCommand("//sel cyl", "Select a cylinder"); - box.appendCommand("//sel convex", "Select a convex polyhedral"); - box.appendCommand("//sel polyhedral", "Select a hollow polyhedral"); - box.appendCommand("//sel fuzzy[=]", "Select all connected blocks (magic wand)"); + box.appendCommand("cuboid", "Select two corners of a cuboid", "//sel cuboid"); + box.appendCommand("extend", "Fast cuboid selection mode", "//sel extend"); + box.appendCommand("poly", "Select a 2D polygon with height", "//sel poly"); + box.appendCommand("ellipsoid", "Select an ellipsoid", "//sel ellipsoid"); + box.appendCommand("sphere", "Select a sphere", "//sel sphere"); + box.appendCommand("cyl", "Select a cylinder", "//sel cyl"); + box.appendCommand("convex", "Select a convex polyhedral", "//sel convex"); + box.appendCommand("polyhedral", "Select a hollow polyhedral", "//sel polyhedral"); + box.appendCommand("fuzzy[=]", "Select all connected blocks (magic wand)", "//sel fuzzy[=]"); - player.printRaw(ColorCodeBuilder.asColorCodes(box)); - return; + player.print(box.create(1)); + return; } - if (args.hasFlag('d')) { + if (setDefaultSelector) { RegionSelectorType found = null; for (RegionSelectorType type : RegionSelectorType.values()) { - if (type.getSelectorClass() == selector.getClass()) { + if (type.getSelectorClass() == newSelector.getClass()) { found = type; break; } @@ -826,9 +640,8 @@ public class SelectionCommands { } } - session.setRegionSelector(world, selector); + session.setRegionSelector(world, newSelector); session.dispatchCUISelection(player); } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionTypeCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionTypeCommands.java deleted file mode 100644 index 72b590c51..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionTypeCommands.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.sk89q.worldedit.command; - -public class SelectionTypeCommands { - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java index b8028725f..a1677bd03 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java @@ -22,32 +22,33 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; 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.entity.Player; import com.sk89q.worldedit.world.snapshot.InvalidSnapshotException; import com.sk89q.worldedit.world.snapshot.Snapshot; import com.sk89q.worldedit.world.storage.MissingWorldException; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; import java.io.File; import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; /** * Snapshot commands. */ -@Command(aliases = {"snapshot", "snap"}, desc = "List, load and view information related to snapshots") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class SnapshotCommands { - private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z"); private final WorldEdit we; @@ -56,14 +57,13 @@ public class SnapshotCommands { } @Command( - aliases = {"list"}, - usage = "[num]", - desc = "List snapshots", - min = 0, - max = 1 + name = "list", + desc = "List snapshots" ) @CommandPermissions("worldedit.snapshots.list") - public void list(Player player, CommandContext args) throws WorldEditException { + public void list(Player player, + @Arg(desc = "# of snapshots to list", def = "5") + int num) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -77,7 +77,7 @@ public class SnapshotCommands { if (!snapshots.isEmpty()) { - int num = args.argsLength() > 0 ? Math.min(40, Math.max(5, args.getInteger(0))) : 5; + num = Math.min(40, Math.max(5, num)); BBC.SNAPSHOT_LIST_HEADER.send(player, player.getWorld().getName()); for (byte i = 0; i < Math.min(num, snapshots.size()); i++) { @@ -92,7 +92,8 @@ public class SnapshotCommands { File dir = config.snapshotRepo.getDirectory(); try { - WorldEdit.logger.info("WorldEdit found no snapshots: looked in: " + dir.getCanonicalPath()); + WorldEdit.logger.info("WorldEdit found no snapshots: looked in: " + + dir.getCanonicalPath()); } catch (IOException e) { WorldEdit.logger.info("WorldEdit found no snapshots: looked in " + "(NON-RESOLVABLE PATH - does it exist?): " @@ -105,14 +106,13 @@ public class SnapshotCommands { } @Command( - aliases = { "use" }, - usage = "", - desc = "Choose a snapshot to use", - min = 1, - max = 1 + name = "use", + desc = "Choose a snapshot to use" ) @CommandPermissions("worldedit.snapshots.restore") - public void use(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void use(Player player, LocalSession session, + @Arg(desc = "Snapeshot to use") + String name) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -121,8 +121,6 @@ public class SnapshotCommands { return; } - String name = args.getString(0); - // Want the latest snapshot? if (name.equalsIgnoreCase("latest")) { try { @@ -148,14 +146,13 @@ public class SnapshotCommands { } @Command( - aliases = { "sel" }, - usage = "", - desc = "Choose the snapshot based on the list id", - min = 1, - max = 1 + name = "sel", + desc = "Choose the snapshot based on the list id" ) @CommandPermissions("worldedit.snapshots.restore") - public void sel(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void sel(Player player, LocalSession session, + @Arg(desc = "The list ID to select") + int index) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); if (config.snapshotRepo == null) { @@ -163,14 +160,6 @@ public class SnapshotCommands { return; } - int index = -1; - try { - index = Integer.parseInt(args.getString(0)); - } catch (NumberFormatException e) { - player.printError("Invalid index, " + args.getString(0) + " is not a valid integer."); - return; - } - if (index < 1) { BBC.SNAPSHOT_INVALID_INDEX.send(player); return; @@ -195,14 +184,13 @@ public class SnapshotCommands { } @Command( - aliases = { "before" }, - usage = "", - desc = "Choose the nearest snapshot before a date", - min = 1, - max = -1 + name = "before", + desc = "Choose the nearest snapshot before a date" ) @CommandPermissions("worldedit.snapshots.restore") - public void before(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void before(Player player, LocalSession session, + @Arg(desc = "The soonest date that may be used") + ZonedDateTime date) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -211,18 +199,12 @@ public class SnapshotCommands { return; } - Calendar date = session.detectDate(args.getJoinedStrings(0)); - - if (date == null) { - BBC.SNAPSHOT_ERROR_DATE.send(player); - } else { try { Snapshot snapshot = config.snapshotRepo.getSnapshotBefore(date, player.getWorld().getName()); if (snapshot == null) { - dateFormat.setTimeZone(session.getTimeZone()); player.printError("Couldn't find a snapshot before " - + dateFormat.format(date.getTime()) + "."); + + dateFormat.withZone(session.getTimeZone().toZoneId()).format(date) + "."); } else { session.setSnapshot(snapshot); BBC.SNAPSHOT_SET.send(player, snapshot.getName()); @@ -230,18 +212,16 @@ public class SnapshotCommands { } catch (MissingWorldException ex) { BBC.SNAPSHOT_NOT_FOUND_WORLD.send(player); } - } } @Command( - aliases = { "after" }, - usage = "", - desc = "Choose the nearest snapshot after a date", - min = 1, - max = -1 + name = "after", + desc = "Choose the nearest snapshot after a date" ) @CommandPermissions("worldedit.snapshots.restore") - public void after(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void after(Player player, LocalSession session, + @Arg(desc = "The soonest date that may be used") + ZonedDateTime date) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -250,24 +230,17 @@ public class SnapshotCommands { return; } - Calendar date = session.detectDate(args.getJoinedStrings(0)); - - if (date == null) { - BBC.SNAPSHOT_ERROR_DATE.send(player); - } else { try { Snapshot snapshot = config.snapshotRepo.getSnapshotAfter(date, player.getWorld().getName()); if (snapshot == null) { - dateFormat.setTimeZone(session.getTimeZone()); player.printError("Couldn't find a snapshot after " - + dateFormat.format(date.getTime()) + "."); + + dateFormat.withZone(session.getTimeZone().toZoneId()).format(date) + "."); } else { session.setSnapshot(snapshot); BBC.SNAPSHOT_SET.send(player, snapshot.getName()); } } catch (MissingWorldException ex) { BBC.SNAPSHOT_NOT_FOUND_WORLD.send(player); - } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java index 446277b9a..0d916a0c8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java @@ -19,16 +19,17 @@ package com.sk89q.worldedit.command; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; + import com.boydti.fawe.config.BBC; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; 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.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.DataException; @@ -37,13 +38,13 @@ import com.sk89q.worldedit.world.snapshot.Snapshot; import com.sk89q.worldedit.world.snapshot.SnapshotRestore; import com.sk89q.worldedit.world.storage.ChunkStore; import com.sk89q.worldedit.world.storage.MissingWorldException; - import java.io.File; import java.io.IOException; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; - -@Command(aliases = {}, desc = "[More Info](http://wiki.sk89q.com/wiki/WorldEdit/Snapshots)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class SnapshotUtilCommands { private final WorldEdit we; @@ -53,14 +54,15 @@ public class SnapshotUtilCommands { } @Command( - aliases = { "restore", "/restore" }, - usage = "[snapshot]", - desc = "Restore the selection from a snapshot", - max = 1 + name = "restore", + aliases = { "/restore" }, + desc = "Restore the selection from a snapshot" ) @Logging(REGION) @CommandPermissions("worldedit.snapshots.restore") - public void restore(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public void restore(Player player, LocalSession session, EditSession editSession, + @Arg(name = "snapshot", desc = "The snapshot to restore", def = "") + String snapshotName) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -72,9 +74,9 @@ public class SnapshotUtilCommands { Region region = session.getSelection(player.getWorld()); Snapshot snapshot; - if (args.argsLength() > 0) { + if (snapshotName != null) { try { - snapshot = config.snapshotRepo.getSnapshot(args.getString(0)); + snapshot = config.snapshotRepo.getSnapshot(snapshotName); } catch (InvalidSnapshotException e) { BBC.SNAPSHOT_NOT_AVAILABLE.send(player); return; @@ -113,27 +115,28 @@ public class SnapshotUtilCommands { // Load chunk store - SnapshotRestore restore; try (ChunkStore chunkStore = snapshot.getChunkStore()) { BBC.SNAPSHOT_LOADED.send(player, snapshot.getName()); // Restore snapshot - restore = new SnapshotRestore(chunkStore, editSession, region); + SnapshotRestore restore = new SnapshotRestore(chunkStore, editSession, region); //player.print(restore.getChunksAffected() + " chunk(s) will be loaded."); restore.restore(); if (restore.hadTotalFailure()) { String error = restore.getLastErrorMessage(); - if (error != null) { - BBC.SNAPSHOT_ERROR_RESTORE.send(player); + if (!restore.getMissingChunks().isEmpty()) { + player.printError("Chunks were not present in snapshot."); + } else if (error != null) { + player.printError("Errors prevented any blocks from being restored."); player.printError("Last error: " + error); } else { - BBC.SNAPSHOT_ERROR_RESTORE_CHUNKS.send(player); + player.printError("No chunks could be loaded. (Bad archive?)"); } } else { player.print(String.format("Restored; %d " - + "missing chunks and %d other errors.", + + "missing chunks and %d other errors.", restore.getMissingChunks().size(), restore.getErrorChunks().size())); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java index ce5f0054f..f1e884356 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.LocalConfiguration; @@ -42,14 +42,11 @@ public class SuperPickaxeCommands { @Command( aliases = {"single"}, - usage = "", desc = "Enable the single block super pickaxe mode", - min = 0, max = 0 ) @CommandPermissions("worldedit.superpickaxe") public void single(Player player, LocalSession session) throws WorldEditException { - session.setSuperPickaxe(new SinglePickaxe()); session.enableSuperPickAxe(); BBC.SUPERPICKAXE_AREA_ENABLED.send(player); 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 2d9e6c62e..4da8518a9 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 @@ -21,16 +21,10 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.brush.InspectBrush; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -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.world.block.BlockState; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.command.tool.BlockDataCyler; import com.sk89q.worldedit.command.tool.BlockReplacer; @@ -38,20 +32,22 @@ 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.LongRangeBuildTool; +import com.sk89q.worldedit.command.tool.NavigationWand; import com.sk89q.worldedit.command.tool.QueryTool; +import com.sk89q.worldedit.command.tool.SelectionWand; 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.function.pattern.Pattern; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.TreeGenerator; -import com.sk89q.worldedit.util.command.parametric.Optional; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.function.pattern.BlockPattern; -import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.util.HandSide; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.world.item.ItemType; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; -@Command(aliases = {"brush", "br", "tool"}, desc = "Bind functions to held items: [More Info](https://goo.gl/xPnPxj)") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class ToolCommands { private final WorldEdit we; @@ -60,132 +56,169 @@ public class ToolCommands { } @Command( - aliases = {"info", "/info"}, - usage = "", - desc = "Block information tool", - min = 0, - max = 0 + name = "none", + desc = "Unbind a bound tool from your current item" + ) + public void none(Player player, LocalSession session) throws WorldEditException { + + session.setTool(player.getItemInHand(HandSide.MAIN_HAND).getType(), null); + player.print("Tool unbound from your current item."); + } + + @Command( + name = "/selwand", + aliases = "selwand", + desc = "Selection wand tool" + ) + @CommandPermissions("worldedit.setwand") + public void selwand(Player player, LocalSession session) throws WorldEditException { + + final ItemType itemType = player.getItemInHand(HandSide.MAIN_HAND).getType(); + session.setTool(itemType, new SelectionWand()); + player.print("Selection wand bound to " + itemType.getName() + "."); + } + + @Command( + name = "/navwand", + aliases = "navwand", + desc = "Navigation wand tool" + ) + @CommandPermissions("worldedit.setwand") + public void navwand(Player player, LocalSession session) throws WorldEditException { + + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new NavigationWand()); + player.print("Navigation wand bound to " + itemStack.getType().getName() + "."); + } + + @Command( + name = "info", + desc = "Block information tool" ) @CommandPermissions("worldedit.tool.info") public void info(Player player, LocalSession session) throws WorldEditException { - session.setTool(new QueryTool(), player); + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new QueryTool()); BBC.TOOL_INFO.send(player, itemStack.getType().getName()); } @Command( - aliases = {"inspect"}, - usage = "", - desc = "Inspect edits within a radius", - help = - "Chooses the inspect brush", - min = 0, - max = 0 + name = "inspect", + desc = "Inspect edits within a radius" ) @CommandPermissions("worldedit.tool.inspect") - public void inspectBrush(Player player, LocalSession session, @Optional("1") double radius) throws WorldEditException { - session.setTool(new InspectBrush(), player); - BBC.TOOL_INSPECT.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + public void inspectBrush(Player player, LocalSession session, + @Arg(desc = "The radius of the brush", def = "1") + double radius) throws WorldEditException { + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new InspectBrush()); + BBC.TOOL_INSPECT.send(player, itemStack.getType().getName()); } @Command( - aliases = {"tree"}, - usage = "[type]", - desc = "Tree generator tool", - min = 0, - max = 1 + name = "tree", + desc = "Tree generator tool" ) @CommandPermissions("worldedit.tool.tree") - @SuppressWarnings("deprecation") - public void tree(Player player, LocalSession session, @Optional("tree") TreeGenerator.TreeType type, CommandContext args) throws WorldEditException { - session.setTool(new TreePlanter(type), player); - BBC.TOOL_TREE.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + public void tree(Player player, LocalSession session, + @Arg(desc = "Type of tree to generate", def = "tree") + TreeGenerator.TreeType type) throws WorldEditException { + + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new TreePlanter(type)); + BBC.TOOL_TREE.send(player, itemStack.getType().getName()); } @Command( - aliases = {"repl"}, - usage = "", - desc = "Block replacer tool", - min = 1, - max = 1 + name = "repl", + desc = "Block replacer tool" ) @CommandPermissions("worldedit.tool.replacer") - public void repl(Player player, LocalSession session, Pattern pattern) throws WorldEditException { - session.setTool(new BlockReplacer(pattern), player); - BBC.TOOL_REPL.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + public void repl(Player player, LocalSession session, + @Arg(desc = "The pattern of blocks to place") + Pattern pattern) throws WorldEditException { + + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new BlockReplacer(pattern)); + BBC.TOOL_REPL.send(player, itemStack.getType().getName()); } @Command( - aliases = {"cycler"}, - usage = "", - desc = "Block data cycler tool", - min = 0, - max = 0 + name = "cycler", + desc = "Block data cycler tool" ) @CommandPermissions("worldedit.tool.data-cycler") public void cycler(Player player, LocalSession session) throws WorldEditException { - session.setTool(new BlockDataCyler(), player); - BBC.TOOL_CYCLER.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new BlockDataCyler()); + BBC.TOOL_CYCLER.send(player, itemStack.getType().getName()); } @Command( - aliases = {"floodfill", "flood"}, - usage = " ", - desc = "Flood fill tool", - min = 2, - max = 2 + name = "floodfill", + aliases = { "flood" }, + desc = "Flood fill tool" ) @CommandPermissions("worldedit.tool.flood-fill") - public void floodFill(Player player, EditSession editSession, LocalSession session, Pattern pattern, int range) throws WorldEditException { + public void floodFill(Player player, LocalSession session, + @Arg(desc = "The pattern to flood fill") + Pattern pattern, + @Arg(desc = "The range to perform the fill") + int range) throws WorldEditException { + LocalConfiguration config = we.getConfiguration(); + if (range > config.maxSuperPickaxeSize) { BBC.TOOL_RANGE_ERROR.send(player, config.maxSuperPickaxeSize); return; } - session.setTool(new FloodFillTool(range, pattern), player); - BBC.TOOL_FLOOD_FILL.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new FloodFillTool(range, pattern)); + BBC.TOOL_FLOOD_FILL.send(player, itemStack.getType().getName()); } @Command( - aliases = {"deltree"}, - usage = "", - desc = "Floating tree remover tool", - min = 0, - max = 0 + name = "deltree", + desc = "Floating tree remover tool" ) @CommandPermissions("worldedit.tool.deltree") - public void deltree(Player player, LocalSession session, CommandContext args) throws WorldEditException { - session.setTool(new FloatingTreeRemover(), player); - BBC.TOOL_DELTREE.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + public void deltree(Player player, LocalSession session) throws WorldEditException { + + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new FloatingTreeRemover()); + BBC.TOOL_DELTREE.send(player, itemStack.getType().getName()); } @Command( - aliases = {"farwand"}, - usage = "", - desc = "Wand at a distance tool", - min = 0, - max = 0 + name = "farwand", + desc = "Wand at a distance tool" ) @CommandPermissions("worldedit.tool.farwand") - public void farwand(Player player, LocalSession session, CommandContext args) throws WorldEditException { - session.setTool(new DistanceWand(), player); - BBC.TOOL_FARWAND.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + public void farwand(Player player, LocalSession session) throws WorldEditException { + + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + session.setTool(itemStack.getType(), new DistanceWand()); + BBC.TOOL_FARWAND.send(player, itemStack.getType().getName()); } @Command( - aliases = {"lrbuild", "/lrbuild"}, - usage = " ", - desc = "Long-range building tool", - min = 2, - max = 2 + name = "lrbuild", + aliases = { "/lrbuild" }, + desc = "Long-range building tool" ) @CommandPermissions("worldedit.tool.lrbuild") - public void longrangebuildtool(Player player, LocalSession session, Pattern secondary, Pattern primary) throws WorldEditException { - session.setTool(new LongRangeBuildTool(primary, secondary), player); - BBC.TOOL_LRBUILD_BOUND.send(player, player.getItemInHand(HandSide.MAIN_HAND).getType().getName()); + public void longrangebuildtool(Player player, LocalSession session, + @Arg(desc = "Pattern to set on left-click") + Pattern primary, + @Arg(desc = "Pattern to set on right-click") + Pattern secondary) throws WorldEditException { + BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); + + session.setTool(itemStack.getType(), new LongRangeBuildTool(primary, secondary)); + BBC.TOOL_LRBUILD_BOUND.send(player, itemStack.getType().getName()); BBC.TOOL_LRBUILD_INFO.send(player, secondary, primary); } - - } 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 bfe93db54..004353be5 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 @@ -19,23 +19,23 @@ package com.sk89q.worldedit.command; -import com.boydti.fawe.config.BBC; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.LocalSession; 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.entity.Player; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.util.HandSide; -import com.sk89q.worldedit.util.command.parametric.Optional; +import org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; /** * Tool commands. */ +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class ToolUtilCommands { private final WorldEdit we; @@ -44,93 +44,98 @@ public class ToolUtilCommands { } @Command( - aliases = { "/", "," }, - usage = "[on|off]", - desc = "Toggle the super pickaxe function", - min = 0, - max = 1 + name = "/", + aliases = { "," }, + desc = "Toggle the super pickaxe function" ) @CommandPermissions("worldedit.superpickaxe") - public void togglePickaxe(Player player, LocalSession session, CommandContext args) throws WorldEditException { - - String newState = args.getString(0, null); - if (session.hasSuperPickAxe()) { - if ("on".equals(newState)) { - player.printError("Super pick axe already enabled."); - return; - } - + public void togglePickaxe(Player player, LocalSession session, + @Arg(desc = "The new super pickaxe state", def = "") + Boolean superPickaxe) { + boolean hasSuperPickAxe = session.hasSuperPickAxe(); + if (superPickaxe != null && superPickaxe == hasSuperPickAxe) { + player.printError("Super pickaxe already " + (superPickaxe ? "enabled" : "disabled") + "."); + return; + } + if (hasSuperPickAxe) { session.disableSuperPickAxe(); - player.print("Super pick axe disabled."); + player.print("Super pickaxe disabled."); } else { - if ("off".equals(newState)) { - player.printError("Super pick axe already disabled."); - return; - } session.enableSuperPickAxe(); - player.print("Super pick axe enabled."); + player.print("Super pickaxe enabled."); } } @Command( - aliases = { "mask" }, - usage = "[mask]", - desc = "Set the brush mask", - min = 0, - max = -1 + name = "mask", + desc = "Set the brush mask" ) @CommandPermissions("worldedit.brush.options.mask") - public void mask(Player player, LocalSession session, @Optional Mask mask) throws WorldEditException { + public void mask(Player player, LocalSession session, + @Arg(desc = "The mask to set", def = "") + Mask mask) throws WorldEditException { + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(mask); if (mask == null) { - session.getBrushTool(player).setMask(null); player.print("Brush mask disabled."); } else { - session.getBrushTool(player).setMask(mask); player.print("Brush mask set."); } } @Command( - aliases = { "mat", "material" }, - usage = "[pattern]", - desc = "Set the brush material", - min = 1, - max = 1 + name = "material", + aliases = { "/material" }, + desc = "Set the brush material" ) @CommandPermissions("worldedit.brush.options.material") - public void material(Player player, LocalSession session, Pattern pattern) throws WorldEditException { - session.getBrushTool(player).setFill(pattern); + public void material(Player player, LocalSession session, + @Arg(desc = "The pattern of blocks to use") + Pattern pattern) throws WorldEditException { + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setFill(pattern); player.print("Brush material set."); } @Command( - aliases = { "range" }, - usage = "[pattern]", - desc = "Set the brush range", - min = 1, - max = 1 - ) + name = "range", + desc = "Set the brush range" + ) @CommandPermissions("worldedit.brush.options.range") - public void range(Player player, LocalSession session, CommandContext args) throws WorldEditException { - int range = args.getInteger(0); - session.getBrushTool(player).setRange(range); + public void range(Player player, LocalSession session, + @Arg(desc = "The range of the brush") + int range) throws WorldEditException { + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setRange(range); player.print("Brush range set."); } @Command( - aliases = { "size" }, - usage = "[size]", - desc = "Set the brush size", - min = 1, - max = 1 + name = "size", + desc = "Set the brush size" ) @CommandPermissions("worldedit.brush.options.size") - public void size(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void size(Player player, LocalSession session, + @Arg(desc = "The size of the brush") + int size) throws WorldEditException { + we.checkMaxBrushRadius(size); - int radius = args.getInteger(0); - we.checkMaxBrushRadius(radius); - session.getBrushTool(player).setSize(radius); + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setSize(size); player.print("Brush size set."); } + + @Command( + name = "tracemask", + desc = "Set the mask used to stop tool traces" + ) + @CommandPermissions("worldedit.brush.options.tracemask") + public void traceMask(Player player, LocalSession session, + @Arg(desc = "The trace mask to set", def = "") + Mask mask) throws WorldEditException { + //TODO TraceMask + //session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setTraceMask(mask); + if (mask == null) { + player.print("Trace mask disabled."); + } else { + player.print("Trace mask set."); + } + } } 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 c241ebb3a..a5324072f 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 @@ -10,7 +10,7 @@ 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.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; 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 8e893f2a8..df5dbb644 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 @@ -19,171 +19,98 @@ package com.sk89q.worldedit.command; +import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; + import com.boydti.fawe.Fawe; -import com.boydti.fawe.FaweAPI; -import com.boydti.fawe.command.FaweParser; import com.boydti.fawe.config.BBC; -import com.boydti.fawe.config.Commands; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.DelegateConsumer; -import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RunnableVal3; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; -import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.chat.Message; -import com.boydti.fawe.util.chat.UsageMessage; import com.boydti.fawe.util.image.ImageUtil; -import com.sk89q.minecraft.util.commands.*; - -import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; - -import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.MaxChangedBlocksException; 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.CreatureButcher; import com.sk89q.worldedit.command.util.EntityRemover; +import com.sk89q.worldedit.command.util.Logging; +import com.sk89q.worldedit.command.util.PrintCommandHelp; +import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.extension.factory.DefaultTransformParser; -import com.sk89q.worldedit.extension.factory.parser.mask.DefaultMaskParser; -import com.sk89q.worldedit.extension.factory.parser.pattern.DefaultPatternParser; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.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; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.EntityVisitor; -import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; -import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.command.binding.Range; -import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Dispatcher; -import com.sk89q.worldedit.util.command.binding.Text; +import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.util.formatting.component.SubtleFormat; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockTypes; - -import javax.imageio.ImageIO; -import java.awt.*; +import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.net.URI; import java.nio.file.Files; -import java.util.*; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.*; +import java.util.Locale; +import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.imageio.ImageIO; +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.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; /** * Utility commands. */ -@Command(aliases = {}, desc = "Various utility commands: [More Info](http://wiki.sk89q.com/wiki/WorldEdit/Utilities)") -public class UtilityCommands extends MethodCommands { +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) +public class UtilityCommands { private final WorldEdit we; public UtilityCommands(WorldEdit we) { - super(we); this.we = we; } @Command( - aliases = {"patterns"}, - usage = "[page=1|search|pattern]", - desc = "View help about patterns", - help = "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", - queued = false - ) - @CommandPermissions("worldedit.patterns") - public void patterns(Player player, LocalSession session, CommandContext args) throws WorldEditException { - displayModifierHelp(player, DefaultPatternParser.class, args); - } - - @Command( - aliases = {"masks"}, - usage = "[page=1|search|mask]", - desc = "View help about masks", - help = "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", - queued = false - ) - @CommandPermissions("worldedit.masks") - public void masks(Player player, LocalSession session, CommandContext args) throws WorldEditException { - displayModifierHelp(player, DefaultMaskParser.class, args); - } - - @Command( - aliases = {"transforms"}, - usage = "[page=1|search|transform]", - desc = "View help about transforms", - help = "Transforms modify how a block is placed\n" + - " - Use [brackets] for arguments\n" + - " - Use , to OR multiple\n" + - " - Use & to AND multiple\n" + - "More Info: https://git.io/v9KHO", - queued = false - ) - @CommandPermissions("worldedit.transforms") - public void transforms(Player player, LocalSession session, CommandContext args) throws WorldEditException { - displayModifierHelp(player, DefaultTransformParser.class, args); - } - - private void displayModifierHelp(Player player, Class clazz, CommandContext args) { - FaweParser parser = FaweAPI.getParser(clazz); - if (args.argsLength() == 0) { - String base = getCommand().aliases()[0]; - UsageMessage msg = new UsageMessage(getCallable(), (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + base, args.getLocals()); - msg.newline().paginate(base, 0, 1).send(player); - return; - } - if (parser != null) { - CommandMapping mapping = parser.getDispatcher().get(args.getString(0)); - if (mapping != null) { - new UsageMessage(mapping.getCallable(), args.getString(0), args.getLocals()) { - @Override - public String separateArg(String arg) { - return "&7[" + arg + "&7]"; - } - }.send(player); - } else { - UtilityCommands.help(args, player, getCommand().aliases()[0] + " ", parser.getDispatcher()); - } - } - } - - @Command( - aliases = {"/heightmapinterface"}, + name = "/heightmapinterface", desc = "Generate the heightmap interface: https://github.com/boy0001/HeightMap" ) @CommandPermissions("fawe.admin") @@ -241,10 +168,10 @@ public class UtilityCommands extends MethodCommands { } @Command( - aliases = {"/cancel", "fcancel"}, - desc = "Cancel your current command", - max = 0, - queued = false + name = "/cancel", + aliases= {"fcancel"}, + desc = "Cancel your current command" + //queued = false ) @CommandPermissions("fawe.cancel") public void cancel(FawePlayer player) { @@ -253,293 +180,434 @@ public class UtilityCommands extends MethodCommands { } @Command( - aliases = { "/fill" }, - usage = " [depth] [direction]", - desc = "Fill a hole", - min = 2, - max = 4 + name = "/fill", + desc = "Fill a hole" ) @CommandPermissions("worldedit.fill") @Logging(PLACEMENT) - public void fill(Player player, LocalSession session, EditSession editSession, Pattern pattern, double radius, @Optional("1") double depth, @Optional("down") @Direction BlockVector3 direction) throws WorldEditException { + public int fill(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The blocks to fill with") + Pattern pattern, + @Arg(desc = "The radius to fill in") + double radius, + @Arg(desc = "The depth to fill", def = "1") + int depth, + @Arg(desc = "Direction to fill", def = "down") BlockVector3 direction) throws WorldEditException { + radius = Math.max(1, radius); we.checkMaxRadius(radius); + depth = Math.max(1, depth); + BlockVector3 pos = session.getPlacementPosition(player); - int affected = editSession.fillDirection(pos, pattern, radius, (int) depth, direction); + int affected = editSession.fillDirection(pos, pattern, radius, depth, direction); player.print(affected + " block(s) have been created."); + return affected; + } + +/* + @Command( + name = "patterns", + desc = "View help about patterns", + help = "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" + //queued = false + ) + @CommandPermissions("worldedit.patterns") + public void patterns(Player player, LocalSession session, CommandContext args) throws WorldEditException { + displayModifierHelp(player, DefaultPatternParser.class, args); } @Command( - aliases = { "/fillr" }, - usage = " [depth]", - desc = "Fill a hole recursively", - min = 2, - max = 3 + name = "masks", + usage = "[page=1|search|mask]", + desc = "View help about masks", + help = "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" + //queued = false + ) + @CommandPermissions("worldedit.masks") + public void masks(Player player, LocalSession session, CommandContext args) throws WorldEditException { + displayModifierHelp(player, DefaultMaskParser.class, args); + } + + @Command( + name = "transforms", + desc = "View help about transforms", + help = "Transforms modify how a block is placed\n" + + " - Use [brackets] for arguments\n" + + " - Use , to OR multiple\n" + + " - Use & to AND multiple\n" + + "More Info: https://git.io/v9KHO", + queued = false + ) + @CommandPermissions("worldedit.transforms") + public void transforms(Player player, LocalSession session, CommandContext args) throws WorldEditException { + displayModifierHelp(player, DefaultTransformParser.class, args); + } + + private void displayModifierHelp(Player player, Class clazz, CommandContext args) { + FaweParser parser = FaweAPI.getParser(clazz); + if (args.argsLength() == 0) { + String base = getCommand().aliases()[0]; + UsageMessage msg = new UsageMessage(getCallable(), "/" + base, args.getLocals()); + msg.newline().paginate(base, 0, 1).send(player); + return; + } + if (parser != null) { + CommandMapping mapping = parser.getDispatcher().get(args.getString(0)); + if (mapping != null) { + new UsageMessage(mapping.getCallable(), args.getString(0), args.getLocals()) { + @Override + public String separateArg(String arg) { + return "&7[" + arg + "&7]"; + } + }.send(player); + } else { + UtilityCommands.help(args, player, getCommand().aliases()[0] + " ", parser.getDispatcher()); + } + } + } +*/ + + @Command( + name = "/fillr", + desc = "Fill a hole recursively" ) @CommandPermissions("worldedit.fill.recursive") @Logging(PLACEMENT) - public void fillr(Player player, LocalSession session, EditSession editSession, Pattern pattern, double radius, @Optional("-1") double depth) throws WorldEditException { + public int fillr(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The blocks to fill with") + Pattern pattern, + @Arg(desc = "The radius to fill in") + double radius, + @Arg(desc = "The depth to fill", def = "") + Integer depth) throws WorldEditException { + radius = Math.max(1, radius); we.checkMaxRadius(radius); + depth = depth == null ? Integer.MAX_VALUE : Math.max(1, depth); + we.checkMaxRadius(radius); + BlockVector3 pos = session.getPlacementPosition(player); - if (depth == -1) depth = Integer.MAX_VALUE; - int affected = editSession.fillXZ(pos, pattern, radius, (int) depth, true); + int affected = editSession.fillXZ(pos, pattern, radius, depth, true); player.print(affected + " block(s) have been created."); + return affected; } @Command( - aliases = { "/drain" }, - usage = "", - flags = "w", - desc = "Drain a pool", - help = "Removes all connected water sources.\n" + - " If -w is specified, also makes waterlogged blocks non-waterlogged.", - min = 1, - max = 1 + name = "/drain", + desc = "Drain a pool" ) @CommandPermissions("worldedit.drain") @Logging(PLACEMENT) - public void drain(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - double radius = Math.max(0, args.getDouble(0)); - boolean waterlogged = args.hasFlag('w'); + public int drain(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius to drain") + double radius, + @Switch(name = 'w', desc = "Also un-waterlog blocks") + boolean waterlogged) throws WorldEditException { + radius = Math.max(0, radius); we.checkMaxRadius(radius); int affected = editSession.drainArea( - session.getPlacementPosition(player), radius, waterlogged); + session.getPlacementPosition(player), radius, waterlogged); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/fixlava", "fixlava" }, - usage = "", - desc = "Fix lava to be stationary", - min = 1, - max = 1 + name = "fixlava", + aliases = { "/fixlava" }, + desc = "Fix lava to be stationary" ) @CommandPermissions("worldedit.fixlava") @Logging(PLACEMENT) - public void fixLava(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - double radius = Math.max(0, args.getDouble(0)); + public int fixLava(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius to fix in") + double radius) throws WorldEditException { + radius = Math.max(0, radius); we.checkMaxRadius(radius); int affected = editSession.fixLiquid(session.getPlacementPosition(player), radius, BlockTypes.LAVA); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/fixwater", "fixwater" }, - usage = "", - desc = "Fix water to be stationary", - min = 1, - max = 1 + name = "fixwater", + aliases = { "/fixwater" }, + desc = "Fix water to be stationary" ) @CommandPermissions("worldedit.fixwater") @Logging(PLACEMENT) - public void fixWater(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - double radius = Math.max(0, args.getDouble(0)); + public int fixWater(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius to fix in") + double radius) throws WorldEditException { + radius = Math.max(0, radius); we.checkMaxRadius(radius); int affected = editSession.fixLiquid(session.getPlacementPosition(player), radius, BlockTypes.WATER); BBC.VISITOR_BLOCK.send(player, affected); + return affected; } @Command( - aliases = { "/removeabove", "removeabove" }, - usage = "[size] [height]", - desc = "Remove blocks above your head.", - min = 0, - max = 2 + name = "removeabove", + aliases = { "/removeabove" }, + desc = "Remove blocks above your head." ) @CommandPermissions("worldedit.removeabove") @Logging(PLACEMENT) - public void removeAbove(Player player, LocalSession session, EditSession editSession, @Optional("1") double size, @Optional("256") double height) throws WorldEditException { - + public int removeAbove(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The apothem of the square to remove from", def = "1") + int size, + @Arg(desc = "The maximum height above you to remove from", def = "") + Integer height) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); - int affected = editSession.removeAbove(session.getPlacementPosition(player), (int) size, (int) height); + int affected = editSession.removeAbove(session.getPlacementPosition(player), size, height); BBC.VISITOR_BLOCK.send(player, affected); + return affected; } @Command( - aliases = { "/removebelow", "removebelow" }, - usage = "[size] [height]", - desc = "Remove blocks below you.", - min = 0, - max = 2 + name = "removebelow", + aliases = { "/removebelow" }, + desc = "Remove blocks below you." ) @CommandPermissions("worldedit.removebelow") @Logging(PLACEMENT) - public void removeBelow(Player player, LocalSession session, EditSession editSession, @Optional("1") double size, @Optional("256") double height) throws WorldEditException { - + public int removeBelow(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The apothem of the square to remove from", def = "1") + int size, + @Arg(desc = "The maximum height below you to remove from", def = "") + Integer height) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); - int affected = editSession.removeBelow(session.getPlacementPosition(player), (int) size, (int) height); + World world = player.getWorld(); + height = height != null ? Math.min((world.getMaxY() + 1), height + 1) : (world.getMaxY() + 1); + + int affected = editSession.removeBelow(session.getPlacementPosition(player), size, height); BBC.VISITOR_BLOCK.send(player, affected); + return affected; } @Command( - aliases = { "/removenear", "removenear" }, - usage = " [size]", - desc = "Remove blocks near you.", - min = 1, - max = 2 + name = "removenear", + aliases = { "/removenear" }, + desc = "Remove blocks near you." ) @CommandPermissions("worldedit.removenear") @Logging(PLACEMENT) - public void removeNear(Player player, LocalSession session, EditSession editSession, Mask mask, @Optional("50") double size) throws WorldEditException { + public int removeNear(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The mask of blocks to remove") + Mask mask, + @Arg(desc = "The radius of the square to remove from", def = "50") + int radius) throws WorldEditException { + radius = Math.max(1, radius); + we.checkMaxRadius(radius); - we.checkMaxRadius(size); - size = Math.max(1, size); - int affected = editSession.removeNear(session.getPlacementPosition(player), mask, (int) size); + int affected = editSession.removeNear(session.getPlacementPosition(player), mask, radius); BBC.VISITOR_BLOCK.send(player, affected); + return affected; } @Command( - aliases = { "/replacenear", "replacenear" }, - usage = " ", - desc = "Replace nearby blocks", - flags = "f", - min = 3, - max = 3 + name = "replacenear", + aliases = { "/replacenear" }, + desc = "Replace nearby blocks" ) @CommandPermissions("worldedit.replacenear") @Logging(PLACEMENT) - public void replaceNear(Player player, LocalSession session, EditSession editSession, double size, @Optional Mask from, Pattern to) throws WorldEditException { + public int replaceNear(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the square to remove in") + int radius, + @Arg(desc = "The mask matching blocks to remove", def = "") + Mask from, + @Arg(desc = "The pattern of blocks to replace with") + Pattern to) throws WorldEditException { + radius = Math.max(1, radius); + we.checkMaxRadius(radius); + + BlockVector3 base = session.getPlacementPosition(player); + BlockVector3 min = base.subtract(radius, radius, radius); + BlockVector3 max = base.add(radius, radius, radius); + Region region = new CuboidRegion(player.getWorld(), min, max); if (from == null) { from = new ExistingBlockMask(editSession); } - int affected; - BlockVector3 base = session.getPlacementPosition(player); - BlockVector3 min = base.subtract((int)size, (int)size, (int)size); - BlockVector3 max = base.add((int)size, (int)size, (int)size); - Region region = new CuboidRegion(player.getWorld(), min, max); - - affected = editSession.replaceBlocks(region, from, to); + int affected = editSession.replaceBlocks(region, from, to); BBC.VISITOR_BLOCK.send(player, affected); + return affected; } @Command( - aliases = { "/snow", "snow" }, - usage = "[radius]", - desc = "Simulates snow", - min = 0, - max = 1 + name = "snow", + aliases = { "/snow" }, + desc = "Simulates snow" ) @CommandPermissions("worldedit.snow") @Logging(PLACEMENT) - public void snow(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; + public int snow(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the circle to snow in", def = "10") + double size) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); int affected = editSession.simulateSnow(session.getPlacementPosition(player), size); player.print(affected + " surfaces covered. Let it snow~"); + return affected; } @Command( - aliases = {"/thaw", "thaw"}, - usage = "[radius]", - desc = "Thaws the area", - min = 0, - max = 1 + name = "thaw", + aliases = { "/thaw" }, + desc = "Thaws the area" ) @CommandPermissions("worldedit.thaw") @Logging(PLACEMENT) - public void thaw(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; + public int thaw(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the circle to thaw in", def = "10") + double size) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); int affected = editSession.thaw(session.getPlacementPosition(player), size); player.print(affected + " surfaces thawed."); + return affected; } @Command( - aliases = { "/green", "green" }, - usage = "[radius]", - desc = "Greens the area", - help = "Converts dirt to grass blocks. -f also converts coarse dirt.", - flags = "f", - min = 0, - max = 1 + name = "green", + aliases = { "/green" }, + desc = "Converts dirt to grass blocks in the area" ) @CommandPermissions("worldedit.green") @Logging(PLACEMENT) - public void green(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - final double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; + public int green(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the circle to convert in", def = "10") + double size, + @Switch(name = 'f', desc = "Also convert coarse dirt") + boolean convertCoarse) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); - final boolean onlyNormalDirt = !args.hasFlag('f'); + final boolean onlyNormalDirt = !convertCoarse; final int affected = editSession.green(session.getPlacementPosition(player), size, onlyNormalDirt); BBC.VISITOR_BLOCK.send(player, affected); + return affected; } @Command( - aliases = { "/ex", "/ext", "/extinguish", "ex", "ext", "extinguish" }, - usage = "[radius]", - desc = "Extinguish nearby fire", - min = 0, - max = 1 - ) + name = "extinguish", + aliases = { "/ex", "/ext", "/extinguish", "ex", "ext" }, + desc = "Extinguish nearby fire" + ) @CommandPermissions("worldedit.extinguish") @Logging(PLACEMENT) - public void extinguish(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public void extinguish(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the square to remove in", def = "") + Integer radius) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); int defaultRadius = config.maxRadius != -1 ? Math.min(40, config.maxRadius) : 40; - int size = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) - : defaultRadius; + int size = radius != null ? Math.max(1, radius) : defaultRadius; we.checkMaxRadius(size); - int affected = editSession.removeNear(session.getPlacementPosition(player), BlockTypes.FIRE.toMask(editSession), size); + Mask mask = new BlockTypeMask(editSession, BlockTypes.FIRE); + int affected = editSession.removeNear(session.getPlacementPosition(player), mask, size); BBC.VISITOR_BLOCK.send(player, affected); } @Command( - aliases = { "butcher" }, - usage = "[radius]", - flags = "plangbtfr", - desc = "Kill all or nearby mobs", - help = - "Kills nearby mobs, based on radius, if none is given uses default in configuration.\n" + - "Flags:\n" + - " -p also kills pets.\n" + - " -n also kills NPCs.\n" + - " -g also kills Golems.\n" + - " -a also kills animals.\n" + - " -b also kills ambient mobs.\n" + - " -t also kills mobs with name tags.\n" + - " -f compounds all previous flags.\n" + - " -r also destroys armor stands.\n" + - " -l currently does nothing.", - min = 0, - max = 1 + name = "butcher", + desc = "Kill all or nearby mobs" ) @CommandPermissions("worldedit.butcher") @Logging(PLACEMENT) - public void butcher(Actor actor, CommandContext args) throws WorldEditException { + public int butcher(Actor actor, + @Arg(desc = "Radius to kill mobs in", def = "") + Integer radius, + @Switch(name = 'p', desc = "Also kill pets") + boolean killPets, + @Switch(name = 'n', desc = "Also kill NPCs") + boolean killNpcs, + @Switch(name = 'g', desc = "Also kill golems") + boolean killGolems, + @Switch(name = 'a', desc = "Also kill animals") + boolean killAnimals, + @Switch(name = 'b', desc = "Also kill ambient mobs") + boolean killAmbient, + @Switch(name = 't', desc = "Also kill mobs with name tags") + boolean killWithName, + @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 { LocalConfiguration config = we.getConfiguration(); Player player = actor instanceof Player ? (Player) actor : null; - // technically the default can be larger than the max, but that's not my problem - int radius = config.butcherDefaultRadius; - - // there might be a better way to do this but my brain is fried right now - if (args.argsLength() > 0) { // user inputted radius, override the default - radius = args.getInteger(0); - if (radius < -1) { - actor.printError("Use -1 to remove all mobs in loaded chunks"); - return; - } - if (config.butcherMaxRadius != -1) { // clamp if there is a max - if (radius == -1) { - radius = config.butcherMaxRadius; - } else { // Math.min does not work if radius is -1 (actually highest possible value) - radius = Math.min(radius, config.butcherMaxRadius); - } + if (radius == null) { + radius = config.butcherDefaultRadius; + } else if (radius < -1) { + actor.printError("Use -1 to remove all mobs in loaded chunks"); + return 0; + } else if (radius == -1) { + if (config.butcherMaxRadius != -1) { + radius = config.butcherMaxRadius; } } + if (config.butcherMaxRadius != -1) { + radius = Math.min(radius, config.butcherMaxRadius); + } CreatureButcher flags = new CreatureButcher(actor); - flags.fromCommand(args); + flags.or(CreatureButcher.Flags.FRIENDLY, killFriendly); // No permission check here. Flags will instead be filtered by the subsequent calls. + flags.or(CreatureButcher.Flags.PETS, killPets, "worldedit.butcher.pets"); + flags.or(CreatureButcher.Flags.NPCS, killNpcs, "worldedit.butcher.npcs"); + flags.or(CreatureButcher.Flags.GOLEMS, killGolems, "worldedit.butcher.golems"); + flags.or(CreatureButcher.Flags.ANIMALS, killAnimals, "worldedit.butcher.animals"); + flags.or(CreatureButcher.Flags.AMBIENT, killAmbient, "worldedit.butcher.ambient"); + flags.or(CreatureButcher.Flags.TAGGED, killWithName, "worldedit.butcher.tagged"); + flags.or(CreatureButcher.Flags.ARMOR_STAND, killArmorStands, "worldedit.butcher.armorstands"); + int killed = killMatchingEntities(radius, player, flags::createFunction); + + actor.print("Killed " + killed + (killed != 1 ? " mobs" : " mob") + (radius < 0 ? "" : " in a radius of " + radius) + "."); + + return killed; + } + + @Command( + name = "remove", + aliases = { "rem", "rement" }, + desc = "Remove all entities of a type" + ) + @CommandPermissions("worldedit.remove") + @Logging(PLACEMENT) + public int remove(Actor actor, + @Arg(desc = "The type of entity to remove") + EntityRemover remover, + @Arg(desc = "The radius of the cuboid to remove from") + int radius) throws WorldEditException { + Player player = actor instanceof Player ? (Player) actor : null; + + if (radius < -1) { + actor.printError("Use -1 to remove all entities in loaded chunks"); + return 0; + } + + int removed = killMatchingEntities(radius, player, remover::createFunction); + + actor.print("Marked " + removed + (removed != 1 ? " entities" : " entity") + " for removal."); + return removed; + } + + private int killMatchingEntities(Integer radius, Player player, Supplier func) throws IncompleteRegionException, MaxChangedBlocksException { List visitors = new ArrayList<>(); LocalSession session = null; EditSession editSession = null; @@ -555,12 +623,12 @@ public class UtilityCommands extends MethodCommands { } else { entities = editSession.getEntities(); } - visitors.add(new EntityVisitor(entities.iterator(), flags.createFunction())); + visitors.add(new EntityVisitor(entities.iterator(), func.get())); } else { Platform platform = we.getPlatformManager().queryCapability(Capability.WORLD_EDITING); for (World world : platform.getWorlds()) { List entities = world.getEntities(); - visitors.add(new EntityVisitor(entities.iterator(), flags.createFunction())); + visitors.add(new EntityVisitor(entities.iterator(), func.get())); } } @@ -570,111 +638,48 @@ public class UtilityCommands extends MethodCommands { killed += visitor.getAffected(); } - BBC.KILL_SUCCESS.send(actor, killed, radius); + BBC.KILL_SUCCESS.send(player, killed, radius); if (editSession != null) { session.remember(editSession); editSession.flushSession(); } + return killed; + } + + // get the formatter with the system locale. in the future, if we can get a local from a player, we can use that + private static final DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.getDefault()); + static { + formatter.applyPattern("#,##0.#####"); // pattern is locale-insensitive. this can translate to "1.234,56789" } @Command( - aliases = { "remove", "rem", "rement" }, - usage = " ", - desc = "Remove all entities of a type", - min = 2, - max = 2 - ) - @CommandPermissions("worldedit.remove") - @Logging(PLACEMENT) - public void remove(Actor actor, CommandContext args) throws WorldEditException, CommandException { - String typeStr = args.getString(0); - int radius = args.getInteger(1); - Player player = actor instanceof Player ? (Player) actor : null; - - if (radius < -1) { - actor.printError("Use -1 to remove all entities in loaded chunks"); - return; - } - - EntityRemover remover = new EntityRemover(); - remover.fromString(typeStr); - - List visitors = new ArrayList<>(); - LocalSession session = null; - EditSession editSession = null; - - if (player != null) { - session = we.getSessionManager().get(player); - BlockVector3 center = session.getPlacementPosition(player); - editSession = session.createEditSession(player); - List entities; - if (radius >= 0) { - CylinderRegion region = CylinderRegion.createRadius(editSession, center, radius); - entities = editSession.getEntities(region); - } else { - entities = editSession.getEntities(); - } - visitors.add(new EntityVisitor(entities.iterator(), remover.createFunction())); - } else { - Platform platform = we.getPlatformManager().queryCapability(Capability.WORLD_EDITING); - for (World world : platform.getWorlds()) { - List entities = world.getEntities(); - visitors.add(new EntityVisitor(entities.iterator(), remover.createFunction())); - } - } - - int removed = 0; - for (EntityVisitor visitor : visitors) { - Operations.completeLegacy(visitor); - removed += visitor.getAffected(); - } - - BBC.KILL_SUCCESS.send(actor, removed, radius); - - if (editSession != null) { - session.remember(editSession); - editSession.flushSession(); - } - } - - @Command( - aliases = { "/calc", "/calculate", "/eval", "/evaluate", "/solve" }, - usage = "", + name = "/calculate", + aliases = { "/calc", "/eval", "/evaluate", "/solve" }, desc = "Evaluate a mathematical expression" ) @CommandPermissions("worldedit.calc") - public void calc(final Actor actor, @Text String input) throws CommandException { + public void calc(Actor actor, + @Arg(desc = "Expression to evaluate", variable = true) + List input) { + Expression expression; try { - FaweLimit limit = FawePlayer.wrap(actor).getLimit(); - final Expression expression = Expression.compile(input); - - ExecutorService executor = Executors.newSingleThreadExecutor(); - Future futureResult = executor.submit((Callable) expression::evaluate); - - Double result = Double.NaN; - try { - result = futureResult.get(limit.MAX_EXPRESSION_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } catch (TimeoutException e) { - futureResult.cancel(true); - e.printStackTrace(); - } - - executor.shutdownNow(); - actor.print("= " + result); - } catch (EvaluationException e) { - actor.printError(String.format( - "'%s' could not be evaluated (error: %s)", input, e.getMessage())); + expression = Expression.compile(String.join(" ", input)); } catch (ExpressionException e) { actor.printError(String.format( - "'%s' could not be parsed as a valid expression", input)); + "'%s' could not be parsed as a valid expression", input)); + return; } + WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> { + double result = expression.evaluate( + new double[]{}, WorldEdit.getInstance().getSessionManager().get(actor).getTimeout()); + String formatted = Double.isNaN(result) ? "NaN" : formatter.format(result); + return SubtleFormat.wrap(input + " = ").append(TextComponent.of(formatted, TextColor.LIGHT_PURPLE)); + }, null); } @Command( - aliases = {"/confirm"}, + name = "/confirm", desc = "Confirm a command" ) @CommandPermissions("fawe.confirm") @@ -685,16 +690,18 @@ public class UtilityCommands extends MethodCommands { } @Command( - aliases = { "/help" }, - usage = "[]", - desc = "Displays help for WorldEdit commands", - min = 0, - max = -1, - queued = false + name = "/help", + desc = "Displays help for WorldEdit commands" ) @CommandPermissions("worldedit.help") - public void help(Actor actor, CommandContext args) throws WorldEditException { - help(args, we, actor); + public void help(Actor actor, + @Switch(name = 's', desc = "List sub-commands of the given command, if applicable") + boolean listSubCommands, + @ArgFlag(name = 'p', desc = "The page to retrieve", def = "1") + int page, + @Arg(desc = "The command to retrieve help for", def = "", variable = true) + List command) throws WorldEditException { + PrintCommandHelp.help(command, page, listSubCommands, we, actor); } protected static CommandMapping detectCommand(Dispatcher dispatcher, String command, boolean isRootLevel) { @@ -903,7 +910,7 @@ public class UtilityCommands extends MethodCommands { UUID uuid = UUID.fromString(f.getName()); return; } - } catch (IllegalArgumentException exception) {} + } catch (IllegalArgumentException ignored) {} super.accept(f); } }; @@ -994,85 +1001,4 @@ public class UtilityCommands extends MethodCommands { return name.toString(); } - public static void help(CommandContext args, WorldEdit we, Actor actor) { - help(args, actor, "/", we.getPlatformManager().getCommandManager().getDispatcher()); - } - - public static void help(CommandContext args, Actor actor, String prefix, CommandCallable callable) { - final int perPage = actor instanceof Player ? 12 : 20; // More pages for console - - HelpBuilder builder = new HelpBuilder(callable, args, perPage) { - @Override - public void displayFailure(String message) { - actor.printError(message); - } - - @Override - public void displayUsage(CommandCallable callable, String command) { - new UsageMessage(callable, command).send(actor); - } - - @Override - public void displayCategories(Map> categories) { - Message msg = new Message(); - msg.prefix().text(BBC.HELP_HEADER_CATEGORIES).newline(); - for (Map.Entry> entry : categories.entrySet()) { - String s1 = Commands.getAlias(UtilityCommands.class, "/help") + " " + entry.getKey(); - String s2 = entry.getValue().size() + ""; - msg.text(BBC.HELP_ITEM_ALLOWED, "&a" + s1, s2); - msg.tooltip(StringMan.join(entry.getValue().keySet(), ", ", CommandMapping::getPrimaryAlias)); - msg.command(s1); - msg.newline(); - } - msg.text(BBC.HELP_FOOTER).link("https://git.io/vSKE5").newline(); - msg.paginate((prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") : prefix), 0, 1); - msg.send(actor); - } - - @Override - public void displayCommands(Map commandMap, String visited, int page, int pageTotal, int effectiveLength) { - Message msg = new Message(); - msg.prefix().text(BBC.HELP_HEADER, page + 1, pageTotal).newline(); - - CommandLocals locals = args.getLocals(); - - if (!visited.isEmpty()) { - visited = visited + " "; - } - - // Add each command - for (Map.Entry cmdEntry : commandMap.entrySet()) { - CommandMapping mapping = cmdEntry.getKey(); - String subPrefix = cmdEntry.getValue(); - - StringBuilder s1 = new StringBuilder(); - s1.append(prefix); - s1.append(subPrefix); - CommandCallable c = mapping.getCallable(); - s1.append(visited); - s1.append(mapping.getPrimaryAlias()); - String s2 = mapping.getDescription().getDescription(); - if (c.testPermission(locals)) { - msg.text(BBC.HELP_ITEM_ALLOWED, s1, s2); - String helpCmd = (prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") + " " : "") + s1; - msg.cmdTip(helpCmd); - msg.newline(); - } else { - msg.text(BBC.HELP_ITEM_DENIED, s1, s2).newline(); - } - } - - if (args.argsLength() == 0) { - msg.text(BBC.HELP_FOOTER).newline(); - } - String baseCommand = (prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") : prefix); - if (effectiveLength > 0) baseCommand += " " + args.getString(0, effectiveLength - 1); - msg.paginate(baseCommand, page + 1, pageTotal); - - msg.send(actor); - } - }; - - builder.run(); - } } 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 935e4ecce..dd9f3c553 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 @@ -24,36 +24,40 @@ import com.boydti.fawe.FaweVersion; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.util.IncendoPaster; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.LocalSession; 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.PrintCommandHelp; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extension.platform.CommandManager; -import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.PlatformManager; -import com.sk89q.worldedit.extension.platform.PlatformManager; - import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.TextStyle; +import java.time.zone.ZoneRulesException; import java.util.Date; import java.util.GregorianCalendar; +import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.TimeZone; +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.ArgFlag; +import org.enginehub.piston.annotation.param.Switch; -@Command(aliases = {"worldedit", "we", "fawe"}, desc = "Updating, informational, debug and help commands") +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class WorldEditCommands { - private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + + private static final DateTimeFormatter dateFormat = DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss z"); private final WorldEdit we; @@ -62,21 +66,20 @@ public class WorldEditCommands { } @Command( - aliases = { "version", "ver" }, - usage = "", - desc = "Get WorldEdit/FAWE version", - min = 0, - max = 0, - queued = false + name = "version", + aliases = { "ver" }, + desc = "Get WorldEdit/FAWE version" + //queued = false ) - public void version(Actor actor) throws WorldEditException { + public void version(Actor actor) { FaweVersion fVer = Fawe.get().getVersion(); String fVerStr = fVer == null ? "unknown" : "-" + fVer.build; - actor.print("FastAsyncWorldEdit-1.13" + fVerStr + " by Empire92"); + actor.print("FastAsyncWorldEdit" + fVerStr + " created by Empire92"); if (fVer != null) { actor.printDebug("----------- Platforms -----------"); FaweVersion version = Fawe.get().getVersion(); - Date date = new GregorianCalendar(2000 + version.year, version.month - 1, version.day).getTime(); + Date date = new GregorianCalendar(2000 + version.year, version.month - 1, version.day) + .getTime(); actor.printDebug(" - DATE: " + date.toLocaleString()); actor.printDebug(" - COMMIT: " + Integer.toHexString(version.hash)); actor.printDebug(" - BUILD: " + version.build); @@ -87,61 +90,55 @@ public class WorldEditCommands { actor.printDebug("----------- Platforms -----------"); for (Platform platform : pm.getPlatforms()) { - actor.printDebug(String.format("* %s", platform.getPlatformName())); + actor.printDebug(String.format("* %s (%s)", platform.getPlatformName(), platform.getPlatformVersion())); } actor.printDebug("----------- Capabilities -----------"); for (Capability capability : Capability.values()) { Platform platform = pm.queryCapability(capability); - actor.printDebug(String.format(" - %s: %s", capability.name(), platform != null ? platform.getPlatformName() : "NONE")); + actor.printDebug(String.format("%s: %s", capability.name(), platform != null ? platform.getPlatformName() : "NONE")); } actor.printDebug("------------------------------------"); actor.printDebug("Wiki: " + "https://github.com/boy0001/FastAsyncWorldedit/wiki"); } @Command( - aliases = { "reload" }, - usage = "", - desc = "Reload configuration and translations", - min = 0, - max = 0 + name = "reload", + desc = "Reload configuration and translations" ) @CommandPermissions("worldedit.reload") - public void reload(Actor actor) throws WorldEditException { + public void reload(Actor actor) { we.getPlatformManager().queryCapability(Capability.CONFIGURATION).reload(); we.getEventBus().post(new ConfigurationLoadEvent(we.getPlatformManager().queryCapability(Capability.CONFIGURATION).getConfiguration())); Fawe.get().setupConfigs(); - CommandManager.getInstance().register(we.getPlatformManager().queryCapability(Capability.USER_COMMANDS)); actor.print("Configuration and translations reloaded!"); } @Command( - aliases = {"debugpaste"}, - usage = "", - desc = "Upload latest.log, config.yml, message.yml and your commands.yml to https://athion.net/ISPaster/paste", - min = 0, - max = 0 + name = "report", + aliases = { "debugpaste" }, + desc = "Writes a report of latest.log, config.yml, message.yml and your commands.yml to https://athion.net/ISPaster/paste" ) - @CommandPermissions("worldedit.debugpaste") - public void debugpaste(Actor actor) throws WorldEditException, IOException { + @CommandPermissions({"worldedit.report", "worldedit.debugpaste"}) + public void report(Actor actor) throws WorldEditException, IOException { BBC.DOWNLOAD_LINK.send(actor, IncendoPaster.debugPaste()); } @Command( - aliases = {"threads"}, - usage = "", - desc = "Print all thread stacks", - min = 0, - max = 0, - queued = false + name = "threads", + desc = "Print all thread stacks" + //queued = false ) @CommandPermissions("worldedit.threads") public void threads(Actor actor) throws WorldEditException { Map stacks = Thread.getAllStackTraces(); for (Map.Entry entry : stacks.entrySet()) { Thread thread = entry.getKey(); - actor.printDebug("--------------------------------------------------------------------------------------------"); - actor.printDebug("Thread: " + thread.getName() + " | Id: " + thread.getId() + " | Alive: " + thread.isAlive()); + actor.printDebug( + "--------------------------------------------------------------------------------------------"); + actor.printDebug( + "Thread: " + thread.getName() + " | Id: " + thread.getId() + " | Alive: " + thread + .isAlive()); for (StackTraceElement elem : entry.getValue()) { actor.printDebug(elem.toString()); } @@ -149,41 +146,47 @@ public class WorldEditCommands { } @Command( - aliases = { "cui" }, - usage = "", - desc = "Complete CUI handshake (internal usage)", - min = 0, - max = 0 + name = "cui", + desc = "Complete CUI handshake (internal usage)" ) - public void cui(Player player, LocalSession session) throws WorldEditException { + public void cui(Player player, LocalSession session) { session.setCUISupport(true); session.dispatchCUISetup(player); } @Command( - aliases = { "tz" }, - usage = "[timezone]", - desc = "Set your timezone for snapshots", - min = 1, - max = 1 + name = "tz", + desc = "Set your timezone for snapshots" ) - public void tz(Player player, LocalSession session, CommandContext args) throws WorldEditException { - TimeZone tz = TimeZone.getTimeZone(args.getString(0)); - session.setTimezone(tz); - BBC.TIMEZONE_SET.send(player, tz.getDisplayName()); - BBC.TIMEZONE_DISPLAY.send(player, dateFormat.format(Calendar.getInstance(tz).getTime())); + public void tz(Player player, LocalSession session, + @Arg(desc = "The timezone to set") + String timezone) { + try { + ZoneId tz = ZoneId.of(timezone); + session.setTimezone(tz); + BBC.TIMEZONE_SET.send(player, tz.getDisplayName( + TextStyle.FULL, Locale.ENGLISH + )); + BBC.TIMEZONE_DISPLAY + .send(player, dateFormat.format(ZonedDateTime.now(tz))); + } catch (ZoneRulesException e) { + player.printError("Invalid timezone"); + } } @Command( - aliases = { "help" }, - usage = "[]", - desc = "Displays help for FAWE commands", - min = 0, - max = -1, - queued = false + name = "help", + desc = "Displays help for FAWE commands" + //queued = false ) @CommandPermissions("worldedit.help") - public void help(Actor actor, CommandContext args) throws WorldEditException { - UtilityCommands.help(args, we, actor); + public void help(Actor actor, + @Switch(name = 's', desc = "List sub-commands of the given command, if applicable") + boolean listSubCommands, + @ArgFlag(name = 'p', desc = "The page to retrieve", def = "1") + int page, + @Arg(desc = "The command to retrieve help for", def = "", variable = true) + List command) throws WorldEditException { + PrintCommandHelp.help(command, page, listSubCommands, we, actor); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/AbstractDirectionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/AbstractDirectionConverter.java new file mode 100644 index 000000000..bd2a28067 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/AbstractDirectionConverter.java @@ -0,0 +1,119 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.google.auto.value.AutoAnnotation; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.sk89q.worldedit.UnknownDirectionException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.MultiDirection; +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.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import javax.annotation.Nullable; +import java.util.List; + +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; + +public abstract class AbstractDirectionConverter implements ArgumentConverter { + + @AutoAnnotation + private static Direction direction(boolean includeDiagonals) { + return new AutoAnnotation_AbstractDirectionConverter_direction(includeDiagonals); + } + + @AutoAnnotation + private static MultiDirection multiDirection(boolean includeDiagonals) { + return new AutoAnnotation_AbstractDirectionConverter_multiDirection(includeDiagonals); + } + + protected static void register(CommandManager commandManager, AbstractDirectionConverter converter, + Class keyClass, boolean includeDiagonals) { + commandManager.registerConverter( + Key.of(keyClass, direction(includeDiagonals)), + converter + ); + commandManager.registerConverter( + Key.of(keyClass, multiDirection(includeDiagonals)), + CommaSeparatedValuesConverter.wrap(converter) + ); + } + + private static final ImmutableSet ORTHOGONAL = ImmutableSet.of( + "north", "south", "east", "west", "up", "down" + ); + private static final ImmutableSet RELATIVE = ImmutableSet.of( + "me", "forward", "back", "left", "right" + ); + private static final ImmutableSet DIAGONAL = ImmutableSet.of( + "northeast", "northwest", "southeast", "southwest" + ); + + private final WorldEdit worldEdit; + private final boolean includeDiagonals; + private final ImmutableList suggestions; + + protected AbstractDirectionConverter(WorldEdit worldEdit, boolean includeDiagonals) { + this.worldEdit = worldEdit; + this.includeDiagonals = includeDiagonals; + suggestions = ImmutableList.builder() + .addAll(ORTHOGONAL) + .addAll(RELATIVE) + .addAll(includeDiagonals ? DIAGONAL : ImmutableList.of()) + .build(); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + Player player = context.injectedValue(Key.of(Player.class)).orElse(null); + try { + return SuccessfulConversion.fromSingle(convertDirection(argument, player, includeDiagonals)); + } catch (Exception e) { + return FailedConversion.from(e); + } + } + + protected abstract D convertDirection(String argument, @Nullable Player player, boolean includeDiagonals) throws UnknownDirectionException; + + @Override + public Component describeAcceptableArguments() { + return TextComponent.of("`me` to use facing direction, or any " + + (includeDiagonals ? "direction" : "non-diagonal direction")); + } + + @Override + public List getSuggestions(String input) { + return limitByPrefix(suggestions.stream(), input); + } + + protected WorldEdit getWorldEdit() { + return worldEdit; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Label.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/Arguments.java similarity index 70% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Label.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/Arguments.java index 8a59732b0..a1b444cbe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Label.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/Arguments.java @@ -17,21 +17,16 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util.formatting.component; +package com.sk89q.worldedit.command.argument; -import com.sk89q.worldedit.util.formatting.Style; -import com.sk89q.worldedit.util.formatting.StyledFragment; + +import org.enginehub.piston.inject.InjectedValueAccess; /** - * Represents a fragment representing a label. + * Key-interface for {@link InjectedValueAccess} for the String arguments. */ -public class Label extends StyledFragment { +public interface Arguments { - /** - * Create a new instance. - */ - public Label() { - super(Style.YELLOW); - } + String get(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/BooleanConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/BooleanConverter.java new file mode 100644 index 000000000..731a736b3 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/BooleanConverter.java @@ -0,0 +1,45 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.google.common.collect.ImmutableSetMultimap; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.MultiKeyConverter; +import org.enginehub.piston.inject.Key; + +public class BooleanConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(Boolean.class), + MultiKeyConverter.builder( + ImmutableSetMultimap.builder() + .putAll(false, "off", "f", "false", "n", "no") + .putAll(true, "on", "t", "true", "y", "yes") + .build() + ) + .errorMessage(arg -> "Not a boolean value: " + arg) + .build() + ); + } + + private BooleanConverter() { + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java new file mode 100644 index 000000000..b3e380768 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java @@ -0,0 +1,90 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.sk89q.worldedit.util.formatting.text.TextComponent.space; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import java.util.List; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; + +public class CommaSeparatedValuesConverter implements ArgumentConverter { + + public static CommaSeparatedValuesConverter wrap(ArgumentConverter delegate) { + return wrapAndLimit(delegate, -1); + } + + public static CommaSeparatedValuesConverter wrapAndLimit(ArgumentConverter delegate, int maximum) { + return new CommaSeparatedValuesConverter<>(delegate, maximum); + } + + private static final Splitter COMMA = Splitter.on(','); + + private final ArgumentConverter delegate; + private final int maximum; + + private CommaSeparatedValuesConverter(ArgumentConverter delegate, int maximum) { + checkArgument(maximum == -1 || maximum > 1, + "Maximum must be bigger than 1, or exactly -1"); + this.delegate = delegate; + this.maximum = maximum; + } + + @Override + public Component describeAcceptableArguments() { + TextComponent.Builder result = TextComponent.builder(""); + if (maximum > -1) { + result.append(TextComponent.of("up to ")) + .append(TextComponent.of(maximum)) + .append(space()); + } + result.append(TextComponent.of("comma separated values of: ")) + .append(delegate.describeAcceptableArguments()); + return result.build(); + } + + @Override + public List getSuggestions(String input) { + String lastInput = Iterables.getLast(COMMA.split(input), ""); + return delegate.getSuggestions(lastInput); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + ImmutableList.Builder result = ImmutableList.builder(); + for (String input : COMMA.split(argument)) { + ConversionResult temp = delegate.convert(input, context); + if (!temp.isSuccessful()) { + return temp; + } + result.addAll(temp.get()); + } + return SuccessfulConversion.from(result.build()); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java new file mode 100644 index 000000000..d83bc51c6 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java @@ -0,0 +1,54 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.sk89q.worldedit.UnknownDirectionException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Direction; +import org.enginehub.piston.CommandManager; + +import javax.annotation.Nullable; +import java.util.Optional; + +public final class DirectionConverter extends AbstractDirectionConverter { + + private DirectionConverter(WorldEdit worldEdit, boolean includeDiagonals) { + super(worldEdit, includeDiagonals); + } + + public static void register(WorldEdit worldEdit, CommandManager commandManager) { + for (boolean includeDiagonals : new boolean[] { false, true }) { + DirectionConverter directionConverter = new DirectionConverter(worldEdit, includeDiagonals); + register(commandManager, directionConverter, Direction.class, includeDiagonals); + } + } + + @Override + protected Direction convertDirection(String argument, @Nullable Player player, boolean includeDiagonals) throws UnknownDirectionException { + final BlockVector3 vec = includeDiagonals + ? getWorldEdit().getDiagonalDirection(player, argument) + : getWorldEdit().getDirection(player, argument); + return Optional.ofNullable(Direction.findClosest(vec.toVector3(), Direction.Flag.ALL)) + .orElseThrow(() -> new UnknownDirectionException(argument)); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionVectorConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionVectorConverter.java new file mode 100644 index 000000000..1295fbceb --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionVectorConverter.java @@ -0,0 +1,49 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.sk89q.worldedit.UnknownDirectionException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.math.BlockVector3; +import org.enginehub.piston.CommandManager; + +import javax.annotation.Nullable; + +public final class DirectionVectorConverter extends AbstractDirectionConverter { + + private DirectionVectorConverter(WorldEdit worldEdit, boolean includeDiagonals) { + super(worldEdit, includeDiagonals); + } + + public static void register(WorldEdit worldEdit, CommandManager commandManager) { + for (boolean includeDiagonals : new boolean[] { false, true }) { + DirectionVectorConverter directionConverter = new DirectionVectorConverter(worldEdit, includeDiagonals); + register(commandManager, directionConverter, BlockVector3.class, includeDiagonals); + } + } + + @Override + protected BlockVector3 convertDirection(String argument, @Nullable Player player, boolean includeDiagonals) throws UnknownDirectionException { + return includeDiagonals + ? getWorldEdit().getDiagonalDirection(player, argument) + : getWorldEdit().getDirection(player, argument); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EditSessionHolder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EditSessionHolder.java new file mode 100644 index 000000000..b5113f545 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EditSessionHolder.java @@ -0,0 +1,87 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; + +import java.util.concurrent.locks.StampedLock; + +/** + * Lazily-created {@link EditSession}. + */ +public class EditSessionHolder { + + private final StampedLock lock = new StampedLock(); + private final WorldEdit worldEdit; + private final Player player; + + public EditSessionHolder(WorldEdit worldEdit, Player player) { + this.worldEdit = worldEdit; + this.player = player; + } + + private EditSession session; + + /** + * Get the session, but does not create it if it doesn't exist. + */ + public EditSession getSession() { + long stamp = lock.tryOptimisticRead(); + EditSession result = session; + if (!lock.validate(stamp)) { + stamp = lock.readLock(); + try { + result = session; + } finally { + lock.unlockRead(stamp); + } + } + return result; + } + + public EditSession getOrCreateSession() { + // use the already-generated result if possible + EditSession result = getSession(); + if (result != null) { + return result; + } + // otherwise, acquire write lock + long stamp = lock.writeLock(); + try { + // check session field again -- maybe another writer hit it in between + result = session; + if (result != null) { + return result; + } + // now we can do the actual creation + LocalSession localSession = worldEdit.getSessionManager().get(player); + EditSession editSession = localSession.createEditSession(player); + editSession.enableStandardMode(); + localSession.tellVersion(player); + return session = editSession; + } finally { + lock.unlockWrite(stamp); + } + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java new file mode 100644 index 000000000..bc52d24f6 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java @@ -0,0 +1,69 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.google.common.collect.ImmutableList; +import com.sk89q.worldedit.command.util.EntityRemover; +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.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.util.List; + +public class EntityRemoverConverter implements ArgumentConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(EntityRemover.class), new EntityRemoverConverter()); + } + + private final List suggestions + = ImmutableList.of("projectiles", "items", "paintings", "itemframes", "boats", "minecarts", "tnt", "xp", "all"); + + private EntityRemoverConverter() { + } + + @Override + public Component describeAcceptableArguments() { + return TextComponent.of( + "projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all" + ); + } + + @Override + public List getSuggestions(String input) { + return limitByPrefix(suggestions.stream(), input); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + try { + return SuccessfulConversion.fromSingle(EntityRemover.fromString(argument)); + } catch (Exception e) { + return FailedConversion.from(e); + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java new file mode 100644 index 000000000..41f8bae07 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java @@ -0,0 +1,72 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.google.common.collect.ImmutableSet; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.util.TreeGenerator; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.MultiKeyConverter; +import org.enginehub.piston.inject.Key; + +import javax.annotation.Nullable; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Set; +import java.util.function.Function; + +public final class EnumConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(SelectorChoice.class), + basic(SelectorChoice.class)); + commandManager.registerConverter(Key.of(TreeGenerator.TreeType.class), + full(TreeGenerator.TreeType.class, + t -> ImmutableSet.copyOf(t.lookupKeys), + null)); + commandManager.registerConverter(Key.of(EditSession.ReorderMode.class), + full(EditSession.ReorderMode.class, + r -> ImmutableSet.of(r.getDisplayName()), + null)); + } + + private static > ArgumentConverter basic(Class enumClass) { + return full(enumClass, e -> ImmutableSet.of(e.name().toLowerCase(Locale.ROOT)), null); + } + + private static > ArgumentConverter basic(Class enumClass, @Nullable E unknownValue) { + return full(enumClass, e -> ImmutableSet.of(e.name().toLowerCase(Locale.ROOT)), unknownValue); + } + + private static > ArgumentConverter full(Class enumClass, + Function> lookupKeys, + @Nullable E unknownValue) { + return MultiKeyConverter.from( + EnumSet.allOf(enumClass), + lookupKeys, + unknownValue + ); + } + + private EnumConverter() { + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java new file mode 100644 index 000000000..d6259abf3 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java @@ -0,0 +1,105 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.registry.AbstractFactory; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.world.World; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.util.List; +import java.util.function.Function; + +public class FactoryConverter implements ArgumentConverter { + + public static void register(WorldEdit worldEdit, CommandManager commandManager) { + commandManager.registerConverter(Key.of(Pattern.class), + new FactoryConverter<>(worldEdit, WorldEdit::getPatternFactory, "pattern")); + commandManager.registerConverter(Key.of(Mask.class), + new FactoryConverter<>(worldEdit, WorldEdit::getMaskFactory, "mask")); + commandManager.registerConverter(Key.of(BaseItem.class), + new FactoryConverter<>(worldEdit, WorldEdit::getItemFactory, "item")); + } + + private final WorldEdit worldEdit; + private final Function> factoryExtractor; + private final String description; + + private FactoryConverter(WorldEdit worldEdit, + Function> factoryExtractor, + String description) { + this.worldEdit = worldEdit; + this.factoryExtractor = factoryExtractor; + this.description = description; + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + Actor actor = context.injectedValue(Key.of(Actor.class)) + .orElseThrow(() -> new IllegalStateException("No actor")); + LocalSession session = WorldEdit.getInstance().getSessionManager().get(actor); + + 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(session); + parserContext.setRestricted(true); + + try { + return SuccessfulConversion.fromSingle( + factoryExtractor.apply(worldEdit).parseFromInput(argument, parserContext) + ); + } catch (InputParseException e) { + return FailedConversion.from(e); + } + } + + @Override + public List getSuggestions(String input) { + return factoryExtractor.apply(worldEdit).getSuggestions(input); + } + + @Override + public Component describeAcceptableArguments() { + return TextComponent.of("any " + description); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegionFactoryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegionFactoryConverter.java new file mode 100644 index 000000000..15624d31b --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegionFactoryConverter.java @@ -0,0 +1,49 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.google.common.collect.ImmutableSetMultimap; +import com.sk89q.worldedit.regions.factory.CuboidRegionFactory; +import com.sk89q.worldedit.regions.factory.CylinderRegionFactory; +import com.sk89q.worldedit.regions.factory.RegionFactory; +import com.sk89q.worldedit.regions.factory.SphereRegionFactory; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.MultiKeyConverter; +import org.enginehub.piston.inject.Key; + +public class RegionFactoryConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(RegionFactory.class), + MultiKeyConverter.builder( + ImmutableSetMultimap.builder() + .put(new CuboidRegionFactory(), "cuboid") + .put(new SphereRegionFactory(), "sphere") + .putAll(new CylinderRegionFactory(1), "cyl", "cylinder") + .build() + ) + .errorMessage(arg -> "Not a known region type: " + arg) + .build() + ); + } + + private RegionFactoryConverter() { + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java new file mode 100644 index 000000000..83999afa1 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java @@ -0,0 +1,111 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.google.common.collect.ImmutableList; +import com.sk89q.worldedit.command.util.SuggestionHelper; +import com.sk89q.worldedit.registry.Keyed; +import com.sk89q.worldedit.registry.Registry; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockCategory; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.fluid.FluidCategory; +import com.sk89q.worldedit.world.fluid.FluidType; +import com.sk89q.worldedit.world.gamemode.GameMode; +import com.sk89q.worldedit.world.item.ItemCategory; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.weather.WeatherType; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.stream.Collectors; + +public final class RegistryConverter implements ArgumentConverter { + + @SuppressWarnings("unchecked") + public static void register(CommandManager commandManager) { + ImmutableList.of( + BlockType.class, + BlockCategory.class, + ItemType.class, + ItemCategory.class, + BiomeType.class, + EntityType.class, + FluidType.class, + FluidCategory.class, + GameMode.class, + WeatherType.class + ).stream() + .map(c -> (Class) c) + .forEach(registryType -> + commandManager.registerConverter(Key.of(registryType), from(registryType)) + ); + } + + @SuppressWarnings("unchecked") + private static RegistryConverter from(Class registryType) { + try { + Field registryField = registryType.getDeclaredField("REGISTRY"); + Registry registry = (Registry) registryField.get(null); + return new RegistryConverter<>(registry); + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException("Not a registry-backed type: " + registryType.getName()); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Registry field inaccessible on " + registryType.getName()); + } + } + + private final Registry registry; + private final TextComponent choices; + + private RegistryConverter(Registry registry) { + this.registry = registry; + this.choices = TextComponent.of("any " + registry.getName()); + } + + @Override + public Component describeAcceptableArguments() { + return this.choices; + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess injectedValueAccess) { + V result = registry.get(argument); + return result == null + ? FailedConversion.from(new IllegalArgumentException( + "Not a valid " + registry.getName() + ": " + argument)) + : SuccessfulConversion.fromSingle(result); + } + + @Override + public List getSuggestions(String input) { + return SuggestionHelper.getRegistrySuggestions(registry, input).collect(Collectors.toList()); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SelectorChoice.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SelectorChoice.java new file mode 100644 index 000000000..924869e70 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SelectorChoice.java @@ -0,0 +1,36 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +public enum SelectorChoice { + CUBOID, + EXTEND, + POLY, + ELLIPSOID, + SPHERE, + CYL, + CONVEX, + HULL, + POLYHEDRON, + LIST, + FUZZY, + MAGIC, + POLYHEDRAL +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/VectorConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/VectorConverter.java new file mode 100644 index 000000000..05ac95573 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/VectorConverter.java @@ -0,0 +1,110 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.google.common.collect.ImmutableList; +import com.google.common.reflect.TypeToken; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector2; +import com.sk89q.worldedit.math.Vector3; +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.ArgumentConverters; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.util.List; +import java.util.function.Function; + +public class VectorConverter implements ArgumentConverter { + public static void register(CommandManager commandManager) { + CommaSeparatedValuesConverter intConverter = CommaSeparatedValuesConverter.wrap(ArgumentConverters.get(TypeToken.of(int.class))); + CommaSeparatedValuesConverter doubleConverter = CommaSeparatedValuesConverter.wrap(ArgumentConverters.get(TypeToken.of(double.class))); + commandManager.registerConverter(Key.of(BlockVector2.class), + new VectorConverter<>( + intConverter, + 2, + cmps -> BlockVector2.at(cmps.get(0), cmps.get(1)), + "block vector with x and z" + )); + commandManager.registerConverter(Key.of(Vector2.class), + new VectorConverter<>( + doubleConverter, + 2, + cmps -> Vector2.at(cmps.get(0), cmps.get(1)), + "vector with x and z" + )); + commandManager.registerConverter(Key.of(BlockVector3.class), + new VectorConverter<>( + intConverter, + 3, + cmps -> BlockVector3.at(cmps.get(0), cmps.get(1), cmps.get(2)), + "block vector with x, y, and z" + )); + commandManager.registerConverter(Key.of(Vector3.class), + new VectorConverter<>( + doubleConverter, + 3, + cmps -> Vector3.at(cmps.get(0), cmps.get(1), cmps.get(2)), + "vector with x, y, and z" + )); + } + + private final ArgumentConverter componentConverter; + private final int componentCount; + private final Function, T> vectorConstructor; + private final String acceptableArguments; + + + private VectorConverter(ArgumentConverter componentConverter, + int componentCount, + Function, T> vectorConstructor, + String acceptableArguments) { + this.componentConverter = componentConverter; + this.componentCount = componentCount; + this.vectorConstructor = vectorConstructor; + this.acceptableArguments = acceptableArguments; + } + + @Override + public Component describeAcceptableArguments() { + return TextComponent.of("any " + acceptableArguments); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + ConversionResult components = componentConverter.convert(argument, context); + if (!components.isSuccessful()) { + return components.failureAsAny(); + } + if (components.get().size() != componentCount) { + return FailedConversion.from(new IllegalArgumentException( + "Must have exactly " + componentCount + " vector components")); + } + T vector = vectorConstructor.apply(ImmutableList.copyOf(components.get())); + return SuccessfulConversion.fromSingle(vector); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ZonedDateTimeConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ZonedDateTimeConverter.java new file mode 100644 index 000000000..53d8d6399 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ZonedDateTimeConverter.java @@ -0,0 +1,61 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.sk89q.worldedit.LocalSession; +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.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Calendar; + +public class ZonedDateTimeConverter implements ArgumentConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(ZonedDateTime.class), new ZonedDateTimeConverter()); + } + + private ZonedDateTimeConverter() { + } + + @Override + public Component describeAcceptableArguments() { + return TextComponent.of("any date"); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + LocalSession session = context.injectedValue(Key.of(LocalSession.class)) + .orElseThrow(() -> new IllegalStateException("Need a local session")); + Calendar date = session.detectDate(argument); + if (date == null) { + return FailedConversion.from(new IllegalArgumentException("Not a date: " + argument)); + } + return SuccessfulConversion.fromSingle(date.toInstant().atZone(ZoneOffset.UTC)); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/package-info.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/package-info.java new file mode 100644 index 000000000..e1d68643b --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/package-info.java @@ -0,0 +1,21 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +@org.enginehub.piston.util.NonnullByDefault +package com.sk89q.worldedit.command.argument; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java index 72cdc50a6..62acc1b16 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java @@ -165,7 +165,7 @@ public class SelectionCommand extends SimpleCommand { return operation; } catch (IncompleteRegionException e) { - WorldEdit.getInstance().getPlatformManager().getCommandManager().getExceptionConverter().convert(e); + WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getExceptionConverter().convert(e); return null; } } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/ShapedBrushCommand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/ShapedBrushCommand.java index 208551c46..1511a0fbf 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/ShapedBrushCommand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/composition/ShapedBrushCommand.java @@ -37,7 +37,6 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.Contextual; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.regions.factory.RegionFactory; -import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.command.argument.CommandArgs; import com.sk89q.worldedit.util.command.composition.CommandExecutor; import com.sk89q.worldedit.util.command.composition.SimpleCommand; @@ -77,7 +76,7 @@ public class ShapedBrushCommand extends SimpleCommand { tool.setFill(null); tool.setBrush(new OperationFactoryBrush(factory, regionFactory, session), permission); } catch (MaxBrushRadiusException | InvalidToolBindException e) { - WorldEdit.getInstance().getPlatformManager().getCommandManager().getExceptionConverter().convert(e); + WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getExceptionConverter().convert(e); } player.print("Set brush to " + factory); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ItemUseFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ItemUseFactory.java new file mode 100644 index 000000000..7ad8866c3 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ItemUseFactory.java @@ -0,0 +1,54 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.factory; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.function.ItemUseFunction; +import com.sk89q.worldedit.function.Contextual; +import com.sk89q.worldedit.function.EditContext; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.World; + +public final class ItemUseFactory implements Contextual { + private final BaseItem item; + private final Direction dir; + + public ItemUseFactory(BaseItem item) { + this(item, Direction.UP); + } + + public ItemUseFactory(BaseItem item, Direction dir) { + this.item = item; + this.dir = dir; + } + + @Override + public RegionFunction createFromContext(EditContext input) { + World world = ((EditSession) input.getDestination()).getWorld(); + return new ItemUseFunction(world, item, dir); + } + + @Override + public String toString() { + return "application of the item " + item.getType(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ReplaceFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ReplaceFactory.java new file mode 100644 index 000000000..fa09c8911 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ReplaceFactory.java @@ -0,0 +1,49 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.factory; + +import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull; + +import com.sk89q.worldedit.extent.NullExtent; +import com.sk89q.worldedit.function.Contextual; +import com.sk89q.worldedit.function.EditContext; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.block.BlockReplace; +import com.sk89q.worldedit.function.pattern.Pattern; + +public class ReplaceFactory implements Contextual { + private final Pattern fill; + + public ReplaceFactory(Pattern fill) { + this.fill = fill; + } + + @Override + public RegionFunction createFromContext(EditContext context) { + return new BlockReplace( + firstNonNull(context.getDestination(), new NullExtent()), + firstNonNull(context.getFill(), fill)); + } + + @Override + public String toString() { + return "replace blocks"; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java new file mode 100644 index 000000000..76d875851 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java @@ -0,0 +1,44 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.factory; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.function.Contextual; +import com.sk89q.worldedit.function.EditContext; +import com.sk89q.worldedit.function.generator.ForestGenerator; +import com.sk89q.worldedit.util.TreeGenerator; + +public final class TreeGeneratorFactory implements Contextual { + private final TreeGenerator.TreeType type; + + public TreeGeneratorFactory(TreeGenerator.TreeType type) { + this.type = type; + } + + @Override + public ForestGenerator createFromContext(EditContext input) { + return new ForestGenerator((EditSession) input.getDestination(), type); + } + + @Override + public String toString() { + return "tree of type " + type; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java index 9f42647db..dc2eceec0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java @@ -26,7 +26,7 @@ import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; @@ -47,18 +47,18 @@ public class AreaPickaxe implements BlockTool { } @Override - public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { int ox = clicked.getBlockX(); int oy = clicked.getBlockY(); int oz = clicked.getBlockZ(); BlockType initialType = clicked.getExtent().getBlock(clicked.toVector().toBlockPoint()).getBlockType(); if (initialType.getMaterial().isAir()) { - return true; + return false; } if (initialType == BlockTypes.BEDROCK && !player.canDestroyBedrock()) { - return true; + return false; } try (EditSession editSession = session.createEditSession(player)) { @@ -71,7 +71,9 @@ public class AreaPickaxe implements BlockTool { if (editSession.getLazyBlock(x, y, z).getBlockType() != initialType) { continue; } + editSession.setBlock(x, y, z, BlockTypes.AIR.getDefaultState()); + } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java index 5f87f9097..30c84b834 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java @@ -51,8 +51,8 @@ public class BlockDataCyler implements DoubleActionBlockTool { private Map> selectedProperties = new HashMap<>(); - private boolean handleCycle(Platform server, LocalConfiguration config, - Player player, LocalSession session, Location clicked, boolean forward) { + private boolean handleCycle(LocalConfiguration config, Player player, LocalSession session, + Location clicked, boolean forward) { World world = (World) clicked.getExtent(); @@ -89,7 +89,7 @@ public class BlockDataCyler implements DoubleActionBlockTool { try { editSession.setBlock(blockPoint, newBlock); - player.print("Value of " + currentProperty.getName() + " is now " + currentProperty.getValues().get(index).toString()); + player.print("Value of " + currentProperty.getName() + " is now " + currentProperty.getValues().get(index)); } catch (MaxChangedBlocksException e) { BBC.BLOCK_CYCLER_LIMIT.send(player); } finally { @@ -111,12 +111,12 @@ public class BlockDataCyler implements DoubleActionBlockTool { @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { - return handleCycle(server, config, player, session, clicked, true); + return handleCycle(config, player, session, clicked, true); } @Override public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { - return handleCycle(server, config, player, session, clicked, false); + return handleCycle(config, player, session, clicked, false); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java index 5995234f9..58e432db1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java @@ -27,9 +27,9 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.block.BlockState; /** @@ -49,7 +49,7 @@ public class BlockReplacer implements DoubleActionBlockTool { } @Override - public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { BlockBag bag = session.getBlockBag(player); try (EditSession editSession = session.createEditSession(player)) { @@ -71,12 +71,12 @@ public class BlockReplacer implements DoubleActionBlockTool { @Override - public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { EditSession editSession = session.createEditSession(player); BlockState targetBlock = editSession.getBlock(clicked.toVector().toBlockPoint()); if (targetBlock != null) { - pattern = (targetBlock); + pattern = targetBlock; player.print("Replacer tool switched to: " + targetBlock.getBlockType().getName()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java index 139fbd640..0ee513d75 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java @@ -27,5 +27,15 @@ import com.sk89q.worldedit.util.Location; public interface BlockTool extends Tool { + /** + * Perform the primary action of this tool. + * + * @param server + * @param config + * @param player + * @param session + * @param clicked + * @return true to cancel the original event which triggered this action (if possible) + */ boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked); } 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 783a6a402..533a130a2 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 @@ -19,6 +19,8 @@ package com.sk89q.worldedit.command.tool; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FawePlayer; @@ -40,14 +42,13 @@ import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MaskTraverser; import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; - -import static com.google.common.base.Preconditions.checkNotNull; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; @@ -67,15 +68,15 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.block.BlockType; - -import javax.annotation.Nullable; import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import javax.annotation.Nullable; /** * Builds a shape at the place being looked at. @@ -92,7 +93,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool } protected static int MAX_RANGE = 500; - protected int range = 240; + protected int range = -1; private VisualMode visualMode = VisualMode.NONE; private TargetMode targetMode = TargetMode.TARGET_BLOCK_RANGE; private Mask targetMask = null; @@ -202,7 +203,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool } } - private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { lock = new ReentrantLock(); boolean multi = stream.readBoolean(); primary = (BrushSettings) stream.readObject(); @@ -323,18 +324,12 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool * @param permission the permission */ public void setBrush(Brush brush, String permission) { - setBrush(brush, permission, null); - update(); - } - - @Deprecated - public void setBrush(Brush brush, String permission, Player player) { - if (player != null) clear(player); BrushSettings current = getContext(); current.clearPerms(); current.setBrush(brush); current.addPermission(permission); update(); + update(); } /** @@ -360,8 +355,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool * * @return the material */ - @Nullable - public Pattern getMaterial() { + @Nullable public Pattern getMaterial() { return getContext().getMaterial(); } @@ -410,6 +404,11 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool this.range = range; } + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { + return act(BrushAction.PRIMARY, server, config, player, session); + } + public BlockVector3 getPosition(EditSession editSession, Player player) { Location loc = player.getLocation(); switch (targetMode) { @@ -456,84 +455,79 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool return TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Vector3 value) { - Location result = tb.getMaskedTargetBlock(useLastBlock); - this.value = result; + this.value = tb.getMaskedTargetBlock(useLastBlock); } }); } public boolean act(BrushAction action, Platform server, LocalConfiguration config, Player player, LocalSession session) { - switch (action) { - case PRIMARY: - setContext(primary); - break; - case SECONDARY: - setContext(secondary); - break; + if (action == BrushAction.PRIMARY) { + setContext(primary); + } else if (action == BrushAction.SECONDARY) { + setContext(secondary); } - - BrushSettings current = getContext(); Brush brush = current.getBrush(); if (brush == null) return false; - EditSession editSession = session.createEditSession(player); - if (current.setWorld(editSession.getWorld().getName()) && !current.canUse(player)) { - BBC.NO_PERM.send(player, StringMan.join(current.getPermissions(), ",")); - return false; - } - - BlockVector3 target = getPosition(editSession, player); - - if (target == null) { - editSession.cancel(); - BBC.NO_BLOCK.send(player); - return false; - } BlockBag bag = session.getBlockBag(player); - Request.request().setEditSession(editSession); - Mask mask = current.getMask(); - if (mask != null) { - Mask existingMask = editSession.getMask(); - if (existingMask == null) { - editSession.setMask(mask); - } else if (existingMask instanceof MaskIntersection) { - ((MaskIntersection) existingMask).add(mask); - } else { - MaskIntersection newMask = new MaskIntersection(existingMask); - newMask.add(mask); - editSession.setMask(newMask); + try (EditSession editSession = session.createEditSession(player)) { + if (current.setWorld(editSession.getWorld().getName()) && !current.canUse(player)) { + BBC.NO_PERM.send(player, StringMan.join(current.getPermissions(), ",")); + return false; + } + + BlockVector3 target = getPosition(editSession, player); + + if (target == null) { + editSession.cancel(); + BBC.NO_BLOCK.send(player); + return true; + } + + Request.request().setEditSession(editSession); + Mask mask = current.getMask(); + if (mask != null) { + Mask existingMask = editSession.getMask(); + + if (existingMask == null) { + editSession.setMask(mask); + } else if (existingMask instanceof MaskIntersection) { + ((MaskIntersection) existingMask).add(mask); + } else { + MaskIntersection newMask = new MaskIntersection(existingMask); + newMask.add(mask); + editSession.setMask(newMask); + } + } + + Mask sourceMask = current.getSourceMask(); + if (sourceMask != null) { + editSession.addSourceMask(sourceMask); + } + ResettableExtent transform = current.getTransform(); + if (transform != null) { + editSession.addTransform(transform); + } + try { + new PatternTraverser(current).reset(editSession); + double size = current.getSize(); + WorldEdit.getInstance().checkMaxBrushRadius(size); + brush.build(editSession, target, current.getMaterial(), size); + } catch (MaxChangedBlocksException e) { + player.printError("Max blocks change limit reached."); + } finally { + session.remember(editSession); } - } - Mask sourceMask = current.getSourceMask(); - if (sourceMask != null) { - editSession.addSourceMask(sourceMask); - } - ResettableExtent transform = current.getTransform(); - if (transform != null) { - editSession.addTransform(transform); - } - try { - new PatternTraverser(current).reset(editSession); - double size = current.getSize(); - WorldEdit.getInstance().checkMaxBrushRadius(size); - brush.build(editSession, target, current.getMaterial(), size); - } catch (WorldEditException e) { - player.printError("Max blocks change limit reached."); // Never happens } finally { if (bag != null) { bag.flushChanges(); } - session.remember(editSession); Request.reset(); } - return true; - } - @Override - public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { - return act(BrushAction.PRIMARY, server, config, player, session); + return true; } @Override @@ -569,7 +563,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool if (this.visualMode != VisualMode.NONE) { clear(player); } - this.visualMode = visualMode != null ? visualMode : VisualMode.NONE; + this.visualMode = visualMode; if (visualMode != VisualMode.NONE) { try { queueVisualization(FawePlayer.wrap(player)); @@ -647,10 +641,9 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool if (position != null) { editSession.setExtent(newVisualExtent); switch (mode) { - case POINT: { + case POINT: editSession.setBlock(position, VisualChunk.VISUALIZE_BLOCK); break; - } case OUTLINE: { new PatternTraverser(current).reset(editSession); brush.build(editSession, position, current.getMaterial(), current.getSize()); 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 d1635da8c..d48ab016f 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 @@ -36,7 +36,7 @@ import com.sk89q.worldedit.util.Location; public class DistanceWand extends BrushTool implements DoubleActionTraceTool { public DistanceWand() { - super("worldedit.wand"); + super("worldedit.selection.pos"); } @Override @@ -46,40 +46,31 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { @Override public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) { - if (session.isToolControlEnabled() && player.hasPermission("worldedit.selection.pos")) { - Location target = getTarget(player); - if (target == null) return true; - - RegionSelector selector = session.getRegionSelector(player.getWorld()); - BlockVector3 blockPoint = target.toVector().toBlockPoint(); - if (selector.selectPrimary(blockPoint, ActorSelectorLimits.forActor(player))) { - selector.explainPrimarySelection(player, session, blockPoint); - } - return true; + Location target = getTarget(player); + if (target == null) return true; + RegionSelector selector = session.getRegionSelector(player.getWorld()); + BlockVector3 blockPoint = target.toVector().toBlockPoint(); + if (selector.selectPrimary(blockPoint, ActorSelectorLimits.forActor(player))) { + selector.explainPrimarySelection(player, session, blockPoint); } - return false; - + return true; } @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { - if (session.isToolControlEnabled() && player.hasPermission("worldedit.selection.pos")) { - Location target = getTarget(player); - if (target == null) return true; - - RegionSelector selector = session.getRegionSelector(player.getWorld()); - BlockVector3 blockPoint = target.toVector().toBlockPoint(); - if (selector.selectSecondary(blockPoint, ActorSelectorLimits.forActor(player))) { - selector.explainSecondarySelection(player, session, blockPoint); - } - return true; + Location target = getTarget(player); + if (target == null) return true; + RegionSelector selector = session.getRegionSelector(player.getWorld()); + BlockVector3 blockPoint = target.toVector().toBlockPoint(); + if (selector.selectSecondary(blockPoint, ActorSelectorLimits.forActor(player))) { + selector.explainSecondarySelection(player, session, blockPoint); } - return false; + return true; } - public Location getTarget(Player player) { + private Location getTarget(Player player) { Location target; if (this.range > -1) { target = player.getBlockTrace(getRange(), true); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java index 78f1a5f3e..f2c37e99f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java @@ -30,6 +30,16 @@ import com.sk89q.worldedit.util.Location; */ public interface DoubleActionBlockTool extends BlockTool { + /** + * Perform the secondary action of this block tool. + * + * @param server + * @param config + * @param player + * @param session + * @param clicked + * @return true to cancel the original event which triggered this action (if possible) + */ 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/DoubleActionTraceTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionTraceTool.java index dde9cca86..1c9b6f83f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionTraceTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionTraceTool.java @@ -29,6 +29,15 @@ import com.sk89q.worldedit.extension.platform.Platform; */ public interface DoubleActionTraceTool extends TraceTool { + /** + * Perform the secondary function of this tool. + * + * @param server + * @param config + * @param player + * @param session + * @return true to cancel the original event which triggered this action (if possible) + */ boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java index c868ff99e..72008347c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java @@ -64,7 +64,7 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo eS.setBlock(pos.toVector().subtract(pos.getDirection()).toBlockPoint(), secondary); } return true; - } catch (MaxChangedBlocksException e) { + } catch (MaxChangedBlocksException ignored) { // one block? eat it } return false; @@ -84,14 +84,19 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo eS.setBlock(pos.toVector().subtract(pos.getDirection()).toBlockPoint(), primary); } return true; - } catch (MaxChangedBlocksException e) { + } catch (MaxChangedBlocksException ignored) { // one block? eat it } - return true; + return false; } - public Location getTargetFace(Player player) { + private Location getTargetFace(Player player) { Location target = player.getBlockTraceFace(getRange(), true); + if (this.range > -1) { + target = player.getBlockTrace(getRange(), true); + } else { + target = player.getBlockTrace(MAX_RANGE, false); + } if (target == null) { BBC.NO_BLOCK.send(player); 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 new file mode 100644 index 000000000..24b8990df --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/NavigationWand.java @@ -0,0 +1,68 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.tool; + +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.entity.Player; +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; + } + + @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; + } + + if (!player.passThroughForwardWall(Math.max(1, maxDist - 10))) { + player.printError("Nothing to pass through (or too far)!"); + } + return true; + } + + @Override + public boolean canUse(Actor actor) { + return true; // skip check here - checked separately for primary/secondary + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java index ef71569f4..469de149b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java @@ -22,11 +22,14 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; @@ -41,23 +44,30 @@ public class QueryTool implements BlockTool { } @Override - public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { World world = (World) clicked.getExtent(); EditSession editSession = session.createEditSession(player); BlockVector3 blockPoint = clicked.toVector().toBlockPoint(); BaseBlock block = editSession.getFullBlock(blockPoint); - player.print("\u00A79@" + clicked.toVector() + ": " + "\u00A7e" - + block.getBlockType().getName() + "\u00A77" + " (" - + block.toString() + ") " - + "\u00A7f" - + " (" + world.getBlockLightLevel(blockPoint) + "/" + world.getBlockLightLevel(blockPoint.add(0, 1, 0)) + ")"); - - if (block instanceof MobSpawnerBlock) { - player.printRaw("\u00A7e" + "Mob Type: " - + ((MobSpawnerBlock) block).getMobType()); + TextComponent.Builder builder = TextComponent.builder(); + builder.append(TextComponent.of("@" + clicked.toVector().toBlockPoint() + ": ", TextColor.BLUE)); + builder.append(TextComponent.of(block.getBlockType().getName(), TextColor.YELLOW)); + builder.append(TextComponent.of(" (" + block + ") ", TextColor.GRAY) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Block state")))); +/* + final OptionalInt internalId = BlockStateIdAccess.getBlockStateId(block.toImmutableState()); + if (internalId.isPresent()) { + builder.append(TextComponent.of(" (" + internalId.getAsInt() + ") ", TextColor.DARK_GRAY) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Internal ID")))); } +*/ + builder.append(TextComponent.of(" (" + world.getBlockLightLevel(blockPoint) + "/" + + world.getBlockLightLevel(blockPoint.add(0, 1, 0)) + ")", TextColor.WHITE) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Block Light/Light Above")))); + + player.print(builder.build()); return true; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java index 2f182db33..5119dfe33 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java @@ -20,22 +20,25 @@ package com.sk89q.worldedit.command.tool; import com.boydti.fawe.object.mask.IdMask; + import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.function.block.BlockReplace; -import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import java.util.Set; + /** * A pickaxe mode that recursively finds adjacent blocks within range of * an initial block and of the same type. @@ -54,34 +57,68 @@ public class RecursivePickaxe implements BlockTool { } @Override - public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { World world = (World) clicked.getExtent(); final BlockVector3 pos = clicked.toBlockPoint(); - EditSession editSession = session.createEditSession(player); BlockVector3 origin = clicked.toVector().toBlockPoint(); BlockType initialType = world.getBlock(origin).getBlockType(); - BlockStateHolder block = editSession.getBlock(pos); - if (block.getBlockType().getMaterial().isAir()) { - return true; + if (initialType.getMaterial().isAir()) { + return false; } - if (block.getBlockType() == BlockTypes.BEDROCK && !player.canDestroyBedrock()) { - return true; + if (initialType == BlockTypes.BEDROCK && !player.canDestroyBedrock()) { + return false; } - editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop); + try (EditSession editSession = session.createEditSession(player)) { + editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop); - final int radius = (int) range; - final BlockReplace replace = new BlockReplace(editSession, (BlockTypes.AIR.getDefaultState())); - editSession.setMask((Mask) null); - RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius, editSession); - visitor.visit(pos); - Operations.completeBlindly(visitor); + final int radius = (int) range; + final BlockReplace replace = new BlockReplace(editSession, (BlockTypes.AIR.getDefaultState())); + editSession.setMask(null); + RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius, editSession); + visitor.visit(pos); + Operations.completeBlindly(visitor); + + editSession.flushQueue(); + session.remember(editSession); + } - editSession.flushQueue(); - session.remember(editSession); return true; } + + private static void recurse(Platform server, EditSession editSession, World world, BlockVector3 pos, + BlockVector3 origin, double size, BlockType initialType, Set visited) throws MaxChangedBlocksException { + + final double distanceSq = origin.distanceSq(pos); + if (distanceSq > size*size || visited.contains(pos)) { + return; + } + + visited.add(pos); + + if (editSession.getBlock(pos).getBlockType() != initialType) { + return; + } + + editSession.setBlock(pos, BlockTypes.AIR.getDefaultState()); + + world.queueBlockBreakEffect(server, pos, initialType, distanceSq); + + recurse(server, editSession, world, pos.add(1, 0, 0), + origin, size, initialType, visited); + recurse(server, editSession, world, pos.add(-1, 0, 0), + origin, size, initialType, visited); + recurse(server, editSession, world, pos.add(0, 0, 1), + origin, size, initialType, visited); + recurse(server, editSession, world, pos.add(0, 0, -1), + origin, size, initialType, visited); + recurse(server, editSession, world, pos.add(0, 1, 0), + origin, size, initialType, visited); + recurse(server, editSession, world, pos.add(0, -1, 0), + origin, size, initialType, visited); + } + } 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 new file mode 100644 index 000000000..43b5f1ea0 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SelectionWand.java @@ -0,0 +1,59 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.tool; + +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.util.Location; + +public class SelectionWand implements DoubleActionBlockTool { + + @Override + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { + RegionSelector selector = session.getRegionSelector(player.getWorld()); + + BlockVector3 blockPoint = clicked.toVector().toBlockPoint(); + if (selector.selectPrimary(blockPoint, ActorSelectorLimits.forActor(player))) { + selector.explainPrimarySelection(player, session, blockPoint); + } + return true; + } + + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { + RegionSelector selector = session.getRegionSelector(player.getWorld()); + BlockVector3 blockPoint = clicked.toVector().toBlockPoint(); + if (selector.selectSecondary(blockPoint, ActorSelectorLimits.forActor(player))) { + selector.explainSecondarySelection(player, session, blockPoint); + } + return true; + } + + @Override + public boolean canUse(Actor actor) { + return actor.hasPermission("worldedit.selection.pos"); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java index 2d865550b..1a9c8da4c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java @@ -27,6 +27,7 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; @@ -42,22 +43,23 @@ public class SinglePickaxe implements BlockTool { } @Override - public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { - World world = (World) clicked.getExtent(); + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { + World world = (World) clicked.getExtent(); BlockVector3 blockPoint = clicked.toBlockPoint(); final BlockType blockType = world.getBlock(blockPoint).getBlockType(); - if (blockType == BlockTypes.BEDROCK - && !player.canDestroyBedrock()) { - return true; + if (blockType == BlockTypes.BEDROCK && !player.canDestroyBedrock()) { + return false; } try (EditSession editSession = session.createEditSession(player)) { editSession.getSurvivalExtent().setToolUse(config.superPickaxeDrop); editSession.setBlock(blockPoint, BlockTypes.AIR.getDefaultState()); session.remember(editSession); + } catch (MaxChangedBlocksException e) { + player.printError("Max blocks change limit reached."); } + return true; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java index f02801fa5..6f18e2c71 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java @@ -26,5 +26,14 @@ import com.sk89q.worldedit.extension.platform.Platform; public interface TraceTool extends Tool { + /** + * Perform the primary action of this trace tool. + * + * @param server + * @param config + * @param player + * @param session + * @return true to cancel the original event which triggered this action (if possible) + */ boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java index 8dbdb233b..d4bc048b3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.command.tool; import com.boydti.fawe.config.BBC; + import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; @@ -27,6 +28,7 @@ import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator; @@ -53,8 +55,9 @@ public class TreePlanter implements BlockTool { try { boolean successful = false; + final BlockVector3 pos = clicked.toVector().add(0, 1, 0).toBlockPoint(); for (int i = 0; i < 10; i++) { - if (treeType.generate(editSession, clicked.add(0, 1, 0).toBlockPoint())) { + if (treeType.generate(editSession, pos)) { successful = true; break; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java index d6abe3cc9..bbb489242 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; @@ -31,14 +32,30 @@ import com.sk89q.worldedit.session.ClipboardHolder; public class ClipboardBrush implements Brush { - private ClipboardHolder holder; - private boolean ignoreAirBlocks; - private boolean usingOrigin; + private final ClipboardHolder holder; + private final boolean ignoreAirBlocks; + private final boolean usingOrigin; + private final boolean pasteEntities; + private final boolean pasteBiomes; + private final Mask sourceMask; public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin) { this.holder = holder; this.ignoreAirBlocks = ignoreAirBlocks; this.usingOrigin = usingOrigin; + this.pasteBiomes = false; + this.pasteEntities = false; + this.sourceMask = null; + } + + public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin, boolean pasteEntities, + boolean pasteBiomes, Mask sourceMask) { + this.holder = holder; + this.ignoreAirBlocks = ignoreAirBlocks; + this.usingOrigin = usingOrigin; + this.pasteEntities = pasteEntities; + this.pasteBiomes = pasteBiomes; + this.sourceMask = sourceMask; } @Override @@ -51,6 +68,9 @@ public class ClipboardBrush implements Brush { .createPaste(editSession) .to(usingOrigin ? position : position.subtract(centerOffset)) .ignoreAirBlocks(ignoreAirBlocks) + .copyEntities(pasteEntities) + .copyBiomes(pasteBiomes) + .maskSource(sourceMask) .build(); Operations.completeLegacy(operation); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java index d1ef66637..1b1ddcb6f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockTypes; @@ -37,7 +36,7 @@ public class CylinderBrush implements Brush { @Override public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException { if (pattern == null) { - pattern = (BlockTypes.COBBLESTONE.getDefaultState()); + pattern = BlockTypes.COBBLESTONE.getDefaultState(); } editSession.makeCylinder(position, pattern, size, size, height, true); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java index 8e2754779..34e3b91cc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java @@ -21,16 +21,11 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; -import java.util.Vector; - public class GravityBrush implements Brush { private final boolean fullHeight; @@ -48,8 +43,8 @@ public class GravityBrush implements Brush { for (int x = position.getBlockX() + size; x > position.getBlockX() - size; --x) { for (int z = position.getBlockZ() + size; z > position.getBlockZ() - size; --z) { int freeSpot = startCheckY; - for (int y = startCheckY; y <= endY; y++) { - BlockStateHolder block = editSession.getLazyBlock(x, y, z); + for (int y = startCheckY; y <= endY; ++y) { + final BlockState block = editSession.getLazyBlock(x, y, z); if (!block.getBlockType().getMaterial().isAir()) { if (y != freeSpot) { editSession.setBlock(x, y, z, BlockTypes.AIR.getDefaultState()); 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 07e852da6..03b50a7e9 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 @@ -21,7 +21,6 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockTypes; @@ -31,7 +30,7 @@ public class SphereBrush implements Brush { @Override public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException { if (pattern == null) { - pattern = (BlockTypes.COBBLESTONE.getDefaultState()); + pattern = BlockTypes.COBBLESTONE.getDefaultState(); } editSession.makeSphere(position, pattern, size, size, size, true); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandBuilder.java new file mode 100644 index 000000000..5afe27446 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandBuilder.java @@ -0,0 +1,196 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.internal.command.exception.ExceptionConverter; +import com.sk89q.worldedit.util.formatting.component.ErrorFormat; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; +import com.sk89q.worldedit.util.task.FutureForwardingTask; +import com.sk89q.worldedit.util.task.Supervisor; +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.exception.CommandExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.concurrent.Callable; +import java.util.function.Consumer; + +public final class AsyncCommandBuilder { + + private static final Logger logger = LoggerFactory.getLogger(AsyncCommandBuilder.class); + + private final Callable callable; + private final Actor sender; + + @Nullable + private Supervisor supervisor; + @Nullable + private String description; + @Nullable + private String delayMessage; + + @Nullable + private Component successMessage; + @Nullable + private Consumer consumer; + + @Nullable + private Component failureMessage; + @Nullable + private ExceptionConverter exceptionConverter; + + private AsyncCommandBuilder(Callable callable, Actor sender) { + checkNotNull(callable); + checkNotNull(sender); + this.callable = callable; + this.sender = sender; + } + + public static AsyncCommandBuilder wrap(Callable callable, Actor sender) { + return new AsyncCommandBuilder<>(callable, sender); + } + + public AsyncCommandBuilder registerWithSupervisor(Supervisor supervisor, String description) { + this.supervisor = checkNotNull(supervisor); + this.description = checkNotNull(description); + return this; + } + + public AsyncCommandBuilder sendMessageAfterDelay(String message) { + this.delayMessage = checkNotNull(message); + return this; + } + + public AsyncCommandBuilder onSuccess(@Nullable Component message, @Nullable Consumer consumer) { + checkArgument(message != null || consumer != null, "Can't have null message AND consumer"); + this.successMessage = message; + this.consumer = consumer; + return this; + } + + public AsyncCommandBuilder onSuccess(@Nullable String message, @Nullable Consumer consumer) { + checkArgument(message != null || consumer != null, "Can't have null message AND consumer"); + this.successMessage = message == null ? null : TextComponent.of(message, TextColor.LIGHT_PURPLE); + this.consumer = consumer; + return this; + } + + public AsyncCommandBuilder onFailure(@Nullable Component message, @Nullable ExceptionConverter exceptionConverter) { + checkArgument(message != null || exceptionConverter != null, "Can't have null message AND exceptionConverter"); + this.failureMessage = message; + this.exceptionConverter = exceptionConverter; + return this; + } + + public AsyncCommandBuilder onFailure(@Nullable String message, @Nullable ExceptionConverter exceptionConverter) { + checkArgument(message != null || exceptionConverter != null, "Can't have null message AND exceptionConverter"); + this.failureMessage = message == null ? null : ErrorFormat.wrap(message); + this.exceptionConverter = exceptionConverter; + return this; + } + + public ListenableFuture buildAndExec(ListeningExecutorService executor) { + final ListenableFuture future = checkNotNull(executor).submit(this::runTask); + if (delayMessage != null) { + FutureProgressListener.addProgressListener(future, sender, delayMessage); + } + if (supervisor != null && description != null) { + supervisor.monitor(FutureForwardingTask.create(future, description, sender)); + } + return future; + } + + private T runTask() { + T result = null; + try { + result = callable.call(); + if (consumer != null) { + consumer.accept(result); + } + if (successMessage != null) { + sender.print(successMessage); + } + } catch (Exception orig) { + Component failure = failureMessage != null ? failureMessage : TextComponent.of("An error occurred"); + try { + if (exceptionConverter != null) { + try { + if (orig instanceof com.sk89q.minecraft.util.commands.CommandException) { + throw new CommandExecutionException(orig, ImmutableList.of()); + } + exceptionConverter.convert(orig); + throw orig; + } catch (CommandException converted) { + Component message; + + // TODO remove this once WG migrates to piston and can use piston exceptions everywhere + message = tryExtractOldCommandException(converted); + + if (message == null) { + if (Strings.isNullOrEmpty(converted.getMessage())) { + message = TextComponent.of("Unknown error."); + } else { + message = converted.getRichMessage(); + } + } + sender.print(failure.append(TextComponent.of(": ")).append(message)); + } + } else { + throw orig; + } + } catch (Throwable unknown) { + sender.print(failure.append(TextComponent.of(": Unknown error. Please see console."))); + logger.error("Uncaught exception occurred in task: " + description, orig); + } + } + return result; + } + + // this is needed right now since worldguard is still on the 2011 command framework which throws and converts + // com.sk89q.minecraft.util.commands.CommandException. the ExceptionConverter currently expects converted + // exceptions to be org.enginehub.piston.CommandException, throw it wraps the resulting InvocationTargetException in + // a CommandExecutionException. here, we unwrap those layers to retrieve the original WG error message + private Component tryExtractOldCommandException(CommandException converted) { + Component message = null; + if (converted instanceof CommandExecutionException) { + Throwable parentCause = converted; + while ((parentCause = parentCause.getCause()) != null) { + if (parentCause instanceof com.sk89q.minecraft.util.commands.CommandException) { + final String msg = parentCause.getMessage(); + if (!Strings.isNullOrEmpty(msg)) { + message = TextComponent.of(msg); + } + break; + } + } + } + return message; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandHelper.java deleted file mode 100644 index f3b707435..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandHelper.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.command.util; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.command.parametric.ExceptionConverter; -import com.sk89q.worldedit.util.task.FutureForwardingTask; -import com.sk89q.worldedit.util.task.Supervisor; -import com.sk89q.worldedit.world.World; - -import javax.annotation.Nullable; - -public class AsyncCommandHelper { - - private final ListenableFuture future; - private final Supervisor supervisor; - private final Actor sender; - private final ExceptionConverter exceptionConverter; - @Nullable - private Object[] formatArgs; - - private AsyncCommandHelper(ListenableFuture future, Supervisor supervisor, Actor sender, ExceptionConverter exceptionConverter) { - checkNotNull(future); - checkNotNull(supervisor); - checkNotNull(sender); - checkNotNull(exceptionConverter); - - this.future = future; - this.supervisor = supervisor; - this.sender = sender; - this.exceptionConverter = exceptionConverter; - } - - public AsyncCommandHelper formatUsing(Object... args) { - this.formatArgs = args; - return this; - } - - private String format(String message) { - if (formatArgs != null) { - return String.format(message, formatArgs); - } else { - return message; - } - } - - public AsyncCommandHelper registerWithSupervisor(String description) { - supervisor.monitor( - FutureForwardingTask.create( - future, format(description), sender)); - return this; - } - - public AsyncCommandHelper sendMessageAfterDelay(String message) { - FutureProgressListener.addProgressListener(future, sender, format(message)); - return this; - } - - public AsyncCommandHelper thenRespondWith(String success, String failure) { - // Send a response message - Futures.addCallback( - future, - new MessageFutureCallback.Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess(format(success)) - .onFailure(format(failure)) - .build()); - return this; - } - - public AsyncCommandHelper thenTellErrorsOnly(String failure) { - // Send a response message - Futures.addCallback( - future, - new MessageFutureCallback.Builder(sender) - .exceptionConverter(exceptionConverter) - .onFailure(format(failure)) - .build()); - return this; - } - - public AsyncCommandHelper forRegionDataLoad(World world, boolean silent) { - checkNotNull(world); - - formatUsing(world.getName()); - registerWithSupervisor("Loading region data for '%s'"); - if (silent) { - thenTellErrorsOnly("Failed to load regions '%s'"); - } else { - sendMessageAfterDelay("(Please wait... loading the region data for '%s')"); - thenRespondWith( - "Loaded region data for '%s'", - "Failed to load regions '%s'"); - } - - return this; - } - - public AsyncCommandHelper forRegionDataSave(World world, boolean silent) { - checkNotNull(world); - - formatUsing(world.getName()); - registerWithSupervisor("Saving region data for '%s'"); - if (silent) { - thenTellErrorsOnly("Failed to save regions '%s'"); - } else { - sendMessageAfterDelay("(Please wait... saving the region data for '%s')"); - thenRespondWith( - "Saved region data for '%s'", - "Failed to load regions '%s'"); - } - - return this; - } - - public static AsyncCommandHelper wrap(ListenableFuture future, Supervisor supervisor, Actor sender, ExceptionConverter exceptionConverter) { - return new AsyncCommandHelper(future, supervisor, sender, exceptionConverter); - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandPermissions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandPermissions.java new file mode 100644 index 000000000..feefb9aba --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandPermissions.java @@ -0,0 +1,39 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import org.enginehub.piston.annotation.CommandCondition; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@CommandCondition(CommandPermissionsConditionGenerator.class) +public @interface CommandPermissions { + + /** + * A list of permissions. Only one permission has to be met + * for the command to be permitted. + * + * @return a list of permissions strings + */ + String[] value(); + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandPermissionsConditionGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandPermissionsConditionGenerator.java new file mode 100644 index 000000000..91ca575bd --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CommandPermissionsConditionGenerator.java @@ -0,0 +1,45 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.collect.ImmutableSet; +import org.enginehub.piston.Command; +import org.enginehub.piston.gen.CommandConditionGenerator; +import org.enginehub.piston.util.NonnullByDefault; + +import java.lang.reflect.Method; +import java.util.Set; + +@NonnullByDefault +public final class CommandPermissionsConditionGenerator implements CommandConditionGenerator { + + public interface Registration { + Registration commandPermissionsConditionGenerator(CommandPermissionsConditionGenerator generator); + } + + @Override + public Command.Condition generateCondition(Method commandMethod) { + CommandPermissions annotation = commandMethod.getAnnotation(CommandPermissions.class); + checkNotNull(annotation, "Annotation is missing from commandMethod"); + Set permissions = ImmutableSet.copyOf(annotation.value()); + return new PermissionCondition(permissions); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CreatureButcher.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CreatureButcher.java index 94c7d5ad2..e43fee9d4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CreatureButcher.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/CreatureButcher.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.command.util; -import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.EntityFunction; @@ -29,7 +28,7 @@ import com.sk89q.worldedit.function.EntityFunction; */ public class CreatureButcher { - final class Flags { + public final class Flags { @SuppressWarnings("PointlessBitwiseExpression") public static final int PETS = 1 << 0; public static final int NPCS = 1 << 1; @@ -39,7 +38,6 @@ public class CreatureButcher { public static final int TAGGED = 1 << 5; public static final int FRIENDLY = PETS | NPCS | ANIMALS | GOLEMS | AMBIENT | TAGGED; public static final int ARMOR_STAND = 1 << 6; - public static final int WITH_LIGHTNING = 1 << 20; private Flags() { } @@ -64,19 +62,6 @@ public class CreatureButcher { } } - public void fromCommand(CommandContext args) { - or(Flags.FRIENDLY , args.hasFlag('f')); // No permission check here. Flags will instead be filtered by the subsequent calls. - or(Flags.PETS , args.hasFlag('p'), "worldedit.butcher.pets"); - or(Flags.NPCS , args.hasFlag('n'), "worldedit.butcher.npcs"); - or(Flags.GOLEMS , args.hasFlag('g'), "worldedit.butcher.golems"); - or(Flags.ANIMALS , args.hasFlag('a'), "worldedit.butcher.animals"); - or(Flags.AMBIENT , args.hasFlag('b'), "worldedit.butcher.ambient"); - or(Flags.TAGGED , args.hasFlag('t'), "worldedit.butcher.tagged"); - or(Flags.ARMOR_STAND , args.hasFlag('r'), "worldedit.butcher.armorstands"); - - or(Flags.WITH_LIGHTNING, args.hasFlag('l'), "worldedit.butcher.lightning"); - } - public EntityFunction createFunction() { return entity -> { boolean killPets = (flags & Flags.PETS) != 0; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java index 529f06f81..1385b2337 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java @@ -19,16 +19,14 @@ package com.sk89q.worldedit.command.util; -import static com.google.common.base.Preconditions.checkNotNull; - import com.boydti.fawe.util.TaskManager; -import com.sk89q.minecraft.util.commands.CommandException; + +import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.function.EntityFunction; -import java.util.regex.Pattern; - import javax.annotation.Nullable; +import java.util.regex.Pattern; /** * The implementation of /remove. @@ -126,17 +124,21 @@ public class EntityRemover { } } - private Type type; - - public void fromString(String str) throws CommandException { + public static EntityRemover fromString(String str) { Type type = Type.findByPattern(str); if (type != null) { - this.type = type; + return new EntityRemover(type); } else { - throw new CommandException("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); + throw new IllegalArgumentException("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); } } + private final Type type; + + private EntityRemover(Type type) { + this.type = type; + } + public EntityFunction createFunction() { final Type type = this.type; checkNotNull(type, "type can't be null"); diff --git a/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/Logging.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/Logging.java similarity index 93% rename from worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/Logging.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/command/util/Logging.java index d8d87dcc0..ec256f16e 100644 --- a/worldedit-core/src/main/java/com/sk89q/minecraft/util/commands/Logging.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/Logging.java @@ -1,68 +1,68 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -//$Id$ - - -package com.sk89q.minecraft.util.commands; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Indicates how the affected blocks should be hinted at in the log. - */ -@Retention(RetentionPolicy.RUNTIME) -public @interface Logging { - - enum LogMode { - /** - * Player position - */ - POSITION, - - /** - * Region selection - */ - REGION, - - /** - * Player orientation and region selection - */ - ORIENTATION_REGION, - - /** - * Either the player position or pos1, depending on the placeAtPos1 flag - */ - PLACEMENT, - - /** - * Log all information available - */ - ALL - } - - /** - * Log mode. - * - * @return either POSITION, REGION, ORIENTATION_REGION, PLACEMENT or ALL - */ - LogMode value(); - -} +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +//$Id$ + + +package com.sk89q.worldedit.command.util; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates how the affected blocks should be hinted at in the log. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface Logging { + + enum LogMode { + /** + * Player position + */ + POSITION, + + /** + * Region selection + */ + REGION, + + /** + * Player orientation and region selection + */ + ORIENTATION_REGION, + + /** + * Either the player position or pos1, depending on the placeAtPos1 flag + */ + PLACEMENT, + + /** + * Log all information available + */ + ALL + } + + /** + * Log mode. + * + * @return either POSITION, REGION, ORIENTATION_REGION, PLACEMENT or ALL + */ + LogMode value(); + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java deleted file mode 100644 index 3abb73545..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.command.util; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.util.concurrent.FutureCallback; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.command.parametric.ExceptionConverter; - -import javax.annotation.Nullable; - -public class MessageFutureCallback implements FutureCallback { - - private final ExceptionConverter exceptionConverter; - private final Actor sender; - @Nullable - private final String success; - @Nullable - private final String failure; - - private MessageFutureCallback(ExceptionConverter exceptionConverter, Actor sender, @Nullable String success, @Nullable String failure) { - this.exceptionConverter = exceptionConverter; - this.sender = sender; - this.success = success; - this.failure = failure; - } - - @Override - public void onSuccess(@Nullable V v) { - if (success != null) { - sender.print(success); - } - } - - @Override - public void onFailure(@Nullable Throwable throwable) { - try { - exceptionConverter.convert(throwable); - } catch (CommandException e) { - String failure = this.failure != null ? this.failure : "An error occurred"; - String message = e.getMessage() != null ? e.getMessage() : "An unknown error occurred. Please see the console!"; - sender.printError(failure + ": " + message); - } - } - - public static class Builder { - private final Actor sender; - @Nullable - private String success; - @Nullable - private String failure; - private ExceptionConverter exceptionConverter; - - public Builder(Actor sender) { - checkNotNull(sender); - - this.sender = sender; - } - - public Builder exceptionConverter(ExceptionConverter exceptionConverter) { - this.exceptionConverter = exceptionConverter; - return this; - } - - public Builder onSuccess(@Nullable String message) { - this.success = message; - return this; - } - - public Builder onFailure(@Nullable String message) { - this.failure = message; - return this; - } - - public MessageFutureCallback build() { - checkNotNull(exceptionConverter); - return new MessageFutureCallback<>(exceptionConverter, sender, success, failure); - } - } - - public static MessageFutureCallback createRegionLoadCallback(ExceptionConverter exceptionConverter, Actor sender) { - return new Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess("Successfully load the region data.") - .build(); - } - - public static MessageFutureCallback createRegionSaveCallback(ExceptionConverter exceptionConverter, Actor sender) { - return new Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess("Successfully saved the region data.") - .build(); - } - -} 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 new file mode 100644 index 000000000..99a4622d6 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PermissionCondition.java @@ -0,0 +1,50 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.sk89q.worldedit.extension.platform.Actor; +import org.enginehub.piston.Command; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.util.Set; + +public class PermissionCondition implements Command.Condition { + + private static final Key ACTOR_KEY = Key.of(Actor.class); + + private final Set permissions; + + public PermissionCondition(Set permissions) { + this.permissions = permissions; + } + + public Set getPermissions() { + return permissions; + } + + @Override + public boolean satisfied(InjectedValueAccess context) { + return permissions.isEmpty() || + context.injectedValue(ACTOR_KEY) + .map(actor -> permissions.stream().anyMatch(actor::hasPermission)) + .orElse(false); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java new file mode 100644 index 000000000..0ac326f1a --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java @@ -0,0 +1,155 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Actor; +import static com.sk89q.worldedit.internal.command.CommandUtil.byCleanName; +import static com.sk89q.worldedit.internal.command.CommandUtil.getSubCommands; +import com.sk89q.worldedit.util.formatting.component.CommandListBox; +import com.sk89q.worldedit.util.formatting.component.CommandUsageBox; +import com.sk89q.worldedit.util.formatting.component.InvalidComponentException; +import static java.util.stream.Collectors.toList; +import org.enginehub.piston.Command; +import org.enginehub.piston.CommandManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Implementation of the //help command. + */ +// Stored in a separate class to prevent import conflicts, and because it's aliased via /we help. +public class PrintCommandHelp { + + private static Command detectCommand(CommandManager manager, String command) { + Optional mapping; + + // First try the command as entered + mapping = manager.getCommand(command); + if (mapping.isPresent()) { + return mapping.get(); + } + + // If tried with slashes, try dropping a slash + if (command.startsWith("/")) { + mapping = manager.getCommand(command.substring(1)); + return mapping.orElse(null); + } + + // Otherwise, check /command, since that's common + mapping = manager.getCommand("/" + command); + return mapping.orElse(null); + } + + public static void help(List commandPath, int page, boolean listSubCommands, WorldEdit we, Actor actor) throws InvalidComponentException { + CommandManager manager = we.getPlatformManager().getPlatformCommandManager().getCommandManager(); + + if (commandPath.isEmpty()) { + printCommands(page, manager.getAllCommands(), actor, ImmutableList.of()); + return; + } + + List visited = new ArrayList<>(); + Command currentCommand = detectCommand(manager, commandPath.get(0)); + if (currentCommand == null) { + actor.printError(String.format("The command '%s' could not be found.", commandPath.get(0))); + return; + } + visited.add(currentCommand); + + // Drill down to the command + for (int i = 1; i < commandPath.size(); i++) { + String subCommand = commandPath.get(i); + Map subCommands = getSubCommands(currentCommand); + + if (subCommands.isEmpty()) { + actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)", + toCommandString(visited), subCommand)); + // full help for single command + CommandUsageBox box = new CommandUsageBox(visited, visited.stream() + .map(Command::getName).collect(Collectors.joining(" "))); + actor.print(box.create()); + return; + } + + if (subCommands.containsKey(subCommand)) { + currentCommand = subCommands.get(subCommand); + visited.add(currentCommand); + } else { + actor.printError(String.format("The sub-command '%s' under '%s' could not be found.", + subCommand, toCommandString(visited))); + // list subcommands for currentCommand + printCommands(page, getSubCommands(Iterables.getLast(visited)).values().stream(), actor, visited); + return; + } + } + + Map subCommands = getSubCommands(currentCommand); + + if (subCommands.isEmpty() || !listSubCommands) { + // Create the message + CommandUsageBox box = new CommandUsageBox(visited, toCommandString(visited)); + actor.print(box.create()); + } else { + printCommands(page, subCommands.values().stream(), actor, visited); + } + } + + private static String toCommandString(List visited) { + return "/" + Joiner.on(" ").join(visited.stream().map(Command::getName).iterator()); + } + + private static void printCommands(int page, Stream commandStream, Actor actor, + List commandList) throws InvalidComponentException { + // Get a list of aliases + List commands = commandStream + .sorted(byCleanName()) + .collect(toList()); + + String used = commandList.isEmpty() ? null : toCommandString(commandList); + CommandListBox box = new CommandListBox( + (used == null ? "Help" : "Subcommands: " + used), + "//help -s -p %page%" + (used == null ? "" : " " + used)); + if (!actor.isPlayer()) { + box.formatForConsole(); + } + + for (Command mapping : commands) { + String alias = (commandList.isEmpty() ? "/" : "") + mapping.getName(); + String command = Stream.concat(commandList.stream(), Stream.of(mapping)) + .map(Command::getName) + .collect(Collectors.joining(" ", "/", "")); + box.appendCommand(alias, mapping.getDescription(), command); + } + + actor.print(box.create(page)); + } + + private PrintCommandHelp() { + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SubCommandPermissionCondition.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SubCommandPermissionCondition.java new file mode 100644 index 000000000..c501bbbeb --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SubCommandPermissionCondition.java @@ -0,0 +1,67 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.google.common.collect.ImmutableSet; +import org.enginehub.piston.Command; +import org.enginehub.piston.inject.InjectedValueAccess; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public final class SubCommandPermissionCondition extends PermissionCondition { + + private final Command.Condition aggregate; + + private SubCommandPermissionCondition(Set perms, Command.Condition aggregate) { + super(perms); + this.aggregate = aggregate; + } + + @Override + public boolean satisfied(InjectedValueAccess context) { + return aggregate.satisfied(context); + } + + public static class Generator { + private final List subCommands; + + public Generator(List subCommands) { + this.subCommands = subCommands; + } + + public Command.Condition build() { + final List conditions = subCommands.stream().map(Command::getCondition).collect(Collectors.toList()); + final List> permConds = conditions.stream().map(c -> c.as(PermissionCondition.class)).collect(Collectors.toList()); + if (permConds.stream().anyMatch(o -> !o.isPresent())) { + // if any sub-command doesn't require permissions, then this command doesn't require permissions + return new PermissionCondition(ImmutableSet.of()); + } + // otherwise, this command requires any one subcommand to be available + final Set perms = permConds.stream().map(Optional::get).flatMap(cond -> cond.getPermissions().stream()).collect(Collectors.toSet()); + final Command.Condition aggregate = permConds.stream().map(Optional::get) + .map(c -> (Command.Condition) c) + .reduce(Command.Condition::or).orElse(TRUE); + return new SubCommandPermissionCondition(perms, aggregate); + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java new file mode 100644 index 000000000..e0014ee56 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java @@ -0,0 +1,173 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.sk89q.worldedit.registry.Keyed; +import com.sk89q.worldedit.registry.NamespacedRegistry; +import com.sk89q.worldedit.registry.Registry; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockCategory; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.enginehub.piston.converter.SuggestionHelper.byPrefix; +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; + +/** + * Internal class for generating common command suggestions. + */ +public final class SuggestionHelper { + private SuggestionHelper() { + } + + public static Stream getBlockCategorySuggestions(String tag, boolean allowRandom) { + if (tag.isEmpty() || tag.equals("#")) { + return Stream.of("##", "##*"); + } + if (tag.startsWith("#")) { + if (tag.equals("##")) { + return Stream.concat(Stream.of("##*"), getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(2)).map(s -> "##" + s)); + } else if (tag.equals("##*") && allowRandom) { + return getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(3)).map(s -> "##*" + s); + } else { + boolean wild = tag.startsWith("##*") && allowRandom; + return getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(wild ? 3 : 2)).map(s -> (wild ? "##*" : "##") + s); + } + } + return Stream.empty(); + } + + public static Stream getBlockPropertySuggestions(String blockType, String props) { + BlockType type = BlockTypes.get(blockType.toLowerCase(Locale.ROOT)); + if (type == null) { + return Stream.empty(); + } + final Map> propertyMap = type.getPropertyMap(); + Set matchedProperties = new HashSet<>(); + String[] propParts = props.split(",", -1); + for (int i = 0; i < propParts.length; i++) { + String[] propVal = propParts[i].split("="); + final String matchProp = propVal[0].toLowerCase(Locale.ROOT); + if (i == propParts.length - 1) { + // suggest for next property + String previous = Arrays.stream(propParts, 0, propParts.length - 1).collect(Collectors.joining(",")) + + (propParts.length == 1 ? "" : ","); + String lastValidInput = (blockType + "[" + previous).toLowerCase(Locale.ROOT); + if (propVal.length == 1) { + // only property, no value yet + final List> matchingProps = propertyMap.entrySet().stream() + .filter(p -> !matchedProperties.contains(p.getKey()) && p.getKey().startsWith(matchProp)) + .map(Map.Entry::getValue).collect(Collectors.toList()); + switch (matchingProps.size()) { + case 0: + return propertyMap.keySet().stream().filter(p -> !matchedProperties.contains(p)).map(prop -> + lastValidInput + prop + "="); + case 1: + return matchingProps.get(0).getValues().stream().map(val -> + lastValidInput + matchingProps.get(0).getName() + "=" + + val.toString().toLowerCase(Locale.ROOT)); + default: + return matchingProps.stream().map(p -> lastValidInput + p.getName() + "="); + } + } else { + Property prop = propertyMap.get(matchProp); + if (prop == null) { + return propertyMap.keySet().stream().map(p -> lastValidInput + p); + } + final List values = prop.getValues().stream().map(v -> v.toString().toLowerCase(Locale.ROOT)).collect(Collectors.toList()); + String matchVal = propVal[1].toLowerCase(Locale.ROOT); + List matchingVals = values.stream().filter(val -> val.startsWith(matchVal)).collect(Collectors.toList()); + if (matchingVals.isEmpty()) { + return values.stream().map(val -> lastValidInput + prop.getName() + "=" + val); + } else { + if (matchingVals.size() == 1 && matchingVals.get(0).equals(matchVal)) { + String currProp = lastValidInput + prop.getName() + "=" + matchVal; + if (matchingVals.size() < values.size()) { + return Stream.of(currProp + "] ", currProp + ","); + } + return Stream.of(currProp + "] "); + } + return matchingVals.stream().map(val -> lastValidInput + prop.getName() + "=" + val); + } + } + } else { + // validate previous properties + if (propVal.length != 2) { + return Stream.empty(); + } + Property prop = propertyMap.get(matchProp); + if (prop == null) { + return Stream.empty(); + } + try { + prop.getValueFor(propVal[1]); + matchedProperties.add(prop.getName()); + } catch (IllegalArgumentException ignored) { + return Stream.empty(); + } + } + } + return Stream.empty(); + } + + public static Stream getRegistrySuggestions(Registry registry, String input) { + if (registry instanceof NamespacedRegistry) { + return getNamespacedRegistrySuggestions(((NamespacedRegistry) registry), input); + } + return limitByPrefix(registry.keySet().stream(), input).stream(); + } + + public static Stream getNamespacedRegistrySuggestions(NamespacedRegistry registry, String input) { + if (input.isEmpty() || input.equals(":")) { + final Set namespaces = registry.getKnownNamespaces(); + if (namespaces.size() == 1) { + return registry.keySet().stream(); + } else { + return namespaces.stream().map(s -> s + ":"); + } + } + if (input.startsWith(":")) { // special case - search across namespaces + final String term = input.substring(1).toLowerCase(Locale.ROOT); + Predicate search = byPrefix(term); + return registry.keySet().stream().filter(s -> search.test(s.substring(s.indexOf(':') + 1))); + } + // otherwise, we actually have some text to search + if (input.indexOf(':') < 0) { + // don't yet have namespace - search namespaces + default + final String lowerSearch = input.toLowerCase(Locale.ROOT); + String defKey = registry.getDefaultNamespace() + ":" + lowerSearch; + return Stream.concat(registry.keySet().stream().filter(s -> s.startsWith(defKey)), + registry.getKnownNamespaces().stream().filter(n -> n.startsWith(lowerSearch)).map(n -> n + ":")); + } + // have a namespace - search that + Predicate search = byPrefix(input.toLowerCase(Locale.ROOT)); + return registry.keySet().stream().filter(search); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/WorldEditAsyncCommandBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/WorldEditAsyncCommandBuilder.java new file mode 100644 index 000000000..f21fb2b6d --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/WorldEditAsyncCommandBuilder.java @@ -0,0 +1,46 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.util.formatting.text.Component; + +import javax.annotation.Nullable; +import java.util.concurrent.Callable; + +/** + * For internal WorldEdit use only. + */ +public final class WorldEditAsyncCommandBuilder { + private WorldEditAsyncCommandBuilder() { + } + + public static void createAndSendMessage(Actor actor, Callable task, @Nullable String desc) { + final AsyncCommandBuilder builder = AsyncCommandBuilder.wrap(task, actor); + if (desc != null) { + builder.sendMessageAfterDelay(desc); + } + builder + .onSuccess((String) null, actor::print) + .onFailure((String) null, WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getExceptionConverter()) + .buildAndExec(WorldEdit.getInstance().getExecutorService()); + } +} 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 d5be2e40d..3673d8842 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 @@ -23,6 +23,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Direction; @@ -210,6 +211,17 @@ public interface Player extends Entity, Actor { */ Location getBlockTrace(int range, boolean useLastBlock); + /** + * Get the point of the block being looked at. May return null. + * Will return the farthest away block before matching the stop mask if useLastBlock is true and no other block is found. + * + * @param range how far to checks for blocks + * @param useLastBlock try to return the last valid block not matching the stop mask found + * @param stopMask the mask used to determine when to stop tracing + * @return point + */ + Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask); + /** * Get the face that the player is looking at. * @@ -219,6 +231,16 @@ public interface Player extends Entity, Actor { */ Location getBlockTraceFace(int range, boolean useLastBlock); + /** + * Get the face that the player is looking at. + * + * @param range the range + * @param useLastBlock try to return the last valid block not matching the stop mask found + * @param stopMask the mask used to determine when to stop tracing + * @return a face + */ + Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask); + /** * Get the point of the block being looked at. May return null. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/event/extent/EditSessionEvent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/event/extent/EditSessionEvent.java index 08bc96511..c7c5edf0e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/event/extent/EditSessionEvent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/event/extent/EditSessionEvent.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockStateHolder; import javax.annotation.Nullable; @@ -71,10 +72,10 @@ public class EditSessionEvent extends Event implements Cancellable { /** * Create a new event. * - * @param world the world - * @param actor the actor, or null if there is no actor specified + * @param world the world + * @param actor the actor, or null if there is no actor specified * @param maxBlocks the maximum number of block changes - * @param stage the stage + * @param stage the stage */ public EditSessionEvent(@Nullable World world, Actor actor, int maxBlocks, Stage stage) { this.world = world; @@ -96,9 +97,7 @@ public class EditSessionEvent extends Event implements Cancellable { * * @return the actor, which may be null if unavailable */ - public - @Nullable - Actor getActor() { + public @Nullable Actor getActor() { return actor; } @@ -107,9 +106,7 @@ public class EditSessionEvent extends Event implements Cancellable { * * @return the world */ - public - @Nullable - World getWorld() { + public @Nullable World getWorld() { return world; } @@ -173,5 +170,4 @@ public class EditSessionEvent extends Event implements Cancellable { return clone; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java index 2e1c58d08..5891b8f98 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java @@ -21,7 +21,7 @@ package com.sk89q.worldedit.event.platform; import com.sk89q.worldedit.event.AbstractCancellable; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import static com.google.common.base.Preconditions.checkNotNull; @@ -67,6 +67,6 @@ public class CommandEvent extends AbstractCancellable implements Runnable { @Override public void run() { - CommandManager.getInstance().handleCommandOnCurrentThread(this); + PlatformCommandManager.getInstance().handleCommandOnCurrentThread(this); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java index 75483745e..17d8d748a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java @@ -20,9 +20,9 @@ package com.sk89q.worldedit.event.platform; import static com.google.common.base.Preconditions.checkNotNull; - import com.sk89q.worldedit.event.Event; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.internal.util.Substring; import java.util.Collections; import java.util.List; @@ -34,7 +34,7 @@ public class CommandSuggestionEvent extends Event { private final Actor actor; private final String arguments; - private List suggestions = Collections.emptyList(); + private List suggestions = Collections.emptyList(); /** * Create a new instance. @@ -71,9 +71,14 @@ public class CommandSuggestionEvent extends Event { /** * Get the list of suggestions that are to be presented. * + *

+ * Each Substring holds the replacement as the substring, + * and the replacement range as the original substring range. + *

+ * * @return the list of suggestions */ - public List getSuggestions() { + public List getSuggestions() { return suggestions; } @@ -82,7 +87,7 @@ public class CommandSuggestionEvent extends Event { * * @param suggestions the list of suggestions */ - public void setSuggestions(List suggestions) { + public void setSuggestions(List suggestions) { checkNotNull(suggestions); this.suggestions = suggestions; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java index 235f0ae3e..7d17467a4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java @@ -26,7 +26,6 @@ import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.internal.registry.AbstractFactory; import com.sk89q.worldedit.world.block.BaseBlock; - import java.util.HashSet; import java.util.Set; @@ -45,9 +44,7 @@ public class BlockFactory extends AbstractFactory { * @param worldEdit the WorldEdit instance. */ public BlockFactory(WorldEdit worldEdit) { - super(worldEdit); - - register(new DefaultBlockParser(worldEdit)); + super(worldEdit, new DefaultBlockParser(worldEdit)); } /** @@ -61,7 +58,7 @@ public class BlockFactory extends AbstractFactory { public Set parseFromListInput(String input, ParserContext context) throws InputParseException { Set blocks = new HashSet<>(); String[] splits = input.split(","); - for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']')) { + for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']', true)) { blocks.add(parseFromInput(token, context)); } return blocks; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/ItemFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/ItemFactory.java index e2a2bee29..71853f79f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/ItemFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/ItemFactory.java @@ -32,9 +32,7 @@ public class ItemFactory extends AbstractFactory { * @param worldEdit the WorldEdit instance. */ public ItemFactory(WorldEdit worldEdit) { - super(worldEdit); - - register(new DefaultItemParser(worldEdit)); + super(worldEdit, new DefaultItemParser(worldEdit)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java index cc326cc46..cdb62a3ea 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java @@ -20,12 +20,16 @@ package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.factory.parser.mask.BiomeMaskParser; import com.sk89q.worldedit.extension.factory.parser.mask.BlockCategoryMaskParser; -import com.sk89q.worldedit.extension.factory.parser.mask.DefaultMaskParser; +import com.sk89q.worldedit.extension.factory.parser.mask.BlockStateMaskParser; +import com.sk89q.worldedit.extension.factory.parser.mask.BlocksMaskParser; import com.sk89q.worldedit.extension.factory.parser.mask.ExistingMaskParser; +import com.sk89q.worldedit.extension.factory.parser.mask.ExpressionMaskParser; import com.sk89q.worldedit.extension.factory.parser.mask.LazyRegionMaskParser; import com.sk89q.worldedit.extension.factory.parser.mask.NegateMaskParser; import com.sk89q.worldedit.extension.factory.parser.mask.NoiseMaskParser; +import com.sk89q.worldedit.extension.factory.parser.mask.OffsetMaskParser; import com.sk89q.worldedit.extension.factory.parser.mask.RegionMaskParser; import com.sk89q.worldedit.extension.factory.parser.mask.SolidMaskParser; import com.sk89q.worldedit.extension.input.InputParseException; @@ -54,9 +58,20 @@ public final class MaskFactory extends AbstractFactory { * @param worldEdit the WorldEdit instance */ public MaskFactory(WorldEdit worldEdit) { - super(worldEdit); + super(worldEdit, new BlocksMaskParser(worldEdit)); + + register(new ExistingMaskParser(worldEdit)); + register(new SolidMaskParser(worldEdit)); + register(new LazyRegionMaskParser(worldEdit)); + register(new RegionMaskParser(worldEdit)); + register(new OffsetMaskParser(worldEdit)); + register(new NoiseMaskParser(worldEdit)); + register(new BlockStateMaskParser(worldEdit)); + register(new NegateMaskParser(worldEdit)); + register(new ExpressionMaskParser(worldEdit)); + register(new BlockCategoryMaskParser(worldEdit)); - register(new DefaultMaskParser(worldEdit)); + register(new BiomeMaskParser(worldEdit)); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java index 8f7ead81a..014229a3e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java @@ -22,7 +22,6 @@ package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.factory.parser.pattern.BlockCategoryPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.ClipboardPatternParser; -import com.sk89q.worldedit.extension.factory.parser.pattern.DefaultPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomStatePatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.SingleBlockPatternParser; @@ -45,9 +44,16 @@ public final class PatternFactory extends AbstractFactory { * @param worldEdit the WorldEdit instance */ public PatternFactory(WorldEdit worldEdit) { - super(worldEdit); + super(worldEdit, new SingleBlockPatternParser(worldEdit)); + + // split and parse each sub-pattern + register(new RandomPatternParser(worldEdit)); + + // individual patterns + register(new ClipboardPatternParser(worldEdit)); + register(new TypeOrStateApplyingPatternParser(worldEdit)); + register(new RandomStatePatternParser(worldEdit)); register(new BlockCategoryPatternParser(worldEdit)); - register(new DefaultPatternParser(worldEdit)); } } 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 bec4903bf..b5b678e27 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,23 +19,18 @@ package com.sk89q.worldedit.extension.factory.parser; -import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.config.BBC; -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.google.common.collect.Maps; 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.blocks.metadata.MobType; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.DisallowedUsageException; import com.sk89q.worldedit.extension.input.InputParseException; @@ -43,24 +38,26 @@ 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; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockCategories; +import com.sk89q.worldedit.world.block.BlockCategory; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.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.Arrays; +import java.util.HashMap; import java.util.Locale; -import java.util.stream.Collectors; +import java.util.Map; import java.util.stream.Stream; /** @@ -110,6 +107,8 @@ public class DefaultBlockParser extends InputParser { } } + private static String[] EMPTY_STRING_ARRAY = {}; + /** * Backwards compatibility for wool colours in block syntax. * @@ -161,8 +160,75 @@ 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 { - String[] blockAndExtraData = input.trim().split("\\|", 2); + BlockType blockType = null; + Map, Object> blockStates = new HashMap<>(); + String[] blockAndExtraData = input.trim().split("\\|"); blockAndExtraData[0] = woolMapper(blockAndExtraData[0]); BlockState state = null; @@ -170,31 +236,21 @@ public class DefaultBlockParser extends InputParser { // Legacy matcher if (context.isTryingLegacy()) { try { - String[] split = blockAndExtraData[0].split(":"); - if (split.length == 1) { + String[] split = blockAndExtraData[0].split(":", 2); + if (split.length == 0) { + 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 { - BlockType type = BlockTypes.get(split[0].toLowerCase()); - 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); - } + state = LegacyMapper.getInstance().getBlockFromLegacy(Integer.parseInt(split[0]), Integer.parseInt(split[1])); } - } catch (NumberFormatException e) { + if (state != null) { + blockType = state.getBlockType(); + } + } catch (NumberFormatException ignored) { } } - CompoundTag nbt = null; if (state == null) { String typeString; String stateString = null; @@ -203,139 +259,139 @@ 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"); } - // PosX - if (typeString.matches("pos[0-9]+")) { - int index = Integer.parseInt(typeString.replaceAll("[a-z]+", "")); + 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).toBaseBlock(); + 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).toBaseBlock(); + if (blockInHand.getClass() != BaseBlock.class) { + return blockInHand; + } + + blockType = blockInHand.getBlockType(); + blockStates.putAll(blockInHand.getStates()); + } else if ("pos1".equalsIgnoreCase(typeString)) { // Get the block type from the "primary position" final World world = context.requireWorld(); final BlockVector3 primaryPosition; try { - primaryPosition = context.requireSession().getRegionSelector(world).getVerticies().get(index - 1); + primaryPosition = context.requireSession().getRegionSelector(world).getPrimaryPosition(); } catch (IncompleteRegionException e) { throw new InputParseException("Your selection is not complete."); } - state = world.getBlock(primaryPosition); + final BlockState blockInHand = world.getBlock(primaryPosition); + + blockType = blockInHand.getBlockType(); + blockStates.putAll(blockInHand.getStates()); } else { - if ("hand".equalsIgnoreCase(typeString)) { - // Get the block type from the item in the user's hand. - state = getBlockInHand(context.requireActor(), HandSide.MAIN_HAND); - } else if ("offhand".equalsIgnoreCase(typeString)) { - // Get the block type from the item in the user's off hand. - state = getBlockInHand(context.requireActor(), HandSide.OFF_HAND); - } 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 (!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()); - - if (type != null) { - state = type.getDefaultState(); - } - if (state == null) { - throw new NoMatchException("Does not match a valid block type: '" + input + "'"); - } - } + // Attempt to lookup a block from ID or name. + blockType = BlockTypes.get(typeString.toLowerCase(Locale.ROOT)); } - 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(); - } + if (blockType == null) { + throw new NoMatchException("Does not match a valid block type: '" + input + "'"); + } + + 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()); + } + 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 (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()); - } + // this should be impossible but IntelliJ isn't that smart + if (blockType == null) { + throw new NoMatchException("Does not match a valid block type: '" + input + "'"); } // Check if the item is allowed - BlockType blockType = state.getBlockType(); + 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 + "'"); + } + } - if (nbt != null) return validate(context, state.toBaseBlock(nbt)); + if (!context.isTryingLegacy()) { + return state.toBaseBlock(); + } - if (blockType == BlockTypes.SIGN || blockType == BlockTypes.WALL_SIGN) { + if (blockType == BlockTypes.SIGN || blockType == BlockTypes.WALL_SIGN + || BlockCategories.SIGNS.contains(blockType)) { // Allow special sign text syntax String[] text = new String[4]; text[0] = blockAndExtraData.length > 1 ? blockAndExtraData[1] : ""; text[1] = blockAndExtraData.length > 2 ? blockAndExtraData[2] : ""; text[2] = blockAndExtraData.length > 3 ? blockAndExtraData[3] : ""; text[3] = blockAndExtraData.length > 4 ? blockAndExtraData[4] : ""; - return validate(context, new SignBlock(state, text)); + return new SignBlock(state, text); } else if (blockType == BlockTypes.SPAWNER) { // Allow setting mob spawn type if (blockAndExtraData.length > 1) { String mobName = blockAndExtraData[1]; - for (MobType mobType : MobType.values()) { - if (mobType.getName().toLowerCase().equals(mobName.toLowerCase())) { - mobName = mobType.getName(); - break; - } + EntityType ent = EntityTypes.get(mobName.toLowerCase(Locale.ROOT)); + if (ent == null) { + throw new NoMatchException("Unknown entity type '" + mobName + "'"); } + mobName = ent.getId(); if (!worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS).isValidMobType(mobName)) { - String finalMobName = mobName.toLowerCase(); - throw new SuggestInputParseException("Unknown mob type '" + mobName + "'", mobName, () -> Stream.of(MobType.values()) - .map(m -> m.getName().toLowerCase()) - .filter(s -> s.startsWith(finalMobName)) - .collect(Collectors.toList())); + throw new NoMatchException("Unknown mob type '" + mobName + "'"); } - return validate(context, new MobSpawnerBlock(state, mobName)); + return new MobSpawnerBlock(state, mobName); } else { - return validate(context, new MobSpawnerBlock(state, MobType.PIG.getName())); + //noinspection ConstantConditions + return new MobSpawnerBlock(state, EntityTypes.PIG.getId()); } } else if (blockType == BlockTypes.PLAYER_HEAD || blockType == BlockTypes.PLAYER_WALL_HEAD) { // allow setting type/player/rotation if (blockAndExtraData.length <= 1) { - return validate(context, new SkullBlock(state)); + return new SkullBlock(state); } String type = blockAndExtraData[1]; - return validate(context, new SkullBlock(state, type.replace(" ", "_"))); // valid MC usernames + return new SkullBlock(state, type.replace(" ", "_")); // valid MC usernames } else { - return validate(context, state.toBaseBlock()); + return state.toBaseBlock(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java index 56b4b18f6..631fed2dd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.extension.factory.parser; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; @@ -33,6 +34,7 @@ import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; import java.util.Locale; +import java.util.stream.Stream; public class DefaultItemParser extends InputParser { @@ -40,6 +42,11 @@ public class DefaultItemParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + return SuggestionHelper.getNamespacedRegistrySuggestions(ItemType.REGISTRY, input); + } + @Override public BaseItem parseFromInput(String input, ParserContext context) throws InputParseException { BaseItem item = null; @@ -62,6 +69,12 @@ public class DefaultItemParser extends InputParser { } } + if ("hand".equalsIgnoreCase(input)) { + return getItemInHand(context.requireActor(), HandSide.MAIN_HAND); + } else if ("offhand".equalsIgnoreCase(input)) { + return getItemInHand(context.requireActor(), HandSide.OFF_HAND); + } + if (item == null) { ItemType type = ItemTypes.get(input.toLowerCase(Locale.ROOT)); if (type != null) { @@ -83,4 +96,5 @@ public class DefaultItemParser extends InputParser { throw new InputParseException("The user is not a player!"); } } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java index 11b8bb54c..9e38e2e82 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java @@ -21,21 +21,21 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.google.common.base.Splitter; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; -import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.function.mask.BiomeMask2D; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.session.request.RequestExtent; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.Biomes; -import com.sk89q.worldedit.world.registry.BiomeRegistry; -import java.util.Collection; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class BiomeMaskParser extends InputParser { @@ -43,6 +43,26 @@ public class BiomeMaskParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("$"); + } + if (input.charAt(0) == '$') { + input = input.substring(1); + final int lastTermIdx = input.lastIndexOf(','); + if (lastTermIdx <= 0) { + return SuggestionHelper.getNamespacedRegistrySuggestions(BiomeType.REGISTRY, input).map(s -> "$" + s); + } + String prev = input.substring(0, lastTermIdx) + ","; + Set prevBiomes = Arrays.stream(prev.split(",", 0)).collect(Collectors.toSet()); + String search = input.substring(lastTermIdx + 1); + return SuggestionHelper.getNamespacedRegistrySuggestions(BiomeType.REGISTRY, search) + .filter(s -> !prevBiomes.contains(s)).map(s -> "$" + prev + s); + } + return Stream.empty(); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("$")) { @@ -50,10 +70,8 @@ public class BiomeMaskParser extends InputParser { } Set biomes = new HashSet<>(); - BiomeRegistry biomeRegistry = worldEdit.getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); - Collection knownBiomes = BiomeType.REGISTRY.values(); for (String biomeName : Splitter.on(",").split(input.substring(1))) { - BiomeType biome = Biomes.findBiomeByName(knownBiomes, biomeName, biomeRegistry); + BiomeType biome = BiomeType.REGISTRY.get(biomeName); if (biome == null) { throw new InputParseException("Unknown biome '" + biomeName + '\''); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java index ec3c65b5c..ee7bebf44 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.BlockCategoryMask; @@ -28,12 +29,20 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.session.request.RequestExtent; import com.sk89q.worldedit.world.block.BlockCategory; +import java.util.Locale; +import java.util.stream.Stream; + public class BlockCategoryMaskParser extends InputParser { public BlockCategoryMaskParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + return SuggestionHelper.getBlockCategorySuggestions(input, false); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("##")) { @@ -41,7 +50,7 @@ public class BlockCategoryMaskParser extends InputParser { } // This means it's a tag mask. - BlockCategory category = BlockCategory.REGISTRY.get(input.substring(2).toLowerCase()); + BlockCategory category = BlockCategory.REGISTRY.get(input.substring(2).toLowerCase(Locale.ROOT)); if (category == null) { throw new InputParseException("Unrecognised tag '" + input.substring(2) + '\''); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java index 0a2bd6e56..95884b821 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java @@ -28,12 +28,22 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.session.request.RequestExtent; +import java.util.stream.Stream; + public class BlockStateMaskParser extends InputParser { public BlockStateMaskParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("^[", "^=["); + } + return Stream.of("^[", "^=[").filter(s -> s.startsWith(input)); // no block type, can't suggest states + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!(input.startsWith("^[") || input.startsWith("^=[")) || !input.endsWith("]")) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java index e67a7e8b1..8580eb8a1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java @@ -30,6 +30,7 @@ import com.sk89q.worldedit.session.request.RequestExtent; import com.sk89q.worldedit.world.block.BaseBlock; import java.util.Set; +import java.util.stream.Stream; /** * Parses mask input strings. @@ -40,6 +41,11 @@ public class BlocksMaskParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + return worldEdit.getBlockFactory().getSuggestions(input).stream(); + } + @Override public Mask parseFromInput(String component, ParserContext context) throws InputParseException { ParserContext tempContext = new ParserContext(context); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java index 5249d47ad..21feb6449 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.ExistingBlockMask; @@ -31,13 +31,15 @@ import java.util.List; public class ExistingMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#existing"); + public ExistingMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#existing"); + return aliases; } @Override 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 58041280d..ac104e198 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 @@ -30,10 +30,10 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.session.SessionOwner; -import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.session.request.RequestExtent; import java.util.function.IntSupplier; +import java.util.stream.Stream; public class ExpressionMaskParser extends InputParser { @@ -41,6 +41,14 @@ public class ExpressionMaskParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("="); + } + return Stream.empty(); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("=")) { @@ -52,10 +60,10 @@ public class ExpressionMaskParser extends InputParser { WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment( new RequestExtent(), Vector3.ONE, Vector3.ZERO); exp.setEnvironment(env); - if (context.getActor() instanceof SessionOwner) { - SessionOwner owner = (SessionOwner) context.getActor(); + if (context.getActor() != null) { + SessionOwner owner = context.getActor(); IntSupplier timeout = () -> WorldEdit.getInstance().getSessionManager().get(owner).getTimeout(); - // TODO timeout + return new ExpressionMask(exp, timeout); } return new ExpressionMask(exp); } catch (ExpressionException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java index 82bc14d2b..29b7af7a9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java @@ -19,9 +19,8 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.RegionMask; @@ -32,17 +31,19 @@ import java.util.List; public class LazyRegionMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#dregion", "#dselection", "#dsel"); + public LazyRegionMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#dregion", "#dselection", "#dsel"); + return aliases; } @Override - public Mask parseFromSimpleInput(String input, ParserContext context) throws InputParseException { + public Mask parseFromSimpleInput(String input, ParserContext context) { return new RegionMask(new RequestSelection()); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java index 9e1c2e9df..e8e4ddf57 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java @@ -26,12 +26,25 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.internal.registry.InputParser; +import java.util.stream.Stream; + public class NegateMaskParser extends InputParser { public NegateMaskParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("!"); + } + if (input.charAt(0) != '!') { + return Stream.empty(); + } + return worldEdit.getMaskFactory().getSuggestions(input.substring(1)).stream().map(s -> "!" + s); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("!")) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java index 0cb1a85e8..40f54ebfe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java @@ -20,13 +20,14 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.NoiseFilter; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.noise.RandomNoise; +import java.util.stream.Stream; + public class NoiseMaskParser extends InputParser { public NoiseMaskParser(WorldEdit worldEdit) { @@ -34,7 +35,18 @@ public class NoiseMaskParser extends InputParser { } @Override - public Mask parseFromInput(String input, ParserContext context) throws InputParseException { + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("%"); + } + if (input.charAt(0) != '%') { + return Stream.empty(); + } + return Stream.of("%10", "%25", "%50", "%75").filter(s -> s.startsWith(input)); + } + + @Override + public Mask parseFromInput(String input, ParserContext context) { if (!input.startsWith("%")) { return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java index ab4882e00..ede9b6c79 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java @@ -31,12 +31,26 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.session.request.RequestExtent; +import java.util.stream.Stream; + public class OffsetMaskParser extends InputParser { public OffsetMaskParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of(">", "<"); + } + final char firstChar = input.charAt(0); + if (firstChar != '>' && firstChar != '<') { + return Stream.empty(); + } + return worldEdit.getMaskFactory().getSuggestions(input.substring(1)).stream().map(s -> firstChar + s); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { final char firstChar = input.charAt(0); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java index 21963835d..bd7ceaa3e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; @@ -32,13 +32,15 @@ import java.util.List; public class RegionMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#region", "#selection", "#sel"); + public RegionMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#region", "#selection", "#sel"); + return aliases; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java index 44e2f07a2..f5ae3244a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; @@ -31,13 +31,15 @@ import java.util.List; public class SolidMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#solid"); + public SolidMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#solid"); + return aliases; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java index 9cc922368..4ff9cd913 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java @@ -20,18 +20,18 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.RandomPattern; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.world.block.BlockCategory; import com.sk89q.worldedit.world.block.BlockType; -import java.util.List; +import java.util.Locale; import java.util.Set; -import java.util.stream.Collectors; +import java.util.stream.Stream; public class BlockCategoryPatternParser extends InputParser { @@ -40,8 +40,8 @@ public class BlockCategoryPatternParser extends InputParser { } @Override - public List getSuggestions() { - return BlockCategory.REGISTRY.keySet().stream().map(str -> "##" + str).collect(Collectors.toList()); + public Stream getSuggestions(String input) { + return SuggestionHelper.getBlockCategorySuggestions(input, true); } @Override @@ -49,7 +49,7 @@ public class BlockCategoryPatternParser extends InputParser { if (!input.startsWith("##")) { return null; } - String tag = input.substring(2).toLowerCase(); + String tag = input.substring(2).toLowerCase(Locale.ROOT); boolean anyState = false; if (tag.startsWith("*")) { tag = tag.substring(1); @@ -69,10 +69,10 @@ public class BlockCategoryPatternParser extends InputParser { if (anyState) { blocks.stream().flatMap(blockType -> blockType.getAllStates().stream()).forEach(state -> - randomPattern.add((state), 1.0)); + randomPattern.add(state, 1.0)); } else { for (BlockType blockType : blocks) { - randomPattern.add((blockType.getDefaultState()), 1.0); + randomPattern.add(blockType.getDefaultState(), 1.0); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java index 1aee6c2a6..bb131484f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; -import com.google.common.collect.Lists; import com.sk89q.worldedit.EmptyClipboardException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; @@ -32,7 +31,8 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.session.ClipboardHolder; -import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; public class ClipboardPatternParser extends InputParser { @@ -41,8 +41,28 @@ public class ClipboardPatternParser extends InputParser { } @Override - public List getSuggestions() { - return Lists.newArrayList("#clipboard", "#copy"); + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("#clipboard"); + } + String[] offsetParts = input.split("@", 2); + String firstLower = offsetParts[0].toLowerCase(Locale.ROOT); + final boolean isClip = "#clipboard".startsWith(firstLower); + final boolean isCopy = "#copy".startsWith(firstLower); + if (isClip || isCopy) { + if (offsetParts.length == 2) { + String coords = offsetParts[1]; + if (coords.isEmpty()) { + return Stream.of(input + "[x,y,z]"); + } + } else { + if (isClip) { + return Stream.of("#clipboard", "#clipboard@[x,y,z]"); + } + return Stream.of("#copy", "#copy@[x,y,z]"); + } + } + return Stream.empty(); } @Override @@ -83,4 +103,5 @@ public class ClipboardPatternParser extends InputParser { throw new InputParseException("No session is available, so no clipboard is available"); } } + } 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 bb5e353ae..5e19a370d 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 @@ -26,10 +26,6 @@ import com.boydti.fawe.object.random.TrueRandom; import com.boydti.fawe.util.StringMan; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; - -import com.google.common.collect.Lists; -import com.sk89q.worldedit.EmptyClipboardException; -import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.PatternCommands; import com.sk89q.worldedit.extension.input.InputParseException; @@ -41,12 +37,10 @@ import com.sk89q.worldedit.function.pattern.RandomPattern; import com.sk89q.worldedit.internal.command.ActorAuthorizer; import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.internal.expression.Expression; -import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.SimpleDispatcher; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.world.block.BlockTypes; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -115,16 +109,16 @@ public class DefaultPatternParser extends FaweParser { if (charMask) { - switch (char0) { - case '$': { - String value = command.substring(1) + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]"); - if (value.contains(":")) { - if (value.charAt(0) == ':') value.replaceFirst(":", ""); - value = value.replaceAll(":", "]["); + if (char0 == '$') { + String value = command.substring(1) + ((entry.getValue().isEmpty()) ? "" + : "[" + StringMan.join(entry.getValue(), "][") + "]"); + if (value.contains(":")) { + if (value.charAt(0) == ':') { + value.replaceFirst(":", ""); } - pattern = parseFromInput(char0 + "[" + value + "]", context); - break; + value = value.replaceAll(":", "]["); } + pattern = parseFromInput(char0 + "[" + value + "]", context); } } if (pattern == null) { @@ -194,14 +188,14 @@ public class DefaultPatternParser extends FaweParser { } if (patterns.isEmpty()) { return null; - } else if (patterns.size() == 1) { + } + if (patterns.size() == 1) { return patterns.get(0); - } else { - RandomPattern random = new RandomPattern(new TrueRandom()); - for (int i = 0; i < patterns.size(); i++) { - random.add(patterns.get(i), chances.get(i)); - } - return random; - } + } + RandomPattern random = new RandomPattern(new TrueRandom()); + for (int i = 0; i < patterns.size(); i++) { + random.add(patterns.get(i), chances.get(i)); + } + return random; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java index a836c1615..81b1b11e3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.sk89q.util.StringUtil; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.factory.BlockFactory; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.pattern.Pattern; @@ -29,6 +28,7 @@ import com.sk89q.worldedit.function.pattern.RandomPattern; import com.sk89q.worldedit.internal.registry.InputParser; import java.util.List; +import java.util.stream.Stream; public class RandomPatternParser extends InputParser { @@ -36,12 +36,35 @@ public class RandomPatternParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); + if (patterns.size() == 1) { + return Stream.empty(); + } + // get suggestions for the last token only + String token = patterns.get(patterns.size() - 1); + String previous = String.join(",", patterns.subList(0, patterns.size() - 1)); + if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) { + String[] p = token.split("%"); + + if (p.length < 2) { + return Stream.empty(); + } else { + token = p[1]; + } + } + final List innerSuggestions = worldEdit.getPatternFactory().getSuggestions(token); + return innerSuggestions.stream().map(s -> previous + "," + s); + } + @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { RandomPattern randomPattern = new RandomPattern(); - String[] splits = input.split(","); - List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']'); + String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); if (patterns.size() == 1) { return null; // let a 'single'-pattern parser handle it } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java index f7d3117e2..d8f27b08c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java @@ -22,16 +22,31 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.RandomStatePattern; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.FuzzyBlockState; + +import java.util.stream.Stream; public class RandomStatePatternParser extends InputParser { public RandomStatePatternParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("*"); + } + if (!input.startsWith("*")) { + return Stream.empty(); + } + + return worldEdit.getBlockFactory().getSuggestions(input.substring(1)).stream().map(s -> "*" + s); + } + @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("*")) { @@ -44,9 +59,11 @@ public class RandomStatePatternParser extends InputParser { context.setPreferringWildcard(wasFuzzy); if (block.getStates().size() == block.getBlockType().getPropertyMap().size()) { // they requested random with *, but didn't leave any states empty - simplify - return (block); + return block; + } else if (block.toImmutableState() instanceof FuzzyBlockState) { + return new RandomStatePattern((FuzzyBlockState) block.toImmutableState()); } else { return null; // only should happen if parseLogic changes } } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java index 3ecf3a9ce..2565b5d70 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java @@ -22,19 +22,25 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.registry.InputParser; +import java.util.stream.Stream; + public class SingleBlockPatternParser extends InputParser { public SingleBlockPatternParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + return worldEdit.getBlockFactory().getSuggestions(input).stream(); + } + @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { - return (worldEdit.getBlockFactory().parseFromInput(input, context)); + return worldEdit.getBlockFactory().parseFromInput(input, context); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java index db0ee8c38..757235940 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java @@ -19,10 +19,8 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; -import com.google.common.base.Splitter; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extent.Extent; @@ -32,10 +30,9 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.StateApplyingPattern; import com.sk89q.worldedit.function.pattern.TypeApplyingPattern; import com.sk89q.worldedit.internal.registry.InputParser; -import com.sk89q.worldedit.world.block.BlockState; - +import java.util.HashMap; import java.util.Map; -import java.util.Set; +import java.util.stream.Stream; public class TypeOrStateApplyingPatternParser extends InputParser { @@ -44,6 +41,30 @@ public class TypeOrStateApplyingPatternParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("^"); + } + if (!input.startsWith("^")) { + return Stream.empty(); + } + input = input.substring(1); + + String[] parts = input.split("\\[", 2); + String type = parts[0]; + + if (parts.length == 1) { + return worldEdit.getBlockFactory().getSuggestions(input).stream().map(s -> "^" + s); + } else { + if (type.isEmpty()) { + return Stream.empty(); // without knowing a type, we can't really suggest states + } else { + return SuggestionHelper.getBlockPropertySuggestions(type, parts[1]).map(s -> "^" + s); + } + } + } + @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("^")) { @@ -60,10 +81,21 @@ public class TypeOrStateApplyingPatternParser extends InputParser { worldEdit.getBlockFactory().parseFromInput(type, context).getBlockType().getDefaultState()); } else { // states given - if (!parts[1].endsWith("]")) throw new InputParseException("Invalid state format."); - Map statesToSet = Splitter.on(',') - .omitEmptyStrings().trimResults().withKeyValueSeparator('=') - .split(parts[1].substring(0, parts[1].length() - 1)); + if (!parts[1].endsWith("]")) throw new InputParseException("State is missing trailing ']'"); + final String[] states = parts[1].substring(0, parts[1].length() - 1).split(","); + Map statesToSet = new HashMap<>(); + for (String state : states) { + if (state.isEmpty()) throw new InputParseException("Empty part in state"); + String[] propVal = state.split("=", 2); + if (propVal.length != 2) throw new InputParseException("Missing '=' separator"); + final String prop = propVal[0]; + if (prop.isEmpty()) throw new InputParseException("Empty property in state"); + final String value = propVal[1]; + if (value.isEmpty()) throw new InputParseException("Empty value in state"); + if (statesToSet.put(prop, value) != null) { + throw new InputParseException("Duplicate properties in state"); + } + } if (type.isEmpty()) { return new StateApplyingPattern(extent, statesToSet); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java index 7cc6e1009..762884ab8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extension.platform; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import java.util.Collections; @@ -39,4 +40,8 @@ public abstract class AbstractPlatform implements Platform { return Collections.emptyList(); } + @Override + public DataFixer getDataFixer() { + return null; + } } 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 42ae1ff6b..7fe98e233 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 @@ -23,6 +23,7 @@ import com.sk89q.worldedit.NotABlockException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MutableBlockVector3; @@ -44,6 +45,7 @@ import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.registry.BlockMaterial; +import javax.annotation.Nullable; import java.io.File; /** @@ -107,7 +109,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { byte free = 0; - BlockVector3 mutablePos = MutableBlockVector3.at(0, 0, 0); + BlockVector3 mutablePos = MutableBlockVector3.ZERO; while (y <= world.getMaximumPoint().getBlockY() + 2) { if (!world.getBlock(mutablePos.setComponents(x, y, z)).getBlockType().getMaterial().isMovementBlocker()) { ++free; @@ -118,7 +120,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { if (free == 2) { final BlockVector3 pos = mutablePos.setComponents(x, y - 2, z); final BlockStateHolder state = world.getBlock(pos); - setPosition(new Location(world, Vector3.at(x + 0.5, y - 2 + BlockTypeUtil.centralTopLimit(state), z + 0.5))); + setPosition(Vector3.at(x + 0.5, y - 2 + BlockTypeUtil.centralTopLimit(state), z + 0.5)); return; } @@ -137,7 +139,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { final BlockVector3 pos = BlockVector3.at(x, y, z); final BlockState id = world.getBlock(pos); if (id.getBlockType().getMaterial().isMovementBlocker()) { - setPosition(new Location(world, Vector3.at(x + 0.5, y + + BlockTypeUtil.centralTopLimit(id), z + 0.5))); + setPosition(Vector3.at(x + 0.5, y + + BlockTypeUtil.centralTopLimit(id), z + 0.5)); return; } @@ -161,7 +163,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { int maxY = world.getMaxY(); if (y >= maxY) return false; - BlockMaterial initialMaterial = world.getBlockType(BlockVector3.at(x, y, z)).getMaterial(); + BlockMaterial initialMaterial = world.getBlock(BlockVector3.at(x, y, z)).getMaterial(); boolean lastState = initialMaterial.isMovementBlocker() && initialMaterial.isFullCube(); @@ -199,6 +201,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { lastState = true; } } + return false; } @@ -206,20 +209,19 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { public boolean descendLevel() { final Location pos = getBlockIn(); final int x = pos.getBlockX(); - int y = Math.max(0, pos.getBlockY()); + int y = Math.max(0, pos.getBlockY() - 1); final int z = pos.getBlockZ(); final Extent world = pos.getExtent(); - BlockMaterial initialMaterial = world.getBlockType(BlockVector3.at(x, y, z)).getMaterial(); + BlockMaterial initialMaterial = world.getBlock(BlockVector3.at(x, y, z)).getMaterial(); boolean lastState = initialMaterial.isMovementBlocker() && initialMaterial.isFullCube(); - double height = 1.85; - double freeEnd = -1; - int maxY = world.getMaxY(); if (y <= 2) return false; + double freeEnd = -1; + double height = 1.85; for (int level = y + 1; level > 0; level--) { BlockState state; if (level >= maxY) state = BlockTypes.VOID_AIR.getDefaultState(); @@ -252,6 +254,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { freeEnd = -1; } } + return false; } @@ -344,13 +347,29 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { @Override public Location getBlockTrace(int range, boolean useLastBlock) { - TargetBlock tb = new TargetBlock(this, range, 0.2); - return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock()); + return getBlockTrace(range, useLastBlock, null); } @Override public Location getBlockTraceFace(int range, boolean useLastBlock) { + return getBlockTraceFace(range, useLastBlock, null); + } + + @Override + public Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask) { TargetBlock tb = new TargetBlock(this, range, 0.2); + if (stopMask != null) { + tb.setStopMask(stopMask); + } + return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock()); + } + + @Override + public Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask) { + TargetBlock tb = new TargetBlock(this, range, 0.2); + if (stopMask != null) { + tb.setStopMask(stopMask); + } return (useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace()); } @@ -393,7 +412,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { if (typeId.hasBlockType()) { return typeId.getBlockType().getDefaultState().toBaseBlock(); } else { - return BlockTypes.AIR.getDefaultState().toBaseBlock(); + throw new NotABlockException(); } } 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 cf64b5974..cdcba32a2 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,6 +23,7 @@ import com.sk89q.worldedit.internal.cui.CUIEvent; 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 java.io.File; @@ -75,6 +76,13 @@ public interface Actor extends Identifiable, SessionOwner, Subject { */ void printError(String msg); + /** + * Print a {@link Component}. + * + * @param component The component to print + */ + void print(Component component); + /** * Returns true if the actor can destroy bedrock. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java index a753b0fd2..e91c799bc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java @@ -50,12 +50,12 @@ public enum Capability { USER_COMMANDS { @Override void initialize(PlatformManager platformManager, Platform platform) { - platformManager.getCommandManager().register(platform); + platformManager.getPlatformCommandManager().registerCommandsWith(platform); } @Override void unload(PlatformManager platformManager, Platform platform) { - platformManager.getCommandManager().unregister(); + platformManager.getPlatformCommandManager().removeCommands(); } }, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java deleted file mode 100644 index bfaf261b6..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.extension.platform; - -import com.boydti.fawe.Fawe; -import com.boydti.fawe.command.AnvilCommands; -import com.boydti.fawe.command.CFICommand; -import com.boydti.fawe.command.MaskBinding; -import com.boydti.fawe.command.PatternBinding; -import com.boydti.fawe.config.BBC; -import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.exception.FaweException; -import com.boydti.fawe.object.task.ThrowableSupplier; -import com.boydti.fawe.util.StringMan; -import com.boydti.fawe.util.TaskManager; -import com.boydti.fawe.util.chat.UsageMessage; -import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper; -import com.google.common.base.Joiner; -import com.sk89q.minecraft.util.commands.*; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.command.*; -import com.sk89q.worldedit.command.argument.ReplaceParser; -import com.sk89q.worldedit.command.argument.TreeGeneratorParser; -import com.sk89q.worldedit.command.composition.ApplyCommand; -import com.sk89q.worldedit.command.composition.DeformCommand; -import com.sk89q.worldedit.command.composition.PaintCommand; -import com.sk89q.worldedit.command.composition.ShapedBrushCommand; -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.extent.Extent; -import com.sk89q.worldedit.function.factory.Deform; -import com.sk89q.worldedit.function.factory.Deform.Mode; -import com.sk89q.worldedit.internal.command.*; -import com.sk89q.worldedit.scripting.CommandScriptLoader; -import com.sk89q.worldedit.session.request.Request; -import com.sk89q.worldedit.util.auth.AuthorizationException; -import com.sk89q.worldedit.util.command.*; -import com.sk89q.worldedit.util.command.composition.ProvidedValue; -import com.sk89q.worldedit.util.command.fluent.CommandGraph; -import com.sk89q.worldedit.util.command.fluent.DispatcherNode; -import com.sk89q.worldedit.util.command.parametric.AParametricCallable; -import com.sk89q.worldedit.util.command.parametric.ExceptionConverter; -import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler; -import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; -import com.sk89q.worldedit.util.eventbus.Subscribe; -import com.sk89q.worldedit.util.logging.DynamicStreamHandler; -import com.sk89q.worldedit.util.logging.LogFormat; -import com.sk89q.worldedit.world.World; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.FileHandler; -import java.util.logging.Level; -import java.util.regex.Pattern; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldedit.util.command.composition.LegacyCommandAdapter.adapt; - -/** - * Handles the registration and invocation of commands. - * - *

This class is primarily for internal usage.

- */ -public final class CommandManager { - - public static final Pattern COMMAND_CLEAN_PATTERN = Pattern.compile("^[/]+"); - private static final Logger log = LoggerFactory.getLogger(CommandManager.class); - private static final java.util.logging.Logger commandLog = - java.util.logging.Logger.getLogger(CommandManager.class.getCanonicalName() + ".CommandLog"); - private static final Pattern numberFormatExceptionPattern = Pattern.compile("^For input string: \"(.*)\"$"); - - private final WorldEdit worldEdit; - private final PlatformManager platformManager; - private volatile Dispatcher dispatcher; - private volatile Platform platform; - private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler(); - private final ExceptionConverter exceptionConverter; - - private ParametricBuilder builder; - private Map methodMap; - private Map commandMap; - - private static CommandManager INSTANCE; - - /** - * Create a new instance. - * - * @param worldEdit the WorldEdit instance - */ - public CommandManager(final WorldEdit worldEdit, PlatformManager platformManager) { - checkNotNull(worldEdit); - checkNotNull(platformManager); - INSTANCE = this; - - this.worldEdit = worldEdit; - this.platformManager = platformManager; - this.exceptionConverter = new WorldEditExceptionConverter(worldEdit); - - // Register this instance for command events - worldEdit.getEventBus().register(this); - - // Setup the logger - commandLog.addHandler(dynamicHandler); - dynamicHandler.setFormatter(new LogFormat()); - - // Set up the commands manager - builder = new ParametricBuilder(); - builder.setAuthorizer(new ActorAuthorizer()); - builder.setDefaultCompleter(new UserCommandCompleter(platformManager)); - builder.addBinding(new WorldEditBinding(worldEdit)); - builder.addBinding(new PatternBinding(worldEdit), com.sk89q.worldedit.function.pattern.Pattern.class); - builder.addBinding(new MaskBinding(worldEdit), com.sk89q.worldedit.function.mask.Mask.class); - builder.addInvokeListener(new LegacyCommandsHandler()); - builder.addInvokeListener(new CommandLoggingHandler(worldEdit, commandLog)); - - this.methodMap = new ConcurrentHashMap<>(); - this.commandMap = new ConcurrentHashMap<>(); - } - - /** - * Register all the methods in the class as commands
- * - You should try to register commands during startup - * - * @param clazz The class containing all the commands - */ - public void registerCommands(Object clazz) { - registerCommands(clazz, new String[0]); - } - - /** - * Create a command with the provided aliases and register all methods of the class as sub commands.
- * - You should try to register commands during startup - * - * @param clazz The class containing all the sub command methods - * @param aliases The aliases to give the command - */ - public synchronized void registerCommands(Object clazz, String... aliases) { - if (dispatcher != null) { - if (aliases.length == 0) { - builder.registerMethodsAsCommands(dispatcher, clazz); - } else { - DispatcherNode graph = new CommandGraph().builder(builder).commands(); - graph = graph.registerMethods(clazz); - dispatcher.registerCommand(graph.graph().getDispatcher(), aliases); - } - platform.registerCommands(dispatcher); - } else { - methodMap.put(clazz, aliases); - } - } - - /** - * Create a command with the provided aliases and register all methods of the class as sub commands.
- * - You should try to register commands during startup - * - * @param clazz The class containing all the sub command methods - * @param aliases The aliases to give the command - */ - public synchronized void registerCommands(Object clazz, CallableProcessor processor, String... aliases) { - if (dispatcher != null) { - if (aliases.length == 0) { - builder.registerMethodsAsCommands(dispatcher, clazz, processor); - } else { - DispatcherNode graph = new CommandGraph().builder(builder).commands(); - graph = graph.registerMethods(clazz, processor); - dispatcher.registerCommand(graph.graph().getDispatcher(), aliases); - } - platform.registerCommands(dispatcher); - } else { - methodMap.put(clazz, aliases); - } - } - - public synchronized void registerCommand(String[] aliases, Command command, CommandCallable callable) { - if (dispatcher != null) { - if (aliases.length == 0) { - dispatcher.registerCommand(callable, command.aliases()); - } else { - DispatcherNode graph = new CommandGraph().builder(builder).commands(); - graph = graph.register(callable, command.aliases()); - dispatcher.registerCommand(graph.graph().getDispatcher(), aliases); - } - platform.registerCommands(dispatcher); - } else { - commandMap.putIfAbsent(callable, new String[][] {aliases, command.aliases()}); - } - } - - public ParametricBuilder getBuilder() { - return builder; - } - - /** - * Initialize the dispatcher - */ - public synchronized void setupDispatcher() { - if (Settings.IMP.ENABLED_COMPONENTS.COMMANDS) { - DispatcherNode graph = new CommandGraph().builder(builder).commands(); - - for (Map.Entry entry : methodMap.entrySet()) { - // add command - String[] aliases = entry.getValue(); - if (aliases.length == 0) { - graph = graph.registerMethods(entry.getKey()); - } else { - graph = graph.group(aliases).registerMethods(entry.getKey()).parent(); - } - } - - for (Map.Entry entry : commandMap.entrySet()) { - String[][] aliases = entry.getValue(); - CommandCallable callable = entry.getKey(); - if (aliases[0].length == 0) { - graph = graph.register(callable, aliases[1]); - } else { - graph = graph.group(aliases[0]).register(callable, aliases[1]).parent(); - } - } - - commandMap.clear(); - methodMap.clear(); - - dispatcher = graph - .group("/anvil") - .describeAs("Anvil command") - .registerMethods(new AnvilCommands(worldEdit)).parent() - .registerMethods(new CFICommand(worldEdit, builder)) - .registerMethods(new BiomeCommands(worldEdit)) - .registerMethods(new ChunkCommands(worldEdit)) - .registerMethods(new ClipboardCommands(worldEdit)) - .registerMethods(new OptionsCommands(worldEdit)) - .registerMethods(new GenerationCommands(worldEdit)) - .registerMethods(new HistoryCommands(worldEdit)) - .registerMethods(new NavigationCommands(worldEdit)) - .registerMethods(new RegionCommands(worldEdit)) - .registerMethods(new ScriptingCommands(worldEdit)) - .registerMethods(new SelectionCommands(worldEdit)) - .registerMethods(new SnapshotUtilCommands(worldEdit)) - .registerMethods(new BrushOptionsCommands(worldEdit)) - .registerMethods(new ToolCommands(worldEdit)) - .registerMethods(new UtilityCommands(worldEdit)) - .registerSubMethods(new WorldEditCommands(worldEdit)) - .registerSubMethods(new SchematicCommands(worldEdit)) - .registerSubMethods(new SnapshotCommands(worldEdit)) - .groupAndDescribe(BrushCommands.class) - .registerMethods(new BrushCommands(worldEdit)) - .registerMethods(new ToolCommands(worldEdit)) - .registerMethods(new BrushOptionsCommands(worldEdit)) - .register(adapt(new ShapedBrushCommand(new DeformCommand(), "worldedit.brush.deform")), "deform") - .register(adapt(new ShapedBrushCommand(new ApplyCommand(new ReplaceParser(), "Set all blocks within region"), "worldedit.brush.set")), "set") - .register(adapt(new ShapedBrushCommand(new PaintCommand(), "worldedit.brush.paint")), "paint") - .register(adapt(new ShapedBrushCommand(new ApplyCommand(), "worldedit.brush.apply")), "apply") - .register(adapt(new ShapedBrushCommand(new PaintCommand(new TreeGeneratorParser("treeType")), "worldedit.brush.forest")), "forest") - .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y-=1", Mode.RAW_COORD), "Raise one block"), "worldedit.brush.raise")), "raise") - .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y+=1", Mode.RAW_COORD), "Lower one block"), "worldedit.brush.lower")), "lower") - .parent() - .group("superpickaxe", "pickaxe", "sp").describeAs("Super-pickaxe commands") - .registerMethods(new SuperPickaxeCommands(worldEdit)) - .parent().graph().getDispatcher(); - - if (platform != null) { - platform.registerCommands(dispatcher); - } - } - } - - public static CommandManager getInstance() { - return INSTANCE; - } - - public ExceptionConverter getExceptionConverter() { - return exceptionConverter; - } - - public void register(Platform platform) { - log.info("Registering commands with " + platform.getClass().getCanonicalName()); - this.platform = platform; - - // 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; - - // Register log - if (!logging || path.isEmpty()) { - dynamicHandler.setHandler(null); - commandLog.setLevel(Level.OFF); - } else { - File file = new File(config.getWorkingDirectory(), path); - commandLog.setLevel(Level.ALL); - - log.info("Logging WorldEdit commands to " + file.getAbsolutePath()); - - try { - dynamicHandler.setHandler(new FileHandler(file.getAbsolutePath(), true)); - } catch (IOException e) { - log.warn("Could not use command log file " + path + ": " + e.getMessage()); - } - } - - setupDispatcher(); - } - - void unregister() { - dynamicHandler.setHandler(null); - } - - public String[] commandDetection(String[] split) { - // Quick script shortcut - if (split[0].matches("^[^/].*\\.js$")) { - String[] newSplit = new String[split.length + 1]; - System.arraycopy(split, 0, newSplit, 1, split.length); - newSplit[0] = "cs"; - newSplit[1] = newSplit[1]; - split = newSplit; - } - - String searchCmd = split[0].toLowerCase(); - - // Try to detect the command - if (!dispatcher.contains(searchCmd)) { - if (worldEdit.getConfiguration().noDoubleSlash && dispatcher.contains("/" + searchCmd)) { - split[0] = "/" + split[0]; - } else if (searchCmd.length() >= 2 && searchCmd.charAt(0) == '/' && dispatcher.contains(searchCmd.substring(1))) { - split[0] = split[0].substring(1); - } - } - - return split; - } - - public void handleCommandOnCurrentThread(CommandEvent event) { - Actor actor = platformManager.createProxyActor(event.getActor()); - String[] split = commandDetection(event.getArguments().split(" ")); - // No command found! - if (!dispatcher.contains(split[0])) { - return; - } - LocalSession session = worldEdit.getSessionManager().get(actor); - Request.request().setSession(session); - if (actor instanceof Entity) { - Extent extent = ((Entity) actor).getExtent(); - if (extent instanceof World) { - Request.request().setWorld(((World) extent)); - } - } - LocalConfiguration config = worldEdit.getConfiguration(); - - CommandLocals locals = new CommandLocals(); - final FawePlayer fp = FawePlayer.wrap(actor); - if (fp == null) { - throw new IllegalArgumentException("FAWE doesn't support: " + actor); - } - final Set failedPermissions = new LinkedHashSet<>(); - locals.put("failed_permissions", failedPermissions); - locals.put(LocalSession.class, session); - if (actor instanceof Player) { - Player player = (Player) actor; - Player unwrapped = LocationMaskedPlayerWrapper.unwrap(player); - actor = new LocationMaskedPlayerWrapper(unwrapped, player.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; - } - } - }; - } - locals.put(Actor.class, actor); - locals.put("arguments", event.getArguments()); - - ThrowableSupplier task = - () -> dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]); - - handleCommandTask(task, locals, actor, session, failedPermissions, fp); - } - - public Object handleCommandTask(ThrowableSupplier task, CommandLocals locals) { - return handleCommandTask(task, locals, null, null, null, null); - } - - private Object handleCommandTask(ThrowableSupplier task, CommandLocals locals, @Nullable - Actor actor, @Nullable LocalSession session, @Nullable Set failedPermissions, @Nullable FawePlayer fp) { - Request.reset(); - if (actor == null) actor = locals.get(Actor.class); - if (session == null) session = locals.get(LocalSession.class); - long start = System.currentTimeMillis(); - try { - // This is a bit of a hack, since the call method can only throw CommandExceptions - // everything needs to be wrapped at least once. Which means to handle all WorldEdit - // exceptions without writing a hook into every dispatcher, we need to unwrap these - // exceptions and rethrow their converted form, if their is one. - try { - Request.request().setActor(actor); - return 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 - Throwable next = t; - exceptionConverter.convert(next); - while (next.getCause() != null) { - next = next.getCause(); - exceptionConverter.convert(next); - } - throw next; - } - } catch (CommandPermissionsException e) { - if (failedPermissions == null) failedPermissions = (Set) locals.get("failed_permissions"); - if (failedPermissions != null) BBC.NO_PERM.send(actor, StringMan.join(failedPermissions, " ")); - } catch (InvalidUsageException e) { - if (e.isFullHelpSuggested()) { - CommandCallable cmd = e.getCommand(); - if (cmd instanceof Dispatcher) { - try { - String args = locals.get("arguments") + ""; - CommandContext context = new CommandContext(("ignoreThis " + args).split(" "), new HashSet<>(), false, locals); - UtilityCommands.help(context, worldEdit, actor); - } catch (CommandException e1) { - e1.printStackTrace(); - } - } else { - if (fp == null) fp = FawePlayer.wrap(actor); - new UsageMessage(cmd, e.getCommandUsed((WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/"), ""), locals).send(fp); - } - String message = e.getMessage(); - if (message != null) { - actor.printError(message); - } - } else { - String message = e.getMessage(); - actor.printRaw((message != null ? message : "The command was not used properly (no more help available).")); - BBC.COMMAND_SYNTAX.send(actor, e.getSimpleUsageString("/")); - } - } catch (CommandException e) { - String message = e.getMessage(); - if (message != null) { - actor.printError(e.getMessage()); - } else { - actor.printError("An unknown FAWE error has occurred! Please see console."); - log.error("An unknown FAWE error occurred", e); - } - } catch (Throwable e) { - Exception faweException = FaweException.get(e); - if (faweException != null) { - BBC.WORLDEDIT_CANCEL_REASON.send(actor, faweException.getMessage()); - } else { - actor.printError("There was an error handling a FAWE command: [See console]"); - actor.printRaw(e.getClass().getName() + ": " + e.getMessage()); - log.error("An unexpected error occurred while handling a FAWE command", e); - } - } finally { - final EditSession editSession = locals.get(EditSession.class); - if (editSession != null) { - editSession.flushQueue(); - worldEdit.flushBlockBag(locals.get(Actor.class), editSession); - session.remember(editSession); - long time = System.currentTimeMillis() - start; - if (time > 1000) { - BBC.ACTION_COMPLETE.send(actor, (time / 1000d)); - } - } - Request.reset(); - } - return null; - } - - @Subscribe - public void handleCommand(CommandEvent event) { - Request.reset(); - Actor actor = event.getActor(); - if (actor instanceof Player) { - actor = LocationMaskedPlayerWrapper.wrap((Player) actor); - } - String args = event.getArguments(); - CommandEvent finalEvent = new CommandEvent(actor, args); - final FawePlayer fp = FawePlayer.wrap(actor); - TaskManager.IMP.taskNow(() -> { - int space0 = args.indexOf(' '); - String arg0 = space0 == -1 ? args : args.substring(0, space0); - CommandMapping cmd = dispatcher.get(arg0); - if (cmd != null && cmd.getCallable() instanceof AParametricCallable) { - Command info = ((AParametricCallable) cmd.getCallable()).getDefinition(); - if (!info.queued()) { - handleCommandOnCurrentThread(finalEvent); - return; - } - } - if (!fp.runAction(new Runnable() { - @Override public void run() { - CommandManager.this.handleCommandOnCurrentThread(finalEvent); - } - }, false, true)) { - BBC.WORLDEDIT_COMMAND_LIMIT.send(fp); - } - finalEvent.setCancelled(true); - }, Fawe.isMainThread()); - } - - @Subscribe - public void handleCommandSuggestion(CommandSuggestionEvent event) { - try { - CommandLocals locals = new CommandLocals(); - locals.put(Actor.class, event.getActor()); - locals.put("arguments", event.getArguments()); - event.setSuggestions(dispatcher.getSuggestions(event.getArguments(), locals)); - } catch (CommandException e) { - event.getActor().printError(e.getMessage()); - } - } - - /** - * Get the command dispatcher instance. - * - * @return the command dispatcher - */ - public Dispatcher getDispatcher() { - return dispatcher; - } - - public static java.util.logging.Logger getLogger() { - return commandLog; - } - - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index 3b37a98b5..fcbd6ff29 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -21,14 +21,14 @@ package com.sk89q.worldedit.extension.platform; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; - -import java.util.List; -import java.util.Map; +import org.enginehub.piston.CommandManager; import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; /** * Represents a platform that WorldEdit has been implemented for. @@ -45,11 +45,25 @@ public interface Platform { */ Registries getRegistries(); + /** + * Gets the Minecraft data version being used by the platform. + * + * @return the data version + */ + int getDataVersion(); + + /** + * Get a DataFixer capable of upgrading old data. + * + * @return a data fixer, or null if not supported by this platform + */ + DataFixer getDataFixer(); + /** * Checks if a mob type is valid. * * @param type The mob type name to check - * @return Whether the name is a valid mod bype + * @return Whether the name is a valid mod type */ boolean isValidMobType(String type); @@ -97,11 +111,11 @@ public interface Platform { @Nullable World matchWorld(World world); /** - * Register the commands contained within the given command dispatcher. + * Register the commands contained within the given command manager. * - * @param dispatcher the dispatcher + * @param commandManager the command manager */ - void registerCommands(Dispatcher dispatcher); + void registerCommands(CommandManager commandManager); /** * Register game hooks. 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 new file mode 100644 index 000000000..c9f61b76b --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -0,0 +1,734 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +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.config.BBC; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.task.ThrowableSupplier; +import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.reflect.TypeToken; +import com.sk89q.minecraft.util.commands.CommandLocals; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.ApplyBrushCommands; +import com.sk89q.worldedit.command.BiomeCommands; +import com.sk89q.worldedit.command.BiomeCommandsRegistration; +import com.sk89q.worldedit.command.BrushCommands; +import com.sk89q.worldedit.command.ChunkCommands; +import com.sk89q.worldedit.command.ClipboardCommands; +import com.sk89q.worldedit.command.GeneralCommands; +import com.sk89q.worldedit.command.GenerationCommands; +import com.sk89q.worldedit.command.HistoryCommands; +import com.sk89q.worldedit.command.NavigationCommands; +import com.sk89q.worldedit.command.RegionCommands; +import com.sk89q.worldedit.command.SchematicCommands; +import com.sk89q.worldedit.command.SchematicCommandsRegistration; +import com.sk89q.worldedit.command.ScriptingCommands; +import com.sk89q.worldedit.command.SelectionCommands; +import com.sk89q.worldedit.command.SnapshotCommands; +import com.sk89q.worldedit.command.SnapshotCommandsRegistration; +import com.sk89q.worldedit.command.SnapshotUtilCommands; +import com.sk89q.worldedit.command.SuperPickaxeCommands; +import com.sk89q.worldedit.command.SuperPickaxeCommandsRegistration; +import com.sk89q.worldedit.command.ToolCommands; +import com.sk89q.worldedit.command.ToolUtilCommands; +import com.sk89q.worldedit.command.UtilityCommands; +import com.sk89q.worldedit.command.WorldEditCommands; +import com.sk89q.worldedit.command.WorldEditCommandsRegistration; +import com.sk89q.worldedit.command.argument.Arguments; +import com.sk89q.worldedit.command.argument.BooleanConverter; +import com.sk89q.worldedit.command.argument.CommaSeparatedValuesConverter; +import com.sk89q.worldedit.command.argument.DirectionConverter; +import com.sk89q.worldedit.command.argument.DirectionVectorConverter; +import com.sk89q.worldedit.command.argument.EntityRemoverConverter; +import com.sk89q.worldedit.command.argument.EnumConverter; +import com.sk89q.worldedit.command.argument.FactoryConverter; +import com.sk89q.worldedit.command.argument.RegionFactoryConverter; +import com.sk89q.worldedit.command.argument.RegistryConverter; +import com.sk89q.worldedit.command.argument.VectorConverter; +import com.sk89q.worldedit.command.argument.ZonedDateTimeConverter; +import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.command.util.SubCommandPermissionCondition; +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.extent.Extent; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.internal.command.CommandArgParser; +import com.sk89q.worldedit.internal.command.CommandLoggingHandler; +import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; +import com.sk89q.worldedit.internal.command.exception.ExceptionConverter; +import com.sk89q.worldedit.internal.command.exception.WorldEditExceptionConverter; +import com.sk89q.worldedit.internal.util.Substring; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.scripting.CommandScriptLoader; +import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.util.auth.AuthorizationException; +import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.parametric.AParametricCallable; +import com.sk89q.worldedit.util.eventbus.Subscribe; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +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.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +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.TextConfig; +import org.enginehub.piston.converter.ArgumentConverters; +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.exception.CommandExecutionException; +import org.enginehub.piston.exception.ConditionFailedException; +import org.enginehub.piston.exception.UsageException; +import org.enginehub.piston.gen.CommandRegistration; +import org.enginehub.piston.impl.CommandManagerServiceImpl; +import org.enginehub.piston.inject.InjectedValueStore; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.inject.MapBackedValueStore; +import org.enginehub.piston.inject.MemoizingValueAccess; +import org.enginehub.piston.inject.MergedValueAccess; +import org.enginehub.piston.part.SubCommandPart; +import org.enginehub.piston.suggestion.Suggestion; +import org.enginehub.piston.util.HelpGenerator; +import org.enginehub.piston.util.ValueProvider; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles the registration and invocation of commands. + * + *

This class is primarily for internal usage.

+ */ +public final class PlatformCommandManager { + + public static final Pattern COMMAND_CLEAN_PATTERN = Pattern.compile("^[/]+"); + private static final Logger log = LoggerFactory.getLogger(PlatformCommandManager.class); + private static final java.util.logging.Logger COMMAND_LOG = + java.util.logging.Logger.getLogger("com.sk89q.worldedit.CommandLog"); + + static { + TextConfig.setCommandPrefix("/"); + } + + private final WorldEdit worldEdit; + private final PlatformManager platformManager; + private final CommandManagerServiceImpl commandManagerService; + private final CommandManager commandManager; + private final InjectedValueStore globalInjectedValues; + private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler(); + private final WorldEditExceptionConverter exceptionConverter; + private final CommandRegistrationHandler registration; + private static PlatformCommandManager INSTANCE; + + /** + * Create a new instance. + * + * @param worldEdit the WorldEdit instance + */ + public PlatformCommandManager(final WorldEdit worldEdit, PlatformManager platformManager) { + checkNotNull(worldEdit); + checkNotNull(platformManager); + INSTANCE = this; + + this.worldEdit = worldEdit; + this.platformManager = platformManager; + this.exceptionConverter = new WorldEditExceptionConverter(worldEdit); + this.commandManagerService = new CommandManagerServiceImpl(); + this.commandManager = commandManagerService.newCommandManager(); + this.globalInjectedValues = MapBackedValueStore.create(); + this.registration = new CommandRegistrationHandler( + ImmutableList.of( + new CommandLoggingHandler(worldEdit, COMMAND_LOG) + )); + // setup separate from main constructor + // ensures that everything is definitely assigned + initialize(); + } + + private void initialize() { + // Register this instance for command events + worldEdit.getEventBus().register(this); + + // Setup the logger + COMMAND_LOG.addHandler(dynamicHandler); + + // Set up the commands manager + registerAlwaysInjectedValues(); + registerArgumentConverters(); + registerAllCommands(); + } + + private void registerArgumentConverters() { + DirectionVectorConverter.register(worldEdit, commandManager); + DirectionConverter.register(worldEdit, commandManager); + FactoryConverter.register(worldEdit, commandManager); + for (int count = 2; count <= 3; count++) { + commandManager.registerConverter(Key.of(double.class, Annotations.radii(count)), + CommaSeparatedValuesConverter.wrapAndLimit(ArgumentConverters.get( + TypeToken.of(double.class) + ), count) + ); + } + VectorConverter.register(commandManager); + EnumConverter.register(commandManager); + RegistryConverter.register(commandManager); + ZonedDateTimeConverter.register(commandManager); + BooleanConverter.register(commandManager); + EntityRemoverConverter.register(commandManager); + RegionFactoryConverter.register(commandManager); + } + + private void registerAlwaysInjectedValues() { + globalInjectedValues.injectValue(Key.of(Region.class, Selection.class), + context -> { + LocalSession localSession = context.injectedValue(Key.of(LocalSession.class)) + .orElseThrow(() -> new IllegalStateException("No LocalSession")); + return context.injectedValue(Key.of(Player.class)) + .map(player -> { + try { + return localSession.getSelection(player.getWorld()); + } catch (IncompleteRegionException e) { + exceptionConverter.convert(e); + throw new AssertionError("Should have thrown a new exception."); + } + }); + }); + globalInjectedValues.injectValue(Key.of(EditSession.class), + context -> { + LocalSession localSession = context.injectedValue(Key.of(LocalSession.class)) + .orElseThrow(() -> new IllegalStateException("No LocalSession")); + return context.injectedValue(Key.of(Player.class)) + .map(player -> { + EditSession editSession = localSession.createEditSession(player); + editSession.enableStandardMode(); + return editSession; + }); + }); + } + + private void registerSubCommands(String name, List aliases, String desc, + CommandRegistration registration, CI instance) { + registerSubCommands(name, aliases, desc, registration, instance, m -> {}); + } + + private void registerSubCommands(String name, List aliases, String desc, + CommandRegistration registration, CI instance, + Consumer additionalConfig) { + commandManager.register(name, cmd -> { + cmd.aliases(aliases); + cmd.description(TextComponent.of(desc)); + cmd.action(Command.Action.NULL_ACTION); + + CommandManager manager = commandManagerService.newCommandManager(); + this.registration.register( + manager, + registration, + instance + ); + 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()); + + cmd.condition(new SubCommandPermissionCondition.Generator(subCommands).build()); + }); + } + + private void registerAllCommands() { + if (Settings.IMP.ENABLED_COMPONENTS.COMMANDS) { + registerSubCommands( + "schematic", + ImmutableList.of("schem", "/schematic", "/schem"), + "Schematic commands for saving/loading areas", + SchematicCommandsRegistration.builder(), + new SchematicCommands(worldEdit) + ); + registerSubCommands( + "snapshot", + ImmutableList.of("snap"), + "Snapshot commands for restoring backups", + SnapshotCommandsRegistration.builder(), + new SnapshotCommands(worldEdit) + ); + registerSubCommands( + "superpickaxe", + ImmutableList.of("pickaxe", "sp"), + "Super-pickaxe commands", + SuperPickaxeCommandsRegistration.builder(), + new SuperPickaxeCommands(worldEdit) + ); + registerSubCommands( + "brush", + ImmutableList.of("br", "/brush", "/br"), + "Brushing commands", + BrushCommandsRegistration.builder(), + new BrushCommands(worldEdit), + manager -> { + PaintBrushCommands.register(commandManagerService, manager, registration); + ApplyBrushCommands.register(commandManagerService, manager, registration); + } + ); + registerSubCommands( + "worldedit", + ImmutableList.of("we"), + "WorldEdit commands", + WorldEditCommandsRegistration.builder(), + new WorldEditCommands(worldEdit) + ); + this.registration.register( + commandManager, + BiomeCommandsRegistration.builder(), + new BiomeCommands() + ); + this.registration.register( + commandManager, + ChunkCommandsRegistration.builder(), + new ChunkCommands(worldEdit) + ); + this.registration.register( + commandManager, + ClipboardCommandsRegistration.builder(), + new ClipboardCommands(worldEdit) + ); + this.registration.register( + commandManager, + GeneralCommandsRegistration.builder(), + new GeneralCommands(worldEdit) + ); + this.registration.register( + commandManager, + GenerationCommandsRegistration.builder(), + new GenerationCommands(worldEdit) + ); + this.registration.register( + commandManager, + HistoryCommandsRegistration.builder(), + new HistoryCommands(worldEdit) + ); + this.registration.register( + commandManager, + NavigationCommandsRegistration.builder(), + new NavigationCommands(worldEdit) + ); + this.registration.register( + commandManager, + RegionCommandsRegistration.builder(), + new RegionCommands(worldEdit) + ); + this.registration.register( + commandManager, + ScriptingCommandsRegistration.builder(), + new ScriptingCommands(worldEdit) + ); + this.registration.register( + commandManager, + SelectionCommandsRegistration.builder(), + new SelectionCommands(worldEdit) + ); + ExpandCommands.register(registration, commandManager, commandManagerService); + this.registration.register( + commandManager, + SnapshotUtilCommandsRegistration.builder(), + new SnapshotUtilCommands(worldEdit) + ); + this.registration.register( + commandManager, + ToolCommandsRegistration.builder(), + new ToolCommands(worldEdit) + ); + this.registration.register( + commandManager, + ToolUtilCommandsRegistration.builder(), + new ToolUtilCommands(worldEdit) + ); + this.registration.register( + commandManager, + UtilityCommandsRegistration.builder(), + new UtilityCommands(worldEdit) + ); + this.registration.register( + commandManager, + AnvilCommandsRegistration.builder(), + new AnvilCommands(worldEdit) + ); + } + } + +// /** +// * Initialize the dispatcher +// */ +// public synchronized void setupDispatcher() { +// if (Settings.IMP.ENABLED_COMPONENTS.COMMANDS) { +// DispatcherNode graph = new CommandGraph().builder(builder).commands(); +// +// for (Map.Entry entry : methodMap.entrySet()) { +// // add command +// String[] aliases = entry.getValue(); +// if (aliases.length == 0) { +// graph = graph.registerMethods(entry.getKey()); +// } else { +// graph = graph.group(aliases).registerMethods(entry.getKey()).parent(); +// } +// } +// +// for (Map.Entry entry : commandMap.entrySet()) { +// String[][] aliases = entry.getValue(); +// CommandCallable callable = entry.getKey(); +// if (aliases[0].length == 0) { +// graph = graph.register(callable, aliases[1]); +// } else { +// graph = graph.group(aliases[0]).register(callable, aliases[1]).parent(); +// } +// } +// +// commandMap.clear(); +// methodMap.clear(); +// +// dispatcher = graph +// .group("/anvil") +// .describeAs("Anvil command") +// .registerMethods(new AnvilCommands(worldEdit)).parent() +// .registerMethods(new CFICommand(worldEdit, builder)) +// .registerMethods(new OptionsCommands(worldEdit)) +// .registerMethods(new BrushOptionsCommands(worldEdit)) +// .registerMethods(new BrushOptionsCommands(worldEdit)) +// .register(adapt(new ShapedBrushCommand(new DeformCommand(), "worldedit.brush.deform")), "deform") +// .register(adapt(new ShapedBrushCommand(new ApplyCommand(new ReplaceParser(), "Set all blocks within region"), "worldedit.brush.set")), "set") +// .register(adapt(new ShapedBrushCommand(new PaintCommand(), "worldedit.brush.paint")), "paint") +// .register(adapt(new ShapedBrushCommand(new ApplyCommand(), "worldedit.brush.apply")), "apply") +// .register(adapt(new ShapedBrushCommand(new PaintCommand(new TreeGeneratorParser("treeType")), "worldedit.brush.forest")), "forest") +// .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y-=1", Mode.RAW_COORD), "Raise one block"), "worldedit.brush.raise")), "raise") +// .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y+=1", Mode.RAW_COORD), "Lower one block"), "worldedit.brush.lower")), "lower") +// .parent() +// .group("superpickaxe", "pickaxe", "sp").describeAs("Super-pickaxe commands") +// .registerMethods(new SuperPickaxeCommands(worldEdit)) +// .parent().graph().getDispatcher(); +// +// if (platform != null) { +// platform.registerCommands(dispatcher); +// } +// } +// } + + public static PlatformCommandManager getInstance() { + return INSTANCE; + } + + public ExceptionConverter getExceptionConverter() { + return exceptionConverter; + } + + 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; + + // Register log + if (!logging || path.isEmpty()) { + dynamicHandler.setHandler(null); + COMMAND_LOG.setLevel(Level.OFF); + } else { + File file = new File(config.getWorkingDirectory(), path); + COMMAND_LOG.setLevel(Level.ALL); + + log.info("Logging WorldEdit commands to " + file.getAbsolutePath()); + + try { + dynamicHandler.setHandler(new FileHandler(file.getAbsolutePath(), true)); + } catch (IOException e) { + log.warn("Could not use command log file " + path + ": " + e.getMessage()); + } + + dynamicHandler.setFormatter(new LogFormat(config.logFormat)); + } + + platform.registerCommands(commandManager); + } + + void removeCommands() { + dynamicHandler.setHandler(null); + } + + private Stream parseArgs(String input) { + return new CommandArgParser(CommandArgParser.spaceSplit(input.substring(1))).parseArgs(); + } + + @Subscribe + public void handleCommand(CommandEvent event) { + Request.reset(); + Actor actor = event.getActor(); + if (actor instanceof Player) { + actor = LocationMaskedPlayerWrapper.wrap((Player) actor); + } + String args = event.getArguments(); + CommandEvent finalEvent = new CommandEvent(actor, args); + final FawePlayer fp = FawePlayer.wrap(actor); + TaskManager.IMP.taskNow(() -> { + int space0 = args.indexOf(' '); + String arg0 = space0 == -1 ? args : args.substring(0, space0); + Optional cmd = commandManager.getCommand(arg0); + if (cmd.isPresent()) { + if (!cmd.queued()) { + handleCommandOnCurrentThread(finalEvent); + return; + } + } + if (!fp.runAction( + () -> PlatformCommandManager.this.handleCommandOnCurrentThread(finalEvent), false, true)) { + BBC.WORLDEDIT_COMMAND_LIMIT.send(fp); + } + finalEvent.setCancelled(true); + }, Fawe.isMainThread()); + } + + public void handleCommandOnCurrentThread(CommandEvent event) { + Actor actor = platformManager.createProxyActor(event.getActor()); + String[] split = parseArgs(event.getArguments()) + .map(Substring::getSubstring) + .toArray(String[]::new); + + // No command found! + if (!commandManager.containsCommand(split[0])) { + return; + } + + LocalSession session = worldEdit.getSessionManager().get(actor); + Request.request().setSession(session); + if (actor instanceof Entity) { + Extent extent = ((Entity) actor).getExtent(); + if (extent instanceof World) { + Request.request().setWorld(((World) extent)); + } + } + LocalConfiguration config = worldEdit.getConfiguration(); + + MemoizingValueAccess context = initializeInjectedValues(event::getArguments, actor); + + final FawePlayer fp = FawePlayer.wrap(actor); + if (fp == null) { + throw new IllegalArgumentException("FAWE doesn't support: " + actor); + } + + ThrowableSupplier task = + () -> commandManager.execute(context,Lists.newArrayList(split)); + + handleCommandTask(task, context, actor, session, event); + } + + public Object handleCommandTask(ThrowableSupplier task, + MemoizingValueAccess context, @NotNull + Actor actor, @Nullable LocalSession session, CommandEvent event) { + String[] split = parseArgs(event.getArguments()) + .map(Substring::getSubstring) + .toArray(String[]::new); + + Request.reset(); + long start = System.currentTimeMillis(); + + try { + // This is a bit of a hack, since the call method can only throw CommandExceptions + // everything needs to be wrapped at least once. Which means to handle all WorldEdit + // exceptions without writing a hook into every dispatcher, we need to unwrap these + // exceptions and rethrow their converted form, if their is one. + try { + commandManager.execute(context, ImmutableList.copyOf(split)); + } catch (Throwable t) { + // Use the exception converter to convert the exception if any of its causes + // can be converted, otherwise throw the original exception + Throwable next = t; + do { + exceptionConverter.convert(next); + next = next.getCause(); + } while (next != null); + + throw t; + } + } catch (ConditionFailedException e) { + if (e.getCondition() instanceof PermissionCondition) { + actor.printError("You are not permitted to do that. Are you in the right mode?"); + } else { + actor.print(e.getRichMessage()); + } + } catch (UsageException e) { + actor.print(TextComponent.builder("") + .color(TextColor.RED) + .append(e.getRichMessage()) + .build()); + ImmutableList cmd = e.getCommands(); + if (!cmd.isEmpty()) { + actor.print(TextComponent.builder("Usage: ") + .color(TextColor.RED) + .append(HelpGenerator.create(e.getCommandParseResult()).getUsage()) + .build()); + } + } catch (CommandExecutionException e) { + handleUnknownException(actor, e.getCause()); + } catch (CommandException e) { + actor.print(TextComponent.builder("") + .color(TextColor.RED) + .append(e.getRichMessage()) + .build()); + } catch (Throwable t) { + handleUnknownException(actor, t); + } finally { + Optional editSessionOpt = + context.snapshotMemory().injectedValue(Key.of(EditSession.class)); + + if (editSessionOpt.isPresent()) { + EditSession editSession = editSessionOpt.get(); + session.remember(editSession); + editSession.flushQueue(); + + long time = System.currentTimeMillis() - start; + if (time > 1000) { + BBC.ACTION_COMPLETE.send(actor, time / 1000D); + } + + worldEdit.flushBlockBag(actor, editSession); + } + Request.reset(); + } + + event.setCancelled(true); + return null; + } + + private MemoizingValueAccess initializeInjectedValues(Arguments arguments, Actor actor) { + InjectedValueStore store = MapBackedValueStore.create(); + store.injectValue(Key.of(Actor.class), ValueProvider.constant(actor)); + if (actor instanceof Player) { + store.injectValue(Key.of(Player.class), ValueProvider.constant((Player) actor)); + } else { + store.injectValue(Key.of(Player.class), context -> { + throw new CommandException(TextComponent.of("This command must be used with a player."), ImmutableList.of()); + }); + } + store.injectValue(Key.of(Arguments.class), ValueProvider.constant(arguments)); + store.injectValue(Key.of(LocalSession.class), + context -> { + LocalSession localSession = worldEdit.getSessionManager().get(actor); + localSession.tellVersion(actor); + return Optional.of(localSession); + }); + + return MemoizingValueAccess.wrap( + MergedValueAccess.of(store, globalInjectedValues) + ); + } + + private void handleUnknownException(Actor actor, Throwable t) { + actor.printError("Please report this error: [See console]"); + actor.printRaw(t.getClass().getName() + ": " + t.getMessage()); + log.error("An unexpected error while handling a WorldEdit command", t); + } + + @Subscribe + public void handleCommandSuggestion(CommandSuggestionEvent event) { + try { + String arguments = event.getArguments(); + List split = parseArgs(arguments).collect(Collectors.toList()); + List argStrings = split.stream() + .map(Substring::getSubstring) + .collect(Collectors.toList()); + MemoizingValueAccess access = initializeInjectedValues(() -> arguments, event.getActor()); + ImmutableSet suggestions; + try { + suggestions = commandManager.getSuggestions(access, argStrings); + } catch (Throwable t) { // catch errors which are *not* command exceptions generated by parsers/suggesters + if (!(t instanceof CommandException)) { + log.debug("Unexpected error occurred while generating suggestions for input: " + arguments, t); + return; + } + throw t; + } + + event.setSuggestions(suggestions.stream() + .map(suggestion -> { + int noSlashLength = arguments.length() - 1; + Substring original = suggestion.getReplacedArgument() == split.size() + ? Substring.from(arguments, noSlashLength, noSlashLength) + : split.get(suggestion.getReplacedArgument()); + // increase original points by 1, for removed `/` in `parseArgs` + return Substring.wrap( + suggestion.getSuggestion(), + original.getStart() + 1, + original.getEnd() + 1 + ); + }).collect(Collectors.toList())); + } catch (ConditionFailedException e) { + if (e.getCondition() instanceof PermissionCondition) { + event.setSuggestions(new ArrayList<>()); + } + } + } + + /** + * Get the command manager instance. + * + * @return the command manager + */ + public CommandManager getCommandManager() { + return commandManager; + } + +} 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 8d03ee506..6773f0604 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 @@ -19,6 +19,8 @@ package com.sk89q.worldedit.extension.platform; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.brush.visualization.VirtualWorld; @@ -37,7 +39,12 @@ import com.sk89q.worldedit.command.tool.DoubleActionTraceTool; import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.command.tool.TraceTool; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.event.platform.*; +import com.sk89q.worldedit.event.platform.BlockInteractEvent; +import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent; +import com.sk89q.worldedit.event.platform.Interaction; +import com.sk89q.worldedit.event.platform.PlatformInitializeEvent; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; +import com.sk89q.worldedit.event.platform.PlayerInputEvent; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -47,10 +54,6 @@ import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.world.World; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.EnumMap; import java.util.Iterator; @@ -58,8 +61,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; - -import static com.google.common.base.Preconditions.checkNotNull; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Manages registered {@link Platform}s for WorldEdit. Platforms are @@ -72,7 +76,7 @@ public class PlatformManager { private static final Logger logger = LoggerFactory.getLogger(PlatformManager.class); private final WorldEdit worldEdit; - private final CommandManager commandManager; + private final PlatformCommandManager platformCommandManager; private final List platforms = new ArrayList<>(); private final Map preferences = new EnumMap<>(Capability.class); private @Nullable String firstSeenVersion; @@ -87,7 +91,7 @@ public class PlatformManager { public PlatformManager(WorldEdit worldEdit) { checkNotNull(worldEdit); this.worldEdit = worldEdit; - this.commandManager = new CommandManager(worldEdit, this); + this.platformCommandManager = new PlatformCommandManager(worldEdit, this); // Register this instance for events worldEdit.getEventBus().register(this); @@ -110,7 +114,7 @@ public class PlatformManager { // Make sure that versions are in sync if (firstSeenVersion != null) { if (!firstSeenVersion.equals(platform.getVersion())) { - logger.warn("Multiple ports of WorldEdit are installed but they report different versions ({0} and {1}). " + + logger.warn("Multiple ports of WorldEdit are installed but they report different versions ({} and {}). " + "If these two versions are truly different, then you may run into unexpected crashes and errors.", new Object[]{ firstSeenVersion, platform.getVersion() }); } @@ -272,8 +276,8 @@ public class PlatformManager { * * @return the command manager */ - public CommandManager getCommandManager() { - return commandManager; + public PlatformCommandManager getPlatformCommandManager() { + return platformCommandManager; } /** @@ -301,7 +305,6 @@ public class PlatformManager { return tool; } - @SuppressWarnings("deprecation") @Subscribe public void handleBlockInteract(BlockInteractEvent event) { // Create a proxy actor with a potentially different world for @@ -310,112 +313,106 @@ public class PlatformManager { Location location = event.getLocation(); + // At this time, only handle interaction from players + if (!(actor instanceof Player)) { + return; + } + Player player = (Player) actor; + LocalSession session = worldEdit.getSessionManager().get(actor); + + Request.reset(); + try { Vector3 vector = location.toVector(); - // At this time, only handle interaction from players - if (actor instanceof Player) { - Player player = (Player) actor; - LocalSession session = worldEdit.getSessionManager().get(actor); - Request.reset(); + VirtualWorld virtual = session.getVirtualWorld(); + if (virtual != null) { + virtual.handleBlockInteract(player, vector.toBlockPoint(), event); + if (event.isCancelled()) return; + } - VirtualWorld virtual = session.getVirtualWorld(); - if (virtual != null) { - virtual.handleBlockInteract(player, vector.toBlockPoint(), event); - if (event.isCancelled()) return; + if (event.getType() == Interaction.HIT) { + // superpickaxe is special because its primary interaction is a left click, not a right click + // in addition, it is implicitly bound to all pickaxe items, not just a single tool item + if (player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) { + if (!actor.hasPermission("worldedit.selection.pos")) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + RegionSelector selector = session.getRegionSelector(player.getWorld()); + final Player maskedPlayerWrapper = + new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), + ((Player) actor).getLocation()); + BlockVector3 blockPoint = vector.toBlockPoint(); + fp.runAction(() -> { + if (selector.selectPrimary(blockPoint, + ActorSelectorLimits.forActor(maskedPlayerWrapper))) { + selector + .explainPrimarySelection(actor, session, blockPoint); + } + }, false, true); + + event.setCancelled(true); + return; } - if (event.getType() == Interaction.HIT) { - if (session.isToolControlEnabled() && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) { - if (!actor.hasPermission("worldedit.selection.pos")) { - return; - } + if (session.hasSuperPickAxe() && player.isHoldingPickAxe()) { + final BlockTool superPickaxe = session.getSuperPickaxe(); + if (superPickaxe != null && superPickaxe.canUse(player)) { FawePlayer fp = FawePlayer.wrap(player); + final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation()); + fp.runAction(() -> reset(superPickaxe).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location), false, true); + event.setCancelled(true); + return; + } + } + + Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); + if (tool instanceof DoubleActionBlockTool && tool.canUse(player)) { + FawePlayer fp = FawePlayer.wrap(player); + final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation()); + fp.runAction(() -> reset(((DoubleActionBlockTool) tool)).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location), false, true); + event.setCancelled(true); + } + + } else if (event.getType() == Interaction.OPEN) { + if (player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) { + if (!actor.hasPermission("worldedit.selection.pos")) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + if (fp.checkAction()) { RegionSelector selector = session.getRegionSelector(player.getWorld()); - final Player maskedPlayerWrapper = - new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), - ((Player) actor).getLocation()); + Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper( + PlayerWrapper.wrap((Player) actor), + ((Player) actor).getLocation()); BlockVector3 blockPoint = vector.toBlockPoint(); fp.runAction(() -> { - if (selector.selectPrimary(blockPoint, + if (selector.selectSecondary(blockPoint, ActorSelectorLimits.forActor(maskedPlayerWrapper))) { - selector - .explainPrimarySelection(actor, session, blockPoint); + selector.explainSecondarySelection(actor, session, + blockPoint); } }, false, true); - - event.setCancelled(true); - return; } - if (session.hasSuperPickAxe() && player.isHoldingPickAxe()) { - final BlockTool superPickaxe = session.getSuperPickaxe(); - if (superPickaxe != null && superPickaxe.canUse(player)) { - FawePlayer fp = FawePlayer.wrap(player); - final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation()); - fp.runAction(() -> reset(superPickaxe).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location), false, true); - event.setCancelled(true); - return; - } - } - Tool tool = session.getTool(player); - if (tool instanceof DoubleActionBlockTool) { - if (tool.canUse(player)) { - FawePlayer fp = FawePlayer.wrap(player); - final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation()); - fp.runAction(new Runnable() { - @Override - public void run() { - reset(((DoubleActionBlockTool) tool)).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location); - } - }, false, true); - event.setCancelled(true); - return; - } - } + event.setCancelled(true); + return; + } - } else if (event.getType() == Interaction.OPEN) { - if (session.isToolControlEnabled() && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().wandItem)) { - if (!actor.hasPermission("worldedit.selection.pos")) { - return; - } - FawePlayer fp = FawePlayer.wrap(player); - if (fp.checkAction()) { - RegionSelector selector = session.getRegionSelector(player.getWorld()); - Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper( - PlayerWrapper.wrap((Player) actor), - ((Player) actor).getLocation()); - BlockVector3 blockPoint = vector.toBlockPoint(); - fp.runAction(() -> { - if (selector.selectSecondary(blockPoint, - ActorSelectorLimits.forActor(maskedPlayerWrapper))) { - selector.explainSecondarySelection(actor, session, - blockPoint); - } - }, false, true); - } - - event.setCancelled(true); - return; - } - - Tool tool = session.getTool(player); - if (tool instanceof BlockTool) { - if (tool.canUse(player)) { - FawePlayer fp = FawePlayer.wrap(player); - if (fp.checkAction()) { - final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation()); - fp.runAction(() -> { - if (tool instanceof BrushTool) { - ((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location); - } else { - reset((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location); - } - }, false, true); - event.setCancelled(true); - return; + Tool tool = session.getTool(player); + if (tool instanceof BlockTool && tool.canUse(player)) { + FawePlayer fp = FawePlayer.wrap(player); + if (fp.checkAction()) { + final Player maskedPlayerWrapper = new LocationMaskedPlayerWrapper(PlayerWrapper.wrap((Player) actor), ((Player) actor).getLocation()); + fp.runAction(() -> { + if (tool instanceof BrushTool) { + ((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location); + } else { + reset((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), maskedPlayerWrapper, session, location); } - } + }, false, true); + event.setCancelled(true); } } } @@ -452,9 +449,10 @@ public class PlatformManager { } try { + Tool tool = session.getTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); switch (event.getInputType()) { case PRIMARY: { - if ((getConfiguration().navigationWandMaxDistance > 0) && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().navigationWand)) { + if (getConfiguration().navigationWandMaxDistance > 0 && player.getItemInHand(HandSide.MAIN_HAND).getType().getId().equals(getConfiguration().navigationWand)) { if (!player.hasPermission("worldedit.navigation.jumpto.tool")) { return; } @@ -470,14 +468,11 @@ public class PlatformManager { return; } - Tool tool = session.getTool(player); - if (tool instanceof DoubleActionTraceTool) { - if (tool.canUse(player)) { - FawePlayer fp = FawePlayer.wrap(player); - fp.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session)); - event.setCancelled(true); - return; - } + if (tool instanceof DoubleActionTraceTool && tool.canUse(player)) { + FawePlayer fp = FawePlayer.wrap(player); + fp.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session)); + event.setCancelled(true); + return; } break; @@ -497,14 +492,12 @@ public class PlatformManager { return; } - Tool tool = session.getTool(player); - if (tool instanceof TraceTool) { - if (tool.canUse(player)) { - FawePlayer fp = FawePlayer.wrap(player); - fp.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session), false, true); - event.setCancelled(true); - return; - } + if (tool instanceof TraceTool && tool.canUse(player)) { + FawePlayer fp = FawePlayer.wrap(player); + //todo this needs to be fixed so the event is canceled after actPrimary is used and returns true + fp.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session), false, true); + event.setCancelled(true); + return; } break; 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 8d4523d01..ae7b964f0 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 @@ -31,6 +31,7 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -143,6 +144,11 @@ public class PlayerProxy extends AbstractPlayerActor { basePlayer.printError(msg); } + @Override + public void print(Component component) { + basePlayer.print(component); + } + @Override public String[] getGroups() { return permActor.getGroups(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractBufferingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractBufferingExtent.java new file mode 100644 index 000000000..7e1b6466a --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractBufferingExtent.java @@ -0,0 +1,67 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; + +import java.util.Optional; + +/** + * Base extent class for buffering changes between {@link #setBlock(BlockVector3, BlockStateHolder)} + * and the delegate extent. This class ensures that {@link #getBlock(BlockVector3)} is properly + * handled, by returning buffered blocks. + */ +public abstract class AbstractBufferingExtent extends AbstractDelegateExtent { + /** + * Create a new instance. + * + * @param extent the extent + */ + protected AbstractBufferingExtent(Extent extent) { + super(extent); + } + + @Override + public abstract > boolean setBlock(BlockVector3 location, T block) throws WorldEditException; + + protected final > boolean setDelegateBlock(BlockVector3 location, T block) throws WorldEditException { + return super.setBlock(location, block); + } + + @Override + public BlockState getBlock(BlockVector3 position) { + return getBufferedBlock(position) + .map(BaseBlock::toImmutableState) + .orElseGet(() -> super.getBlock(position)); + } + + @Override + public BaseBlock getFullBlock(BlockVector3 position) { + return getBufferedBlock(position) + .orElseGet(() -> super.getFullBlock(position)); + } + + protected abstract Optional getBufferedBlock(BlockVector3 position); + +} 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 fb3bbe620..619a839f4 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 @@ -19,11 +19,11 @@ package com.sk89q.worldedit.extent; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.jnbt.anvil.generator.GenBase; import com.boydti.fawe.jnbt.anvil.generator.Resource; import com.boydti.fawe.object.extent.LightingExtent; - -import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; @@ -40,15 +40,15 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; +import java.util.List; import javax.annotation.Nullable; -import java.util.List; /** * A base class for {@link Extent}s that merely passes extents onto another. */ public class AbstractDelegateExtent implements LightingExtent { + private transient final Extent extent; protected MutableBlockVector3 mutable = new MutableBlockVector3(0, 0, 0); @@ -74,12 +74,8 @@ public class AbstractDelegateExtent implements LightingExtent { return extent.getMaxY(); } + @Override - public BlockType getBlockType(BlockVector3 position) { - return extent.getBlockType(position); - } - - public int getBlockLight(int x, int y, int z) { if (extent instanceof LightingExtent) { return ((LightingExtent) extent).getBlockLight(x, y, z); @@ -87,6 +83,7 @@ public class AbstractDelegateExtent implements LightingExtent { return getBrightness(x, y, z); } + @Override public int getOpacity(int x, int y, int z) { if (extent instanceof LightingExtent) { return ((LightingExtent) extent).getOpacity(x, y, z); @@ -102,6 +99,7 @@ public class AbstractDelegateExtent implements LightingExtent { return 0; } + @Override public int getBrightness(int x, int y, int z) { if (extent instanceof LightingExtent) { return ((LightingExtent) extent).getBrightness(x, y, z); @@ -133,6 +131,7 @@ public class AbstractDelegateExtent implements LightingExtent { return setBlock(mutable.setComponents(x, y, z), block); } + @Override public BlockState getBlock(BlockVector3 position) { return extent.getBlock(position); } @@ -227,6 +226,7 @@ public class AbstractDelegateExtent implements LightingExtent { return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); } + @Override public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) { return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask); } 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 49ac534bb..1c3d2d33a 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 @@ -318,7 +318,7 @@ public interface Extent extends InputExtent, OutputExtent { default boolean contains(BlockVector3 pt) { BlockVector3 min = getMinimumPoint(); BlockVector3 max = getMaximumPoint(); - return (pt.containedWithin(min, max)); + return pt.containedWithin(min, max); } default void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { @@ -350,7 +350,7 @@ public interface Extent extends InputExtent, OutputExtent { int[] counter = new int[BlockTypes.size()]; for (final BlockVector3 pt : region) { - BlockType type = getBlockType(pt); + BlockType type = getBlock(pt).getBlockType(); counter[type.getInternalId()]++; } List> distribution = new ArrayList<>(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java index 601643652..ff564bf1c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java @@ -25,7 +25,6 @@ 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.BlockType; /** * Provides the current state of blocks, entities, and so on. @@ -48,10 +47,6 @@ public interface InputExtent { */ BlockState getBlock(BlockVector3 position); - default BlockType getBlockType(BlockVector3 position) { - return getBlock(position).getBlockType(); - } - /** * Get a lazy, immutable snapshot of the block at the given location that only * immediately contains information about the block's type (and metadata). diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java index 5af7a9405..57c6bf390 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.extent; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector2; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java index 2d59364ea..61e87fd43 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/NullExtent.java @@ -78,6 +78,7 @@ public class NullExtent implements Extent { return BlockTypes.AIR.getDefaultState(); } + @Override public BlockState getLazyBlock(BlockVector3 position) { return BlockTypes.AIR.getDefaultState(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index 96adbf2bf..6ac7a20a7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -37,7 +37,6 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index 24e4e2a85..aeaf07e85 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.extent.clipboard; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java index 61b3b3543..fb1ca8334 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java @@ -33,7 +33,6 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.session.ClipboardHolder; import java.io.File; import java.io.FileInputStream; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormats.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormats.java index 375f4c2dc..581724f2b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormats.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormats.java @@ -19,8 +19,6 @@ package com.sk89q.worldedit.extent.clipboard.io; -import static com.google.common.base.Preconditions.checkNotNull; - import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.clipboard.LazyClipboardHolder; @@ -28,6 +26,8 @@ import com.boydti.fawe.object.clipboard.MultiClipboardHolder; import com.boydti.fawe.object.clipboard.URIClipboardHolder; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MainUtil; + +import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @@ -60,8 +60,6 @@ import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static com.google.common.base.Preconditions.checkNotNull; - public class ClipboardFormats { private static final Map aliasMap = new HashMap<>(); @@ -160,7 +158,7 @@ public class ClipboardFormats { * It is not in SchematicCommands because it may rely on internal register calls. */ public static String[] getFileExtensionArray() { - return fileExtensionMap.keySet().toArray(new String[fileExtensionMap.keySet().size()]); + return fileExtensionMap.keySet().toArray(new String[0]); } private ClipboardFormats() { @@ -190,8 +188,7 @@ public class ClipboardFormats { if (message) BBC.WEB_UNAUTHORIZED.send(player, url); return null; } - MultiClipboardHolder clipboards = loadAllFromUrl(url); - return clipboards; + return loadAllFromUrl(url); } else { if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS && Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}").matcher(input).find() && !player.hasPermission("worldedit.schematic.load.other")) { BBC.NO_PERM.send(player, "worldedit.schematic.load.other"); @@ -218,7 +215,7 @@ public class ClipboardFormats { return null; } if (format == null && input.matches(".*\\.[\\w].*")) { - String extension = input.substring(input.lastIndexOf('.') + 1, input.length()); + String extension = input.substring(input.lastIndexOf('.') + 1); format = findByExtension(extension); } f = MainUtil.resolve(dir, input, format, true); @@ -230,7 +227,7 @@ public class ClipboardFormats { } } if (f == null || !f.exists() || !MainUtil.isInSubDirectory(working, f)) { - if (message) player.printError("Schematic " + input + " does not exist! (" + ((f == null) ? false : f.exists()) + "|" + f + "|" + (f == null ? false : !MainUtil.isInSubDirectory(working, f)) + ")"); + if (message) player.printError("Schematic " + input + " does not exist! (" + ((f != null) && f.exists()) + "|" + f + "|" + (f != null && !MainUtil.isInSubDirectory(working, f)) + ")"); return null; } if (format == null && f.isFile()) { @@ -262,7 +259,7 @@ public class ClipboardFormats { HashSet extensions = new HashSet<>(Arrays.asList(ClipboardFormats.getFileExtensionArray())); File[] files = dir.listFiles(pathname -> { String input = pathname.getName(); - String extension = input.substring(input.lastIndexOf('.') + 1, input.length()); + String extension = input.substring(input.lastIndexOf('.') + 1); return (extensions.contains(extension.toLowerCase())); }); LazyClipboardHolder[] clipboards = new LazyClipboardHolder[files.length]; @@ -287,7 +284,7 @@ public class ClipboardFormats { ClipboardFormat format = findByExtension(filename); if (format != null) { FastByteArrayOutputStream out = new FastByteArrayOutputStream(); - int len = 0; + int len; while ((len = zip.read(buffer)) > 0) { out.write(buffer, 0, len); } @@ -302,7 +299,7 @@ public class ClipboardFormats { } } } - LazyClipboardHolder[] arr = clipboards.toArray(new LazyClipboardHolder[clipboards.size()]); + LazyClipboardHolder[] arr = clipboards.toArray(new LazyClipboardHolder[0]); try { MultiClipboardHolder multi = new MultiClipboardHolder(url.toURI()); for (LazyClipboardHolder h : arr) multi.add(h); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java index 3997f28c7..fca8d982c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.extent.clipboard.io; +import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.collect.ImmutableList; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; @@ -32,12 +34,18 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.EntityNBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.FlowerPotCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NoteBlockCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SkullBlockCompatibilityHandler; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; @@ -47,27 +55,30 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Reads schematic files that are compatible with MCEdit and other editors. */ public class MCEditSchematicReader extends NBTSchematicReader { - private static final List COMPATIBILITY_HANDLERS = new ArrayList<>(); - - static { - COMPATIBILITY_HANDLERS.add(new SignCompatibilityHandler()); - // TODO Add a handler for skulls, flower pots, note blocks, etc. - } - private static final Logger log = LoggerFactory.getLogger(MCEditSchematicReader.class); private final NBTInputStream inputStream; + private final DataFixer fixer; + private static final ImmutableList COMPATIBILITY_HANDLERS + = ImmutableList.of( + new SignCompatibilityHandler(), + new FlowerPotCompatibilityHandler(), + new NoteBlockCompatibilityHandler(), + new SkullBlockCompatibilityHandler() + ); + private static final ImmutableList ENTITY_COMPATIBILITY_HANDLERS + = ImmutableList.of( + new Pre13HangingCompatibilityHandler() + ); /** * Create a new instance. @@ -77,6 +88,9 @@ public class MCEditSchematicReader extends NBTSchematicReader { public MCEditSchematicReader(NBTInputStream inputStream) { checkNotNull(inputStream); this.inputStream = inputStream; + this.fixer = null; + //com.sk89q.worldedit.WorldEdit.getInstance().getPlatformManager().queryCapability( + //com.sk89q.worldedit.extension.platform.Capability.WORLD_EDITING).getDataFixer(); } @Override @@ -162,51 +176,44 @@ public class MCEditSchematicReader extends NBTSchematicReader { // Need to pull out tile entities List tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue(); Map> tileEntitiesMap = new HashMap<>(); + Map blockStates = new HashMap<>(); for (Tag tag : tileEntities) { if (!(tag instanceof CompoundTag)) continue; CompoundTag t = (CompoundTag) tag; - - int x = 0; - int y = 0; - int z = 0; - - Map values = new HashMap<>(); - - for (Map.Entry entry : t.getValue().entrySet()) { - switch (entry.getKey()) { - case "x": - if (entry.getValue() instanceof IntTag) { - x = ((IntTag) entry.getValue()).getValue(); - } - break; - case "y": - if (entry.getValue() instanceof IntTag) { - y = ((IntTag) entry.getValue()).getValue(); - } - break; - case "z": - if (entry.getValue() instanceof IntTag) { - z = ((IntTag) entry.getValue()).getValue(); - } - break; - } - - values.put(entry.getKey(), entry.getValue()); - } - + Map values = new HashMap<>(t.getValue()); + String id = t.getString("id"); + values.put("id", new StringTag(convertBlockEntityId(id))); + int x = t.getInt("x"); + int y = t.getInt("y"); + int z = t.getInt("z"); int index = y * width * length + z * width + x; - BlockState block = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); - if (block != null) { + + BlockState block = getBlockState(blocks[index], blockData[index]); + BlockState newBlock = block; + if (newBlock != null) { for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) { - if (handler.isAffectedBlock(block)) { - handler.updateNBT(block, values); + if (handler.isAffectedBlock(newBlock)) { + newBlock = handler.updateNBT(block, values); + if (newBlock == null || values.isEmpty()) { + break; + } } } } + if (values.isEmpty()) { + t = null; + } + + if (fixer != null && t != null) { + t = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, t, -1); + } BlockVector3 vec = BlockVector3.at(x, y, z); - tileEntitiesMap.put(vec, values); + if (t != null) { + tileEntitiesMap.put(vec, t.getValue()); + } + blockStates.put(vec, newBlock); } BlockArrayClipboard clipboard = new BlockArrayClipboard(region); @@ -220,7 +227,7 @@ public class MCEditSchematicReader extends NBTSchematicReader { for (int z = 0; z < length; ++z) { int index = y * width * length + z * width + x; BlockVector3 pt = BlockVector3.at(x, y, z); - BlockState state = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); + BlockState state = blockStates.computeIfAbsent(pt, p -> getBlockState(blocks[index], blockData[index])); try { if (state != null) { @@ -230,7 +237,8 @@ public class MCEditSchematicReader extends NBTSchematicReader { clipboard.setBlock(region.getMinimumPoint().add(pt), state); } } else { - log.warn("Unknown block when pasting schematic: " + blocks[index] + ":" + blockData[index] + ". Please report this issue."); + log.warn("Unknown block when pasting schematic: " + + blocks[index] + ":" + blockData[index] + ". Please report this issue."); } } catch (WorldEditException e) { switch (failedBlockSets) { @@ -253,21 +261,29 @@ public class MCEditSchematicReader extends NBTSchematicReader { // Entities // ==================================================================== - List entityTags = getTag(schematic, "Entities", ListTag.class).getValue(); - if (entityTags != null) { + ListTag entityList = getTag(schematic, "Entities", ListTag.class); + if (entityList != null) { + List entityTags = entityList.getValue(); for (Tag tag : entityTags) { if (tag instanceof CompoundTag) { CompoundTag compound = (CompoundTag) tag; + if (fixer != null) { + compound = fixer.fixUp(DataFixer.FixTypes.ENTITY, compound, -1); + } String id = convertEntityId(compound.getString("id")); Location location = NBTConversions.toLocation(clipboard, compound.getListTag("Pos"), compound.getListTag("Rotation")); - if (!id.isEmpty()) { - EntityType entityType = EntityTypes.get(id.toLowerCase()); + EntityType entityType = EntityTypes.get(id.toLowerCase(Locale.ROOT)); if (entityType != null) { + for (EntityNBTCompatibilityHandler compatibilityHandler : ENTITY_COMPATIBILITY_HANDLERS) { + if (compatibilityHandler.isAffectedEntity(entityType, compound)) { + compound = compatibilityHandler.updateNBT(entityType, compound); + } + } BaseEntity state = new BaseEntity(entityType, compound); clipboard.createEntity(location, state); } else { - log.warn("Unknown entity when pasting schematic: " + id.toLowerCase()); + log.warn("Unknown entity when pasting schematic: " + id.toLowerCase(Locale.ROOT)); } } } @@ -279,32 +295,100 @@ public class MCEditSchematicReader extends NBTSchematicReader { private String convertEntityId(String id) { switch(id) { + case "AreaEffectCloud": return "area_effect_cloud"; + case "ArmorStand": return "armor_stand"; + case "CaveSpider": return "cave_spider"; + case "MinecartChest": return "chest_minecart"; + case "DragonFireball": return "dragon_fireball"; + case "ThrownEgg": return "egg"; + case "EnderDragon": return "ender_dragon"; + case "ThrownEnderpearl": return "ender_pearl"; + case "FallingSand": return "falling_block"; + case "FireworksRocketEntity": return "fireworks_rocket"; + case "MinecartFurnace": return "furnace_minecart"; + case "MinecartHopper": return "hopper_minecart"; + case "EntityHorse": return "horse"; + case "ItemFrame": return "item_frame"; + case "LeashKnot": return "leash_knot"; + case "LightningBolt": return "lightning_bolt"; + case "LavaSlime": return "magma_cube"; + case "MinecartRideable": return "minecart"; + case "MushroomCow": return "mooshroom"; + case "Ozelot": return "ocelot"; + case "PolarBear": return "polar_bear"; + case "ThrownPotion": return "potion"; + case "ShulkerBullet": return "shulker_bullet"; + case "SmallFireball": return "small_fireball"; + case "MinecartSpawner": return "spawner_minecart"; + case "SpectralArrow": return "spectral_arrow"; + case "PrimedTnt": return "tnt"; + case "MinecartTNT": return "tnt_minecart"; + case "VillagerGolem": return "villager_golem"; + case "WitherBoss": return "wither"; + case "WitherSkull": return "wither_skull"; + case "PigZombie": return "zombie_pigman"; + case "XPOrb": case "xp_orb": return "experience_orb"; + case "ThrownExpBottle": case "xp_bottle": return "experience_bottle"; + case "EyeOfEnderSignal": case "eye_of_ender_signal": return "eye_of_ender"; + case "EnderCrystal": case "ender_crystal": return "end_crystal"; - case "fireworks_rocket": - return "firework_rocket"; + case "fireworks_rocket": return "firework_rocket"; + case "MinecartCommandBlock": case "commandblock_minecart": return "command_block_minecart"; - case "snowman": - return "snow_golem"; - case "villager_golem": - return "iron_golem"; - case "evocation_fangs": - return "evoker_fangs"; - case "evocation_illager": - return "evoker"; - case "vindication_illager": - return "vindicator"; - case "illusion_illager": - return "illusioner"; + case "snowman": return "snow_golem"; + case "villager_golem": return "iron_golem"; + case "evocation_fangs": return "evoker_fangs"; + case "evocation_illager": return "evoker"; + case "vindication_illager": return "vindicator"; + case "illusion_illager": return "illusioner"; + default: return id; } - return id; + } + + private String convertBlockEntityId(String id) { + switch (id) { + case "Cauldron": + return "brewing_stand"; + case "Control": + return "command_block"; + case "DLDetector": + return "daylight_detector"; + case "Trap": + return "dispenser"; + case "EnchantTable": + return "enchanting_table"; + case "EndGateway": + return "end_gateway"; + case "AirPortal": + return "end_portal"; + case "EnderChest": + return "ender_chest"; + case "FlowerPot": + return "flower_pot"; + case "RecordPlayer": + return "jukebox"; + case "MobSpawner": + return "mob_spawner"; + case "Music": + case "noteblock": + return "note_block"; + case "Structure": + return "structure_block"; + default: + return id; + } + } + + private BlockState getBlockState(int id, int data) { + return LegacyMapper.getInstance().getBlockFromLegacy(id, data); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java index 12f1f1b41..085d29531 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java @@ -19,14 +19,12 @@ package com.sk89q.worldedit.extent.clipboard.io; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; +import javax.annotation.Nullable; import java.io.IOException; import java.util.Map; -import javax.annotation.Nullable; - /** * Base class for NBT schematic readers */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index c6649a0c9..4c752cb91 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -31,34 +31,35 @@ import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.object.io.FastByteArraysInputStream; import com.boydti.fawe.util.IOUtil; -import com.google.common.collect.Maps; + +import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.IntArrayTag; import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NamedTag; -import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.world.biome.BiomeTypes; -import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.extension.input.InputParseException; -import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; 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.storage.NBTConversions; import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; import org.slf4j.Logger; @@ -66,30 +67,21 @@ import org.slf4j.LoggerFactory; import java.io.DataInputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkNotNull; /** * Reads schematic files using the Sponge Schematic Specification. */ public class SpongeSchematicReader extends NBTSchematicReader { - private static final List COMPATIBILITY_HANDLERS = new ArrayList<>(); - - static { - // If NBT Compat handlers are needed - add them here. - } - private static final Logger log = LoggerFactory.getLogger(SpongeSchematicReader.class); private final NBTInputStream inputStream; + private DataFixer fixer = null; + private int dataVersion = -1; /** * Create a new instance. @@ -108,7 +100,7 @@ public class SpongeSchematicReader extends NBTSchematicReader { @Override public Clipboard read(UUID uuid) throws IOException { - return readVersion1(uuid); + return reader(uuid); } private int width, height, length; @@ -133,13 +125,56 @@ public class SpongeSchematicReader extends NBTSchematicReader { } } - private Clipboard readVersion1(UUID uuid) throws IOException { + private Clipboard reader(UUID uuid) throws IOException { + NamedTag rootTag = inputStream.readNamedTag(); + if (!rootTag.getName().equals("Schematic")) { + throw new IOException("Tag 'Schematic' does not exist or is not first"); + } + CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); + + // Check + Map schematic = schematicTag.getValue(); + + int version = requireTag(schematic, "Version", IntTag.class).getValue(); + final Platform platform = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.WORLD_EDITING); + int liveDataVersion = platform.getDataVersion(); + + if (version == 1) { + dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1- + fixer = platform.getDataFixer(); + return readVersion1(uuid); + } else if (version == 2) { + dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); + if (dataVersion > liveDataVersion) { + log.warn("Schematic was made in a newer Minecraft version ({} > {}). Data may be incompatible.", + dataVersion, liveDataVersion); + } else if (dataVersion < liveDataVersion) { + fixer = platform.getDataFixer(); + if (fixer != null) { + log.info("Schematic was made in an older Minecraft version ({} < {}), will attempt DFU.", + dataVersion, liveDataVersion); + } else { + log.info("Schematic was made in an older Minecraft version ({} < {}), but DFU is not available. Data may be incompatible.", + dataVersion, liveDataVersion); + } + } + + BlockArrayClipboard clip = readVersion1(uuid); + return readVersion2(clip, schematicTag); + } + + throw new IOException("This schematic version is currently not supported"); + } + + private BlockArrayClipboard readVersion1(UUID uuid) throws IOException { width = height = length = offsetX = offsetY = offsetZ = Integer.MIN_VALUE; final BlockArrayClipboard clipboard = new BlockArrayClipboard(new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(0, 0, 0)), fc); FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream(); FastByteArrayOutputStream biomesOut = new FastByteArrayOutputStream(); + NBTStreamer streamer = new NBTStreamer(inputStream); streamer.addReader("Schematic.Width", (BiConsumer) (i, v) -> width = v); streamer.addReader("Schematic.Height", (BiConsumer) (i, v) -> height = v); @@ -151,7 +186,12 @@ public class SpongeSchematicReader extends NBTSchematicReader { streamer.addReader("Schematic.Palette", (BiConsumer>) (i, v) -> { palette = new char[v.size()]; for (Map.Entry entry : v.entrySet()) { - BlockState state = BlockState.get(entry.getKey()); + BlockState state = null; + try { + state = BlockState.get(entry.getKey()); + } catch (InputParseException e) { + e.printStackTrace(); + } int index = ((IntTag) entry.getValue()).getValue(); palette[index] = (char) state.getOrdinal(); } @@ -176,39 +216,33 @@ public class SpongeSchematicReader extends NBTSchematicReader { } } }); - streamer.addReader("Schematic.TileEntities.#", new BiConsumer() { - @Override - public void accept(Integer index, CompoundTag value) { - if (fc == null) { - setupClipboard(0, uuid); - } - int[] pos = value.getIntArray("Pos"); - int x = pos[0]; - int y = pos[1]; - int z = pos[2]; - fc.setTile(x, y, z, value); + streamer.addReader("Schematic.TileEntities.#", (BiConsumer) (index, value) -> { + if (fc == null) { + setupClipboard(0, uuid); } + int[] pos = value.getIntArray("Pos"); + int x = pos[0]; + int y = pos[1]; + int z = pos[2]; + fc.setTile(x, y, z, value); }); - streamer.addReader("Schematic.Entities.#", new BiConsumer() { - @Override - public void accept(Integer index, CompoundTag compound) { - if (fc == null) { - setupClipboard(0, uuid); - } - String id = compound.getString("id"); - if (id.isEmpty()) { - return; - } - ListTag positionTag = compound.getListTag("Pos"); - ListTag directionTag = compound.getListTag("Rotation"); - EntityType type = EntityTypes.parse(id); - if (type != null) { - compound.getValue().put("Id", new StringTag(type.getId())); - BaseEntity state = new BaseEntity(type, compound); - fc.createEntity(clipboard, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), (float) directionTag.asDouble(0), (float) directionTag.asDouble(1), state); - } else { - Fawe.debug("Invalid entity: " + id); - } + streamer.addReader("Schematic.Entities.#", (BiConsumer) (index, compound) -> { + if (fc == null) { + setupClipboard(0, uuid); + } + String id = compound.getString("id"); + if (id.isEmpty()) { + return; + } + ListTag positionTag = compound.getListTag("Pos"); + ListTag directionTag = compound.getListTag("Rotation"); + EntityType type = EntityTypes.parse(id); + if (type != null) { + compound.getValue().put("Id", new StringTag(type.getId())); + BaseEntity state = new BaseEntity(type, compound); + fc.createEntity(clipboard, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), (float) directionTag.asDouble(0), (float) directionTag.asDouble(1), state); + } else { + Fawe.debug("Invalid entity: " + id); } }); streamer.readFully(); @@ -240,7 +274,7 @@ public class SpongeSchematicReader extends NBTSchematicReader { try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays())))) { int volume = width * length; for (int index = 0; index < volume; index++) { - fc.setBiome(index, BiomeTypes.get(fis.read())); + fc.setBiome(index, BiomeTypes.register(fis.read())); } } } @@ -249,6 +283,106 @@ public class SpongeSchematicReader extends NBTSchematicReader { return clipboard; } + private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag) throws IOException { + Map schematic = schematicTag.getValue(); + if (schematic.containsKey("BiomeData")) { + readBiomes(version1, schematic); + } + if (schematic.containsKey("Entities")) { + readEntities(version1, schematic); + } + return version1; + } + + private void readBiomes(BlockArrayClipboard clipboard, Map schematic) throws IOException { + ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class); + IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class); + CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class); + + Map palette = new HashMap<>(); + if (maxTag.getValue() != paletteTag.getValue().size()) { + throw new IOException("Biome palette size does not match expected size."); + } + + for (Map.Entry palettePart : paletteTag.getValue().entrySet()) { + String key = palettePart.getKey(); + if (fixer != null) { + key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion); + } + BiomeType biome = BiomeTypes.register(key); + if (biome == null) { + log.warn("Unknown biome type :" + key + + " in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); + } + Tag idTag = palettePart.getValue(); + if (!(idTag instanceof IntTag)) { + throw new IOException("Biome mapped to non-Int tag."); + } + palette.put(((IntTag) idTag).getValue(), biome); + } + + int width = clipboard.getDimensions().getX(); + + byte[] biomes = dataTag.getValue(); + int biomeIndex = 0; + int biomeJ = 0; + int bVal; + int varIntLength; + BlockVector2 min = clipboard.getMinimumPoint().toBlockVector2(); + while (biomeJ < biomes.length) { + bVal = 0; + varIntLength = 0; + + while (true) { + bVal |= (biomes[biomeJ] & 127) << (varIntLength++ * 7); + if (varIntLength > 5) { + throw new IOException("VarInt too big (probably corrupted data)"); + } + if (((biomes[biomeJ] & 128) != 128)) { + biomeJ++; + break; + } + biomeJ++; + } + int z = biomeIndex / width; + int x = biomeIndex % width; + BiomeType type = palette.get(bVal); + clipboard.setBiome(min.add(x, z), type); + biomeIndex++; + } + } + + private void readEntities(BlockArrayClipboard clipboard, Map schematic) throws IOException { + List entList = requireTag(schematic, "Entities", ListTag.class).getValue(); + if (entList.isEmpty()) { + return; + } + for (Tag et : entList) { + if (!(et instanceof CompoundTag)) { + continue; + } + CompoundTag entityTag = (CompoundTag) et; + Map tags = entityTag.getValue(); + String id = requireTag(tags, "Id", StringTag.class).getValue(); + entityTag = entityTag.createBuilder().putString("id", id).remove("Id").build(); + + if (fixer != null) { + entityTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, entityTag, dataVersion); + } + + EntityType entityType = EntityTypes.get(id); + if (entityType != null) { + Location location = NBTConversions.toLocation(clipboard, + requireTag(tags, "Pos", ListTag.class), + requireTag(tags, "Rotation", ListTag.class)); + BaseEntity state = new BaseEntity(entityType, entityTag); + clipboard.createEntity(location, state); + } else { + log.warn("Unknown entity when pasting schematic: " + id); + } + } + } + @Override public void close() throws IOException { inputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java index 10f953cd5..a1a67b615 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java @@ -23,23 +23,18 @@ import com.boydti.fawe.object.clipboard.FaweClipboard; import com.boydti.fawe.util.IOUtil; import static com.google.common.base.Preconditions.checkNotNull; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.FloatTag; -import com.sk89q.jnbt.IntArrayTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTConstants; -import com.sk89q.jnbt.NBTOutputStream; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; +import com.google.common.collect.Maps; +import com.sk89q.jnbt.*; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -57,12 +52,16 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; /** * Writes schematic files using the Sponge schematic format. */ public class SpongeSchematicWriter implements ClipboardWriter { + private static final int CURRENT_VERSION = 2; + private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; private final NBTOutputStream outputStream; @@ -78,16 +77,16 @@ public class SpongeSchematicWriter implements ClipboardWriter { @Override public void write(Clipboard clipboard) throws IOException { - write1(clipboard); + // For now always write the latest version. Maybe provide support for earlier if more appear. + write2(clipboard); } /** - * Writes a version 1 schematic file. + * Writes a version 2 schematic file. * * @param clipboard The clipboard - * @throws IOException If an error occurs */ - private void write1(Clipboard clipboard) throws IOException { + private void write2(Clipboard clipboard) throws IOException { Region region = clipboard.getRegion(); BlockVector3 origin = clipboard.getOrigin(); BlockVector3 min = region.getMinimumPoint(); @@ -109,7 +108,7 @@ public class SpongeSchematicWriter implements ClipboardWriter { final DataOutput rawStream = outputStream.getOutputStream(); outputStream.writeLazyCompoundTag("Schematic", out -> { out.writeNamedTag("Version", 1); - out.writeNamedTag("Width", (short) width); + out.writeNamedTag("Width", (short) width); out.writeNamedTag("Height", (short) height); out.writeNamedTag("Length", (short) length); @@ -240,8 +239,8 @@ public class SpongeSchematicWriter implements ClipboardWriter { // Store our location data, overwriting any values.put("id", new StringTag(state.getType().getId())); - values.put("Pos", writeVector(entity.getLocation(), "Pos")); - values.put("Rotation", writeRotation(entity.getLocation(), "Rotation")); + values.put("Pos", writeVector(entity.getLocation())); + values.put("Rotation", writeRotation(entity.getLocation())); CompoundTag entityTag = new CompoundTag(values); entities.add(entityTag); @@ -252,10 +251,88 @@ public class SpongeSchematicWriter implements ClipboardWriter { } else { out.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities)); } + + // version 2 stuff + if (clipboard.hasBiomes()) { + writeBiomes(clipboard, out); + } + + if (!clipboard.getEntities().isEmpty()) { + writeEntities(clipboard, out); + } + }); } - private static Tag writeVector(Vector3 vector, String name) { + private void writeBiomes(Clipboard clipboard, NBTOutputStream schematic) throws IOException { + BlockVector3 min = clipboard.getMinimumPoint(); + int width = clipboard.getRegion().getWidth(); + int length = clipboard.getRegion().getLength(); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * length); + + int paletteMax = 0; + Map palette = new HashMap<>(); + + for (int z = 0; z < length; z++) { + int z0 = min.getBlockZ() + z; + for (int x = 0; x < width; x++) { + int x0 = min.getBlockX() + x; + BlockVector2 pt = BlockVector2.at(x0, z0); + BiomeType biome = clipboard.getBiome(pt); + + String biomeKey = biome.getId(); + int biomeId; + if (palette.containsKey(biomeKey)) { + biomeId = palette.get(biomeKey); + } else { + biomeId = paletteMax; + palette.put(biomeKey, biomeId); + paletteMax++; + } + + while ((biomeId & -128) != 0) { + buffer.write(biomeId & 127 | 128); + biomeId >>>= 7; + } + buffer.write(biomeId); + } + } + + schematic.writeNamedTag("BiomePaletteMax", new IntTag(paletteMax)); + + Map paletteTag = new HashMap<>(); + palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value))); + + schematic.writeNamedTag("BiomePalette", new CompoundTag(paletteTag)); + schematic.writeNamedTag("BiomeData", new ByteArrayTag(buffer.toByteArray())); + } + + private void writeEntities(Clipboard clipboard, NBTOutputStream schematic) throws IOException { + List entities = clipboard.getEntities().stream().map(e -> { + BaseEntity state = e.getState(); + if (state == null) { + return null; + } + Map values = Maps.newHashMap(); + CompoundTag rawData = state.getNbtData(); + if (rawData != null) { + values.putAll(rawData.getValue()); + } + values.remove("id"); + values.put("Id", new StringTag(state.getType().getId())); + values.put("Pos", writeVector(e.getLocation().toVector())); + values.put("Rotation", writeRotation(e.getLocation())); + + return new CompoundTag(values); + }).filter(Objects::nonNull).collect(Collectors.toList()); + if (entities.isEmpty()) { + return; + } + schematic.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities)); + } + + private static Tag writeVector(Vector3 vector) { List list = new ArrayList<>(); list.add(new DoubleTag(vector.getX())); list.add(new DoubleTag(vector.getY())); @@ -263,7 +340,7 @@ public class SpongeSchematicWriter implements ClipboardWriter { return new ListTag(DoubleTag.class, list); } - private static Tag writeRotation(Location location, String name) { + private static Tag writeRotation(Location location) { List list = new ArrayList<>(); list.add(new FloatTag(location.getYaw())); list.add(new FloatTag(location.getPitch())); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/EntityNBTCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/EntityNBTCompatibilityHandler.java new file mode 100644 index 000000000..389a596cb --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/EntityNBTCompatibilityHandler.java @@ -0,0 +1,28 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.world.entity.EntityType; + +public interface EntityNBTCompatibilityHandler { + boolean isAffectedEntity(EntityType type, CompoundTag entityTag); + CompoundTag updateNBT(EntityType type, CompoundTag entityTag); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java new file mode 100644 index 000000000..92e998f75 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java @@ -0,0 +1,97 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.registry.LegacyMapper; + +import java.util.Map; + +public class FlowerPotCompatibilityHandler implements NBTCompatibilityHandler { + @Override + public > boolean isAffectedBlock(B block) { + return block.getBlockType() == BlockTypes.FLOWER_POT; + } + + @Override + public > B updateNBT(B block, Map values) { + Tag item = values.get("Item"); + if (item instanceof StringTag) { + String id = ((StringTag) item).getValue(); + if (id.isEmpty()) { + return (B) BlockTypes.FLOWER_POT.getDefaultState(); + } + int data = 0; + Tag dataTag = values.get("Data"); + if (dataTag instanceof IntTag) { + data = ((IntTag) dataTag).getValue(); + } + BlockState newState = convertLegacyBlockType(id, data); + if (newState != null) { + values.clear(); + return (B) newState; // generics pls :\ + } + } + return block; + } + + private BlockState convertLegacyBlockType(String id, int data) { + int newId = 0; + switch (id) { + case "minecraft:red_flower": + newId = 38; // now poppy + break; + case "minecraft:yellow_flower": + newId = 37; // now dandelion + break; + case "minecraft:sapling": + newId = 6; // oak_sapling + break; + case "minecraft:deadbush": + case "minecraft:tallgrass": + newId = 31; // dead_bush with fern and grass (not 32!) + break; + default: + break; + } + String plantedName = null; + if (newId == 0 && id.startsWith("minecraft:")) { + plantedName = id.substring(10); + } else { + BlockState plantedWithData = LegacyMapper.getInstance().getBlockFromLegacy(newId, data); + if (plantedWithData != null) { + plantedName = plantedWithData.getBlockType().getId().substring(10); // remove "minecraft:" + } + } + if (plantedName != null) { + BlockType potAndPlanted = BlockTypes.get("minecraft:potted_" + plantedName); + if (potAndPlanted != null) { + return potAndPlanted.getDefaultState(); + } + } + return null; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java index 88c344566..0f631f561 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java @@ -26,5 +26,5 @@ import java.util.Map; public interface NBTCompatibilityHandler { > boolean isAffectedBlock(B block); - > void updateNBT(B block, Map values); + > B updateNBT(B block, Map values); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java new file mode 100644 index 000000000..a839efe29 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java @@ -0,0 +1,63 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.registry.state.IntegerProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.Map; + +public class NoteBlockCompatibilityHandler implements NBTCompatibilityHandler { + private static final IntegerProperty NoteProperty; + + static { + IntegerProperty temp; + try { + temp = (IntegerProperty) (Property) BlockTypes.NOTE_BLOCK.getProperty("note"); + } catch (NullPointerException | IllegalArgumentException | ClassCastException e) { + temp = null; + } + NoteProperty = temp; + } + + @Override + public > boolean isAffectedBlock(B block) { + return NoteProperty != null && block.getBlockType() == BlockTypes.NOTE_BLOCK; + } + + @Override + public > B updateNBT(B block, Map values) { + // note that instrument was not stored (in state or nbt) previously. + // it will be updated to the block below when it gets set into the world for the first time + Tag noteTag = values.get("note"); + if (noteTag instanceof ByteTag) { + Byte note = ((ByteTag) noteTag).getValue(); + if (note != null) { + values.clear(); + return (B) block.with(NoteProperty, (int) note).toImmutableState(); + } + } + return block; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java new file mode 100644 index 000000000..c42fab428 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java @@ -0,0 +1,61 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.CompoundTagBuilder; +import com.sk89q.worldedit.internal.helper.MCDirections; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.entity.EntityType; + +public class Pre13HangingCompatibilityHandler implements EntityNBTCompatibilityHandler { + + @Override + public boolean isAffectedEntity(EntityType type, CompoundTag tag) { + if (!type.getId().startsWith("minecraft:")) { + return false; + } + boolean hasLegacyDirection = tag.containsKey("Dir") || tag.containsKey("Direction"); + boolean hasFacing = tag.containsKey("Facing"); + return hasLegacyDirection || hasFacing; + } + + @Override + public CompoundTag updateNBT(EntityType type, CompoundTag tag) { + boolean hasLegacyDir = tag.containsKey("Dir"); + boolean hasLegacyDirection = tag.containsKey("Direction"); + boolean hasPre113Facing = tag.containsKey("Facing"); + Direction newDirection; + if (hasLegacyDir) { + newDirection = MCDirections.fromPre13Hanging(MCDirections.fromLegacyHanging((byte) tag.asInt("Dir"))); + } else if (hasLegacyDirection) { + newDirection = MCDirections.fromPre13Hanging(tag.asInt("Direction")); + } else if (hasPre113Facing) { + newDirection = MCDirections.fromPre13Hanging(tag.asInt("Facing")); + } else { + return tag; + } + byte hangingByte = (byte) MCDirections.toHanging(newDirection); + CompoundTagBuilder builder = tag.createBuilder(); + builder.putByte("Facing", hangingByte); + return builder.build(); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java index 46a91b878..ff1ebd53f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java @@ -26,7 +26,6 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; @@ -40,7 +39,7 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler { } @Override - public > void updateNBT(B block, Map values) { + public > B updateNBT(B block, Map values) { for (int i = 0; i < 4; ++i) { String key = "Text" + (i + 1); Tag value = values.get(key); @@ -70,5 +69,6 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler { values.put("Text" + (i + 1), new StringTag(jsonTextObject.toString())); } } + return block; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java new file mode 100644 index 000000000..f4fe1a3ce --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java @@ -0,0 +1,100 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.registry.state.DirectionalProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.Map; + +public class SkullBlockCompatibilityHandler implements NBTCompatibilityHandler { + + private static final DirectionalProperty FacingProperty; + + static { + DirectionalProperty tempFacing; + try { + tempFacing = (DirectionalProperty) (Property) BlockTypes.SKELETON_WALL_SKULL.getProperty("facing"); + } catch (NullPointerException | IllegalArgumentException | ClassCastException e) { + tempFacing = null; + } + FacingProperty = tempFacing; + } + + @Override + public > boolean isAffectedBlock(B block) { + return block.getBlockType() == BlockTypes.SKELETON_SKULL + || block.getBlockType() == BlockTypes.SKELETON_WALL_SKULL; + } + + @Override + public > B updateNBT(B block, Map values) { + boolean isWall = block.getBlockType() == BlockTypes.SKELETON_WALL_SKULL; + Tag typeTag = values.get("SkullType"); + if (typeTag instanceof ByteTag) { + String skullType = convertSkullType(((ByteTag) typeTag).getValue(), isWall); + if (skullType != null) { + BlockType type = BlockTypes.get("minecraft:" + skullType); + if (type != null) { + BlockState state = type.getDefaultState(); + if (isWall) { + Property newProp = type.getProperty("facing"); + state = state.with(newProp, block.getState(FacingProperty)); + } else { + Tag rotTag = values.get("Rot"); + if (rotTag instanceof ByteTag) { + Property newProp = type.getProperty("rotation"); + state = state.with(newProp, (int) ((ByteTag) rotTag).getValue()); + } + } + values.remove("SkullType"); + values.remove("Rot"); + return (B) state; + } + } + } + return block; + } + + private String convertSkullType(Byte oldType, boolean isWall) { + switch (oldType) { + case 0: + return isWall ? "skeleton_wall_skull" : "skeleton_skull"; + case 1: + return isWall ? "wither_skeleton_wall_skull" : "wither_skeleton_skull"; + case 2: + return isWall ? "zombie_wall_head" : "zombie_head"; + case 3: + return isWall ? "player_wall_head" : "player_head"; + case 4: + return isWall ? "creeper_wall_head" : "creeper_head"; + case 5: + return isWall ? "dragon_wall_head" : "dragon_head"; + default: + return null; + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java index 3e1a7d4fa..55ddbcb73 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java @@ -61,6 +61,8 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder BlockCategories.WOODEN_PRESSURE_PLATES.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.LAST)); BlockCategories.CARPETS.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.LAST)); BlockCategories.RAILS.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.LAST)); + BlockCategories.BEDS.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.LAST)); + BlockCategories.SMALL_FLOWERS.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.LAST)); priorityMap.put(BlockTypes.BLACK_BED, PlacementPriority.LAST); priorityMap.put(BlockTypes.BLUE_BED, PlacementPriority.LAST); priorityMap.put(BlockTypes.BROWN_BED, PlacementPriority.LAST); @@ -128,6 +130,7 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder // Final BlockCategories.DOORS.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.FINAL)); BlockCategories.BANNERS.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.FINAL)); + BlockCategories.SIGNS.getAll().forEach(type -> priorityMap.put(type, PlacementPriority.FINAL)); priorityMap.put(BlockTypes.SIGN, PlacementPriority.FINAL); priorityMap.put(BlockTypes.WALL_SIGN, PlacementPriority.FINAL); priorityMap.put(BlockTypes.CACTUS, PlacementPriority.FINAL); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java index 3d1fc566b..00e9c8d91 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java @@ -29,7 +29,6 @@ import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.helper.MCDirections; -import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.transform.AffineTransform; @@ -40,12 +39,12 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.PropertyKey; import com.sk89q.worldedit.util.Direction; import static com.sk89q.worldedit.util.Direction.*; -import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.util.ArrayList; @@ -59,7 +58,9 @@ import java.util.Map; * given transform. */ public class BlockTransformExtent extends ResettableExtent { + private Transform transform; + private Transform transformInverse; private int[] BLOCK_ROTATION_BITMASK; private int[][] BLOCK_TRANSFORM; @@ -93,19 +94,11 @@ public class BlockTransformExtent extends ResettableExtent { } private static long[] adapt(Direction... dirs) { - long[] arr = new long[dirs.length]; - for (int i = 0; i < arr.length; i++) { - arr[i] = 1L << dirs[i].ordinal(); - } - return arr; + return Arrays.stream(dirs).mapToLong(dir -> 1L << dir.ordinal()).toArray(); } private static long[] adapt(Long... dirs) { - long[] arr = new long[dirs.length]; - for (int i = 0; i < arr.length; i++) { - arr[i] = dirs[i]; - } - return arr; + return Arrays.stream(dirs).mapToLong(dir -> dir).toArray(); } private static long[] getDirections(AbstractProperty property) { @@ -388,11 +381,6 @@ public class BlockTransformExtent extends ResettableExtent { } } - @Override - public ResettableExtent setExtent(Extent extent) { - return super.setExtent(extent); - } - /** * Get the transform. * @@ -402,19 +390,30 @@ public class BlockTransformExtent extends ResettableExtent { return transform; } + /** + * Transform a block without making a copy. + * + * @param block the block + * @param reverse true to transform in the opposite direction + * @return the same block + */ + private > T transformBlock(T block, boolean reverse) { + return transform(block, reverse ? transform.inverse() : transform); + } + @Override public BlockState getBlock(BlockVector3 position) { - return transform(super.getBlock(position)); + return transformBlock(super.getBlock(position), false); } @Override public BaseBlock getFullBlock(BlockVector3 position) { - return transform(super.getFullBlock(position)); + return transformBlock(super.getFullBlock(position), false); } @Override public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { - return super.setBlock(location, transformInverse(block)); + return super.setBlock(location, transformBlock(block, true)); } public void setTransform(Transform affine) { @@ -432,7 +431,7 @@ public class BlockTransformExtent extends ResettableExtent { * @param transform the transform * @return the same block */ - public static > B transform(B block, Transform transform) { + public static > B transform(@NotNull B block, @NotNull Transform transform) { // performance critical BlockState state = block.toImmutableState(); @@ -503,16 +502,41 @@ public class BlockTransformExtent extends ResettableExtent { return transform(super.getLazyBlock(position)); } - @Override - public BiomeType getBiome(BlockVector2 position) { - return super.getBiome(position); - } - @Override public boolean setBlock(int x, int y, int z, BlockStateHolder block) throws WorldEditException { return super.setBlock(x, y, z, transformInverse(block)); } + /** + * Get the new value with the transformed direction. + * + * @param allowedStates the allowed states + * @param transform the transform + * @param oldDirection the old direction to transform + * @return a new state or null if none could be found + */ + @Nullable + private static Vector3 getNewStateValue(List allowedStates, Transform transform, Vector3 oldDirection) { + Vector3 newDirection = transform.apply(oldDirection).subtract(transform.apply(Vector3.ZERO)).normalize(); + Vector3 newValue = null; + double closest = -2; + boolean found = false; + + for (Direction v : allowedStates) { + double dot = v.toVector().normalize().dot(newDirection); + if (dot >= closest) { + closest = dot; + newValue = v.toVector(); + found = true; + } + } + + if (found) { + return newValue; + } else { + return null; + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java index d935716c2..941d49d13 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java @@ -20,7 +20,6 @@ package com.sk89q.worldedit.extent.world; import static com.google.common.base.Preconditions.checkNotNull; - import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; @@ -28,8 +27,6 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockStateHolder; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Makes changes to the world as if a player had done so during survival mode. * @@ -42,6 +39,7 @@ public class SurvivalModeExtent extends AbstractDelegateExtent { private final World world; private boolean toolUse = false; + private boolean stripNbt = false; /** * Create a new instance. @@ -80,13 +78,26 @@ public class SurvivalModeExtent extends AbstractDelegateExtent { this.toolUse = toolUse; } + public boolean hasStripNbt() { + return stripNbt; + } + + public void setStripNbt(boolean stripNbt) { + this.stripNbt = stripNbt; + } + @Override public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { if (toolUse && block.getBlockType().getMaterial().isAir()) { world.simulateBlockMine(location); return true; } else { - return super.setBlock(location, block); + // Can't be an inlined check due to inconsistent generic return type + if (stripNbt) { + return super.setBlock(location, block.toBaseBlock(null)); + } else { + return super.setBlock(location, block); + } } } @@ -96,7 +107,12 @@ public class SurvivalModeExtent extends AbstractDelegateExtent { world.simulateBlockMine(BlockVector3.at(x, y, z)); return true; } else { - return super.setBlock(x, y, z, block); + // Can't be an inlined check due to inconsistent generic return type + if (stripNbt) { + return super.setBlock(x, y, z, block.toBaseBlock(null)); + } else { + return super.setBlock(x, y, z, block); + } } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/CombinedRegionFunction.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/CombinedRegionFunction.java index d04e63fa4..3e5ba674d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/CombinedRegionFunction.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/CombinedRegionFunction.java @@ -34,7 +34,7 @@ import java.util.List; */ public class CombinedRegionFunction implements RegionFunction { - private RegionFunction[] functions; + private final List functions = new ArrayList<>(); /** * Create a combined region function. @@ -49,7 +49,7 @@ public class CombinedRegionFunction implements RegionFunction { */ public CombinedRegionFunction(Collection functions) { checkNotNull(functions); - this.functions = functions.toArray(new RegionFunction[functions.size()]); + this.functions.addAll(functions); } /** @@ -58,7 +58,7 @@ public class CombinedRegionFunction implements RegionFunction { * @param function an array of functions to match */ public CombinedRegionFunction(RegionFunction... function) { - this.functions = function; + this(Arrays.asList(checkNotNull(function))); } public static CombinedRegionFunction combine(RegionFunction function, RegionFunction add) { @@ -82,9 +82,7 @@ public class CombinedRegionFunction implements RegionFunction { */ public void add(Collection functions) { checkNotNull(functions); - ArrayList functionsList = new ArrayList<>(Arrays.asList(this.functions)); - functionsList.addAll(functions); - this.functions = functionsList.toArray(new RegionFunction[functionsList.size()]); + this.functions.addAll(functions); } /** @@ -100,7 +98,9 @@ public class CombinedRegionFunction implements RegionFunction { public boolean apply(BlockVector3 position) throws WorldEditException { boolean ret = false; for (RegionFunction function : functions) { - ret |= (function.apply(position)); + if (function.apply(position)) { + ret = true; + } } return ret; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/GroundFunction.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/GroundFunction.java index 9334dc843..5e4c7c545 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/GroundFunction.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/GroundFunction.java @@ -88,7 +88,6 @@ public class GroundFunction implements LayerFunction { return true; } } - return false; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/ItemUseFunction.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/ItemUseFunction.java new file mode 100644 index 000000000..905ccd3e6 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/ItemUseFunction.java @@ -0,0 +1,47 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.World; + +public final class ItemUseFunction implements RegionFunction { + private final World world; + private final BaseItem item; + private final Direction dir; + + public ItemUseFunction(World world, BaseItem item) { + this(world, item, Direction.UP); + } + + public ItemUseFunction(World world, BaseItem item, Direction dir) { + this.world = world; + this.item = item; + this.dir = dir; + } + + @Override + public boolean apply(BlockVector3 position) throws WorldEditException { + return world.useItem(position, item, dir); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java new file mode 100644 index 000000000..94f3256f3 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java @@ -0,0 +1,72 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.biome; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.FlatRegionFunction; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.biome.BiomeType; + +/** + * Copies the biome from one extent to another. + */ +public class ExtentBiomeCopy implements FlatRegionFunction { + + private final Extent source; + private final Extent destination; + private final BlockVector2 from; + private final BlockVector2 to; + private final Transform transform; + + /** + * Make a new biome copy. + * + * @param source the source extent + * @param from the source offset + * @param destination the destination extent + * @param to the destination offset + * @param transform a transform to apply to positions (after source offset, before destination offset) + */ + public ExtentBiomeCopy(Extent source, BlockVector2 from, Extent destination, BlockVector2 to, Transform transform) { + checkNotNull(source); + checkNotNull(from); + checkNotNull(destination); + checkNotNull(to); + checkNotNull(transform); + this.source = source; + this.from = from; + this.destination = destination; + this.to = to; + this.transform = transform; + } + + @Override + public boolean apply(BlockVector2 position) throws WorldEditException { + BiomeType biome = source.getBiome(position); + BlockVector2 orig = position.subtract(from); + BlockVector2 transformed = transform.apply(orig.toVector3(0)).toVector2().toBlockPoint(); + + return destination.setBiome(transformed.add(to), biome); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java index 168b9692b..1e5c62873 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java @@ -69,11 +69,12 @@ public class ExtentBlockCopy implements RegionFunction { @Override public boolean apply(BlockVector3 position) throws WorldEditException { + BaseBlock block = source.getFullBlock(position); BlockVector3 orig = position.subtract(from); BlockVector3 transformed = transform.apply(orig.toVector3()).toBlockPoint(); // Apply transformations to NBT data if necessary - BaseBlock block = transformNbtData(source.getFullBlock(position)); + block = transformNbtData(block); return destination.setBlock(transformed.add(to), block); } 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 a96275d67..2242e83a7 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 @@ -20,6 +20,7 @@ package com.sk89q.worldedit.function.block; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.LayerFunction; @@ -79,8 +80,16 @@ 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); + + if (block.equalsFuzzy(targetBlock)) { + return false; + } + + return editSession.setBlock(position, targetBlock); } + @Override public boolean apply(BlockVector3 position, int depth) throws WorldEditException { if (mask.test(position)) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java index bac2a3fc6..cea01978c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java @@ -101,9 +101,18 @@ public class ExtentEntityCopy implements EntityFunction { if (state != null && state.getType() != EntityTypes.PLAYER) { Location newLocation; Location location = entity.getLocation(); + // If the entity has stored the location in the NBT data, we use that location + CompoundTag tag = state.getNbtData(); + boolean hasTilePosition = tag != null && tag.containsKey("TileX") && tag.containsKey("TileY") && tag.containsKey("TileZ"); + if (hasTilePosition) { + location = location.setPosition(Vector3.at(tag.asInt("TileX"), tag.asInt("TileY"), tag.asInt("TileZ")).add(0.5, 0.5, 0.5)); + } Vector3 pivot = from.round().add(0.5, 0.5, 0.5); Vector3 newPosition = transform.apply(location.toVector().subtract(pivot)); + if (hasTilePosition) { + newPosition = newPosition.subtract(0.5, 0.5, 0.5); + } Vector3 newDirection; newDirection = transform.isIdentity() ? @@ -206,5 +215,4 @@ public class ExtentEntityCopy implements EntityFunction { return state; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/GardenPatchGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/GardenPatchGenerator.java index 14bbf7e2b..5d1aa01c5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/GardenPatchGenerator.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/GardenPatchGenerator.java @@ -23,7 +23,6 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.RegionFunction; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; @@ -187,7 +186,7 @@ public class GardenPatchGenerator implements RegionFunction { * @return a pumpkin pattern */ public static Pattern getPumpkinPattern() { - return (BlockTypes.PUMPKIN.getDefaultState()); + return BlockTypes.PUMPKIN.getDefaultState(); } /** @@ -208,6 +207,6 @@ public class GardenPatchGenerator implements RegionFunction { * @return a melon pattern */ public static Pattern getMelonPattern() { - return (BlockTypes.MELON.getDefaultState()); + return BlockTypes.MELON.getDefaultState(); } } 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 e17ea0b14..544170221 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 @@ -19,11 +19,10 @@ package com.sk89q.worldedit.function.mask; -import com.boydti.fawe.object.collection.FastBitSet; -import com.boydti.fawe.util.MainUtil; -import com.boydti.fawe.util.StringMan; - import static com.google.common.base.Preconditions.checkNotNull; + +import com.boydti.fawe.object.collection.FastBitSet; +import com.boydti.fawe.util.StringMan; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.NullExtent; import com.sk89q.worldedit.math.BlockVector3; @@ -34,13 +33,12 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Nullable; /** * A mask that checks whether blocks at the given positions are matched by @@ -65,7 +63,6 @@ public class BlockMask extends AbstractExtentMask { */ public BlockMask(Extent extent, Collection blocks) { super(extent); - MainUtil.warnDeprecated(BlockMaskBuilder.class); checkNotNull(blocks); this.bitSets = new BlockMaskBuilder().addBlocks(blocks).optimize().getBits(); } @@ -78,7 +75,6 @@ public class BlockMask extends AbstractExtentMask { */ public BlockMask(Extent extent, BaseBlock... block) { super(extent); - MainUtil.warnDeprecated(BlockMaskBuilder.class); checkNotNull(block); this.bitSets = new BlockMaskBuilder().addBlocks(block).optimize().getBits(); } 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 db4ea88e9..2836ac366 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 @@ -4,21 +4,19 @@ import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.object.collection.FastBitSet; import com.boydti.fawe.object.string.MutableCharSequence; import com.boydti.fawe.util.StringMan; + import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.registry.state.AbstractProperty; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.PropertyKey; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import java.util.*; import java.util.function.BiPredicate; -import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; public class BlockMaskBuilder { @@ -71,12 +69,15 @@ public class BlockMaskBuilder { result = bitSets[type.getInternalId()] != null; remove(type); } - } else if (value.length() == 0) { - - } else if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) { - result = filterRegex(type, key, value.toString()); } else { - result = filterOperator(type, key, operator, value); + if (value.length() == 0) { + return result; + } + if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) { + result = filterRegex(type, key, value.toString()); + } else { + result = filterOperator(type, key, operator, value); + } } return result; } @@ -229,7 +230,7 @@ public class BlockMaskBuilder { throw new SuggestInputParseException(input + " does not have: " + property, input, () -> { Set keys = new HashSet<>(); finalTypes.forEach(t -> t.getProperties().stream().forEach(p -> keys.add(p.getKey()))); - return keys.stream().map(p -> p.getId()) + return keys.stream().map(PropertyKey::getId) .filter(p -> StringMan.blockStateMatches(property, p)) .sorted(StringMan.blockStateComparator(property)) .collect(Collectors.toList()); @@ -243,10 +244,7 @@ public class BlockMaskBuilder { private boolean optimizedStates = true; public boolean isEmpty() { - for (long[] bitSet : bitSets) { - if (bitSet != null) return false; - } - return true; + return Arrays.stream(bitSets).noneMatch(Objects::nonNull); } public BlockMaskBuilder() { @@ -258,17 +256,13 @@ public class BlockMaskBuilder { } public BlockMaskBuilder addAll() { - for (int i = 0; i < bitSets.length; i++) { - bitSets[i] = BlockMask.ALL; - } + Arrays.fill(bitSets, BlockMask.ALL); optimizedStates = true; return this; } public BlockMaskBuilder clear() { - for (int i = 0; i < bitSets.length; i++) { - bitSets[i] = null; - } + Arrays.fill(bitSets, null); optimizedStates = true; return this; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java index 2a6279469..3126aa0a7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.function.mask; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockType; @@ -109,7 +110,7 @@ public class BlockTypeMask extends AbstractExtentMask { @Override public boolean test(BlockVector3 vector) { - return types[getExtent().getBlockType(vector).getInternalId()]; + return types[getExtent().getBlock(vector).getInternalId()]; } @Nullable diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java index a3ac57a03..00ce54dec 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.math.BlockVector3; import java.util.ArrayList; import java.util.Collection; import java.util.List; + import java.util.Map; import javax.annotation.Nullable; @@ -60,7 +61,9 @@ public class MaskUnion extends MaskIntersection { @Override public boolean test(BlockVector3 vector) { - for (Mask mask : getMasksArray()) { + Mask[] masks = getMasksArray(); + + for (Mask mask : masks) { if (mask.test(vector)) { return true; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java index a16c0109a..47d5a460a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java @@ -15,7 +15,7 @@ public class SingleBlockTypeMask extends AbstractExtentMask { @Override public boolean test(BlockVector3 vector) { - return getExtent().getBlockType(vector).getInternalId() == internalId; + return getExtent().getBlock(vector).getInternalId() == internalId; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/BackwardsExtentBlockCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/BackwardsExtentBlockCopy.java index 28bd3a04a..cafda10f5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/BackwardsExtentBlockCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/BackwardsExtentBlockCopy.java @@ -56,14 +56,6 @@ public class BackwardsExtentBlockCopy implements Operation { } private BlockVector3 transform(Transform transform, BlockVector3 pt) { -// mutable.mutX(((pt.getBlockX() - origin.getBlockX()))); -// mutable.mutY(((pt.getBlockY() - origin.getBlockY()))); -// mutable.mutZ(((pt.getBlockZ() - origin.getBlockZ()))); -// BlockVector3 tmp = transform.apply(new Vector3(pt.getBlockX() - origin.getBlockX(), pt.getBlockY() - origin.getBlockY(), pt.getBlockZ() - origin.getBlockZ())).toBlockPoint(); -// tmp.mutX((tmp.getBlockX() + origin.getBlockX())); -// tmp.mutY((tmp.getBlockY() + origin.getBlockY())); -// tmp.mutZ((tmp.getBlockZ() + origin.getBlockZ())); -// return tmp; return transform.apply(Vector3.at(pt.getBlockX() - origin.getBlockX(), pt.getBlockY() - origin.getBlockY(), pt.getBlockZ() - origin.getBlockZ())).toBlockPoint().add(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 72239cb43..d8c4eee74 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.function.operation; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.example.MappedFaweQueue; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.extent.BlockTranslateExtent; @@ -27,12 +30,11 @@ import com.boydti.fawe.object.function.block.BiomeCopy; import com.boydti.fawe.object.function.block.CombinedBlockCopy; import com.boydti.fawe.object.function.block.SimpleBlockCopy; import com.boydti.fawe.util.MaskTraverser; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.collect.Lists; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.function.CombinedRegionFunction; @@ -49,17 +51,16 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.entity.EntityTypes; - -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; /** * Makes a copy of a portion of one extent to another extent or another point. - *

+ * *

This is a forward extent copy, meaning that it iterates over the blocks * in the source extent, and will copy as many blocks as there are in the * source. Therefore, interpolation will not occur to fill in the gaps.

@@ -74,12 +75,12 @@ public class ForwardExtentCopy implements Operation { private int repetitions = 1; private Mask sourceMask = Masks.alwaysTrue(); private boolean removingEntities; + private boolean copyingEntities = true; // default to true for backwards compatibility, sort of + private boolean copyingBiomes; private RegionFunction sourceFunction = null; private Transform transform = new Identity(); private Transform currentTransform = null; private int affected; - private boolean copyEntities = true; - private boolean copyBiomes = false; private RegionFunction filterFunction; /** @@ -120,7 +121,7 @@ public class ForwardExtentCopy implements Operation { /** * Get the transformation that will occur on every point. - *

+ * *

The transformation will stack with each repetition.

* * @return a transformation @@ -142,7 +143,7 @@ public class ForwardExtentCopy implements Operation { /** * Get the mask that gets applied to the source extent. - *

+ * *

This mask can be used to filter what will be copied from the source.

* * @return a source mask @@ -151,32 +152,6 @@ public class ForwardExtentCopy implements Operation { return sourceMask; } - /** - * Set whether entities should be copied along with blocks. - * - * @param copyEntities true if copying - */ - public void setCopyingEntities(boolean copyEntities) { - this.copyEntities = copyEntities; - } - - /** - * Return whether entities should be copied along with blocks. - * - * @return true if copying - */ - public boolean isCopyingEntities() { - return copyEntities; - } - - public void setCopyBiomes(boolean copyBiomes) { - this.copyBiomes = copyBiomes; - } - - public boolean isCopyBiomes() { - return copyBiomes; - } - /** * Set a mask that gets applied to the source extent. * @@ -188,10 +163,6 @@ public class ForwardExtentCopy implements Operation { this.sourceMask = sourceMask; } - public void setFilterFunction(RegionFunction filterFunction) { - this.filterFunction = filterFunction; - } - /** * Get the function that gets applied to all source blocks after * the copy has been made. @@ -208,7 +179,6 @@ public class ForwardExtentCopy implements Operation { * * @param function a source function, or null if none is to be applied */ - @Deprecated public void setSourceFunction(RegionFunction function) { this.sourceFunction = function; } @@ -232,6 +202,24 @@ public class ForwardExtentCopy implements Operation { this.repetitions = repetitions; } + /** + * Return whether entities should be copied along with blocks. + * + * @return true if copying + */ + public boolean isCopyingEntities() { + return copyingEntities; + } + + /** + * Set whether entities should be copied along with blocks. + * + * @param copyingEntities true if copying + */ + public void setCopyingEntities(boolean copyingEntities) { + this.copyingEntities = copyingEntities; + } + /** * Return whether entities that are copied should be removed. * @@ -250,6 +238,27 @@ public class ForwardExtentCopy implements Operation { this.removingEntities = removingEntities; } + /** + * Return whether biomes should be copied along with blocks. + * + * @return true if copying biomes + */ + public boolean isCopyingBiomes() { + return copyingBiomes; + } + + /** + * Set whether biomes should be copies along with blocks. + * + * @param copyingBiomes true if copying + */ + public void setCopyingBiomes(boolean copyingBiomes) { + if (copyingBiomes && !(region instanceof FlatRegion)) { + throw new UnsupportedOperationException("Can't copy biomes from region that doesn't implement FlatRegion"); + } + this.copyingBiomes = copyingBiomes; + } + /** * Get the number of affected objects. * @@ -298,7 +307,7 @@ public class ForwardExtentCopy implements Operation { new MaskTraverser(sourceMask).reset(transExt); copy = new RegionMaskingFilter(sourceMask, copy); } - if (copyBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) { + if (copyingBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) { copy = CombinedRegionFunction.combine(copy, new BiomeCopy(source, finalDest)); } blockCopy = new BackwardsExtentBlockCopy(region, from, transform, copy); @@ -352,14 +361,14 @@ public class ForwardExtentCopy implements Operation { if (maskFunc != null) copy = new RegionMaskTestFunction(sourceMask, copy, maskFunc); else copy = new RegionMaskingFilter(sourceMask, copy); } - if (copyBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) { + if (copyingBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) { copy = CombinedRegionFunction.combine(copy, new BiomeCopy(source, finalDest)); } blockCopy = new RegionVisitor(region, copy, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null); } List entities; - if (isCopyingEntities()) { + if (copyingEntities) { // filter players since they can't be copied entities = source.getEntities(region) .stream() @@ -376,6 +385,11 @@ public class ForwardExtentCopy implements Operation { if (!entities.isEmpty()) { ExtentEntityCopy entityCopy = new ExtentEntityCopy(from.toVector3(), destination, to.toVector3(), currentTransform); entityCopy.setRemoving(removingEntities); + List entities2 = Lists.newArrayList(source.getEntities(region)); + entities2.removeIf(entity -> { + EntityProperties properties = entity.getFacet(EntityProperties.class); + return properties != null && !properties.isPasteable(); + }); EntityVisitor entityVisitor = new EntityVisitor(entities.iterator(), entityCopy); Operations.completeBlindly(entityVisitor); } @@ -396,7 +410,10 @@ public class ForwardExtentCopy implements Operation { @Override public void addStatusMessages(List messages) { + StringBuilder msg = new StringBuilder(); + msg.append(affected).append(" objects(s)"); + msg.append(" affected."); + messages.add(msg.toString()); } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java index 34bb3f080..f1317501b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java @@ -27,8 +27,6 @@ import com.sk89q.worldedit.WorldEditException; */ public final class Operations { - private static final RunContext context = new RunContext(); - private Operations() { } @@ -40,7 +38,7 @@ public final class Operations { */ public static void complete(Operation op) throws WorldEditException { while (op != null) { - op = op.resume(context); + op = op.resume(new RunContext()); } } @@ -54,7 +52,7 @@ public final class Operations { public static void completeLegacy(Operation op) throws MaxChangedBlocksException { while (op != null) { try { - op = op.resume(context); + op = op.resume(new RunContext()); } catch (MaxChangedBlocksException e) { throw e; } catch (WorldEditException e) { @@ -73,7 +71,7 @@ public final class Operations { public static void completeBlindly(Operation op) { while (op != null) { try { - op = op.resume(context); + op = op.resume(new RunContext()); } catch (WorldEditException e) { throw new RuntimeException(e); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java index 2c000c668..14f09b94b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java @@ -19,9 +19,7 @@ package com.sk89q.worldedit.function.pattern; -import com.sk89q.minecraft.util.commands.Link; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.command.UtilityCommands; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; @@ -30,7 +28,6 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; /** * Returns a {@link BlockStateHolder} for a given position. */ -@Link(clazz = UtilityCommands.class, value = "patterns") public interface Pattern { /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomStatePattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomStatePattern.java new file mode 100644 index 000000000..f2e2870a9 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomStatePattern.java @@ -0,0 +1,45 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.pattern; + +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.FuzzyBlockState; + +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +public class RandomStatePattern implements Pattern { + + private final Random rand = new Random(); + private final List blocks; + + public RandomStatePattern(FuzzyBlockState state) { + blocks = state.getBlockType().getAllStates().stream().filter(state::equalsFuzzy) + .map(BlockState::toBaseBlock).collect(Collectors.toList()); + } + + @Override + public BaseBlock apply(BlockVector3 position) { + return blocks.get(rand.nextInt(blocks.size())); + } +} 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 4d5e4fcf1..51f8c0f67 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 @@ -21,53 +21,26 @@ package com.sk89q.worldedit.function.pattern; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.registry.state.PropertyKey; +import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; -import java.lang.ref.SoftReference; - /** * Removes the waterlogged state from blocks if possible. If not possible, returns air. */ public class WaterloggedRemover extends AbstractExtentPattern { - private static SoftReference cache = new SoftReference<>(null); - - private synchronized BlockState[] getRemap() { - BlockState[] remap = cache.get(); - if (remap != null) return remap; - cache = new SoftReference<>(remap = new BlockState[BlockTypes.states.length]); - - // init - for (int i = 0; i < remap.length; i++) { - BlockState state = remap[i]; - BlockType type = state.getBlockType(); - if (!type.hasProperty(PropertyKey.WATERLOGGED)) { - continue; - } - if (state.getState(PropertyKey.WATERLOGGED) == Boolean.TRUE) { - remap[i] = state.with(PropertyKey.WATERLOGGED, false); - } - } - return remap; - } - - private final BlockState[] remap; - public WaterloggedRemover(Extent extent) { super(extent); - this.remap = getRemap(); } @Override public BaseBlock apply(BlockVector3 position) { BaseBlock block = getExtent().getFullBlock(position); - BlockState newState = remap[block.getOrdinal()]; - if (newState != null) { - return newState.toBaseBlock(block.getNbtData()); + @SuppressWarnings("unchecked") + Property prop = (Property) block.getBlockType().getPropertyMap().getOrDefault("waterlogged", null); + if (prop != null) { + return block.with(prop, false); } return BlockTypes.AIR.getDefaultState().toBaseBlock(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java index 8dcdbc558..90587d4ca 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.function.visitor; +import static com.google.common.base.Preconditions.checkNotNull; + import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.example.MappedFaweQueue; @@ -26,8 +28,6 @@ import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.HasFaweQueue; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.object.collection.BlockVectorSet; - -import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.operation.Operation; @@ -35,7 +35,6 @@ import com.sk89q.worldedit.function.operation.RunContext; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.util.Direction; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -82,13 +81,13 @@ public abstract class BreadthFirstSearch implements Operation { } private final RegionFunction function; - private BlockVectorSet visited; - private final MappedFaweQueue mFaweQueue; private BlockVectorSet queue; + private BlockVectorSet visited; + private int affected = 0; private int currentDepth = 0; private final int maxDepth; private List directions = new ArrayList<>(); - private int affected = 0; + private final MappedFaweQueue mFaweQueue; private int maxBranch = Integer.MAX_VALUE; /** @@ -184,9 +183,9 @@ public abstract class BreadthFirstSearch implements Operation { * @param position the position */ public void visit(BlockVector3 position) { - if (!visited.contains(position)) { - BlockVector3 blockVector = position; - isVisitable(blockVector, blockVector); // Ignore this, just to initialize mask on this point + BlockVector3 blockVector = position; + if (!visited.contains(blockVector)) { + isVisitable(position, position); // Ignore this, just to initialize mask on this point queue.add(blockVector); visited.add(blockVector); } @@ -307,11 +306,6 @@ public abstract class BreadthFirstSearch implements Operation { return currentDepth; } - @Override - public void addStatusMessages(List messages) { - messages.add(BBC.VISITOR_BLOCK.format(getAffected())); - } - @Override public void cancel() { queue.clear(); @@ -319,4 +313,9 @@ public abstract class BreadthFirstSearch implements Operation { affected = 0; } + @Override + public void addStatusMessages(List messages) { + messages.add(BBC.VISITOR_BLOCK.format(getAffected())); + } + } 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 9694e79a3..be01a3c59 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 @@ -54,7 +54,6 @@ public class RegionVisitor implements Operation { * @param region * @param function */ - @Deprecated public RegionVisitor(Region region, RegionFunction function) { this(region, function, (FaweQueue) null); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java index dbc7d3b29..bd2b55b4f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java @@ -19,7 +19,8 @@ package com.sk89q.worldedit.internal.annotation; -import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.math.BlockVector3; +import org.enginehub.piston.inject.InjectAnnotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -27,12 +28,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotates a {@link Vector3} parameter to inject a direction. + * Annotates a {@link BlockVector3} parameter to inject a direction. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) +@InjectAnnotation public @interface Direction { - + String AIM = "me"; boolean includeDiagonals() default false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/MultiDirection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/MultiDirection.java new file mode 100644 index 000000000..efd1ba701 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/MultiDirection.java @@ -0,0 +1,37 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.annotation; + +import org.enginehub.piston.inject.InjectAnnotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates a {@code List} parameter to inject multiple direction. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@InjectAnnotation +public @interface MultiDirection { + boolean includeDiagonals() default false; +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Radii.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Radii.java new file mode 100644 index 000000000..82423fba9 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Radii.java @@ -0,0 +1,40 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.annotation; + +import org.enginehub.piston.inject.InjectAnnotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates a {@code double} parameter to inject multiple radii values. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@InjectAnnotation +public @interface Radii { + /** + * Number of radii values to inject at maximum. May inject less. + */ + int value(); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Selection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Selection.java index 9647c360f..1886f43f7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Selection.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Selection.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.internal.annotation; +import org.enginehub.piston.inject.InjectAnnotation; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -29,6 +31,6 @@ import java.lang.annotation.Target; */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) +@InjectAnnotation public @interface Selection { - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/ChunkDeleter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/ChunkDeleter.java new file mode 100644 index 000000000..642708cbf --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/ChunkDeleter.java @@ -0,0 +1,346 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.anvil; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.sk89q.worldedit.math.BlockVector2; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiPredicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class ChunkDeleter { + + public static final String DELCHUNKS_FILE_NAME = "delete_chunks.json"; + private static final Logger logger = LoggerFactory.getLogger(ChunkDeleter.class); + + private static final Comparator chunkSorter = Comparator.comparing( + pos -> (pos.getBlockX() & 31) + (pos.getBlockZ() & 31) * 32); + + private static Gson chunkDeleterGson = new GsonBuilder() + .registerTypeAdapter(BlockVector2.class, new BlockVector2Adapter()) + .setPrettyPrinting() + .create(); + + public static ChunkDeletionInfo readInfo(Path chunkFile) throws IOException, JsonSyntaxException { + String json = new String(Files.readAllBytes(chunkFile), StandardCharsets.UTF_8); + return chunkDeleterGson.fromJson(json, ChunkDeletionInfo.class); + } + + public static void writeInfo(ChunkDeletionInfo info, Path chunkFile) throws IOException, JsonIOException { + String json = chunkDeleterGson.toJson(info, new TypeToken() {}.getType()); + try (BufferedWriter writer = Files.newBufferedWriter(chunkFile)) { + writer.write(json); + } + } + + public static void runFromFile(Path chunkFile, boolean deleteOnSuccess) { + ChunkDeleter chunkDeleter; + try { + chunkDeleter = createFromFile(chunkFile); + } catch (JsonSyntaxException | IOException e) { + logger.error("Could not parse chunk deletion file. Invalid file?", e); + return; + } + logger.info("Found chunk deletions. Proceeding with deletion..."); + long start = System.currentTimeMillis(); + if (chunkDeleter.runDeleter()) { + logger.info("Successfully deleted {} matching chunks (out of {}, taking {} ms).", + chunkDeleter.getDeletedChunkCount(), chunkDeleter.getDeletionsRequested(), + System.currentTimeMillis() - start); + if (deleteOnSuccess) { + boolean deletedFile = false; + try { + deletedFile = Files.deleteIfExists(chunkFile); + } catch (IOException ignored) { + } + if (!deletedFile) { + logger.warn("Chunk deletion file could not be cleaned up. This may have unintended consequences" + + " on next startup, or if /delchunks is used again."); + } + } + } else { + logger.error("Error occurred while deleting chunks. " + + "If world errors occur, stop the server and restore the *.bak backup files."); + } + } + + private ChunkDeleter(ChunkDeletionInfo chunkDeletionInfo) { + this.chunkDeletionInfo = chunkDeletionInfo; + } + + private static ChunkDeleter createFromFile(Path chunkFile) throws IOException { + ChunkDeletionInfo info = readInfo(chunkFile); + if (info == null) { + throw new IOException("Read null json. Empty file?"); + } + return new ChunkDeleter(info); + } + + private final ChunkDeletionInfo chunkDeletionInfo; + private Set backedUpRegions = new HashSet<>(); + private boolean shouldPreload; + private int debugRate = 100; + private int totalChunksDeleted = 0; + private int deletionsRequested = 0; + + private boolean runDeleter() { + return chunkDeletionInfo.batches.stream().allMatch(this::runBatch); + } + + private boolean runBatch(ChunkDeletionInfo.ChunkBatch chunkBatch) { + int chunkCount = chunkBatch.getChunkCount(); + logger.debug("Processing deletion batch with {} chunks.", chunkCount); + final Map> regionToChunkList = groupChunks(chunkBatch); + BiPredicate predicate = createPredicates(chunkBatch.deletionPredicates); + shouldPreload = chunkBatch.chunks == null; + deletionsRequested += chunkCount; + debugRate = chunkCount / 10; + + return regionToChunkList.entrySet().stream().allMatch(entry -> { + Path regionPath = entry.getKey(); + if (!Files.exists(regionPath)) return true; + if (chunkBatch.backup && !backedUpRegions.contains(regionPath)) { + try { + backupRegion(regionPath); + } catch (IOException e) { + logger.warn("Error backing up region file: " + regionPath + ". Aborting the process.", e); + return false; + } + } + return deleteChunks(regionPath, entry.getValue(), predicate); + }); + } + + private Map> groupChunks(ChunkDeletionInfo.ChunkBatch chunkBatch) { + Path worldPath = Paths.get(chunkBatch.worldPath); + if (chunkBatch.chunks != null) { + return chunkBatch.chunks.stream() + .collect(Collectors.groupingBy(RegionFilePos::new)) + .entrySet().stream().collect(Collectors.toMap( + e -> worldPath.resolve("region").resolve(e.getKey().getFileName()), + e -> e.getValue().stream().sorted(chunkSorter))); + } else { + final BlockVector2 minChunk = chunkBatch.minChunk; + final BlockVector2 maxChunk = chunkBatch.maxChunk; + final RegionFilePos minRegion = new RegionFilePos(minChunk); + final RegionFilePos maxRegion = new RegionFilePos(maxChunk); + Map> groupedChunks = new HashMap<>(); + for (int regX = minRegion.getX(); regX <= maxRegion.getX(); regX++) { + for (int regZ = minRegion.getZ(); regZ <= maxRegion.getZ(); regZ++) { + final Path regionPath = worldPath.resolve("region").resolve(new RegionFilePos(regX, regZ).getFileName()); + if (!Files.exists(regionPath)) continue; + int startX = regX << 5; + int endX = (regX << 5) + 31; + int startZ = regZ << 5; + int endZ = (regZ << 5) + 31; + + int minX = Math.max(Math.min(startX, endX), minChunk.getBlockX()); + int minZ = Math.max(Math.min(startZ, endZ), minChunk.getBlockZ()); + int maxX = Math.min(Math.max(startX, endX), maxChunk.getBlockX()); + int maxZ = Math.min(Math.max(startZ, endZ), maxChunk.getBlockZ()); + Stream stream = Stream.iterate(BlockVector2.at(minX, minZ), + bv2 -> { + int nextX = bv2.getBlockX(); + int nextZ = bv2.getBlockZ(); + if (++nextX > maxX) { + nextX = minX; + if (++nextZ > maxZ) { + return null; + } + } + return BlockVector2.at(nextX, nextZ); + }); + groupedChunks.put(regionPath, stream); + } + } + return groupedChunks; + } + } + + private BiPredicate createPredicates(List deletionPredicates) { + if (deletionPredicates == null) return (r, p) -> true; + return deletionPredicates.stream() + .map(this::createPredicate) + .reduce(BiPredicate::and) + .orElse((r, p) -> true); + } + + private BiPredicate createPredicate(ChunkDeletionInfo.DeletionPredicate deletionPredicate) { + if ("modification".equals(deletionPredicate.property)) { + int time; + try { + time = Integer.parseInt(deletionPredicate.value); + } catch (NumberFormatException e) { + throw new IllegalStateException("Modification time predicate specified invalid time: " + deletionPredicate.value); + } + switch (deletionPredicate.comparison) { + case "<": + return (r, p) -> { + try { + return r.getModificationTime(p) < time; + } catch (IOException e) { + return false; + } + }; + case ">": + return (r, p) -> { + try { + return r.getModificationTime(p) > time; + } catch (IOException e) { + return false; + } + }; + default: + throw new IllegalStateException("Unexpected comparison value: " + deletionPredicate.comparison); + } + } + throw new IllegalStateException("Unexpected property value: " + deletionPredicate.property); + } + + private void backupRegion(Path regionFile) throws IOException { + Path backupFile = regionFile.resolveSibling(regionFile.getFileName() + ".bak"); + Files.copy(regionFile, backupFile, StandardCopyOption.REPLACE_EXISTING); + backedUpRegions.add(backupFile); + } + + private boolean deleteChunks(Path regionFile, Stream chunks, + BiPredicate deletionPredicate) { + try (RegionAccess region = new RegionAccess(regionFile, shouldPreload)) { + for (Iterator iterator = chunks.iterator(); iterator.hasNext();) { + BlockVector2 chunk = iterator.next(); + if (chunk == null) break; + if (deletionPredicate.test(region, chunk)) { + region.deleteChunk(chunk); + totalChunksDeleted++; + if (debugRate != 0 && totalChunksDeleted % debugRate == 0) { + logger.debug("Deleted {} chunks so far.", totalChunksDeleted); + } + } else { + logger.debug("Chunk did not match predicates: " + chunk); + } + } + return true; + } catch (IOException e) { + logger.warn("Error deleting chunks from region: " + regionFile + ". Aborting the process.", e); + return false; + } + } + + public int getDeletedChunkCount() { + return totalChunksDeleted; + } + + public int getDeletionsRequested() { + return deletionsRequested; + } + + private static class BlockVector2Adapter extends TypeAdapter { + @Override + public void write(JsonWriter out, BlockVector2 value) throws IOException { + out.beginArray(); + out.value(value.getBlockX()); + out.value(value.getBlockZ()); + out.endArray(); + } + + @Override + public BlockVector2 read(JsonReader in) throws IOException { + in.beginArray(); + int x = in.nextInt(); + int z = in.nextInt(); + in.endArray(); + return BlockVector2.at(x, z); + } + } + + private static class RegionFilePos { + private final int x; + private final int z; + + RegionFilePos(BlockVector2 chunk) { + this.x = chunk.getBlockX() >> 5; + this.z = chunk.getBlockZ() >> 5; + } + + RegionFilePos(int regX, int regZ) { + this.x = regX; + this.z = regZ; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public String getFileName() { + return "r." + x + "." + z + ".mca"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RegionFilePos that = (RegionFilePos) o; + + if (x != that.x) return false; + return z == that.z; + + } + + @Override + public int hashCode() { + int result = x; + result = 31 * result + z; + return result; + } + + @Override + public String toString() { + return "(" + x + ", " + z + ")"; + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/ChunkDeletionInfo.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/ChunkDeletionInfo.java new file mode 100644 index 000000000..fcb34ccdc --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/ChunkDeletionInfo.java @@ -0,0 +1,54 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.anvil; + +import com.sk89q.worldedit.math.BlockVector2; + +import java.util.List; + +/** + * Internal class. Subject to changes. + */ +public class ChunkDeletionInfo { + + public List batches; + + public static class ChunkBatch { + public String worldPath; + public boolean backup; + public List deletionPredicates; + // specify either list of chunks, or min-max + public List chunks; + public BlockVector2 minChunk; + public BlockVector2 maxChunk; + + public int getChunkCount() { + if (chunks != null) return chunks.size(); + final BlockVector2 dist = maxChunk.subtract(minChunk).add(1, 1); + return dist.getBlockX() * dist.getBlockZ(); + } + } + + public static class DeletionPredicate { + public String property; + public String comparison; + public String value; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/RegionAccess.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/RegionAccess.java new file mode 100644 index 000000000..9446773a6 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/anvil/RegionAccess.java @@ -0,0 +1,101 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.anvil; + +import com.sk89q.worldedit.math.BlockVector2; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.file.Path; + +/** + * Internal class. Subject to changes. + */ +class RegionAccess implements AutoCloseable { + + private RandomAccessFile raf; + private int[] offsets; + private int[] timestamps; + + RegionAccess(Path file) throws IOException { + this(file, false); + } + + RegionAccess(Path file, boolean preload) throws IOException { + raf = new RandomAccessFile(file.toFile(), "rw"); + if (preload) { + readHeaders(); + } + } + + private void readHeaders() throws IOException { + offsets = new int[1024]; + timestamps = new int[1024]; + for (int idx = 0; idx < 1024; ++idx) { + offsets[idx] = raf.readInt(); + } + for (int idx = 0; idx < 1024; ++idx) { + timestamps[idx] = raf.readInt(); + } + } + + private static int indexChunk(BlockVector2 pos) { + int x = pos.getBlockX() & 31; + int z = pos.getBlockZ() & 31; + return x + z * 32; + } + + int getModificationTime(BlockVector2 pos) throws IOException { + int idx = indexChunk(pos); + if (timestamps != null) { + return timestamps[idx]; + } + raf.seek(idx * 4L + 4096); + return raf.readInt(); + } + + int getChunkSize(BlockVector2 pos) throws IOException { + int idx = indexChunk(pos); + if (offsets != null) { + return offsets[idx] & 0xFF; + } + raf.seek(idx * 4L); + // 3 bytes for offset + raf.read(); + raf.read(); + raf.read(); + // one byte for size - note, yes, could do raf.readInt() & 0xFF but that does extra checks + return raf.read(); + } + + void deleteChunk(BlockVector2 pos) throws IOException { + int idx = indexChunk(pos); + raf.seek(idx * 4L); + raf.writeInt(0); + if (offsets != null) { + offsets[idx] = 0; + } + } + + @Override + public void close() throws IOException { + raf.close(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java new file mode 100644 index 000000000..c7ce23311 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java @@ -0,0 +1,112 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.command; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.sk89q.worldedit.internal.util.Substring; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class CommandArgParser { + + public static ImmutableList spaceSplit(String string) { + ImmutableList.Builder result = ImmutableList.builder(); + int index = 0; + for (String part : Splitter.on(' ').split(string)) { + result.add(Substring.from(string, index, index + part.length())); + index += part.length() + 1; + } + return result.build(); + } + + private enum State { + NORMAL, + QUOTE + } + + private final Stream.Builder args = Stream.builder(); + private final List input; + private final List currentArg = new ArrayList<>(); + private int index = 0; + private State state = State.NORMAL; + + public CommandArgParser(List input) { + this.input = input; + } + + public Stream parseArgs() { + for (; index < input.size(); index++) { + Substring nextPart = input.get(index); + switch (state) { + case NORMAL: + handleNormal(nextPart); + break; + case QUOTE: + handleQuote(nextPart); + } + } + return args.build(); + } + + private void handleNormal(Substring part) { + if (part.getSubstring().startsWith("\"")) { + state = State.QUOTE; + currentArg.add(Substring.wrap( + part.getSubstring().substring(1), + part.getStart(), part.getEnd() + )); + } else { + currentArg.add(part); + finishArg(); + } + } + + private void handleQuote(Substring part) { + if (part.getSubstring().endsWith("\"")) { + state = State.NORMAL; + currentArg.add(Substring.wrap( + part.getSubstring().substring(0, part.getSubstring().length() - 1), + part.getStart(), part.getEnd() + )); + finishArg(); + } else { + currentArg.add(part); + } + } + + private void finishArg() { + // Merge the arguments into a single, space-joined, string + // Keep the original start + end points. + int start = currentArg.get(0).getStart(); + int end = Iterables.getLast(currentArg).getEnd(); + args.add(Substring.wrap(currentArg.stream() + .map(Substring::getSubstring) + .collect(Collectors.joining(" ")), + start, end + )); + currentArg.clear(); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java index 63945769e..f0ff4d2ca 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java @@ -19,31 +19,29 @@ package com.sk89q.worldedit.internal.command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.Logging; +import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.math.Vector3; -import com.sk89q.worldedit.util.command.parametric.AbstractInvokeListener; -import com.sk89q.worldedit.util.command.parametric.InvokeHandler; -import com.sk89q.worldedit.util.command.parametric.ParameterData; -import com.sk89q.worldedit.util.command.parametric.ParameterException; +import org.enginehub.piston.CommandParameters; +import org.enginehub.piston.gen.CommandCallListener; +import org.enginehub.piston.inject.Key; -import java.io.Closeable; import java.lang.reflect.Method; +import java.util.Optional; import java.util.logging.Handler; import java.util.logging.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Logs called commands to a logger. */ -public class CommandLoggingHandler extends AbstractInvokeListener implements InvokeHandler, Closeable { +public class CommandLoggingHandler implements CommandCallListener, AutoCloseable { private final WorldEdit worldEdit; private final Logger logger; @@ -62,11 +60,7 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv } @Override - public void preProcess(Object object, Method method, ParameterData[] parameters, CommandContext context) throws CommandException, ParameterException { - } - - @Override - public void preInvoke(Object object, Method method, ParameterData[] parameters, Object[] args, CommandContext context) throws CommandException { + public void beforeCall(Method method, CommandParameters parameters) { Logging loggingAnnotation = method.getAnnotation(Logging.class); Logging.LogMode logMode; StringBuilder builder = new StringBuilder(); @@ -77,78 +71,66 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv logMode = loggingAnnotation.value(); } - Actor sender = context.getLocals().get(Actor.class); - Player player; + Optional playerOpt = parameters.injectedValue(Key.of(Actor.class)) + .filter(Player.class::isInstance) + .map(Player.class::cast); - if (sender == null) { + if (!playerOpt.isPresent()) { return; } - if (sender instanceof Player) { - player = (Player) sender; - } else { - return; - } + Player player = playerOpt.get(); - builder.append("WorldEdit: ").append(sender.getName()); - if (sender.isPlayer()) { - builder.append(" (in \"").append(player.getWorld().getName()).append("\")"); - } + builder.append("WorldEdit: ").append(player.getName()); + builder.append(" (in \"").append(player.getWorld().getName()).append("\")"); - builder.append(": ").append(context.getCommand()); + builder.append(": ").append(parameters.getMetadata().getCalledName()); - if (context.argsLength() > 0) { - builder.append(" ").append(context.getJoinedStrings(0)); - } + builder.append(": ") + .append(Stream.concat( + Stream.of(parameters.getMetadata().getCalledName()), + parameters.getMetadata().getArguments().stream() + ).collect(Collectors.joining(" "))); - if (logMode != null && sender.isPlayer()) { + if (logMode != null) { Vector3 position = player.getLocation().toVector(); LocalSession session = worldEdit.getSessionManager().get(player); switch (logMode) { - case PLACEMENT: - try { - position = session.getPlacementPosition(player).toVector3(); - } catch (IncompleteRegionException e) { + case PLACEMENT: + try { + position = session.getPlacementPosition(player).toVector3(); + } catch (IncompleteRegionException e) { + break; + } + /* FALL-THROUGH */ + + case POSITION: + builder.append(" - Position: ").append(position); break; - } - /* FALL-THROUGH */ - case POSITION: - builder.append(" - Position: ").append(position); - break; + case ALL: + builder.append(" - Position: ").append(position); + /* FALL-THROUGH */ - case ALL: - builder.append(" - Position: ").append(position); - /* FALL-THROUGH */ + case ORIENTATION_REGION: + builder.append(" - Orientation: ").append(player.getCardinalDirection().name()); + /* FALL-THROUGH */ - case ORIENTATION_REGION: - builder.append(" - Orientation: ").append(player.getCardinalDirection().name()); - /* FALL-THROUGH */ - - case REGION: - try { - builder.append(" - Region: ") + case REGION: + try { + builder.append(" - Region: ") .append(session.getSelection(player.getWorld())); - } catch (IncompleteRegionException e) { + } catch (IncompleteRegionException e) { + break; + } break; - } - break; } } logger.info(builder.toString()); } - @Override - public void postInvoke(Object object, Method method, ParameterData[] parameters, Object[] args, CommandContext context) throws CommandException { - } - - @Override - public InvokeHandler createInvokeHandler() { - return this; - } - @Override public void close() { for (Handler h : logger.getHandlers()) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandRegistrationHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandRegistrationHandler.java new file mode 100644 index 000000000..1a0b440cb --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandRegistrationHandler.java @@ -0,0 +1,51 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.command; + +import com.google.common.collect.ImmutableList; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.gen.CommandCallListener; +import org.enginehub.piston.gen.CommandRegistration; + +import java.util.List; + +public class CommandRegistrationHandler { + + private static final CommandPermissionsConditionGenerator PERM_GEN = new CommandPermissionsConditionGenerator(); + + private final List callListeners; + + public CommandRegistrationHandler(List callListeners) { + this.callListeners = ImmutableList.copyOf(callListeners); + } + + public void register(CommandManager manager, CommandRegistration registration, CI instance) { + registration.containerInstance(instance) + .commandManager(manager) + .listeners(callListeners); + if (registration instanceof CommandPermissionsConditionGenerator.Registration) { + ((CommandPermissionsConditionGenerator.Registration) registration).commandPermissionsConditionGenerator( + PERM_GEN + ); + } + registration.build(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java new file mode 100644 index 000000000..c74afd0dc --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java @@ -0,0 +1,132 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.command; + +import static com.google.common.base.Preconditions.checkState; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; +import com.sk89q.worldedit.internal.util.Substring; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import static java.util.stream.Collectors.toList; +import org.enginehub.piston.Command; +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.part.SubCommandPart; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class CommandUtil { + + public static Map getSubCommands(Command currentCommand) { + return currentCommand.getParts().stream() + .filter(p -> p instanceof SubCommandPart) + .flatMap(p -> ((SubCommandPart) p).getCommands().stream()) + .collect(Collectors.toMap(Command::getName, Function.identity())); + } + + private static String clean(String input) { + return PlatformCommandManager.COMMAND_CLEAN_PATTERN.matcher(input).replaceAll(""); + } + + private static final Comparator BY_CLEAN_NAME = + Comparator.comparing(c -> clean(c.getName())); + + public static Comparator byCleanName() { + return BY_CLEAN_NAME; + } + + /** + * Fix {@code suggestions} to replace the last space-separated word in {@code arguments}. + */ + public static List fixSuggestions(String arguments, List suggestions) { + Substring lastArg = Iterables.getLast(CommandArgParser.spaceSplit(arguments)); + return suggestions.stream() + .map(suggestion -> CommandUtil.suggestLast(lastArg, suggestion)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(toList()); + } + + /** + * Given the last word of a command, mutate the suggestion to replace the last word, if + * possible. + */ + private static Optional suggestLast(Substring last, Substring suggestion) { + if (suggestion.getStart() == last.getEnd()) { + // this suggestion is for the next argument. + if (last.getSubstring().isEmpty()) { + return Optional.of(suggestion.getSubstring()); + } + return Optional.of(last.getSubstring() + " " + suggestion.getSubstring()); + } + StringBuilder builder = new StringBuilder(last.getSubstring()); + int start = suggestion.getStart() - last.getStart(); + int end = suggestion.getEnd() - last.getStart(); + if (start < 0) { + // Quoted suggestion, can't complete it here. + return Optional.empty(); + } + checkState(end <= builder.length(), + "Suggestion ends too late, last=%s, suggestion=", last, suggestion); + builder.replace(start, end, suggestion.getSubstring()); + return Optional.of(builder.toString()); + } + + /** + * Require {@code condition} to be {@code true}, otherwise throw a {@link CommandException} + * with the given message. + * + * @param condition the condition to check + * @param message the message for failure + */ + public static void checkCommandArgument(boolean condition, String message) { + checkCommandArgument(condition, TextComponent.of(message)); + } + + /** + * Require {@code condition} to be {@code true}, otherwise throw a {@link CommandException} + * with the given message. + * + * @param condition the condition to check + * @param message the message for failure + */ + public static void checkCommandArgument(boolean condition, Component message) { + if (!condition) { + throw new CommandException(message, ImmutableList.of()); + } + } + + public static T requireIV(Key type, String name, InjectedValueAccess injectedValueAccess) { + return injectedValueAccess.injectedValue(type).orElseThrow(() -> + new IllegalStateException("No injected value for " + name + " (type " + type + ")") + ); + } + + private CommandUtil() { + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index 2656039ce..bb46b8d73 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -357,7 +357,7 @@ public class WorldEditBinding { String input = context.next(); if (input != null) { - if (MathMan.isInteger(input)) return BiomeTypes.get(Integer.parseInt(input)); + if (MathMan.isInteger(input)) return BiomeTypes.register(Integer.parseInt(input)); BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverter.java similarity index 75% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverter.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverter.java index 1eb9491ca..27a494175 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverter.java @@ -17,33 +17,33 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util.command.parametric; +package com.sk89q.worldedit.internal.command.exception; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.WrappedCommandException; + +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.exception.CommandExecutionException; /** - * Used to convert a recognized {@link Throwable} into an appropriate + * Used to convert a recognized {@link Throwable} into an appropriate * {@link CommandException}. - * - *

Methods (when invoked by a {@link ParametricBuilder}-created command) may throw - * relevant exceptions that are not caught by the command manager, but translate - * into reasonable exceptions for an application. However, unknown exceptions are - * normally simply wrapped in a {@link WrappedCommandException} and bubbled up. Only + * + *

Methods may throw relevant exceptions that are not caught by the command manager, + * but translate into reasonable exceptions for an application. However, unknown exceptions are + * normally simply wrapped in a {@link CommandExecutionException} and bubbled up. Only * normal {@link CommandException}s will be printed correctly, so a converter translates * one of these unknown exceptions into an appropriate {@link CommandException}.

- * + * *

This also allows the code calling the command to not need be aware of these * application-specific exceptions, as they will all be converted to * {@link CommandException}s that are handled normally.

*/ public interface ExceptionConverter { - + /** * Attempt to convert the given throwable into a {@link CommandException}. - * + * *

If the exception is not recognized, then nothing should be thrown.

- * + * * @param t the throwable * @throws CommandException a command exception */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverterHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverterHelper.java similarity index 85% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverterHelper.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverterHelper.java index 5b2d9fd5d..42959c5fb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverterHelper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverterHelper.java @@ -17,10 +17,11 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util.command.parametric; +package com.sk89q.worldedit.internal.command.exception; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.WrappedCommandException; +import com.google.common.collect.ImmutableList; +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.exception.CommandExecutionException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -31,24 +32,24 @@ import java.util.List; /** * An implementation of an {@link ExceptionConverter} that automatically calls * the correct method defined on this object. - * + * *

Only public methods will be used. Methods will be called in order of decreasing * levels of inheritance (between classes where one inherits the other). For two * different inheritance branches, the order between them is undefined.

*/ public abstract class ExceptionConverterHelper implements ExceptionConverter { - + private final List handlers; @SuppressWarnings("unchecked") public ExceptionConverterHelper() { List handlers = new ArrayList<>(); - + for (Method method : this.getClass().getMethods()) { if (method.getAnnotation(ExceptionMatch.class) == null) { continue; } - + Class[] parameters = method.getParameterTypes(); if (parameters.length == 1) { Class cls = parameters[0]; @@ -58,9 +59,9 @@ public abstract class ExceptionConverterHelper implements ExceptionConverter { } } } - + Collections.sort(handlers); - + this.handlers = handlers; } @@ -75,18 +76,21 @@ public abstract class ExceptionConverterHelper implements ExceptionConverter { if (e.getCause() instanceof CommandException) { throw (CommandException) e.getCause(); } - throw new WrappedCommandException(e); + if (e.getCause() instanceof com.sk89q.minecraft.util.commands.CommandException) { + throw new CommandException(e.getCause(), ImmutableList.of()); + } + throw new CommandExecutionException(e, ImmutableList.of()); } catch (IllegalArgumentException | IllegalAccessException e) { - throw new WrappedCommandException(e); + throw new CommandExecutionException(e, ImmutableList.of()); } } } } - + private static class ExceptionHandler implements Comparable { final Class cls; final Method method; - + private ExceptionHandler(Class cls, Method method) { this.cls = cls; this.method = method; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionMatch.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionMatch.java similarity index 95% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionMatch.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionMatch.java index 619bc9d46..99acf81fa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionMatch.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionMatch.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util.command.parametric; +package com.sk89q.worldedit.internal.command.exception; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java similarity index 67% rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java index 10294c0a2..94d628128 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java @@ -17,11 +17,10 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.internal.command; +package com.sk89q.worldedit.internal.command.exception; import static com.google.common.base.Preconditions.checkNotNull; - -import com.sk89q.minecraft.util.commands.CommandException; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.DisallowedItemException; import com.sk89q.worldedit.EmptyClipboardException; import com.sk89q.worldedit.IncompleteRegionException; @@ -37,11 +36,12 @@ import com.sk89q.worldedit.command.InsufficientArgumentsException; import com.sk89q.worldedit.command.tool.InvalidToolBindException; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.regions.RegionOperationException; -import com.sk89q.worldedit.util.command.parametric.ExceptionConverterHelper; -import com.sk89q.worldedit.util.command.parametric.ExceptionMatch; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.io.file.FileSelectionAbortedException; import com.sk89q.worldedit.util.io.file.FilenameResolutionException; import com.sk89q.worldedit.util.io.file.InvalidFilenameException; +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.exception.UsageException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -59,105 +59,115 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper { this.worldEdit = worldEdit; } + private CommandException newCommandException(String message, Throwable cause) { + return new CommandException(TextComponent.of(String.valueOf(message)), cause, ImmutableList.of()); + } + @ExceptionMatch public void convert(NumberFormatException e) throws CommandException { final Matcher matcher = numberFormat.matcher(e.getMessage()); if (matcher.matches()) { - throw new CommandException("Number expected; string \"" + matcher.group(1) - + "\" given."); + throw newCommandException("Number expected; string \"" + matcher.group(1) + + "\" given.", e); } else { - throw new CommandException("Number expected; string given."); + throw newCommandException("Number expected; string given.", e); } } @ExceptionMatch public void convert(IncompleteRegionException e) throws CommandException { - throw new CommandException("Make a region selection first."); + throw newCommandException("Make a region selection first.", e); } @ExceptionMatch public void convert(UnknownItemException e) throws CommandException { - throw new CommandException("Block name '" + e.getID() + "' was not recognized."); + throw newCommandException("Block name '" + e.getID() + "' was not recognized.", e); } @ExceptionMatch public void convert(InvalidItemException e) throws CommandException { - throw new CommandException(e.getMessage()); + throw newCommandException(e.getMessage(), e); } @ExceptionMatch public void convert(DisallowedItemException e) throws CommandException { - throw new CommandException("Block '" + e.getID() - + "' not allowed (see WorldEdit configuration)."); + throw newCommandException("Block '" + e.getID() + + "' not allowed (see WorldEdit configuration).", e); } @ExceptionMatch public void convert(MaxChangedBlocksException e) throws CommandException { - throw new CommandException("Max blocks changed in an operation reached (" - + e.getBlockLimit() + ")."); + throw newCommandException("Max blocks changed in an operation reached (" + + e.getBlockLimit() + ").", e); } @ExceptionMatch public void convert(MaxBrushRadiusException e) throws CommandException { - throw new CommandException("Maximum brush radius (in configuration): " + worldEdit.getConfiguration().maxBrushRadius); + throw newCommandException("Maximum brush radius (in configuration): " + worldEdit.getConfiguration().maxBrushRadius, e); } @ExceptionMatch public void convert(MaxRadiusException e) throws CommandException { - throw new CommandException("Maximum radius (in configuration): " + worldEdit.getConfiguration().maxRadius); + throw newCommandException("Maximum radius (in configuration): " + worldEdit.getConfiguration().maxRadius, e); } @ExceptionMatch public void convert(UnknownDirectionException e) throws CommandException { - throw new CommandException("Unknown direction: " + e.getDirection()); + throw newCommandException("Unknown direction: " + e.getDirection(), e); } @ExceptionMatch public void convert(InsufficientArgumentsException e) throws CommandException { - throw new CommandException(e.getMessage()); + throw newCommandException(e.getMessage(), e); } @ExceptionMatch public void convert(RegionOperationException e) throws CommandException { - throw new CommandException(e.getMessage()); + throw newCommandException(e.getMessage(), e); } @ExceptionMatch public void convert(ExpressionException e) throws CommandException { - throw new CommandException(e.getMessage()); + throw newCommandException(e.getMessage(), e); } @ExceptionMatch public void convert(EmptyClipboardException e) throws CommandException { - throw new CommandException("Your clipboard is empty. Use //copy first."); + throw newCommandException("Your clipboard is empty. Use //copy first.", e); } @ExceptionMatch public void convert(InvalidFilenameException e) throws CommandException { - throw new CommandException("Filename '" + e.getFilename() + "' invalid: " - + e.getMessage()); + throw newCommandException("Filename '" + e.getFilename() + "' invalid: " + + e.getMessage(), e); } @ExceptionMatch public void convert(FilenameResolutionException e) throws CommandException { - throw new CommandException( - "File '" + e.getFilename() + "' resolution error: " + e.getMessage()); + throw newCommandException( + "File '" + e.getFilename() + "' resolution error: " + e.getMessage(), e); } @ExceptionMatch public void convert(InvalidToolBindException e) throws CommandException { - throw new CommandException("Can't bind tool to " + e.getItemType().getName() + ": " + e.getMessage()); + throw newCommandException("Can't bind tool to " + e.getItemType().getName() + ": " + e.getMessage(), e); } @ExceptionMatch public void convert(FileSelectionAbortedException e) throws CommandException { - throw new CommandException("File selection aborted."); + throw newCommandException("File selection aborted.", e); } @ExceptionMatch public void convert(WorldEditException e) throws CommandException { - throw new CommandException(e.getMessage(), e); + throw newCommandException(e.getMessage(), e); + } + + // Prevent investigation into UsageExceptions + @ExceptionMatch + public void convert(UsageException e) throws CommandException { + throw e; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java index e35ba22d5..476a00fae 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java @@ -32,6 +32,8 @@ import com.sk89q.worldedit.internal.expression.runtime.Functions; import com.sk89q.worldedit.internal.expression.runtime.RValue; import com.sk89q.worldedit.internal.expression.runtime.ReturnException; import com.sk89q.worldedit.internal.expression.runtime.Variable; +import com.sk89q.worldedit.session.request.Request; + import java.util.ArrayDeque; import java.util.HashMap; import java.util.List; @@ -159,7 +161,18 @@ public class Expression { } private double evaluateRootTimed(int timeout) throws EvaluationException { - Future result = evalThread.submit(this::evaluateRoot); + Request request = Request.request(); + Future result = evalThread.submit(() -> { + Request local = Request.request(); + local.setSession(request.getSession()); + local.setWorld(request.getWorld()); + local.setEditSession(request.getEditSession()); + try { + return Expression.this.evaluateRoot(); + } finally { + Request.reset(); + } + }); try { return result.get(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java index f94eba3cd..09ec8df2e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java @@ -54,7 +54,7 @@ public class Function extends Node { return invokeMethod(method, args); } - public static double invokeMethod(Method method, Object[] args) throws EvaluationException { + protected static double invokeMethod(Method method, Object[] args) throws EvaluationException { try { return (Double) method.invoke(null, args); } catch (InvocationTargetException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java index 16e25e354..1aabf26b1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java @@ -30,6 +30,48 @@ public final class MCDirections { } public static Direction fromHanging(int i) { + switch (i) { + case 0: + return Direction.DOWN; + case 1: + return Direction.UP; + case 2: + return Direction.NORTH; + case 3: + return Direction.SOUTH; + case 4: + return Direction.WEST; + case 5: + return Direction.EAST; + default: + return Direction.DOWN; + } + } + + public static int toHanging(Direction direction) { + switch (direction) { + case DOWN: + return 0; + case UP: + return 1; + case NORTH: + return 2; + case SOUTH: + return 3; + case WEST: + return 4; + case EAST: + return 5; + default: + return 0; + } + } + + public static Direction fromPre13Hanging(int i) { + return fromHorizontalHanging(i); + } + + public static Direction fromHorizontalHanging(int i) { switch (i) { case 0: return Direction.SOUTH; @@ -44,7 +86,7 @@ public final class MCDirections { } } - public static int toHanging(Direction direction) { + public static int toHorizontalHanging(Direction direction) { switch (direction) { case SOUTH: return 0; @@ -68,15 +110,6 @@ public final class MCDirections { } } - public static byte toLegacyHanging(int i) { - switch (i) { - case 0: return (byte) 2; - case 1: return (byte) 1; - case 2: return (byte) 0; - default: return (byte) 3; - } - } - public static Direction fromRotation(int i) { switch (i) { case 0: diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java index fcee6abcc..a5844306d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java @@ -29,6 +29,7 @@ import com.sk89q.worldedit.extension.input.ParserContext; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * An abstract implementation of a factory for internal usage. @@ -45,10 +46,13 @@ public abstract class AbstractFactory { * Create a new factory. * * @param worldEdit the WorldEdit instance + * @param defaultParser the parser to fall back to */ - protected AbstractFactory(WorldEdit worldEdit) { + protected AbstractFactory(WorldEdit worldEdit, InputParser defaultParser) { checkNotNull(worldEdit); + checkNotNull(defaultParser); this.worldEdit = worldEdit; + this.parsers.add(defaultParser); } /** @@ -76,6 +80,12 @@ public abstract class AbstractFactory { throw new NoMatchException("No match for '" + input + "'"); } + public List getSuggestions(String input) { + return parsers.stream().flatMap( + p -> p.getSuggestions(input) + ).collect(Collectors.toList()); + } + /** * Registers an InputParser to this factory * @@ -84,6 +94,6 @@ public abstract class AbstractFactory { public void register(InputParser inputParser) { checkNotNull(inputParser); - parsers.add(inputParser); + parsers.add(parsers.size() - 1, inputParser); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java index 575bb9550..3e490d8ca 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java @@ -23,8 +23,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; -import java.util.Collections; -import java.util.List; +import java.util.stream.Stream; /** * Input parser interface for {@link AbstractFactory}. @@ -43,11 +42,11 @@ public abstract class InputParser { public abstract E parseFromInput(String input, ParserContext context) throws InputParseException; /** - * Gets a list of suggestions of input to this parser. + * Gets a stream of suggestions of input to this parser. * - * @return a list of suggestions + * @return a stream of suggestions */ - public List getSuggestions() { - return Collections.emptyList(); + public Stream getSuggestions(String input) { + return Stream.empty(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java index d0647f9cc..5aa25daaa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java @@ -19,12 +19,13 @@ package com.sk89q.worldedit.internal.registry; -import com.google.common.collect.Lists; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; /** * An input parser that only performs a single function from aliases. @@ -65,7 +66,16 @@ public abstract class SimpleInputParser extends InputParser { } @Override - public List getSuggestions() { - return Lists.newArrayList(getPrimaryMatcher()); + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of(getPrimaryMatcher()); + } + final String prefix = input.toLowerCase(Locale.ROOT); + for (String alias : getMatchedAliases()) { + if (alias.startsWith(prefix)) { + return Stream.of(alias); + } + } + return Stream.empty(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/DocumentationPrinter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/DocumentationPrinter.java index 90871c4c1..ef0138c9b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/DocumentationPrinter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/DocumentationPrinter.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.internal.util; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.NestedCommand; import com.sk89q.worldedit.command.*; @@ -79,10 +79,10 @@ public final class DocumentationPrinter { if (!f.getName().matches("^.*\\.java$")) { continue; } - + String className = "com.sk89q.worldedit.commands." + f.getName().substring(0, f.getName().lastIndexOf(".")); - + Class cls; try { cls = Class.forName(className, true, @@ -90,7 +90,7 @@ public final class DocumentationPrinter { } catch (ClassNotFoundException e) { continue; } - + classes.add(cls); }*/ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/Substring.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/Substring.java new file mode 100644 index 000000000..fa4049cda --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/Substring.java @@ -0,0 +1,97 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.util; + +import java.util.Objects; + +/** + * An explicit substring. Provides the range from which it was taken. + */ +public final class Substring { + + /** + * Take a substring from {@code original}, and {@link #wrap(String, int, int)} it into + * a Substring. + */ + public static Substring from(String original, int start) { + return wrap(original.substring(start), start, original.length()); + } + + /** + * Take a substring from {@code original}, and {@link #wrap(String, int, int)} it into + * a Substring. + */ + public static Substring from(String original, int start, int end) { + return wrap(original.substring(start, end), start, end); + } + + /** + * Wrap the given parameters into a Substring instance. + */ + public static Substring wrap(String substring, int start, int end) { + return new Substring(substring, start, end); + } + + private final String substring; + private final int start; + private final int end; + + private Substring(String substring, int start, int end) { + this.substring = substring; + this.start = start; + this.end = end; + } + + public String getSubstring() { + return substring; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Substring substring1 = (Substring) o; + return start == substring1.start && + end == substring1.end && + substring.equals(substring1.substring); + } + + @Override + public int hashCode() { + return Objects.hash(substring, start, end); + } + + @Override + public String toString() { + return "Substring{" + + "substring='" + substring + "'" + + ",start=" + start + + ",end=" + end + + "}"; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java index 4a6d8d27b..d5f2bbf9e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.math; -import com.google.common.collect.ComparisonChain; import com.sk89q.worldedit.math.transform.AffineTransform; import java.util.Comparator; @@ -28,7 +27,7 @@ import java.util.Comparator; * An immutable 2-dimensional vector. */ public class BlockVector2 { - + public static final BlockVector2 ZERO = new BlockVector2(0, 0); public static final BlockVector2 UNIT_X = new BlockVector2(1, 0); public static final BlockVector2 UNIT_Z = new BlockVector2(0, 1); @@ -48,18 +47,26 @@ public class BlockVector2 { * cdef * */ - public static final Comparator COMPARING_GRID_ARRANGEMENT = (a, b) -> { - return ComparisonChain.start() - .compare(a.getBlockZ(), b.getBlockZ()) - .compare(a.getBlockX(), b.getBlockX()) - .result(); - }; + public static final Comparator COMPARING_GRID_ARRANGEMENT = + Comparator.comparingInt(BlockVector2::getZ).thenComparingInt(BlockVector2::getX); public static BlockVector2 at(double x, double z) { return at((int) Math.floor(x), (int) Math.floor(z)); } public static BlockVector2 at(int x, int z) { + switch (x) { + case 0: + if (z == 0) { + return ZERO; + } + break; + case 1: + if (z == 1) { + return ONE; + } + break; + } return new BlockVector2(x, z); } @@ -342,6 +349,27 @@ public class BlockVector2 { return divide(n, n); } + /** + * Shift all components right. + * + * @param x the value to shift x by + * @param z the value to shift z by + * @return a new vector + */ + public BlockVector2 shr(int x, int z) { + return at(this.x >> x, this.z >> z); + } + + /** + * Shift all components right by {@code n}. + * + * @param n the value to shift by + * @return a new vector + */ + public BlockVector2 shr(int n) { + return shr(n, n); + } + /** * Get the length of the vector. * @@ -571,5 +599,4 @@ public class BlockVector2 { public String toString() { return "(" + x + ", " + z + ")"; } - -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java index 8fb8da940..4641f8327 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.math; import static com.google.common.base.Preconditions.checkArgument; -import com.google.common.collect.ComparisonChain; import com.sk89q.worldedit.math.transform.AffineTransform; import java.util.Comparator; @@ -45,19 +44,29 @@ public class BlockVector3 { } public static BlockVector3 at(int x, int y, int z) { + // switch for efficiency on typical cases + // in MC y is rarely 0/1 on selections + switch (y) { + case 0: + if (x == 0 && z == 0) { + return ZERO; + } + break; + case 1: + if (x == 1 && z == 1) { + return ONE; + } + break; + } return new BlockVector3(x, y, z); } // thread-safe initialization idiom private static final class YzxOrderComparator { - private static final Comparator YZX_ORDER = (a, b) -> { - //noinspection SuspiciousNameCombination - return ComparisonChain.start() - .compare(a.y, b.y) - .compare(a.z, b.z) - .compare(a.x, b.x) - .result(); - }; + private static final Comparator YZX_ORDER = + Comparator.comparingInt(BlockVector3::getY) + .thenComparingInt(BlockVector3::getZ) + .thenComparingInt(BlockVector3::getX); } /** @@ -369,6 +378,50 @@ public class BlockVector3 { return divide(n, n, n); } + /** + * Shift all components right. + * + * @param x the value to shift x by + * @param y the value to shift y by + * @param z the value to shift z by + * @return a new vector + */ + public BlockVector3 shr(int x, int y, int z) { + return at(this.x >> x, this.y >> y, this.z >> z); + } + + /** + * Shift all components right by {@code n}. + * + * @param n the value to shift by + * @return a new vector + */ + public BlockVector3 shr(int n) { + return shr(n, n, n); + } + + /** + * Shift all components left. + * + * @param x the value to shift x by + * @param y the value to shift y by + * @param z the value to shift z by + * @return a new vector + */ + public BlockVector3 shl(int x, int y, int z) { + return at(this.x << x, this.y << y, this.z << z); + } + + /** + * Shift all components left by {@code n}. + * + * @param n the value to shift by + * @return a new vector + */ + public BlockVector3 shl(int n) { + return shl(n, n, n); + } + /** * Get the length of the vector. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MathUtils.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MathUtils.java index 3a2e3af39..bb8541cb2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MathUtils.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MathUtils.java @@ -63,14 +63,14 @@ public final class MathUtils { dInt += 360; } switch (dInt) { - case 0: - return 1.0; - case 90: - return 0.0; - case 180: - return -1.0; - case 270: - return 0.0; + case 0: + return 1.0; + case 90: + return 0.0; + case 180: + return -1.0; + case 270: + return 0.0; } } return Math.cos(Math.toRadians(degrees)); @@ -92,14 +92,14 @@ public final class MathUtils { dInt += 360; } switch (dInt) { - case 0: - return 0.0; - case 90: - return 1.0; - case 180: - return 0.0; - case 270: - return -1.0; + case 0: + return 0.0; + case 90: + return 1.0; + case 180: + return 0.0; + case 270: + return -1.0; } } return Math.sin(Math.toRadians(degrees)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector2.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector2.java index bc71607c2..4aaadd81f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector2.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableBlockVector2.java @@ -2,7 +2,7 @@ package com.sk89q.worldedit.math; public class MutableBlockVector2 extends BlockVector2 { - private static ThreadLocal MUTABLE_CACHE = ThreadLocal.withInitial(() -> new MutableBlockVector2()); + private static ThreadLocal MUTABLE_CACHE = ThreadLocal.withInitial(MutableBlockVector2::new); public static MutableBlockVector2 get(int x, int z) { return MUTABLE_CACHE.get().setComponents(x, z); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector2.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector2.java index 4b4133d5c..584eae658 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector2.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector2.java @@ -1,9 +1,9 @@ package com.sk89q.worldedit.math; -import java.io.Serializable; - public class MutableVector2 extends Vector2 { - public MutableVector2() {} + + public MutableVector2() { + } /** * Construct an instance. @@ -47,21 +47,25 @@ public class MutableVector2 extends Vector2 { this.z = z; return this; } + @Override public MutableVector2 mutX(int x) { this.x = x; return this; } + @Override public MutableVector2 mutZ(int z) { this.z = z; return this; } + @Override public MutableVector2 mutX(double x) { this.x = x; return this; } + @Override public MutableVector2 mutZ(double z) { this.z = z; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java index 27edc3437..eba183599 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/MutableVector3.java @@ -2,7 +2,8 @@ package com.sk89q.worldedit.math; public class MutableVector3 extends Vector3 { - public MutableVector3() {} + public MutableVector3() { + } public MutableVector3(double x, double y, double z) { super(x, y, z); @@ -45,11 +46,13 @@ public class MutableVector3 extends Vector3 { this.x = x; return this; } + @Override public MutableVector3 mutZ(int z) { this.z = z; return this; } + @Override public MutableVector3 mutX(double x) { this.x = x; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector2.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector2.java index 86ff8857f..6bbf93d70 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector2.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector2.java @@ -33,6 +33,19 @@ public class Vector2 { public static final Vector2 ONE = new Vector2(1, 1); public static Vector2 at(double x, double z) { + int xTrunc = (int) x; + switch (xTrunc) { + case 0: + if (x == 0 && z == 0) { + return ZERO; + } + break; + case 1: + if (x == 1 && z == 1) { + return ONE; + } + break; + } return new Vector2(x, z); } @@ -445,7 +458,7 @@ public class Vector2 { Math.max(z, v2.z) ); } - + public static BlockVector2 toBlockPoint(double x, double z) { return BlockVector2.at(x, z); } @@ -491,7 +504,7 @@ public class Vector2 { @Override public int hashCode() { - return (((int) x) << 16) ^ ((int) z); + return ((int) x << 16) ^ ((int) z); } @Override @@ -499,4 +512,4 @@ public class Vector2 { return "(" + x + ", " + z + ")"; } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector3.java index 5d69f7c9d..ec0bf21bf 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/Vector3.java @@ -25,7 +25,6 @@ import com.boydti.fawe.util.MathMan; import com.google.common.collect.ComparisonChain; import com.sk89q.worldedit.math.transform.AffineTransform; -import javax.annotation.Nullable; import java.util.Comparator; /** @@ -40,6 +39,21 @@ public class Vector3 { public static final Vector3 ONE = new Vector3(1, 1, 1); public static Vector3 at(double x, double y, double z) { + // switch for efficiency on typical cases + // in MC y is rarely 0/1 on selections + int yTrunc = (int) y; + switch (yTrunc) { + case 0: + if (x == 0 && y == 0 && z == 0) { + return ZERO; + } + break; + case 1: + if (x == 1 && y == 1 && z == 1) { + return ONE; + } + break; + } return new Vector3(x, y, z); } @@ -56,7 +70,7 @@ public class Vector3 { /** * Returns a comparator that sorts vectors first by Y, then Z, then X. - * + * *

* Useful for sorting by chunk block storage order. */ @@ -632,14 +646,14 @@ public class Vector3 { @Override public int hashCode() { - return (((int) x) ^ (((int) z) << 12)) ^ (((int) y) << 24); + return (int) x ^ (int) z << 12 ^ (int) y << 24; } @Override public String toString() { - String x = (getX() == getBlockX() ? "" + getBlockX() : "" + getX()); - String y = (getY() == getBlockY() ? "" + getBlockY() : "" + getY()); - String z = (getZ() == getBlockZ() ? "" + getBlockZ() : "" + getZ()); + String x = "" + getX(); + String y = "" + getY(); + String z = "" + getZ(); return "(" + x + ", " + y + ", " + z + ")"; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java index c88ff812d..f090e5232 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.math.convolution; import com.boydti.fawe.object.visitor.Fast2DIterator; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.function.mask.Mask; @@ -252,12 +253,12 @@ public class HeightMap { * @return number of blocks affected * @throws MaxChangedBlocksException */ + public int apply(int[] data) throws MaxChangedBlocksException { checkNotNull(data); BlockVector3 minY = region.getMinimumPoint(); int originX = minY.getBlockX(); - int originY = minY.getBlockY(); int originZ = minY.getBlockZ(); int maxY = region.getMaximumPoint().getBlockY(); 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 e78422cd8..ef5808da3 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 @@ -19,6 +19,9 @@ 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.config.Settings; import com.boydti.fawe.object.collection.BlockVectorSet; import com.sk89q.worldedit.math.BlockVector2; @@ -27,15 +30,12 @@ import com.sk89q.worldedit.math.MutableBlockVector2; import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.storage.ChunkStore; -import org.jetbrains.annotations.NotNull; import java.util.AbstractSet; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; +import org.jetbrains.annotations.NotNull; /** * An axis-aligned cuboid. It can be defined using two corners of the cuboid. @@ -300,10 +300,18 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { recalculate(); } + @Override + public void shift(BlockVector3 change) throws RegionOperationException { + pos1 = pos1.add(change); + pos2 = pos2.add(change); + + recalculate(); + } + @Override public Set getChunks() { - BlockVector3 min = getMinimumPoint(); - BlockVector3 max = getMaximumPoint(); + BlockVector3 min = getMinimumPoint(); + BlockVector3 max = getMaximumPoint(); final int maxX = max.getBlockX() >> ChunkStore.CHUNK_SHIFTS; final int minX = min.getBlockX() >> ChunkStore.CHUNK_SHIFTS; final int maxZ = max.getBlockZ() >> ChunkStore.CHUNK_SHIFTS; @@ -323,7 +331,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { @Override public BlockVector2 next() { - MutableBlockVector2 result = pos; + MutableBlockVector2 result = pos; // calc next pos.setComponents(pos.getX() - 1, pos.getZ()); if (pos.getX() <= minX) { @@ -352,7 +360,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { @Override public boolean contains(Object o) { if (o instanceof BlockVector2) { - BlockVector2 cv = (BlockVector2) o; + BlockVector2 cv = (BlockVector2) o; return cv.getX() >= minX && cv.getX() <= maxX && cv.getZ() >= minZ && cv.getZ() <= maxZ; } else { return false; @@ -360,12 +368,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { } }; } - public void shift(BlockVector3 change) throws RegionOperationException { - pos1 = pos1.add(change); - pos2 = pos2.add(change); - recalculate(); - } @Override public Set getChunkCubes() { Set chunks = new BlockVectorSet(); @@ -394,14 +397,16 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { public boolean contains(int x, int z) { return x >= this.minX && x <= this.maxX && z >= this.minZ && z <= this.maxZ; } - @Override + + @Override public boolean contains(BlockVector3 position) { BlockVector3 min = getMinimumPoint(); BlockVector3 max = getMaximumPoint(); return position.containedWithin(min, max); } - @NotNull @Override + + @Override public Iterator iterator() { if (Settings.IMP.HISTORY.COMPRESSION_LEVEL >= 9 || useOldIterator) { return iterator_old(); @@ -490,7 +495,6 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { return new Iterator() { private BlockVector3 min = getMinimumPoint(); private BlockVector3 max = getMaximumPoint(); - private int nextX = min.getBlockX(); private int nextY = min.getBlockY(); private int nextZ = min.getBlockZ(); @@ -546,12 +550,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { @Override public BlockVector2 next() { - if (!hasNext()) throw new NoSuchElementException() { - @Override - public synchronized Throwable fillInStackTrace() { - return this; - } - }; + if (!hasNext()) throw new NoSuchElementException(); BlockVector2 answer = BlockVector2.at(nextX, nextZ); if (++nextX > max.getBlockX()) { nextX = min.getBlockX(); @@ -593,15 +592,15 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { } public static boolean contains(CuboidRegion region) { - BlockVector3 min = region.getMinimumPoint(); - BlockVector3 max = region.getMaximumPoint(); + BlockVector3 min = region.getMinimumPoint(); + BlockVector3 max = region.getMaximumPoint(); return region.contains(min.getBlockX(), min.getBlockY(), min.getBlockZ()) && region.contains(max.getBlockX(), max.getBlockY(), max.getBlockZ()); } /** * Make a cuboid from the center. * - * @param origin the origin + * @param origin the origin * @param apothem the apothem, where 0 is the minimum value to make a 1x1 cuboid * @return a cuboid region */ @@ -611,6 +610,4 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { BlockVector3 size = BlockVector3.ONE.multiply(apothem); return new CuboidRegion(origin.subtract(size), origin.add(size)); } - - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java index 263c2b2a1..10cfc56a5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.regions; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; 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 c1184afb9..f54a42800 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 @@ -179,11 +179,7 @@ public class EllipsoidRegion extends AbstractRegion { this.radius = radius; radiusSqr = radius.multiply(radius); radiusLengthSqr = (int) radiusSqr.getX(); - if (radius.getY() == radius.getX() && radius.getX() == radius.getZ()) { - this.sphere = true; - } else { - this.sphere = false; - } + this.sphere = radius.getY() == radius.getX() && radius.getX() == radius.getZ(); } @Override 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 5ea83211e..1027b7d4e 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 @@ -323,6 +323,7 @@ public class Polygonal2DRegion extends AbstractRegion implements FlatRegion { return inside; } + @Override public boolean contains(BlockVector3 position) { return contains(points, minY, maxY, position); } @@ -460,7 +461,7 @@ public class Polygonal2DRegion extends AbstractRegion implements FlatRegion { public Polygonal2DRegion clone() { Polygonal2DRegion clone = (Polygonal2DRegion) super.clone(); clone.points = new ArrayList<>(points); - return clone; + return clone; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java index e8efdef5a..22c8cda2f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/RegionSelector.java @@ -26,10 +26,10 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; import com.sk89q.worldedit.world.World; -import javax.annotation.Nullable; -import java.util.Collections; import java.util.List; +import javax.annotation.Nullable; + /** * Region selectors create {@link Region}s from a series of "selected points." * They are used, for example, to allow users to create a {@link CuboidRegion} @@ -156,12 +156,4 @@ public interface RegionSelector { */ List getInformationLines(); - /** - * Get the verticies - * @return - * @throws IncompleteRegionException - */ - default List getVerticies() throws IncompleteRegionException { - return Collections.singletonList(getPrimaryPosition()); - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java index 2717ae8ed..0eaddea57 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java @@ -19,9 +19,9 @@ package com.sk89q.worldedit.regions.selector; -import com.boydti.fawe.config.BBC; - import static com.google.common.base.Preconditions.checkNotNull; + +import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.extension.platform.Actor; @@ -37,7 +37,6 @@ import com.sk89q.worldedit.regions.polyhedron.Triangle; import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; import com.sk89q.worldedit.world.World; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -45,6 +44,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import javax.annotation.Nullable; + /** * Creates a {@code ConvexPolyhedralRegion} from a user's selections. */ @@ -276,8 +277,4 @@ public class ConvexPolyhedralRegionSelector implements RegionSelector, CUIRegion } } - @Override - public List getVerticies() { - return new ArrayList<>(region.getVertices()); - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java index 2cb510965..7656bc546 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java @@ -19,18 +19,14 @@ package com.sk89q.worldedit.regions.selector; -import com.boydti.fawe.config.BBC; -import com.boydti.fawe.config.Commands; -import com.boydti.fawe.util.chat.Message; - import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.command.SelectionCommands; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.internal.cui.CUIRegion; import com.sk89q.worldedit.internal.cui.SelectionPointEvent; +import com.sk89q.worldedit.internal.cui.SelectionShapeEvent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; @@ -40,7 +36,6 @@ import com.sk89q.worldedit.world.World; import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -129,7 +124,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion { public boolean selectPrimary(BlockVector3 position, SelectorLimits limits) { checkNotNull(position); - if (position1 != null && position1.equals(position)) { + if (position.equals(position1)) { return false; } @@ -142,7 +137,7 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion { public boolean selectSecondary(BlockVector3 position, SelectorLimits limits) { checkNotNull(position); - if (position2 != null && position2.equals(position)) { + if (position.equals(position2)) { return false; } @@ -157,15 +152,12 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion { checkNotNull(session); checkNotNull(pos); - Message msg; + //TODO Re-add better translation if (position1 != null && position2 != null) { - msg = BBC.SELECTOR_POS.m(1, position1, region.getArea()); + player.print("First position set to " + position1 + " (" + region.getArea() + ")."); } else { - msg = BBC.SELECTOR_POS.m(1, position1, ""); + player.print("First position set to " + position1 + "."); } - String prefix = WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/"; - String cmd = prefix + Commands.getAlias(SelectionCommands.class, "/pos1") + " " + pos.getBlockX() + "," + pos.getBlockY() + "," + pos.getBlockZ(); - msg.suggestTip(cmd).send(player); session.dispatchCUIEvent(player, new SelectionPointEvent(0, pos, getArea())); } @@ -176,15 +168,12 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion { checkNotNull(session); checkNotNull(pos); - Message msg; + //TODO Re-add better translation if (position1 != null && position2 != null) { - msg = BBC.SELECTOR_POS.m(2, position2, region.getArea()); + player.print("Second position set to " + position2 + " (" + region.getArea() + ")."); } else { - msg = BBC.SELECTOR_POS.m(2, position2, ""); + player.print("Second position set to " + position2 + "."); } - String prefix = WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/"; - String cmd = prefix + Commands.getAlias(SelectionCommands.class, "/pos2") + " " + pos.getBlockX() + "," + pos.getBlockY() + "," + pos.getBlockZ(); - msg.suggestTip(cmd).send(player); session.dispatchCUIEvent(player, new SelectionPointEvent(1, pos, getArea())); } @@ -194,6 +183,8 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion { checkNotNull(player); checkNotNull(session); + session.dispatchCUIEvent(player, new SelectionShapeEvent(getTypeID())); + if (position1 != null) { session.dispatchCUIEvent(player, new SelectionPointEvent(0, position1, getArea())); } @@ -309,9 +300,4 @@ public class CuboidRegionSelector implements RegionSelector, CUIRegion { return "cuboid"; } - @Override - public List getVerticies() { - return Arrays.asList(position1, position2); - } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java index 8394e8d54..a89730c4b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/CylinderRegionSelector.java @@ -19,9 +19,9 @@ package com.sk89q.worldedit.regions.selector; -import com.boydti.fawe.config.BBC; - import static com.google.common.base.Preconditions.checkNotNull; + +import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.extension.platform.Actor; @@ -39,11 +39,12 @@ import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; import com.sk89q.worldedit.world.World; -import javax.annotation.Nullable; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; +import javax.annotation.Nullable; + /** * Creates a {@code CylinderRegionSelector} from a user's selections. */ @@ -182,7 +183,7 @@ public class CylinderRegionSelector implements RegionSelector, CUIRegion { if (!center.equals(Vector3.ZERO)) { BBC.SELECTOR_RADIUS.send(player, NUMBER_FORMAT.format(region.getRadius().getX()) + "/" + NUMBER_FORMAT.format(region.getRadius().getZ()), region.getArea()); } else { - BBC.SELECTION_WAND.send(player); + player.printError("You must select the center point before setting the radius."); return; } @@ -284,5 +285,4 @@ public class CylinderRegionSelector implements RegionSelector, CUIRegion { return "cuboid"; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java index 5d1215dac..aec4963d8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/EllipsoidRegionSelector.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.regions.selector; import com.boydti.fawe.config.BBC; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.extension.platform.Actor; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java index deaeafb10..36ea818fd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/Polygonal2DRegionSelector.java @@ -19,9 +19,9 @@ package com.sk89q.worldedit.regions.selector; -import com.boydti.fawe.config.BBC; - import static com.google.common.base.Preconditions.checkNotNull; + +import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.extension.platform.Actor; @@ -37,11 +37,12 @@ import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; import com.sk89q.worldedit.world.World; -import javax.annotation.Nullable; import java.util.Collections; import java.util.List; import java.util.Optional; +import javax.annotation.Nullable; + /** * Creates a {@code Polygonal2DRegion} from a user's selections. */ @@ -282,4 +283,5 @@ public class Polygonal2DRegionSelector implements RegionSelector, CUIRegion { public String getLegacyTypeID() { return "polygon2d"; } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java index 11c1ab12a..8f5a96269 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/SphereRegionSelector.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.regions.selector; -import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.math.BlockVector3; @@ -88,7 +87,11 @@ public class SphereRegionSelector extends EllipsoidRegionSelector { @Override public void explainSecondarySelection(Actor player, LocalSession session, BlockVector3 pos) { - BBC.SELECTOR_RADIUS.send(player, region.getRadius().getX(), region.getArea()); + if (isDefined()) { + player.print("Radius set to " + region.getRadius().getX() + " (" + region.getArea() + ")."); + } else { + player.print("Radius set to " + region.getRadius().getX() + "."); + } session.describeCUI(player); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java index 5e2ae1c43..61eb51a50 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java @@ -22,7 +22,7 @@ package com.sk89q.worldedit.registry; import java.util.HashSet; import java.util.Set; -public abstract class Category implements RegistryItem { +public abstract class Category implements RegistryItem { private final Set set = new HashSet<>(); protected final String id; private boolean empty = true; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java index 4d362bde8..33c20673f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java @@ -23,11 +23,15 @@ import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import javax.annotation.Nullable; -public final class NamespacedRegistry extends Registry { +public final class NamespacedRegistry extends Registry { private static final String MINECRAFT_NAMESPACE = "minecraft"; - + private final Set knownNamespaces = new HashSet<>(); private final String defaultNamespace; private final List values = new ArrayList<>(); private int lastInternalId = 0; @@ -41,11 +45,18 @@ public final class NamespacedRegistry extends Registry -1, "key is not namespaced"); - V existing = super.get(key); + final int i = key.indexOf(':'); + checkState(i > 0, "key is not namespaced"); + final V existing = super.get(key); if (existing != null) { throw new UnsupportedOperationException("Replacing existing registrations is not supported"); } @@ -53,7 +64,7 @@ public final class NamespacedRegistry extends Registry extends Registry getKnownNamespaces() { + return Collections.unmodifiableSet(knownNamespaces); + } + + /** + * Get the default namespace for this registry. + * + * @return the default namespace + */ + public String getDefaultNamespace() { + return defaultNamespace; + } + private String orDefaultNamespace(final String key) { if (key.indexOf(':') == -1) { return defaultNamespace + ':' + key; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java index f9edfc255..1fe5d6862 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.registry; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -30,9 +31,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -import javax.annotation.Nullable; - -public class Registry implements Iterable { +public class Registry implements Iterable { private final Map map = new HashMap<>(); private final String name; @@ -40,7 +39,13 @@ public class Registry implements Iterable { this.name = name; } - public @Nullable V get(final String key) { + public String getName() { + return name; + } + + @Nullable + public V get(final String key) { + checkState(key.equals(key.toLowerCase(Locale.ROOT)), "key must be lowercase"); return this.map.get(key); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/RegistryItem.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/RegistryItem.java index 71ac68278..98b3251d7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/RegistryItem.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/RegistryItem.java @@ -1,3 +1,4 @@ + package com.sk89q.worldedit.registry; public interface RegistryItem { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java index 8aca17dd3..37ddacd98 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java @@ -8,11 +8,10 @@ import com.boydti.fawe.util.MainUtil; import com.google.common.base.Charsets; import com.google.common.io.CharStreams; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.command.BrushProcessor; +import com.sk89q.worldedit.command.BrushCommands; import com.sk89q.worldedit.extension.factory.parser.mask.DefaultMaskParser; -import com.sk89q.worldedit.extension.factory.parser.pattern.ClipboardPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.DefaultPatternParser; -import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.util.command.ProcessedCallable; import com.sk89q.worldedit.util.command.parametric.FunctionParametricCallable; @@ -63,15 +62,15 @@ public class CommandScriptLoader { if (name.endsWith(".js")) { Fawe.debug("Loading script: " + name); List cmds = getCommands(file, Collections.emptyMap()); - FaweParser parser = null; if (aliases.length == 1) { + FaweParser parser = null; switch (aliases[0]) { case "brush": if (!cmds.isEmpty()) { - BrushProcessor processor = new BrushProcessor(WorldEdit.getInstance()); + BrushCommands processor = new BrushCommands(WorldEdit.getInstance()); for (FunctionParametricCallable cmd : cmds) { ProcessedCallable processed = new ProcessedCallable(cmd, processor); - CommandManager.getInstance().registerCommand(aliases, cmd.getCommand(), processed); + PlatformCommandManager.getInstance().registerCommand(aliases, cmd.getCommand(), processed); } } return; @@ -90,7 +89,7 @@ public class CommandScriptLoader { } } for (FunctionParametricCallable cmd : cmds) { - CommandManager.getInstance().registerCommand(aliases, cmd.getCommand(), cmd); + PlatformCommandManager.getInstance().registerSubCommands(name);registerCommand(aliases, cmd.getCommand(), cmd); } } } @@ -100,4 +99,4 @@ public class CommandScriptLoader { String script = new String(Files.readAllBytes(file.toPath())) + loader; return (List) engine.evaluate(script, file.getPath(), vars); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java index e97e7ebf6..b95d800a7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java @@ -19,10 +19,13 @@ package com.sk89q.worldedit.scripting; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.DisallowedItemException; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.UnknownItemException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.InsufficientArgumentsException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.ParserContext; @@ -56,7 +59,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Get an edit session. Every subsequent call returns a new edit session. * Usually you only need to use one edit session. - * + * * @return an edit session */ public EditSession remember() { @@ -71,7 +74,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Get the player. - * + * * @return the calling player */ public Player getPlayer() { @@ -80,7 +83,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Get the player's session. - * + * * @return a session */ public LocalSession getSession() { @@ -89,7 +92,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Get the configuration for WorldEdit. - * + * * @return the configuration */ public LocalConfiguration getConfiguration() { @@ -98,7 +101,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Get a list of edit sessions that have been created. - * + * * @return a list of created {@code EditSession}s */ public List getEditSessions() { @@ -107,7 +110,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Print a regular message to the user. - * + * * @param message a message */ public void print(String message) { @@ -116,7 +119,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Print an error message to the user. - * + * * @param message a message */ public void error(String message) { @@ -125,7 +128,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { /** * Print an raw message to the user. - * + * * @param message a message */ public void printRaw(String message) { @@ -184,8 +187,8 @@ public class CraftScriptContext extends CraftScriptEnvironment { * * @param list the input * @return pattern - * @throws UnknownItemException - * @throws DisallowedItemException + * @throws UnknownItemException + * @throws DisallowedItemException */ public Pattern getBlockPattern(String list) throws WorldEditException { ParserContext context = new ParserContext(); @@ -201,8 +204,8 @@ public class CraftScriptContext extends CraftScriptEnvironment { * @param list a list * @param allBlocksAllowed true if all blocks are allowed * @return set - * @throws UnknownItemException - * @throws DisallowedItemException + * @throws UnknownItemException + * @throws DisallowedItemException */ public Set getBlocks(String list, boolean allBlocksAllowed) throws WorldEditException { ParserContext context = new ParserContext(); @@ -219,15 +222,15 @@ public class CraftScriptContext extends CraftScriptEnvironment { * directory traversal exploits by checking the root directory and the file * directory. On success, a {@code java.io.File} object will be * returned. - * + * *

Use this method if you need to read a file from a directory.

- * + * * @param folder sub-directory to look in * @param filename filename (user-submitted) * @param defaultExt default extension to append if there is none * @param exts list of extensions for file open dialog, null for no filter * @return a file - * @throws FilenameException + * @throws FilenameException */ public File getSafeOpenFile(String folder, String filename, String defaultExt, String... exts) throws FilenameException { File dir = controller.getWorkingDirectoryFile(folder); @@ -240,15 +243,15 @@ public class CraftScriptContext extends CraftScriptEnvironment { * directory traversal exploits by checking the root directory and the file * directory. On success, a {@code java.io.File} object will be * returned. - * + * *

Use this method if you need to read a file from a directory.

- * + * * @param folder sub-directory to look in * @param filename filename (user-submitted) * @param defaultExt default extension to append if there is none * @param exts list of extensions for file save dialog, null for no filter * @return a file - * @throws FilenameException + * @throws FilenameException */ public File getSafeSaveFile(String folder, String filename, String defaultExt, String... exts) throws FilenameException { File dir = controller.getWorkingDirectoryFile(folder); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java index 02a377f22..ef05f8474 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java @@ -58,8 +58,7 @@ public class NashornCraftScriptEngine implements CraftScriptEngine { } try { - Object result = engine.eval(script, bindings); - return result; + return engine.eval(script, bindings); } catch (Error e) { e.printStackTrace(); throw new ScriptException(e.getMessage()); @@ -72,7 +71,6 @@ public class NashornCraftScriptEngine implements CraftScriptEngine { throw e; } throw e; - } finally { } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java index bf64e4e12..d621676af 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java @@ -120,15 +120,13 @@ public class RhinoScriptEngine extends AbstractScriptEngine { public ScriptEngineFactory getFactory() { if (factory != null) { return factory; - } else { - return new RhinoScriptEngineFactory(); } + return new RhinoScriptEngineFactory(); } private Scriptable setupScope(Context cx, ScriptContext context) { ScriptableObject scriptable = new ImporterTopLevel(cx); - Scriptable scope = cx.initStandardObjects(scriptable); //ScriptableObject.putProperty(scope, "argv", Context.javaToJS(args, scope)); - return scope; + return cx.initStandardObjects(scriptable); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java index 9e0e25f84..f04810f45 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java @@ -19,17 +19,16 @@ package com.sk89q.worldedit.session; -import com.boydti.fawe.util.MaskTraverser; - import static com.google.common.base.Preconditions.checkNotNull; -import com.sk89q.worldedit.EditSession; + import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.transform.BlockTransformExtent; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector3; @@ -44,11 +43,12 @@ public class PasteBuilder { private final Transform transform; private final Extent targetExtent; + private Mask sourceMask = Masks.alwaysTrue(); + private BlockVector3 to = BlockVector3.ZERO; private boolean ignoreAirBlocks; - private boolean ignoreBiomes; - private boolean ignoreEntities; - private RegionFunction canApply; + private boolean copyEntities = true; // default because it used to be this way + private boolean copyBiomes; /** * Create a new instance. @@ -75,6 +75,23 @@ public class PasteBuilder { return this; } + /** + * Set a custom mask of blocks to ignore from the source. + * This provides a more flexible alternative to {@link #ignoreAirBlocks(boolean)}, for example + * one might want to ignore structure void if copying a Minecraft Structure, etc. + * + * @param sourceMask + * @return this builder instance + */ + public PasteBuilder maskSource(Mask sourceMask) { + if (sourceMask == null) { + this.sourceMask = Masks.alwaysTrue(); + return this; + } + this.sourceMask = sourceMask; + return this; + } + /** * Set whether air blocks in the source are skipped over when pasting. * @@ -85,18 +102,29 @@ public class PasteBuilder { return this; } - public PasteBuilder ignoreBiomes(boolean ignoreBiomes) { - this.ignoreBiomes = ignoreBiomes; + /** + * Set whether the copy should include source entities. + * Note that this is true by default for legacy reasons. + * + * @param copyEntities + * @return this builder instance + */ + public PasteBuilder copyEntities(boolean copyEntities) { + this.copyEntities = copyEntities; return this; } - public PasteBuilder ignoreEntities(boolean ignoreEntities) { - this.ignoreEntities = ignoreEntities; + /** + * Set whether the copy should include source biomes (if available). + * + * @param copyBiomes + * @return this builder instance + */ + public PasteBuilder copyBiomes(boolean copyBiomes) { + this.copyBiomes = copyBiomes; return this; } - public PasteBuilder filter(RegionFunction function) { - this.canApply = function; return this; } @@ -106,28 +134,17 @@ public class PasteBuilder { * @return the operation */ public Operation build() { - Extent extent = clipboard; - if (!transform.isIdentity()) { - extent = new BlockTransformExtent(extent, transform); - } + BlockTransformExtent extent = new BlockTransformExtent(clipboard, transform); ForwardExtentCopy copy = new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(), targetExtent, to); copy.setTransform(transform); - copy.setCopyingEntities(!ignoreEntities); - copy.setCopyBiomes((!ignoreBiomes) && (!(clipboard instanceof BlockArrayClipboard) || ((BlockArrayClipboard) clipboard).IMP.hasBiomes())); - if (this.canApply != null) { - copy.setFilterFunction(this.canApply); - } - if (targetExtent instanceof EditSession) { - Mask sourceMask = ((EditSession) targetExtent).getSourceMask(); - if (sourceMask != null) { - new MaskTraverser(sourceMask).reset(extent); - copy.setSourceMask(sourceMask); - ((EditSession) targetExtent).setSourceMask(null); - } - } if (ignoreAirBlocks) { - copy.setSourceMask(new ExistingBlockMask(clipboard)); + copy.setSourceMask(sourceMask == Masks.alwaysTrue() ? new ExistingBlockMask(clipboard) + : new MaskIntersection(sourceMask, new ExistingBlockMask(clipboard))); + } else { + copy.setSourceMask(sourceMask); } + copy.setCopyingEntities(copyEntities); + copy.setCopyingBiomes(copyBiomes && clipboard.hasBiomes()); return copy; } 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 b261cc9f0..8347a6001 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 @@ -27,6 +27,8 @@ import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.tool.InvalidToolBindException; +import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent; import com.sk89q.worldedit.session.request.Request; @@ -36,6 +38,8 @@ import com.sk89q.worldedit.session.storage.VoidStore; import com.sk89q.worldedit.util.concurrency.EvenMoreExecutors; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.world.gamemode.GameModes; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.item.ItemTypes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,6 +67,8 @@ public class SessionManager { private static final int FLUSH_PERIOD = 1000 * 60; private static final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 5)); private static final Logger log = LoggerFactory.getLogger(SessionManager.class); + private static boolean warnedInvalidTool; + private final Timer timer = new Timer(); private final WorldEdit worldEdit; private final Map sessions = new HashMap<>(); @@ -187,6 +193,19 @@ public class SessionManager { return false; } + private void setDefaultWand(String sessionItem, String configItem, LocalSession session, Tool wand) throws InvalidToolBindException { + ItemType wandItem = null; + if (sessionItem != null) { + wandItem = ItemTypes.get(sessionItem); + } + if (wandItem == null) { + wandItem = ItemTypes.get(configItem); + } + if (wandItem != null) { + session.setTool(wandItem, wand); + } + } + /** * Save a map of sessions to disk. * @@ -242,7 +261,7 @@ public class SessionManager { * @return the key object */ protected UUID getKey(SessionKey key) { - return key.getUniqueId(); + return key.getUniqueId(); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java index e0786b03e..8e588bb96 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/Request.java @@ -21,10 +21,10 @@ package com.sk89q.worldedit.session.request; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.World; + import javax.annotation.Nullable; /** @@ -49,9 +49,7 @@ public final class Request { * * @return the world, which may be null */ - public - @Nullable - World getWorld() { + public @Nullable World getWorld() { return world; } @@ -68,9 +66,7 @@ public final class Request { this.extent = extent; } - public - @Nullable - Extent getExtent() { + public @Nullable Extent getExtent() { if (extent != null) return extent; if (editSession != null) return editSession; if (world != null) return world; @@ -91,9 +87,7 @@ public final class Request { * * @return the session, which may be null */ - public - @Nullable - LocalSession getSession() { + public @Nullable LocalSession getSession() { return session; } @@ -111,9 +105,7 @@ public final class Request { * * @return the edit session, which may be null */ - public - @Nullable - EditSession getEditSession() { + public @Nullable EditSession getEditSession() { return editSession; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java index dc5aac815..ab47f7c51 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java @@ -45,7 +45,8 @@ public class RequestExtent implements Extent { if (request == null || !request.isValid()) { request = Request.request(); } - return request.getEditSession(); + final EditSession editSession = request.getEditSession(); + return editSession == null ? request.getWorld() : editSession; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java index 5d3aa84e4..d7a4e2bc5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.util; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.extent.Extent; @@ -47,7 +48,7 @@ public class Location extends Vector3 { * @param extent the extent */ public Location(Extent extent) { - this(extent, Vector3.ZERO, Vector3.ZERO); + this(extent, Vector3.ZERO, 0f, 90f); } /** @@ -60,7 +61,7 @@ public class Location extends Vector3 { * @param z the Z coordinate */ public Location(Extent extent, double x, double y, double z) { - this(extent, Vector3.at(x, y, z), Vector3.ZERO); + this(extent, Vector3.at(x, y, z), 0f, 90f); } /** @@ -71,7 +72,7 @@ public class Location extends Vector3 { * @param position the position vector */ public Location(Extent extent, Vector3 position) { - this(extent, position, Vector3.ZERO); + this(extent, position, 0f, 90f); } /** @@ -124,7 +125,6 @@ public class Location extends Vector3 { * @param yaw the yaw, in degrees * @param pitch the pitch, in degrees */ - public Location(Extent extent, Vector3 position, float yaw, float pitch) { super(position); checkNotNull(extent); @@ -292,6 +292,18 @@ public class Location extends Vector3 { return new Location(extent, position, yaw, pitch); } + @Override public Location clampY(int min, int max) { + checkArgument(min <= max, "minimum cannot be greater than maximum"); + if (y < min) { + return new Location(extent, x, min, z); + } + if (y > max) { + return new Location(extent, x, max, z); + } + return this; + + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -309,8 +321,4 @@ public class Location extends Vector3 { return true; } - @Override - public int hashCode() { - return super.hashCode(); - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java index 20195fcbf..4a5a5cae6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java @@ -25,8 +25,6 @@ import com.sk89q.util.StringUtil; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.util.report.Unreported; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; import com.sk89q.worldedit.world.snapshot.SnapshotRepository; import org.slf4j.Logger; @@ -104,7 +102,6 @@ public class PropertiesConfiguration extends LocalConfiguration { } superPickaxeDrop = getBool("super-pickaxe-drop-items", superPickaxeDrop); superPickaxeManyDrop = getBool("super-pickaxe-many-drop-items", superPickaxeManyDrop); - noDoubleSlash = getBool("no-double-slash", noDoubleSlash); useInventory = getBool("use-inventory", useInventory); useInventoryOverride = getBool("use-inventory-override", useInventoryOverride); useInventoryCreativeOverride = getBool("use-inventory-creative-override", useInventoryCreativeOverride); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java index 3542f0263..2af093290 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java @@ -92,8 +92,6 @@ public class YAMLConfiguration extends LocalConfiguration { superPickaxeManyDrop = config.getBoolean( "super-pickaxe.many-drop-items", superPickaxeManyDrop); - noDoubleSlash = config.getBoolean("no-double-slash", noDoubleSlash); - useInventory = config.getBoolean("use-inventory.enable", useInventory); useInventoryOverride = config.getBoolean("use-inventory.allow-override", useInventoryOverride); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java index 4786e5f3e..084cc4f38 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java @@ -21,7 +21,7 @@ package com.sk89q.worldedit.util.command.fluent; import com.boydti.fawe.config.Commands; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.worldedit.util.command.CallableProcessor; import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.Dispatcher; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingMap.java index 7500d5afa..ecbb2fa5e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingMap.java @@ -1,7 +1,7 @@ package com.sk89q.worldedit.util.command.parametric; import com.boydti.fawe.util.StringMan; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.worldedit.util.command.CommandMapping; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java index 3a062d2cf..5f886a07a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java @@ -3,7 +3,7 @@ package com.sk89q.worldedit.util.command.parametric; import com.boydti.fawe.util.StringMan; import com.google.common.primitives.Chars; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java index fbc1794c2..21b70b01e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.util.command.parametric; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.worldedit.util.command.MissingParameterException; @@ -41,7 +41,7 @@ public class LegacyCommandsHandler extends AbstractInvokeListener implements Inv @Override public void preProcess(Object object, Method method, - ParameterData[] parameters, CommandContext context) + ParameterData[] parameters, CommandContext context) throws CommandException, ParameterException { } @@ -50,12 +50,12 @@ public class LegacyCommandsHandler extends AbstractInvokeListener implements Inv ParameterData[] parameters, Object[] args, CommandContext context) throws ParameterException { Command annotation = method.getAnnotation(Command.class); - + if (annotation != null) { if (context.argsLength() < annotation.min()) { throw new MissingParameterException(); } - + if (annotation.max() != -1 && context.argsLength() > annotation.max()) { throw new UnconsumedParameterException( context.getRemainingString(annotation.max())); @@ -73,21 +73,21 @@ public class LegacyCommandsHandler extends AbstractInvokeListener implements Inv public void updateDescription(Object object, Method method, ParameterData[] parameters, SimpleDescription description) { Command annotation = method.getAnnotation(Command.class); - + // Handle the case for old commands where no usage is set and all of its // parameters are provider bindings, so its usage information would // be blank and would imply that there were no accepted parameters - if (annotation != null && annotation.usage().isEmpty() + if (annotation != null && annotation.usage().isEmpty() && (annotation.min() > 0 || annotation.max() > 0)) { boolean hasUserParameters = false; - + for (ParameterData parameter : parameters) { if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) { hasUserParameters = true; break; } } - + if (!hasUserParameters) { description.overrideUsage("(unknown usage information)"); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java index e2bd3f99a..d8180627b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java @@ -19,13 +19,11 @@ package com.sk89q.worldedit.util.command.parametric; -import com.boydti.fawe.command.FawePrimitiveBinding; -import com.boydti.fawe.config.Commands; - import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.command.FawePrimitiveBinding; +import com.boydti.fawe.config.Commands; import com.google.common.collect.ImmutableBiMap.Builder; -import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -42,11 +40,11 @@ import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; import com.sk89q.worldedit.util.command.binding.StandardBindings; import com.sk89q.worldedit.util.command.binding.Switch; import com.thoughtworks.paranamer.Paranamer; - import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import org.enginehub.piston.annotation.Command; /** * Creates commands using annotations placed on methods and individual parameters of diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java index c5bc616b4..5add01e93 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.util.command.parametric; import com.google.common.primitives.Chars; -import com.sk89q.minecraft.util.commands.Command; +import org.enginehub.piston.annotation.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/ColorCodeBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/ColorCodeBuilder.java deleted file mode 100644 index dbaa904ce..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/ColorCodeBuilder.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.util.formatting; - -import com.google.common.base.Joiner; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -public class ColorCodeBuilder { - - private static final ColorCodeBuilder instance = new ColorCodeBuilder(); - private static final Joiner newLineJoiner = Joiner.on("\n"); - public static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 47; - - /** - * Convert a message into color-coded text. - * - * @param message the message - * @return a list of lines - */ - public String[] build(StyledFragment message) { - StringBuilder builder = new StringBuilder(); - buildFragment(builder, message, message.getStyle(), new StyleSet()); - return builder.toString().split("\r?\n"); - } - - /** - * Build a fragment. - * - * @param builder the string builder - * @param message the message - * @param parentStyle the parent style - * @param lastStyle the last style - * @return the last style used - */ - private StyleSet buildFragment(StringBuilder builder, StyledFragment message, StyleSet parentStyle, StyleSet lastStyle) { - for (Fragment node : message.getChildren()) { - if (node instanceof StyledFragment) { - StyledFragment fragment = (StyledFragment) node; - lastStyle = buildFragment( - builder, fragment, - parentStyle.extend(message.getStyle()), lastStyle); - } else { - StyleSet style = parentStyle.extend(message.getStyle()); - builder.append(getAdditive(style, lastStyle)); - builder.append(node); - lastStyle = style; - } - } - - return lastStyle; - } - - /** - * Get the formatting codes. - * - * @param style the style - * @return the color codes - */ - public static String getFormattingCode(StyleSet style) { - StringBuilder builder = new StringBuilder(); - if (style.isBold()) { - builder.append(Style.BOLD); - } - if (style.isItalic()) { - builder.append(Style.ITALIC); - } - if (style.isUnderline()) { - builder.append(Style.UNDERLINE); - } - if (style.isStrikethrough()) { - builder.append(Style.STRIKETHROUGH); - } - return builder.toString(); - } - - /** - * Get the formatting and color codes. - * - * @param style the style - * @return the color codes - */ - public static String getCode(StyleSet style) { - StringBuilder builder = new StringBuilder(); - builder.append(getFormattingCode(style)); - if (style.getColor() != null) { - builder.append(style.getColor()); - } - return builder.toString(); - } - - /** - * Get the additional color codes needed to set the given style when the current - * style is the other given one. - * - * @param resetTo the style to reset to - * @param resetFrom the style to reset from - * @return the color codes - */ - public static String getAdditive(StyleSet resetTo, StyleSet resetFrom) { - if (!resetFrom.hasFormatting() && resetTo.hasFormatting()) { - StringBuilder builder = new StringBuilder(); - builder.append(getFormattingCode(resetTo)); - if (resetFrom.getColor() != resetTo.getColor()) { - builder.append(resetTo.getColor()); - } - return builder.toString(); - } else if (!resetFrom.hasEqualFormatting(resetTo) || - (resetFrom.getColor() != null && resetTo.getColor() == null)) { - // Have to set reset code and add back all the formatting codes - return Style.RESET + getCode(resetTo); - } else { - if (resetFrom.getColor() != resetTo.getColor()) { - return String.valueOf(resetTo.getColor()); - } - } - - return ""; - } - - /** - * Word wrap the given text and maintain color codes throughout lines. - * - *

This is borrowed from Bukkit.

- * - * @param rawString the raw string - * @param lineLength the maximum line length - * @return a list of lines - */ - private String[] wordWrap(String rawString, int lineLength) { - // A null string is a single line - if (rawString == null) { - return new String[] {""}; - } - - // A string shorter than the lineWidth is a single line - if (rawString.length() <= lineLength && !rawString.contains("\n")) { - return new String[] {rawString}; - } - - char[] rawChars = (rawString + ' ').toCharArray(); // add a trailing space to trigger pagination - StringBuilder word = new StringBuilder(); - StringBuilder line = new StringBuilder(); - List lines = new LinkedList<>(); - int lineColorChars = 0; - - for (int i = 0; i < rawChars.length; i++) { - char c = rawChars[i]; - - // skip chat color modifiers - if (c == Style.COLOR_CHAR) { - word.append(Style.getByChar(rawChars[i + 1])); - lineColorChars += 2; - i++; // Eat the next character as we have already processed it - continue; - } - - if (c == ' ' || c == '\n') { - if (line.length() == 0 && word.length() > lineLength) { // special case: extremely long word begins a line - String wordStr = word.toString(); - String transformed; - if ((transformed = transform(wordStr)) != null) { - line.append(transformed); - } else { - lines.addAll(Arrays.asList(word.toString().split("(?<=\\G.{" + lineLength + "})"))); - } - } else if (line.length() + word.length() - lineColorChars == lineLength) { // Line exactly the correct length...newline - line.append(' '); - line.append(word); - lines.add(line.toString()); - line = new StringBuilder(); - lineColorChars = 0; - } else if (line.length() + 1 + word.length() - lineColorChars > lineLength) { // Line too long...break the line - String wordStr = word.toString(); - String transformed; - if (word.length() > lineLength && (transformed = transform(wordStr)) != null) { - if (line.length() + 1 + transformed.length() - lineColorChars > lineLength) { - lines.add(line.toString()); - line = new StringBuilder(transformed); - lineColorChars = 0; - } else { - if (line.length() > 0) { - line.append(' '); - } - line.append(transformed); - } - } else { - for (String partialWord : wordStr.split("(?<=\\G.{" + lineLength + "})")) { - lines.add(line.toString()); - line = new StringBuilder(partialWord); - } - lineColorChars = 0; - } - } else { - if (line.length() > 0) { - line.append(' '); - } - line.append(word); - } - word = new StringBuilder(); - - if (c == '\n') { // Newline forces the line to flush - lines.add(line.toString()); - line = new StringBuilder(); - } - } else { - word.append(c); - } - } - - if(line.length() > 0) { // Only add the last line if there is anything to add - lines.add(line.toString()); - } - - // Iterate over the wrapped lines, applying the last color from one line to the beginning of the next - if (lines.get(0).isEmpty() || lines.get(0).charAt(0) != Style.COLOR_CHAR) { - lines.set(0, Style.WHITE + lines.get(0)); - } - for (int i = 1; i < lines.size(); i++) { - final String pLine = lines.get(i-1); - final String subLine = lines.get(i); - - char color = pLine.charAt(pLine.lastIndexOf(Style.COLOR_CHAR) + 1); - if (subLine.isEmpty() || subLine.charAt(0) != Style.COLOR_CHAR) { - lines.set(i, Style.getByChar(color) + subLine); - } - } - - return lines.toArray(new String[lines.size()]); - } - - /** - * Callback for transforming a word, such as a URL. - * - * @param word the word - * @return the transformed value, or null to do nothing - */ - protected String transform(String word) { - return null; - } - - /** - * Convert the given styled fragment into color codes. - * - * @param fragment the fragment - * @return color codes - */ - public static String asColorCodes(StyledFragment fragment) { - return newLineJoiner.join(instance.build(fragment)); - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/Fragment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/Fragment.java deleted file mode 100644 index 94ba7f20a..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/Fragment.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.util.formatting; - -/** - * A fragment of text. - */ -public class Fragment { - - private final StringBuilder builder = new StringBuilder(); - - Fragment() { - } - - public Fragment append(String str) { - builder.append(Style.stripColor(str)); - return this; - } - - public Fragment append(Object obj) { - append(String.valueOf(obj)); - return this; - } - - public Fragment append(StringBuffer sb) { - append(String.valueOf(sb)); - return this; - } - - public Fragment append(CharSequence s) { - append(String.valueOf(s)); - return this; - } - - public Fragment append(boolean b) { - append(String.valueOf(b)); - return this; - } - - public Fragment append(char c) { - append(String.valueOf(c)); - return this; - } - - public Fragment append(int i) { - append(String.valueOf(i)); - return this; - } - - public Fragment append(long lng) { - append(String.valueOf(lng)); - return this; - } - - public Fragment append(float f) { - append(String.valueOf(f)); - return this; - } - - public Fragment append(double d) { - append(String.valueOf(d)); - return this; - } - - public Fragment newLine() { - append("\n"); - return this; - } - - @Override - public String toString() { - return builder.toString(); - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/Style.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/Style.java deleted file mode 100644 index d6c70eeb8..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/Style.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.util.formatting; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.collect.Maps; - -import java.util.Map; -import java.util.regex.Pattern; - -/** - * All supported color values for chat. - * - *

From Bukkit.

- */ -public enum Style { - /** - * Represents black - */ - BLACK('0', 0x00), - /** - * Represents dark blue - */ - BLUE_DARK('1', 0x1), - /** - * Represents dark green - */ - GREEN_DARK('2', 0x2), - /** - * Represents dark blue (aqua) - */ - CYAN_DARK('3', 0x3), - /** - * Represents dark red - */ - RED_DARK('4', 0x4), - /** - * Represents dark purple - */ - PURPLE_DARK('5', 0x5), - /** - * Represents gold - */ - YELLOW_DARK('6', 0x6), - /** - * Represents gray - */ - GRAY('7', 0x7), - /** - * Represents dark gray - */ - GRAY_DARK('8', 0x8), - /** - * Represents blue - */ - BLUE('9', 0x9), - /** - * Represents green - */ - GREEN('a', 0xA), - /** - * Represents aqua - */ - CYAN('b', 0xB), - /** - * Represents red - */ - RED('c', 0xC), - /** - * Represents light purple - */ - PURPLE('d', 0xD), - /** - * Represents yellow - */ - YELLOW('e', 0xE), - /** - * Represents white - */ - WHITE('f', 0xF), - /** - * Represents magical characters that change around randomly - */ - RANDOMIZE('k', 0x10, true), - /** - * Makes the text bold. - */ - BOLD('l', 0x11, true), - /** - * Makes a line appear through the text. - */ - STRIKETHROUGH('m', 0x12, true), - /** - * Makes the text appear underlined. - */ - UNDERLINE('n', 0x13, true), - /** - * Makes the text italic. - */ - ITALIC('o', 0x14, true), - /** - * Resets all previous chat colors or formats. - */ - RESET('r', 0x15); - - /** - * The special character which prefixes all chat color codes. Use this if you need to dynamically - * convert color codes from your custom format. - */ - public static final char COLOR_CHAR = '\u00A7'; - private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + COLOR_CHAR + "[0-9A-FK-OR]"); - - private final int intCode; - private final char code; - private final boolean isFormat; - private final String toString; - private final static Map BY_ID = Maps.newHashMap(); - private final static Map BY_CHAR = Maps.newHashMap(); - - Style(char code, int intCode) { - this(code, intCode, false); - } - - Style(char code, int intCode, boolean isFormat) { - this.code = code; - this.intCode = intCode; - this.isFormat = isFormat; - this.toString = new String(new char[] {COLOR_CHAR, code}); - } - - /** - * Gets the char value associated with this color - * - * @return A char value of this color code - */ - public char getChar() { - return code; - } - - @Override - public String toString() { - return toString; - } - - /** - * Checks if this code is a format code as opposed to a color code. - * - * @return the if the code is a formatting code - */ - public boolean isFormat() { - return isFormat; - } - - /** - * Checks if this code is a color code as opposed to a format code. - * - * @return the if the code is a color - */ - public boolean isColor() { - return !isFormat && this != RESET; - } - - /** - * Gets the color represented by the specified color code - * - * @param code Code to check - * @return Associative Style with the given code, or null if it doesn't exist - */ - public static Style getByChar(char code) { - return BY_CHAR.get(code); - } - - /** - * Gets the color represented by the specified color code - * - * @param code Code to check - * @return Associative Style with the given code, or null if it doesn't exist - */ - public static Style getByChar(String code) { - checkNotNull(code); - checkArgument(!code.isEmpty(), "Code must have at least one character"); - - return BY_CHAR.get(code.charAt(0)); - } - - /** - * Strips the given message of all color codes - * - * @param input String to strip of color - * @return A copy of the input string, without any coloring - */ - public static String stripColor(final String input) { - if (input == null) { - return null; - } - - return STRIP_COLOR_PATTERN.matcher(input).replaceAll(""); - } - - /** - * Translates a string using an alternate color code character into a string that uses the internal - * ChatColor.COLOR_CODE color code character. The alternate color code character will only be replaced - * if it is immediately followed by 0-9, A-F, a-f, K-O, k-o, R or r. - * - * @param altColorChar The alternate color code character to replace. Ex: & - * @param textToTranslate Text containing the alternate color code character. - * @return Text containing the ChatColor.COLOR_CODE color code character. - */ - public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { - char[] b = textToTranslate.toCharArray(); - for (int i = 0; i < b.length - 1; i++) { - if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i+1]) > -1) { - b[i] = Style.COLOR_CHAR; - b[i+1] = Character.toLowerCase(b[i+1]); - } - } - return new String(b); - } - - /** - * Gets the ChatColors used at the end of the given input string. - * - * @param input Input string to retrieve the colors from. - * @return Any remaining ChatColors to pass onto the next line. - */ - public static String getLastColors(String input) { - String result = ""; - int length = input.length(); - - // Search backwards from the end as it is faster - for (int index = length - 1; index > -1; index--) { - char section = input.charAt(index); - if (section == COLOR_CHAR && index < length - 1) { - char c = input.charAt(index + 1); - Style color = getByChar(c); - - if (color != null) { - result = color + result; - - // Once we find a color or reset we can stop searching - if (color.isColor() || color == RESET) { - break; - } - } - } - } - - return result; - } - - static { - for (Style color : values()) { - BY_ID.put(color.intCode, color); - BY_CHAR.put(color.code, color); - } - } -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/StyleSet.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/StyleSet.java deleted file mode 100644 index 408dc1e43..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/StyleSet.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.util.formatting; - -/** - * Represents set of styles, such as color, bold, etc. - */ -public class StyleSet { - - private Boolean bold; - private Boolean italic; - private Boolean underline; - private Boolean strikethrough; - private Style color; - - /** - * Create a new style set with no properties set. - */ - public StyleSet() { - } - - /** - * Create a new style set with the given styles. - * - *

{@link Style#RESET} will be ignored if provided.

- * - * @param styles a list of styles - */ - public StyleSet(Style... styles) { - for (Style style : styles) { - if (style.isColor()) { - color = style; - } else if (style == Style.BOLD) { - bold = true; - } else if (style == Style.ITALIC) { - italic = true; - } else if (style == Style.UNDERLINE) { - underline = true; - } else if (style == Style.STRIKETHROUGH) { - strikethrough = true; - } - } - } - - /** - * Get whether this style set is bold. - * - * @return true, false, or null if unset - */ - public Boolean getBold() { - return bold; - } - - /** - * Get whether the text is bold. - * - * @return true if bold - */ - public boolean isBold() { - return getBold() != null && getBold(); - } - - /** - * Set whether the text is bold. - * - * @param bold true, false, or null to unset - */ - public void setBold(Boolean bold) { - this.bold = bold; - } - - /** - * Get whether this style set is italicized. - * - * @return true, false, or null if unset - */ - public Boolean getItalic() { - return italic; - } - - /** - * Get whether the text is italicized. - * - * @return true if italicized - */ - public boolean isItalic() { - return getItalic() != null && getItalic(); - } - - /** - * Set whether the text is italicized. - * - * @param italic false, or null to unset - */ - public void setItalic(Boolean italic) { - this.italic = italic; - } - - /** - * Get whether this style set is underlined. - * - * @return true, false, or null if unset - */ - public Boolean getUnderline() { - return underline; - } - - /** - * Get whether the text is underlined. - * - * @return true if underlined - */ - public boolean isUnderline() { - return getUnderline() != null && getUnderline(); - } - - /** - * Set whether the text is underline. - * - * @param underline false, or null to unset - */ - public void setUnderline(Boolean underline) { - this.underline = underline; - } - - /** - * Get whether this style set is stricken through. - * - * @return true, false, or null if unset - */ - public Boolean getStrikethrough() { - return strikethrough; - } - - /** - * Get whether the text is stricken through. - * - * @return true if there is strikethrough applied - */ - public boolean isStrikethrough() { - return getStrikethrough() != null && getStrikethrough(); - } - - /** - * Set whether the text is stricken through. - * - * @param strikethrough false, or null to unset - */ - public void setStrikethrough(Boolean strikethrough) { - this.strikethrough = strikethrough; - } - - /** - * Get the color of the text. - * - * @return true, false, or null if unset - */ - public Style getColor() { - return color; - } - - /** - * Set the color of the text. - * - * @param color the color - */ - public void setColor(Style color) { - this.color = color; - } - - /** - * Return whether text formatting (bold, italics, underline, strikethrough) is set. - * - * @return true if formatting is set - */ - public boolean hasFormatting() { - return getBold() != null || getItalic() != null - || getUnderline() != null || getStrikethrough() != null; - } - - /** - * Return where the text formatting of the given style set is different from - * that assigned to this one. - * - * @param other the other style set - * @return true if there is a difference - */ - public boolean hasEqualFormatting(StyleSet other) { - return getBold() == other.getBold() && getItalic() == other.getItalic() - && getUnderline() == other.getUnderline() && - getStrikethrough() == other.getStrikethrough(); - } - - /** - * Create a new instance with styles inherited from this one but with new styles - * from the given style set. - * - * @param style the style set - * @return a new style set instance - */ - public StyleSet extend(StyleSet style) { - StyleSet newStyle = clone(); - if (style.getBold() != null) { - newStyle.setBold(style.getBold()); - } - if (style.getItalic() != null) { - newStyle.setItalic(style.getItalic()); - } - if (style.getUnderline() != null) { - newStyle.setUnderline(style.getUnderline()); - } - if (style.getStrikethrough() != null) { - newStyle.setStrikethrough(style.getStrikethrough()); - } - if (style.getColor() != null) { - newStyle.setColor(style.getColor()); - } - return newStyle; - } - - @Override - public StyleSet clone() { - StyleSet style = new StyleSet(); - style.setBold(getBold()); - style.setItalic(getItalic()); - style.setUnderline(getUnderline()); - style.setStrikethrough(getStrikethrough()); - style.setColor(getColor()); - return style; - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/StyledFragment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/StyledFragment.java deleted file mode 100644 index 9ef38446b..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/StyledFragment.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.util.formatting; - -import java.util.ArrayList; -import java.util.List; - -/** - * A fragment of text that can be styled. - */ -public class StyledFragment extends Fragment { - - private final List children = new ArrayList<>(); - private StyleSet style; - private Fragment lastText; - - public StyledFragment() { - style = new StyleSet(); - } - - public StyledFragment(StyleSet style) { - this.style = style; - } - - public StyledFragment(Style... styles) { - this.style = new StyleSet(styles); - } - - public StyleSet getStyle() { - return style; - } - - public void setStyles(StyleSet style) { - this.style = style; - } - - public List getChildren() { - return children; - } - - protected Fragment lastText() { - Fragment text; - if (!children.isEmpty()) { - text = children.get(children.size() - 1); - if (text == lastText) { - return text; - } - } - - text = new Fragment(); - this.lastText = text; - children.add(text); - return text; - } - - public StyledFragment createFragment(Style... styles) { - StyledFragment fragment = new StyledFragment(styles); - append(fragment); - return fragment; - } - - public StyledFragment append(StyledFragment fragment) { - children.add(fragment); - return this; - } - - @Override - public StyledFragment append(String str) { - lastText().append(str); - return this; - } - - @Override - public StyledFragment append(Object obj) { - append(String.valueOf(obj)); - return this; - } - - @Override - public StyledFragment append(StringBuffer sb) { - append(String.valueOf(sb)); - return this; - } - - @Override - public StyledFragment append(CharSequence s) { - append(String.valueOf(s)); - return this; - } - - @Override - public StyledFragment append(boolean b) { - append(String.valueOf(b)); - return this; - } - - @Override - public StyledFragment append(char c) { - append(String.valueOf(c)); - return this; - } - - @Override - public StyledFragment append(int i) { - append(String.valueOf(i)); - return this; - } - - @Override - public StyledFragment append(long lng) { - append(String.valueOf(lng)); - return this; - } - - @Override - public StyledFragment append(float f) { - append(String.valueOf(f)); - return this; - } - - @Override - public StyledFragment append(double d) { - append(String.valueOf(d)); - return this; - } - - @Override - public StyledFragment newLine() { - append("\n"); - return this; - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Code.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Annotations.java similarity index 69% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Code.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Annotations.java index 953d83fe3..f1f1319e9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Code.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Annotations.java @@ -17,21 +17,22 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util.formatting.component; +package com.sk89q.worldedit.extension.platform; -import com.sk89q.worldedit.util.formatting.Style; -import com.sk89q.worldedit.util.formatting.StyledFragment; +import com.google.auto.value.AutoAnnotation; +import com.sk89q.worldedit.internal.annotation.Radii; /** - * Represents a fragment representing a command that is to be typed. + * Holder for generated annotation classes. */ -public class Code extends StyledFragment { +class Annotations { - /** - * Create a new instance. - */ - public Code() { - super(Style.CYAN); + @AutoAnnotation + static Radii radii(int value) { + return new AutoAnnotation_Annotations_radii(value); + } + + private Annotations() { } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/BlockDistributionResult.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/BlockDistributionResult.java new file mode 100644 index 000000000..797557a52 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/BlockDistributionResult.java @@ -0,0 +1,90 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.google.common.base.Strings; +import com.sk89q.worldedit.util.Countable; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockType; + +import java.util.List; + +public class BlockDistributionResult extends PaginationBox { + + private final List> distribution; + private final int totalBlocks; + private final boolean separateStates; + + public BlockDistributionResult(List> distribution, boolean separateStates) { + super("Block Distribution", "//distr -p %page%" + (separateStates ? " -d" : "")); + this.distribution = distribution; + // note: doing things like region.getArea is inaccurate for non-cuboids. + this.totalBlocks = distribution.stream().mapToInt(Countable::getAmount).sum(); + this.separateStates = separateStates; + setComponentsPerPage(7); + } + + @Override + public Component getComponent(int number) { + Countable c = distribution.get(number); + TextComponent.Builder line = TextComponent.builder(); + + final int count = c.getAmount(); + + final double perc = count / (double) totalBlocks * 100; + final int maxDigits = (int) (Math.log10(totalBlocks) + 1); + final int curDigits = (int) (Math.log10(count) + 1); + line.append(String.format("%s%.3f%% ", perc < 10 ? " " : "", perc), TextColor.GOLD); + final int space = maxDigits - curDigits; + String pad = Strings.repeat(" ", space == 0 ? 2 : 2 * space + 1); + line.append(String.format("%s%s", count, pad), TextColor.YELLOW); + + final BlockState state = c.getID(); + final BlockType blockType = state.getBlockType(); + TextComponent blockName = TextComponent.of(blockType.getName(), TextColor.LIGHT_PURPLE); + TextComponent toolTip; + if (separateStates && state != blockType.getDefaultState()) { + toolTip = TextComponent.of(state.getAsString(), TextColor.GRAY); + blockName = blockName.append(TextComponent.of("*", TextColor.LIGHT_PURPLE)); + } else { + toolTip = TextComponent.of(blockType.getId(), TextColor.GRAY); + } + blockName = blockName.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, toolTip)); + line.append(blockName); + + return line.build(); + } + + @Override + public int getComponentsSize() { + return distribution.size(); + } + + @Override + public Component create(int page) throws InvalidComponentException { + super.getContents().append(TextComponent.of("Total Block Count: " + totalBlocks, TextColor.GRAY)) + .append(TextComponent.newline()); + return super.create(page); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CodeFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CodeFormat.java new file mode 100644 index 000000000..a92590bac --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CodeFormat.java @@ -0,0 +1,48 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +/** + * Represents a fragment representing a command that is to be typed. + */ +public class CodeFormat extends TextComponentProducer { + + private CodeFormat() { + getBuilder().content("").color(TextColor.AQUA); + } + + /** + * Creates a CodeFormat with the given message. + * + * @param texts The text + * @return The Component + */ + public static TextComponent wrap(String ... texts) { + CodeFormat code = new CodeFormat(); + for (String text: texts) { + code.append(text); + } + + return code.create(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandListBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandListBox.java index 9df10e637..fd579c6fb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandListBox.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandListBox.java @@ -19,32 +19,86 @@ package com.sk89q.worldedit.util.formatting.component; -import com.boydti.fawe.config.BBC; +import com.google.common.collect.Lists; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; -public class CommandListBox extends MessageBox { +import java.util.List; - private boolean first = true; +public class CommandListBox extends PaginationBox { + + private List commands = Lists.newArrayList(); + private boolean hideHelp; /** * Create a new box. * * @param title the title */ - public CommandListBox(String title) { - super(title); + public CommandListBox(String title, String pageCommand) { + super(title, pageCommand); } - public CommandListBox appendCommand(String alias, String description) { - return appendCommand(alias, description, true); + @Override + public Component getComponent(int number) { + return commands.get(number).createComponent(hideHelp); } - CommandListBox appendCommand(String alias, String description, boolean allowed) { - if (!first) { - getContents().newLine(); + @Override + public int getComponentsSize() { + return commands.size(); + } + + public void appendCommand(String alias, Component description) { + appendCommand(alias, description, null); + } + + public void appendCommand(String alias, String description, String insertion) { + appendCommand(alias, TextComponent.of(description), insertion); + } + + public void appendCommand(String alias, Component description, String insertion) { + commands.add(new CommandEntry(alias, description, insertion)); + } + + public boolean isHidingHelp() { + return hideHelp; + } + + public void setHidingHelp(boolean hideHelp) { + this.hideHelp = hideHelp; + } + + private static class CommandEntry { + private final String alias; + private final Component description; + private final String insertion; + + CommandEntry(String alias, Component description, String insertion) { + this.alias = alias; + this.description = description; + this.insertion = insertion; } - getContents().append((allowed ? BBC.HELP_ITEM_ALLOWED : BBC.HELP_ITEM_DENIED).format(alias, description)); - first = false; - return this; - } + Component createComponent(boolean hideHelp) { + TextComponentProducer line = new TextComponentProducer(); + if (!hideHelp) { + line.append(SubtleFormat.wrap("? ") + .clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "//help " + insertion)) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Additional Help")))); + } + TextComponent command = TextComponent.of(alias, TextColor.GOLD); + if (insertion == null) { + line.append(command); + } else { + line.append(command + .clickEvent(ClickEvent.of(ClickEvent.Action.SUGGEST_COMMAND, insertion)) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to select")))); + } + return line.append(TextComponent.of(": ")).append(description).create(); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java index 7f350edf4..e8abe0e38 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java @@ -19,90 +19,69 @@ package com.sk89q.worldedit.util.formatting.component; -import com.boydti.fawe.config.BBC; - -import static com.google.common.base.Preconditions.checkNotNull; -import com.sk89q.minecraft.util.commands.CommandLocals; -import com.sk89q.worldedit.extension.platform.CommandManager; -import com.sk89q.worldedit.util.command.CommandCallable; -import com.sk89q.worldedit.util.command.CommandMapping; -import com.sk89q.worldedit.util.command.Description; -import com.sk89q.worldedit.util.command.Dispatcher; -import com.sk89q.worldedit.util.command.PrimaryAliasComparator; -import com.sk89q.worldedit.util.formatting.StyledFragment; +import com.google.common.collect.Iterables; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextDecoration; +import org.enginehub.piston.ColorConfig; +import org.enginehub.piston.Command; +import org.enginehub.piston.CommandParameters; +import org.enginehub.piston.util.HelpGenerator; import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.List; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.internal.command.CommandUtil.getSubCommands; + /** * A box to describe usage of a command. */ -public class CommandUsageBox extends StyledFragment { +public class CommandUsageBox extends TextComponentProducer { /** * Create a new usage box. * - * @param command the command to describe - * @param commandString the command that was used, such as "/we" or "/brush sphere" + * @param commands the commands to describe + * @param commandString the commands that were used, such as "/we" or "/brush sphere" */ - public CommandUsageBox(CommandCallable command, String commandString) { - this(command, commandString, null); + public CommandUsageBox(List commands, String commandString) throws InvalidComponentException { + this(commands, commandString, null); } /** * Create a new usage box. * - * @param command the command to describe - * @param commandString the command that was used, such as "/we" or "/brush sphere" - * @param locals list of locals to use + * @param commands the commands to describe + * @param commandString the commands that were used, such as "/we" or "/brush sphere" + * @param parameters list of parameters to use */ - public CommandUsageBox(CommandCallable command, String commandString, @Nullable CommandLocals locals) { - checkNotNull(command); + public CommandUsageBox(List commands, String commandString, @Nullable CommandParameters parameters) throws InvalidComponentException { + checkNotNull(commands); checkNotNull(commandString); - if (command instanceof Dispatcher) { - attachDispatcherUsage((Dispatcher) command, commandString, locals); - } else { - attachCommandUsage(command.getDescription(), commandString); - } + attachCommandUsage(commands, commandString); } - private void attachDispatcherUsage(Dispatcher dispatcher, String commandString, @Nullable CommandLocals locals) { - CommandListBox box = new CommandListBox(BBC.HELP_HEADER_SUBCOMMANDS.f()); - String prefix = !commandString.isEmpty() ? commandString + " " : ""; - - List list = new ArrayList<>(dispatcher.getCommands()); - list.sort(new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN)); - - for (CommandMapping mapping : list) { - boolean perm = locals == null || mapping.getCallable().testPermission(locals); - box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getDescription(), perm); + private void attachCommandUsage(List commands, String commandString) { + TextComponentProducer boxContent = new TextComponentProducer() + .append(HelpGenerator.create(commands).getFullHelp()); + if (getSubCommands(Iterables.getLast(commands)).size() > 0) { + boxContent.append(TextComponent.newline()) + .append(TextComponent.builder("> ") + .color(ColorConfig.getHelpText()) + .append(TextComponent.builder("List Subcommands") + .color(ColorConfig.getMainText()) + .decoration(TextDecoration.ITALIC, true) + .clickEvent(ClickEvent.runCommand("//help -s " + commandString)) + .hoverEvent(HoverEvent.showText(TextComponent.of("List all subcommands of this command"))) + .build()) + .build()); } + MessageBox box = new MessageBox("Help for " + commandString, + boxContent); - append(box); - } - - private void attachCommandUsage(Description description, String commandString) { - MessageBox box = new MessageBox(BBC.HELP_HEADER_COMMAND.f(commandString)); - StyledFragment contents = box.getContents(); - - if (description.getUsage() != null) { - contents.append(new Label().append(BBC.COMMAND_SYNTAX.f(description.getUsage()))); - } else { - contents.append(new Subtle().append("Usage information is not available.")); - } - - contents.newLine(); - - if (description.getHelp() != null) { - contents.append(description.getHelp()); - } else if (description.getDescription() != null) { - contents.append(description.getDescription()); - } else { - contents.append(new Subtle().append("No further help is available.")); - } - - append(box); + append(box.create()); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/ErrorFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/ErrorFormat.java new file mode 100644 index 000000000..35343ca56 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/ErrorFormat.java @@ -0,0 +1,51 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +/** + * Represents a fragment representing an error. + */ +public class ErrorFormat extends TextComponentProducer { + + /** + * Create a new instance. + */ + private ErrorFormat() { + getBuilder().content("").color(TextColor.RED); + } + + /** + * Creates an ErrorFormat with the given message. + * + * @param texts The text + * @return The Component + */ + public static TextComponent wrap(String ... texts) { + ErrorFormat error = new ErrorFormat(); + for (String component : texts) { + error.append(component); + } + + return error.create(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Subtle.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/InvalidComponentException.java similarity index 74% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Subtle.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/InvalidComponentException.java index 34316c087..bcdfb7f10 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/Subtle.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/InvalidComponentException.java @@ -19,19 +19,11 @@ package com.sk89q.worldedit.util.formatting.component; -import com.sk89q.worldedit.util.formatting.Style; -import com.sk89q.worldedit.util.formatting.StyledFragment; +import com.sk89q.worldedit.WorldEditException; -/** - * Represents a subtle part of the message. - */ -public class Subtle extends StyledFragment { +public class InvalidComponentException extends WorldEditException { - /** - * Create a new instance. - */ - public Subtle() { - super(Style.GRAY); + public InvalidComponentException(String message) { + super(message); } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/LabelFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/LabelFormat.java new file mode 100644 index 000000000..f0a487114 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/LabelFormat.java @@ -0,0 +1,51 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +/** + * Represents a fragment representing a label. + */ +public class LabelFormat extends TextComponentProducer { + + /** + * Create a new instance. + */ + private LabelFormat() { + getBuilder().content("").color(TextColor.YELLOW); + } + + /** + * Creates a LabelFormat with the given message. + * + * @param texts The text + * @return The Component + */ + public static TextComponent wrap(String ... texts) { + LabelFormat label = new LabelFormat(); + for (String component : texts) { + label.append(component); + } + + return label.create(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java index dd8694590..e5fceb008 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java @@ -19,43 +19,76 @@ package com.sk89q.worldedit.util.formatting.component; -import com.sk89q.worldedit.util.formatting.StyledFragment; - - import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Strings; +import com.google.common.collect.Sets; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; +import com.sk89q.worldedit.util.formatting.text.format.TextDecoration; + /** * Makes for a box with a border above and below. */ -public class MessageBox extends StyledFragment { +public class MessageBox extends TextComponentProducer { - private final StyledFragment contents = new StyledFragment(); + private static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 47; + + private TextComponentProducer contents; /** * Create a new box. */ - public MessageBox(String title) { + public MessageBox(String title, TextComponentProducer contents) { checkNotNull(title); - append(title); - newLine(); - append(contents); + + append(centerAndBorder(TextComponent.of(title))).newline(); + this.contents = contents; } - private String createBorder(int count) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < count; i++) { - builder.append("-"); + protected Component centerAndBorder(TextComponent text) { + TextComponentProducer line = new TextComponentProducer(); + int leftOver = GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH - getLength(text); + int side = (int) Math.floor(leftOver / 2.0); + if (side > 0) { + if (side > 1) { + line.append(createBorder(side - 1)); + } + line.append(TextComponent.space()); } - return builder.toString(); + line.append(text); + if (side > 0) { + line.append(TextComponent.space()); + if (side > 1) { + line.append(createBorder(side - 1)); + } + } + return line.create(); + } + + private static int getLength(TextComponent text) { + return text.content().length() + text.children().stream().filter(c -> c instanceof TextComponent) + .mapToInt(c -> getLength((TextComponent) c)).sum(); + } + + private TextComponent createBorder(int count) { + return TextComponent.of(Strings.repeat("-", count), + TextColor.YELLOW, Sets.newHashSet(TextDecoration.STRIKETHROUGH)); } /** - * Get the internal contents. + * Gets the message box contents producer. * - * @return the contents + * @return The contents producer */ - public StyledFragment getContents() { + public TextComponentProducer getContents() { return contents; } + @Override + public TextComponent create() { + append(contents.create()); + return super.create(); + } } 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 new file mode 100644 index 000000000..3f9b95267 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java @@ -0,0 +1,153 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +import javax.annotation.Nullable; +import java.util.List; + +public abstract class PaginationBox extends MessageBox { + + private static final int IDEAL_ROWS_FOR_PLAYER = 8; + + private String pageCommand; + private int componentsPerPage = IDEAL_ROWS_FOR_PLAYER; + private int currentPage = -1; + + /** + * Creates a Paginated component + * + * @param title The title + */ + protected PaginationBox(String title) { + this(title, null); + } + + public abstract Component getComponent(int number); + + public abstract int getComponentsSize(); + + public void setComponentsPerPage(int componentsPerPage) { + this.componentsPerPage = componentsPerPage; + } + + public void formatForConsole() { + this.pageCommand = null; + this.componentsPerPage = 20; + } + + protected final int getCurrentPage() { + return currentPage; + } + + /** + * Creates a Paginated component + * + * @param title The title + * @param pageCommand The command to run to switch page, with %page% representing page number + */ + protected PaginationBox(String title, @Nullable String pageCommand) { + super(title, new TextComponentProducer()); + + if (pageCommand != null && !pageCommand.contains("%page%")) { + throw new IllegalArgumentException("pageCommand must contain %page% if provided."); + } + this.pageCommand = pageCommand; + } + + public Component create(int page) throws InvalidComponentException { + if (page == 1 && getComponentsSize() == 0) { + return getContents().reset().append("No results found.").create(); + } + int pageCount = (int) Math.ceil(getComponentsSize() / (double) componentsPerPage); + if (page < 1 || page > pageCount) { + throw new InvalidComponentException("Invalid page number."); + } + currentPage = page; + final int lastComp = Math.min(page * componentsPerPage, getComponentsSize()); + for (int i = (page - 1) * componentsPerPage; i < lastComp; i++) { + getContents().append(getComponent(i)); + if (i + 1 != lastComp) { + getContents().newline(); + } + } + if (pageCount == 1) { + return super.create(); + } + getContents().newline(); + TextComponent pageNumberComponent = TextComponent.of("Page ", TextColor.YELLOW) + .append(TextComponent.of(String.valueOf(page), TextColor.GOLD)) + .append(TextComponent.of(" of ")) + .append(TextComponent.of(String.valueOf(pageCount), TextColor.GOLD)); + if (pageCommand != null) { + TextComponentProducer navProducer = new TextComponentProducer(); + if (page > 1) { + TextComponent prevComponent = TextComponent.of("<<< ", TextColor.GOLD) + .clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND, pageCommand.replace("%page%", String.valueOf(page - 1)))) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to navigate"))); + navProducer.append(prevComponent); + } + navProducer.append(pageNumberComponent); + if (page < pageCount) { + TextComponent nextComponent = TextComponent.of(" >>>", TextColor.GOLD) + .clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND, pageCommand.replace("%page%", String.valueOf(page + 1)))) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to navigate"))); + navProducer.append(nextComponent); + } + getContents().append(centerAndBorder(navProducer.create())); + } else { + getContents().append(centerAndBorder(pageNumberComponent)); + } + return super.create(); + } + + @Override + public TextComponent create() { + throw new IllegalStateException("Pagination components must be created with a page"); + } + + public static PaginationBox fromStrings(String header, @Nullable String pageCommand, List lines) { + return new ListPaginationBox(header, pageCommand, lines); + } + + private static class ListPaginationBox extends PaginationBox { + private final List lines; + + ListPaginationBox(String header, String pageCommand, List lines) { + super(header, pageCommand); + this.lines = lines; + } + + @Override + public Component getComponent(int number) { + return TextComponent.of(lines.get(number)); + } + + @Override + public int getComponentsSize() { + return lines.size(); + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SchematicPaginationBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SchematicPaginationBox.java new file mode 100644 index 000000000..14d6de67f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SchematicPaginationBox.java @@ -0,0 +1,74 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import static com.google.common.base.Preconditions.checkArgument; +import com.google.common.collect.Multimap; +import com.google.common.io.Files; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +import java.io.File; +import java.util.regex.Pattern; + +public class SchematicPaginationBox extends PaginationBox { + private final String prefix; + private final File[] files; + + public SchematicPaginationBox(String rootDir, File[] files, String pageCommand) { + super("Available schematics", pageCommand); + this.prefix = rootDir == null ? "" : rootDir; + this.files = files; + } + + @Override + public Component getComponent(int number) { + checkArgument(number < files.length && number >= 0); + File file = files[number]; + Multimap exts = ClipboardFormats.getFileExtensionMap(); + String format = exts.get(Files.getFileExtension(file.getName())) + .stream().findFirst().map(ClipboardFormat::getName).orElse("Unknown"); + boolean inRoot = file.getParentFile().getName().equals(prefix); + + String path = inRoot ? file.getName() : file.getPath().split(Pattern.quote(prefix + File.separator))[1]; + + return TextComponent.builder() + .content("") + .append(TextComponent.of("[L]") + .color(TextColor.GOLD) + .clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "/schem load " + path)) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to load")))) + .append(TextComponent.space()) + .append(TextComponent.of(path) + .color(TextColor.DARK_GREEN) + .hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of(format)))) + .build(); + } + + @Override + public int getComponentsSize() { + return files.length; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SubtleFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SubtleFormat.java new file mode 100644 index 000000000..310cc4e01 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/SubtleFormat.java @@ -0,0 +1,51 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +/** + * Represents a subtle part of the message. + */ +public class SubtleFormat extends TextComponentProducer { + + /** + * Create a new instance. + */ + private SubtleFormat() { + getBuilder().content("").color(TextColor.GRAY); + } + + /** + * Creates a SubtleFormat with the given message. + * + * @param texts The text + * @return The Component + */ + public static TextComponent wrap(String ... texts) { + SubtleFormat subtle = new SubtleFormat(); + for (String component : texts) { + subtle.append(component); + } + + return subtle.create(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/TextComponentProducer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/TextComponentProducer.java new file mode 100644 index 000000000..b22b44f08 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/TextComponentProducer.java @@ -0,0 +1,83 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; + +public class TextComponentProducer { + + private TextComponent.Builder builder = TextComponent.builder().content(""); + + public TextComponent.Builder getBuilder() { + return builder; + } + + /** + * Adds a component as a child to this Producer. + * + * @param component The component + * @return The producer, for chaining + */ + public TextComponentProducer append(Component component) { + getBuilder().append(component); + return this; + } + + /** + * Adds a string as a child to this Producer. + * + * @param string The text + * @return The producer, for chaining + */ + public TextComponentProducer append(String string) { + getBuilder().append(TextComponent.of(string)); + return this; + } + + /** + * Adds a newline as a child to this Producer. + * + * @return The producer, for chaining + */ + public TextComponentProducer newline() { + getBuilder().append(TextComponent.newline()); + return this; + } + + /** + * Create a TextComponent from this producer. + * + * @return The component + */ + public TextComponent create() { + return builder.build(); + } + + /** + * Reset the producer to a clean slate. + * + * @return The producer, for chaining + */ + public TextComponentProducer reset() { + builder = TextComponent.builder().content(""); + return this; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/io/ResourceLoader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/io/ResourceLoader.java new file mode 100644 index 000000000..09ac4724f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/io/ResourceLoader.java @@ -0,0 +1,43 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.io; + +import java.io.IOException; +import java.net.URL; + +public class ResourceLoader { + + private ResourceLoader() { + } + + public static URL getResource(Class clazz, String name) throws IOException { + URL url = clazz.getResource(name); + if (url == null) { + try { + return new URL("modjar://worldedit/" + clazz.getName().substring(0, clazz.getName().lastIndexOf('.')).replace(".", "/") + "/" + + name); + } catch (Exception e) { + // Not forge. + } + throw new IOException("Could not find " + name); + } + return url; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java index e95a45bf9..74d4d31b0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java @@ -19,21 +19,16 @@ package com.sk89q.worldedit.util.paste; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.worldedit.command.util.AsyncCommandHelper; +import com.sk89q.worldedit.command.util.AsyncCommandBuilder; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.command.parametric.ExceptionConverter; import com.sk89q.worldedit.util.task.Supervisor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.net.URL; +import java.util.concurrent.Callable; -public class ActorCallbackPaste { +public final class ActorCallbackPaste { - private static final Logger LOGGER = LoggerFactory.getLogger(ActorCallbackPaste.class); + private static final Paster paster = new IncendoPaste("fastasyncworldedit"); private ActorCallbackPaste() { } @@ -47,25 +42,15 @@ public class ActorCallbackPaste { * @param content The content * @param successMessage The message, formatted with {@link String#format(String, Object...)} on success */ - public static void pastebin(Supervisor supervisor, final Actor sender, String content, final String successMessage, final ExceptionConverter exceptionConverter) { - ListenableFuture future = new IncendoPaste("fastasyncworldedit").paste(content); + public static void pastebin(Supervisor supervisor, final Actor sender, String content, final String successMessage) { + Callable task = paster.paste(content); - AsyncCommandHelper.wrap(future, supervisor, sender, exceptionConverter) - .registerWithSupervisor("Submitting content to a pastebin service...") - .sendMessageAfterDelay("(Please wait... sending output to pastebin...)"); - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(URL url) { - sender.print(String.format(successMessage, url)); - } - - @Override - public void onFailure(Throwable throwable) { - LOGGER.warn("Failed to submit pastebin", throwable); - sender.printError("Failed to submit to a pastebin. Please see console for the error."); - } - }); + AsyncCommandBuilder.wrap(task, sender) + .registerWithSupervisor(supervisor, "Submitting content to a pastebin service.") + .sendMessageAfterDelay("(Please wait... sending output to pastebin...)") + .onSuccess((String) null, url -> sender.print(String.format(successMessage, url))) + .onFailure("Failed to submit paste", null) + .buildAndExec(Pasters.getExecutor()); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java index 915e6bb97..ac7484846 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.util.paste; -import com.google.common.util.concurrent.ListenableFuture; import com.sk89q.worldedit.util.net.HttpRequest; import org.json.simple.JSONValue; @@ -35,8 +34,8 @@ public class EngineHubPaste implements Paster { private static final Pattern URL_PATTERN = Pattern.compile("https?://.+$"); @Override - public ListenableFuture paste(String content) { - return Pasters.getExecutor().submit(new PasteTask(content)); + public Callable paste(String content) { + return new PasteTask(content); } private static final class PasteTask implements Callable { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/IncendoPaste.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/IncendoPaste.java index 38c460764..c81c3417c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/IncendoPaste.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/IncendoPaste.java @@ -2,7 +2,6 @@ package com.sk89q.worldedit.util.paste; import com.boydti.fawe.Fawe; import com.google.common.base.Charsets; -import com.google.common.util.concurrent.ListenableFuture; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -13,6 +12,7 @@ import java.net.URLConnection; import java.nio.file.Files; import java.util.*; import java.util.concurrent.Callable; +import java.util.stream.Collectors; /** * Single class paster for the Incendo paste service @@ -53,19 +53,19 @@ public final class IncendoPaste implements Paster{ } @Override - public ListenableFuture paste(String content) { - return Pasters.getExecutor().submit(new PasteTask(content)); + public Callable paste(String content) { + return new PasteTask(content); } - + private final class PasteTask implements Callable{ private PasteTask(String content) {} - + @Override public URL call() throws Exception { return new URL(debugPaste()); } - + } /** @@ -150,14 +150,11 @@ public final class IncendoPaste implements Paster{ throw new IllegalStateException(String.format("Server returned status: %d %s", httpURLConnection.getResponseCode(), httpURLConnection.getResponseMessage())); } - final StringBuilder input = new StringBuilder(); + final String input; try (final BufferedReader inputStream = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()))) { - String line; - while ((line = inputStream.readLine()) != null) { - input.append(line).append("\n"); - } + input = inputStream.lines().map(line -> line + "\n").collect(Collectors.joining()); } - return input.toString(); + return input; } /** @@ -265,12 +262,9 @@ public final class IncendoPaste implements Paster{ private static String readFile(final File file) throws IOException { final StringBuilder content = new StringBuilder(); - final List lines = new ArrayList<>(); + final List lines; try (final BufferedReader reader = new BufferedReader(new FileReader(file))) { - String line; - while ((line = reader.readLine()) != null) { - lines.add(line); - } + lines = reader.lines().collect(Collectors.toList()); } for (int i = Math.max(0, lines.size() - 1000); i < lines.size(); i++) { content.append(lines.get(i)).append("\n"); @@ -278,4 +272,4 @@ public final class IncendoPaste implements Paster{ return content.toString(); } -} \ No newline at end of file +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.java index dcbef09b0..5e01dee84 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.util.paste; -import com.google.common.util.concurrent.ListenableFuture; import com.sk89q.worldedit.util.net.HttpRequest; import java.io.IOException; @@ -43,12 +42,12 @@ public class Pastebin implements Paster { } @Override - public ListenableFuture paste(String content) { + public Callable paste(String content) { if (mungingLinks) { content = content.replaceAll("http://", "http_//"); } - return Pasters.getExecutor().submit(new PasteTask(content)); + return new PasteTask(content); } private final class PasteTask implements Callable { @@ -89,5 +88,5 @@ public class Pastebin implements Paster { } } } - + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Paster.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Paster.java index 7a7d74cac..a65ccd6c1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Paster.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Paster.java @@ -19,12 +19,11 @@ package com.sk89q.worldedit.util.paste; -import com.google.common.util.concurrent.ListenableFuture; - import java.net.URL; +import java.util.concurrent.Callable; public interface Paster { - ListenableFuture paste(String content); + Callable paste(String content); } 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 271c356ed..2a915b59f 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 @@ -36,6 +36,7 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import javax.annotation.Nullable; +import java.nio.file.Path; import java.util.PriorityQueue; /** @@ -56,6 +57,11 @@ public abstract class AbstractWorld implements World { return setBlock(pt, block, true); } + @Override + public Path getStoragePath() { + return null; + } + @Override public int getMaxY() { return getMaximumPoint().getBlockY(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/DataFixer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/DataFixer.java new file mode 100644 index 000000000..70182550d --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/DataFixer.java @@ -0,0 +1,53 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world; + +import com.google.common.annotations.Beta; +import com.sk89q.jnbt.CompoundTag; + +/** + * This entire class is subject to heavy changes. Do not use this as API. + */ +@Beta +public interface DataFixer { + + final class FixType { + private FixType() { + } + } + + final class FixTypes { + private FixTypes() { + } + + public static FixType CHUNK = new FixType<>(); + public static FixType BLOCK_ENTITY = new FixType<>(); + public static FixType ENTITY = new FixType<>(); + public static FixType BLOCK_STATE = new FixType<>(); + public static FixType BIOME = new FixType<>(); + public static FixType ITEM_TYPE = new FixType<>(); + } + + default T fixUp(FixType type, T original) { + return fixUp(type, original, -1); + } + + T fixUp(FixType type, T original, int srcVer); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java index c6040cd60..2e7038682 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/SimpleWorld.java @@ -20,28 +20,26 @@ package com.sk89q.worldedit.world; import com.boydti.fawe.util.SetQueue; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; -import com.sk89q.worldedit.function.mask.BlockTypeMask; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.TreeGenerator; -import com.sk89q.worldedit.world.block.*; -import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.mask.BlockTypeMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.world.block.BaseBlock; +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.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; - -import java.util.HashMap; -import java.util.PriorityQueue; - import javax.annotation.Nullable; /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index cad0be0f5..471279281 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -38,6 +38,9 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.weather.WeatherType; +import javax.annotation.Nullable; +import java.nio.file.Path; + /** * Represents a world (dimension). */ @@ -50,6 +53,15 @@ public interface World extends Extent { */ String getName(); + /** + * Get the folder in which this world is stored. May return null if unknown + * or if this world is not serialized to disk. + * + * @return world storage path + */ + @Nullable + Path getStoragePath(); + /** * Get the maximum Y. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java index 3214de29b..79a4984c3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java @@ -19,16 +19,18 @@ package com.sk89q.worldedit.world.biome; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.RegistryItem; import com.sk89q.worldedit.registry.NamespacedRegistry; /** * All the types of biomes in the game. */ -public class BiomeType implements RegistryItem { +public class BiomeType implements RegistryItem, Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("biome type"); - private final String id; + + private String id; public BiomeType(String id) { this.id = id; @@ -51,6 +53,7 @@ public class BiomeType implements RegistryItem { * * @return The id */ + @Override public String getId() { return this.id; } @@ -62,7 +65,7 @@ public class BiomeType implements RegistryItem { @Override public int hashCode() { - return this.internalId; + return this.id.hashCode(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java index d8eec08be..f12228e2d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java @@ -20,15 +20,16 @@ package com.sk89q.worldedit.world.biome; import javax.annotation.Nullable; -import java.util.Collection; /** * Stores a list of common Biome String IDs. */ -public class BiomeTypes { +public final class BiomeTypes { @Nullable public static final BiomeType BADLANDS = get("minecraft:badlands"); @Nullable public static final BiomeType BADLANDS_PLATEAU = get("minecraft:badlands_plateau"); + @Nullable public static final BiomeType BAMBOO_JUNGLE = get("minecraft:bamboo_jungle"); + @Nullable public static final BiomeType BAMBOO_JUNGLE_HILLS = get("minecraft:bamboo_jungle_hills"); @Nullable public static final BiomeType BEACH = get("minecraft:beach"); @Nullable public static final BiomeType BIRCH_FOREST = get("minecraft:birch_forest"); @Nullable public static final BiomeType BIRCH_FOREST_HILLS = get("minecraft:birch_forest_hills"); @@ -104,15 +105,15 @@ public class BiomeTypes { private BiomeTypes() { } + private static BiomeType register(final String id) { + return register(new BiomeType(id)); + } + + public static BiomeType register(final BiomeType biome) { + return BiomeType.REGISTRY.register(biome.getId(), biome); + } + public static @Nullable BiomeType get(final String id) { return BiomeType.REGISTRY.get(id); } - - public static BiomeType get(int internalId) { - return BiomeType.REGISTRY.getByInternalId(internalId); - } - - public static Collection values() { - return BiomeType.REGISTRY.values(); - } } 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 0b0997992..88db98d1e 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 @@ -24,6 +24,7 @@ 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; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; @@ -44,7 +45,7 @@ import java.util.Objects; * snapshot of blocks correctly, so, for example, the NBT data for a block * may be missing.

*/ -public class BaseBlock implements BlockStateHolder { +public class BaseBlock implements BlockStateHolder, TileEntityBlock { private BlockState blockState; @Nullable protected CompoundTag nbtData; @@ -236,22 +237,6 @@ public class BaseBlock implements BlockStateHolder { return this; } - @Override - public BaseBlock toBaseBlock(CompoundTag compoundTag) { - if (compoundTag == null) { - return this.blockState.toBaseBlock(); - } else if (compoundTag == this.nbtData) { - return this; - } else { - return new BaseBlock(this.blockState, compoundTag); - } - } - - @Override - public int hashCode() { - return getOrdinal(); - } - @Override public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { return extent.setBlock(set, this); @@ -277,18 +262,38 @@ public class BaseBlock implements BlockStateHolder { return toImmutableState().with(property, value).toBaseBlock(getNbtData()); } - @Override - public V getState(PropertyKey property) { - return toImmutableState().getState(property); - } - @Override - public String toString() { - if (getNbtData() != null) { - return getAsString() + " {" + String.valueOf(getNbtData()) + "}"; + public BaseBlock toBaseBlock(CompoundTag compoundTag) { + if (compoundTag == null) { + return this.blockState.toBaseBlock(); + } else if (compoundTag == this.nbtData) { + return this; } else { - return getAsString(); + return new BaseBlock(this.blockState, compoundTag); } } + @Override + public V getState(PropertyKey property) { + return toImmutableState().getState(property); + } + + @Override + public int hashCode() { + int ret = toImmutableState().hashCode() << 3; + if (hasNbtData()) { + ret += getNbtData().hashCode(); + } + return ret; + } + + @Override + public String toString() { +// if (getNbtData() != null) { // TODO Maybe make some JSON serialiser to make this not awful. +// return blockState.getAsString() + " {" + String.valueOf(getNbtData()) + "}"; +// } else { + return blockState.getAsString(); +// } + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java index 28c3385d5..f85fcca37 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java @@ -26,17 +26,24 @@ public final class BlockCategories { public static final BlockCategory ACACIA_LOGS = get("minecraft:acacia_logs"); public static final BlockCategory ANVIL = get("minecraft:anvil"); + public static final BlockCategory BAMBOO_PLANTABLE_ON = get("minecraft:bamboo_plantable_on"); public static final BlockCategory BANNERS = get("minecraft:banners"); + public static final BlockCategory BEDS = get("minecraft:beds"); public static final BlockCategory BIRCH_LOGS = get("minecraft:birch_logs"); public static final BlockCategory BUTTONS = get("minecraft:buttons"); public static final BlockCategory CARPETS = get("minecraft:carpets"); - public static final BlockCategory CORALS = get("minecraft:corals"); public static final BlockCategory CORAL_BLOCKS = get("minecraft:coral_blocks"); + public static final BlockCategory CORAL_PLANTS = get("minecraft:coral_plants"); + public static final BlockCategory CORALS = get("minecraft:corals"); public static final BlockCategory DARK_OAK_LOGS = get("minecraft:dark_oak_logs"); + public static final BlockCategory DIRT_LIKE = get("minecraft:dirt_like"); public static final BlockCategory DOORS = get("minecraft:doors"); + public static final BlockCategory DRAGON_IMMUNE = get("minecraft:dragon_immune"); public static final BlockCategory ENDERMAN_HOLDABLE = get("minecraft:enderman_holdable"); + public static final BlockCategory FENCES = get("minecraft:fences"); public static final BlockCategory FLOWER_POTS = get("minecraft:flower_pots"); public static final BlockCategory ICE = get("minecraft:ice"); + public static final BlockCategory IMPERMEABLE = get("minecraft:impermeable"); public static final BlockCategory JUNGLE_LOGS = get("minecraft:jungle_logs"); public static final BlockCategory LEAVES = get("minecraft:leaves"); public static final BlockCategory LOGS = get("minecraft:logs"); @@ -45,16 +52,27 @@ public final class BlockCategories { public static final BlockCategory RAILS = get("minecraft:rails"); public static final BlockCategory SAND = get("minecraft:sand"); public static final BlockCategory SAPLINGS = get("minecraft:saplings"); + public static final BlockCategory SIGNS = get("minecraft:signs"); public static final BlockCategory SLABS = get("minecraft:slabs"); + public static final BlockCategory SMALL_FLOWERS = get("minecraft:small_flowers"); public static final BlockCategory SPRUCE_LOGS = get("minecraft:spruce_logs"); public static final BlockCategory STAIRS = get("minecraft:stairs"); + public static final BlockCategory STANDING_SIGNS = get("minecraft:standing_signs"); public static final BlockCategory STONE_BRICKS = get("minecraft:stone_bricks"); + public static final BlockCategory TRAPDOORS = get("minecraft:trapdoors"); + public static final BlockCategory UNDERWATER_BONEMEALS = get("minecraft:underwater_bonemeals"); public static final BlockCategory VALID_SPAWN = get("minecraft:valid_spawn"); + public static final BlockCategory WALL_CORALS = get("minecraft:wall_corals"); + public static final BlockCategory WALL_SIGNS = get("minecraft:wall_signs"); + public static final BlockCategory WALLS = get("minecraft:walls"); + public static final BlockCategory WITHER_IMMUNE = get("minecraft:wither_immune"); public static final BlockCategory WOODEN_BUTTONS = get("minecraft:wooden_buttons"); public static final BlockCategory WOODEN_DOORS = get("minecraft:wooden_doors"); + public static final BlockCategory WOODEN_FENCES = get("minecraft:wooden_fences"); public static final BlockCategory WOODEN_PRESSURE_PLATES = get("minecraft:wooden_pressure_plates"); public static final BlockCategory WOODEN_SLABS = get("minecraft:wooden_slabs"); public static final BlockCategory WOODEN_STAIRS = get("minecraft:wooden_stairs"); + public static final BlockCategory WOODEN_TRAPDOORS = get("minecraft:wooden_trapdoors"); public static final BlockCategory WOOL = get("minecraft:wool"); private BlockCategories() { 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 161b1c304..19a1cbc82 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 @@ -22,6 +22,7 @@ package com.sk89q.worldedit.world.block; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.registry.Category; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.NamespacedRegistry; import java.util.Set; @@ -30,7 +31,7 @@ import java.util.Set; * A category of blocks. This is due to the splitting up of * blocks such as wool into separate ids. */ -public class BlockCategory extends Category { +public class BlockCategory extends Category implements Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block tag"); 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 4fe002ee7..d25bd6bce 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 @@ -66,7 +66,7 @@ public class BlockState implements BlockStateHolder, FawePattern { * @deprecated magic number * @return BlockState */ - + @Deprecated public static BlockState getFromInternalId(int combinedId) throws InputParseException { return BlockTypes.getFromStateId(combinedId).withStateId(combinedId); @@ -208,7 +208,6 @@ public class BlockState implements BlockStateHolder, FawePattern { return getBlockType().withPropertyId(propertyId); } - @Override public BlockType getBlockType() { return this.blockType; @@ -250,6 +249,16 @@ public class BlockState implements BlockStateHolder, FawePattern { } } + @Override + public V getState(final Property property) { + try { + AbstractProperty ap = (AbstractProperty) property; + return (V) ap.getValue(this.getInternalId()); + } catch (ClassCastException e) { + throw new IllegalArgumentException("Property not found: " + property); + } + } + @Override public BlockState with(final PropertyKey property, final V value) { try { @@ -262,7 +271,7 @@ public class BlockState implements BlockStateHolder, FawePattern { } @Override - public final Map, Object> getStates() { + public Map, Object> getStates() { BlockType type = this.getBlockType(); // Lazily initialize the map Map map = Maps.asMap(type.getPropertiesSet(), (Function) this::getState); @@ -270,13 +279,19 @@ public class BlockState implements BlockStateHolder, FawePattern { } @Override - public final V getState(final Property property) { - try { - AbstractProperty ap = (AbstractProperty) property; - return (V) ap.getValue(this.getInternalId()); - } catch (ClassCastException e) { - throw new IllegalArgumentException("Property not found: " + property); + public boolean equalsFuzzy(BlockStateHolder o) { + if (null == o) { + return false; } + if (this == o) { + // Added a reference equality check for speediness + return true; + } + + if (o.getClass() == BlockState.class) { + return o.getOrdinal() == this.getOrdinal(); + } + return o.equalsFuzzy(this); } @Override @@ -291,7 +306,7 @@ public class BlockState implements BlockStateHolder, FawePattern { @Deprecated @Override - public final V getState(final PropertyKey key) { + public V getState(PropertyKey key) { return getState(getBlockType().getProperty(key)); } @@ -303,14 +318,6 @@ public class BlockState implements BlockStateHolder, FawePattern { return new BaseBlock(this, compoundTag); } - @Override - public boolean equalsFuzzy(BlockStateHolder o) { - if (o.getClass() == BlockState.class) { - return o.getOrdinal() == this.getOrdinal(); - } - return o.equalsFuzzy(this); - } - @Override public int getInternalId() { return internalId; 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 cf2f0a8dc..cfd32e8db 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 @@ -21,30 +21,30 @@ package com.sk89q.worldedit.world.block; import static com.google.common.base.Preconditions.checkArgument; -import com.boydti.fawe.util.ReflectionUtils; +import com.google.common.collect.ImmutableList; +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.function.mask.Mask; import com.sk89q.worldedit.function.mask.SingleBlockTypeMask; import com.sk89q.worldedit.function.pattern.FawePattern; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.registry.Keyed; -import com.sk89q.worldedit.world.item.ItemTypes; -import com.sk89q.worldedit.world.registry.BlockMaterial; -import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.registry.NamespacedRegistry; import com.sk89q.worldedit.registry.state.AbstractProperty; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.PropertyKey; import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.item.ItemTypes; +import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.LegacyMapper; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; +import javax.annotation.Nullable; public class BlockType implements FawePattern, Keyed { private final String id; @@ -71,7 +71,7 @@ public class BlockType implements FawePattern, Keyed { */ @Override public String getId() { - return this.id; + return this.id; } public String getNamespace() { @@ -104,35 +104,17 @@ public class BlockType implements FawePattern, Keyed { if (settings.stateOrdinals == null) return settings.defaultState; return BlockTypes.states[settings.stateOrdinals[propertyId]]; } - + @Deprecated public BlockState withStateId(int internalStateId) { // return this.withPropertyId(internalStateId >> BlockTypes.BIT_OFFSET); } /** - * Properties string in the form property1=foo,prop2=bar - * @param properties - * @return - */ - public BlockState withProperties(String properties) { // - int id = getInternalId(); - for (String keyPair : properties.split(",")) { - String[] split = keyPair.split("="); - String name = split[0]; - String value = split[1]; - AbstractProperty btp = settings.propertiesMap.get(name); - id = btp.modify(id, btp.getValueFor(value)); - } - return withStateId(id); - } - - /** - * Gets the properties of this BlockType in a key->property mapping. + * Gets the properties of this BlockType in a {@code key->property} mapping. * * @return The properties map */ - @Deprecated public Map> getPropertyMap() { return this.settings.propertiesMap; } @@ -142,9 +124,8 @@ public class BlockType implements FawePattern, Keyed { * * @return the properties */ - @Deprecated public List> getProperties() { - return this.settings.propertiesList; + return ImmutableList.copyOf(this.getPropertyMap().values()); } @Deprecated @@ -158,16 +139,19 @@ public class BlockType implements FawePattern, Keyed { * @param name The name * @return The property */ - @Deprecated public Property getProperty(String name) { - return (Property) this.settings.propertiesMap.get(name); + // Assume it works, CCE later at runtime if not. + @SuppressWarnings("unchecked") + Property property = (Property) getPropertyMap().get(name); + checkArgument(property != null, "%s has no property named %s", this, name); + return property; } public boolean hasProperty(PropertyKey key) { int ordinal = key.ordinal(); return this.settings.propertiesMapArr.length > ordinal ? this.settings.propertiesMapArr[ordinal] != null : false; } - + public Property getProperty(PropertyKey key) { try { return (Property) this.settings.propertiesMapArr[key.ordinal()]; @@ -185,7 +169,7 @@ public class BlockType implements FawePattern, Keyed { return this.settings.defaultState; } - public FuzzyBlockState getFuzzyMatcher() { // + public FuzzyBlockState getFuzzyMatcher() { return new FuzzyBlockState(this); } @@ -222,7 +206,6 @@ public class BlockType implements FawePattern, Keyed { return withStateId(id); } - /** * Gets whether this block type has an item representation. * @@ -262,6 +245,7 @@ public class BlockType implements FawePattern, Keyed { * * @return legacy id or 0, if unknown */ + @Deprecated public int getLegacyCombinedId() { Integer combinedId = LegacyMapper.getInstance().getLegacyCombined(this); return combinedId == null ? 0 : combinedId; @@ -278,21 +262,21 @@ public class BlockType implements FawePattern, Keyed { return this.settings.internalId; } + @Override + public String toString() { + return getId(); + } + @Override public int hashCode() { - return settings.internalId; + return this.id.hashCode(); } @Override public boolean equals(Object obj) { - return obj == this; + return obj instanceof BlockType && this.id.equals(((BlockType) obj).id); } - - @Override - public String toString() { - return getId(); - } - + @Override public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { @@ -310,7 +294,7 @@ public class BlockType implements FawePattern, Keyed { @Deprecated - public int getLegacyId() { // + public int getLegacyId() { Integer id = LegacyMapper.getInstance().getLegacyCombined(this.getDefaultState()); if (id != null) { return id >> 4; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java index cb2d43167..cf7f1fa08 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.world.block; import static com.google.common.base.Preconditions.checkNotNull; + import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.PropertyKey; @@ -38,11 +39,7 @@ public class FuzzyBlockState extends BlockState { private final Map props; FuzzyBlockState(BlockType blockType) { - this(blockType.getDefaultState(), null); - } - - public FuzzyBlockState(BlockState state) { - this(state, null); + super(blockType); } private FuzzyBlockState(BlockState state, Map, Object> values) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java index c9e52424e..daf291e8c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java @@ -83,18 +83,17 @@ public class AnvilChunk13 implements Chunk { continue; } - int blocksPerChunkSection = 16 * 16 * 16; - BlockState[] chunkSectionBlocks = new BlockState[blocksPerChunkSection]; - blocks[y] = chunkSectionBlocks; - // parse palette List paletteEntries = sectionTag.getList("Palette", CompoundTag.class); int paletteSize = paletteEntries.size(); + if (paletteSize == 0) { + continue; + } BlockState[] palette = new BlockState[paletteSize]; for (int paletteEntryId = 0; paletteEntryId < paletteSize; paletteEntryId++) { CompoundTag paletteEntry = paletteEntries.get(paletteEntryId); BlockType type = BlockTypes.get(paletteEntry.getString("Name")); - if(type == null) { + if (type == null) { throw new InvalidFormatException("Invalid block type: " + paletteEntry.getString("Name")); } BlockState blockState = type.getDefaultState(); @@ -121,11 +120,16 @@ public class AnvilChunk13 implements Chunk { // parse block states long[] blockStatesSerialized = NBTUtils.getChildTag(sectionTag.getValue(), "BlockStates", LongArrayTag.class).getValue(); + + int blocksPerChunkSection = 16 * 16 * 16; + BlockState[] chunkSectionBlocks = new BlockState[blocksPerChunkSection]; + blocks[y] = chunkSectionBlocks; + long currentSerializedValue = 0; int nextSerializedItem = 0; int remainingBits = 0; for (int blockPos = 0; blockPos < blocksPerChunkSection; blockPos++) { - int localBlockId = 0; + int localBlockId; if (remainingBits < paletteBits) { int bitsNextLong = paletteBits - remainingBits; localBlockId = (int) currentSerializedValue; @@ -159,14 +163,13 @@ public class AnvilChunk13 implements Chunk { * @throws DataException */ private void populateTileEntities() throws DataException { + tileEntities = new HashMap<>(); if (!rootTag.getValue().containsKey("TileEntities")) { return; } List tags = NBTUtils.getChildTag(rootTag.getValue(), "TileEntities", ListTag.class).getValue(); - tileEntities = new HashMap<>(); - for (Tag tag : tags) { if (!(tag instanceof CompoundTag)) { throw new InvalidFormatException("CompoundTag expected in TileEntities"); @@ -225,6 +228,7 @@ public class AnvilChunk13 implements Chunk { if (state.getMaterial().hasContainer()) { CompoundTag tileEntity = getBlockTileEntity(position); + return state.toBaseBlock(tileEntity); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/OldChunk.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/OldChunk.java index dfeeccca9..3cc7e002d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/OldChunk.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/OldChunk.java @@ -187,6 +187,7 @@ public class OldChunk implements Chunk { } if (state.getBlockType().getMaterial().hasContainer()) { CompoundTag tileEntity = getBlockTileEntity(position); + if (tileEntity != null) { return state.toBaseBlock(tileEntity); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java index ac5fbfe5e..966cbaa77 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java @@ -19,10 +19,11 @@ package com.sk89q.worldedit.world.entity; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.RegistryItem; import com.sk89q.worldedit.registry.NamespacedRegistry; -public class EntityType implements RegistryItem { +public class EntityType implements RegistryItem, Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("entity type"); @@ -36,6 +37,7 @@ public class EntityType implements RegistryItem { this.id = id; } + @Override public String getId() { return this.id; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java index 1d35cadf3..eccc116f1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java @@ -29,6 +29,7 @@ public class EntityTypes { @Nullable public static final EntityType BAT = get("minecraft:bat"); @Nullable public static final EntityType BLAZE = get("minecraft:blaze"); @Nullable public static final EntityType BOAT = get("minecraft:boat"); + @Nullable public static final EntityType CAT = get("minecraft:cat"); @Nullable public static final EntityType CAVE_SPIDER = get("minecraft:cave_spider"); @Nullable public static final EntityType CHEST_MINECART = get("minecraft:chest_minecart"); @Nullable public static final EntityType CHICKEN = get("minecraft:chicken"); @@ -56,6 +57,7 @@ public class EntityTypes { @Nullable public static final EntityType FIREBALL = get("minecraft:fireball"); @Nullable public static final EntityType FIREWORK_ROCKET = get("minecraft:firework_rocket"); @Nullable public static final EntityType FISHING_BOBBER = get("minecraft:fishing_bobber"); + @Nullable public static final EntityType FOX = get("minecraft:fox"); @Nullable public static final EntityType FURNACE_MINECART = get("minecraft:furnace_minecart"); @Nullable public static final EntityType GHAST = get("minecraft:ghast"); @Nullable public static final EntityType GIANT = get("minecraft:giant"); @@ -77,14 +79,17 @@ public class EntityTypes { @Nullable public static final EntityType MULE = get("minecraft:mule"); @Nullable public static final EntityType OCELOT = get("minecraft:ocelot"); @Nullable public static final EntityType PAINTING = get("minecraft:painting"); + @Nullable public static final EntityType PANDA = get("minecraft:panda"); @Nullable public static final EntityType PARROT = get("minecraft:parrot"); @Nullable public static final EntityType PHANTOM = get("minecraft:phantom"); @Nullable public static final EntityType PIG = get("minecraft:pig"); + @Nullable public static final EntityType PILLAGER = get("minecraft:pillager"); @Nullable public static final EntityType PLAYER = get("minecraft:player"); @Nullable public static final EntityType POLAR_BEAR = get("minecraft:polar_bear"); @Nullable public static final EntityType POTION = get("minecraft:potion"); @Nullable public static final EntityType PUFFERFISH = get("minecraft:pufferfish"); @Nullable public static final EntityType RABBIT = get("minecraft:rabbit"); + @Nullable public static final EntityType RAVAGER = get("minecraft:ravager"); @Nullable public static final EntityType SALMON = get("minecraft:salmon"); @Nullable public static final EntityType SHEEP = get("minecraft:sheep"); @Nullable public static final EntityType SHULKER = get("minecraft:shulker"); @@ -103,12 +108,14 @@ public class EntityTypes { @Nullable public static final EntityType STRAY = get("minecraft:stray"); @Nullable public static final EntityType TNT = get("minecraft:tnt"); @Nullable public static final EntityType TNT_MINECART = get("minecraft:tnt_minecart"); + @Nullable public static final EntityType TRADER_LLAMA = get("minecraft:trader_llama"); @Nullable public static final EntityType TRIDENT = get("minecraft:trident"); @Nullable public static final EntityType TROPICAL_FISH = get("minecraft:tropical_fish"); @Nullable public static final EntityType TURTLE = get("minecraft:turtle"); @Nullable public static final EntityType VEX = get("minecraft:vex"); @Nullable public static final EntityType VILLAGER = get("minecraft:villager"); @Nullable public static final EntityType VINDICATOR = get("minecraft:vindicator"); + @Nullable public static final EntityType WANDERING_TRADER = get("minecraft:wandering_trader"); @Nullable public static final EntityType WITCH = get("minecraft:witch"); @Nullable public static final EntityType WITHER = get("minecraft:wither"); @Nullable public static final EntityType WITHER_SKELETON = get("minecraft:wither_skeleton"); @@ -125,7 +132,7 @@ public class EntityTypes { public static @Nullable EntityType get(final String id) { return EntityType.REGISTRY.get(id); } - + public static EntityType parse(String id) { if (id.startsWith("minecraft:")) id = id.substring(10); switch (id) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidCategory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidCategory.java index 52685cb56..721c22aad 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidCategory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidCategory.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.world.fluid; import com.sk89q.worldedit.registry.Category; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.NamespacedRegistry; import java.util.Collections; @@ -29,7 +30,7 @@ import java.util.Set; * A category of fluids. This is due to the splitting up of * blocks such as wool into separate ids. */ -public class FluidCategory extends Category { +public class FluidCategory extends Category implements Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("fluid tag"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidType.java index 1639e00c7..51c4b3d52 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/fluid/FluidType.java @@ -19,14 +19,15 @@ package com.sk89q.worldedit.world.fluid; -import com.sk89q.worldedit.registry.RegistryItem; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.NamespacedRegistry; +import com.sk89q.worldedit.registry.RegistryItem; /** * Minecraft now has a 'fluid' system. This is a * stub class to represent what it may be in the future. */ -public class FluidType implements RegistryItem { +public class FluidType implements RegistryItem, Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("fluid type"); @@ -41,17 +42,20 @@ public class FluidType implements RegistryItem { * * @return The id */ + @Override public String getId() { return this.id; } private int internalId; + //UNUSED @Override public void setInternalId(int internalId) { this.internalId = internalId; } + //UNUSED @Override public int getInternalId() { return internalId; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameMode.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameMode.java index 9cfd4b111..b64d960de 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameMode.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameMode.java @@ -19,9 +19,10 @@ package com.sk89q.worldedit.world.gamemode; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.Registry; -public class GameMode { +public class GameMode implements Keyed { public static final Registry REGISTRY = new Registry<>("game mode"); @@ -31,6 +32,7 @@ public class GameMode { this.id = id; } + @Override public String getId() { return this.id; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameModes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameModes.java index ab5b9fc69..f3a0fc4b2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameModes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/gamemode/GameModes.java @@ -21,9 +21,8 @@ package com.sk89q.worldedit.world.gamemode; import javax.annotation.Nullable; -public class GameModes { +public final class GameModes { - public static final GameMode NOT_SET = register(""); public static final GameMode SURVIVAL = register("survival"); public static final GameMode CREATIVE = register("creative"); public static final GameMode ADVENTURE = register("adventure"); @@ -40,7 +39,8 @@ public class GameModes { return GameMode.REGISTRY.register(gameMode.getId(), gameMode); } - public static @Nullable GameMode get(final String id) { + @Nullable + public static GameMode get(final String id) { return GameMode.REGISTRY.get(id); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategories.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategories.java index 021d6834c..7dd006871 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategories.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategories.java @@ -26,33 +26,42 @@ public final class ItemCategories { public static final ItemCategory ACACIA_LOGS = get("minecraft:acacia_logs"); public static final ItemCategory ANVIL = get("minecraft:anvil"); + public static final ItemCategory ARROWS = get("minecraft:arrows"); public static final ItemCategory BANNERS = get("minecraft:banners"); + public static final ItemCategory BEDS = get("minecraft:beds"); public static final ItemCategory BIRCH_LOGS = get("minecraft:birch_logs"); public static final ItemCategory BOATS = get("minecraft:boats"); public static final ItemCategory BUTTONS = get("minecraft:buttons"); public static final ItemCategory CARPETS = get("minecraft:carpets"); - public static final ItemCategory CORAL = get("minecraft:coral"); - public static final ItemCategory CORAL_PLANTS = get("minecraft:coral_plants"); + public static final ItemCategory COALS = get("minecraft:coals"); public static final ItemCategory DARK_OAK_LOGS = get("minecraft:dark_oak_logs"); public static final ItemCategory DOORS = get("minecraft:doors"); + public static final ItemCategory FENCES = get("minecraft:fences"); public static final ItemCategory FISHES = get("minecraft:fishes"); public static final ItemCategory JUNGLE_LOGS = get("minecraft:jungle_logs"); public static final ItemCategory LEAVES = get("minecraft:leaves"); public static final ItemCategory LOGS = get("minecraft:logs"); + public static final ItemCategory MUSIC_DISCS = get("minecraft:music_discs"); public static final ItemCategory OAK_LOGS = get("minecraft:oak_logs"); public static final ItemCategory PLANKS = get("minecraft:planks"); public static final ItemCategory RAILS = get("minecraft:rails"); public static final ItemCategory SAND = get("minecraft:sand"); public static final ItemCategory SAPLINGS = get("minecraft:saplings"); + public static final ItemCategory SIGNS = get("minecraft:signs"); public static final ItemCategory SLABS = get("minecraft:slabs"); + public static final ItemCategory SMALL_FLOWERS = get("minecraft:small_flowers"); public static final ItemCategory SPRUCE_LOGS = get("minecraft:spruce_logs"); public static final ItemCategory STAIRS = get("minecraft:stairs"); public static final ItemCategory STONE_BRICKS = get("minecraft:stone_bricks"); + public static final ItemCategory TRAPDOORS = get("minecraft:trapdoors"); + public static final ItemCategory WALLS = get("minecraft:walls"); public static final ItemCategory WOODEN_BUTTONS = get("minecraft:wooden_buttons"); public static final ItemCategory WOODEN_DOORS = get("minecraft:wooden_doors"); + public static final ItemCategory WOODEN_FENCES = get("minecraft:wooden_fences"); public static final ItemCategory WOODEN_PRESSURE_PLATES = get("minecraft:wooden_pressure_plates"); public static final ItemCategory WOODEN_SLABS = get("minecraft:wooden_slabs"); public static final ItemCategory WOODEN_STAIRS = get("minecraft:wooden_stairs"); + public static final ItemCategory WOODEN_TRAPDOORS = get("minecraft:wooden_trapdoors"); public static final ItemCategory WOOL = get("minecraft:wool"); private ItemCategories() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategory.java index c5efdb4b9..8858dc9f9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemCategory.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.registry.Category; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.NamespacedRegistry; import java.util.Set; @@ -31,7 +32,7 @@ import java.util.Set; * A category of items. This is due to the splitting up of * items such as wool into separate ids. */ -public class ItemCategory extends Category { +public class ItemCategory extends Category implements Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("item tag"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java index 48170fe3f..ff37090cb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java @@ -23,18 +23,20 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.registry.RegistryItem; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.NamespacedRegistry; +import com.sk89q.worldedit.registry.RegistryItem; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import javax.annotation.Nullable; -public class ItemType implements RegistryItem { +public class ItemType implements RegistryItem, Keyed { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("item type"); private String id; + private String name; private BlockType blockType; private boolean initBlockType; private BaseItem defaultState; @@ -47,6 +49,7 @@ public class ItemType implements RegistryItem { this.id = id; } + @Override public String getId() { return this.id; } @@ -69,12 +72,14 @@ public class ItemType implements RegistryItem { * @return The name, or ID */ public String getName() { - String name = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getItemRegistry().getName(this); if (name == null) { - return getId(); - } else { - return name; + name = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries() + .getItemRegistry().getName(this); + if (name == null) { + name = ""; + } } + return name.isEmpty() ? getId() : name; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java index 4b896af0e..711ee7b43 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java @@ -19,11 +19,11 @@ package com.sk89q.worldedit.world.item; -import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.registry.LegacyMapper; import javax.annotation.Nullable; import java.util.Collection; +import java.util.Optional; public final class ItemTypes { @@ -37,6 +37,7 @@ public final class ItemTypes { @Nullable public static final ItemType ACACIA_PLANKS = get("minecraft:acacia_planks"); @Nullable public static final ItemType ACACIA_PRESSURE_PLATE = get("minecraft:acacia_pressure_plate"); @Nullable public static final ItemType ACACIA_SAPLING = get("minecraft:acacia_sapling"); + @Nullable public static final ItemType ACACIA_SIGN = get("minecraft:acacia_sign"); @Nullable public static final ItemType ACACIA_SLAB = get("minecraft:acacia_slab"); @Nullable public static final ItemType ACACIA_STAIRS = get("minecraft:acacia_stairs"); @Nullable public static final ItemType ACACIA_TRAPDOOR = get("minecraft:acacia_trapdoor"); @@ -45,12 +46,17 @@ public final class ItemTypes { @Nullable public static final ItemType AIR = get("minecraft:air"); @Nullable public static final ItemType ALLIUM = get("minecraft:allium"); @Nullable public static final ItemType ANDESITE = get("minecraft:andesite"); + @Nullable public static final ItemType ANDESITE_SLAB = get("minecraft:andesite_slab"); + @Nullable public static final ItemType ANDESITE_STAIRS = get("minecraft:andesite_stairs"); + @Nullable public static final ItemType ANDESITE_WALL = get("minecraft:andesite_wall"); @Nullable public static final ItemType ANVIL = get("minecraft:anvil"); @Nullable public static final ItemType APPLE = get("minecraft:apple"); @Nullable public static final ItemType ARMOR_STAND = get("minecraft:armor_stand"); @Nullable public static final ItemType ARROW = get("minecraft:arrow"); @Nullable public static final ItemType AZURE_BLUET = get("minecraft:azure_bluet"); @Nullable public static final ItemType BAKED_POTATO = get("minecraft:baked_potato"); + @Nullable public static final ItemType BAMBOO = get("minecraft:bamboo"); + @Nullable public static final ItemType BARREL = get("minecraft:barrel"); @Nullable public static final ItemType BARRIER = get("minecraft:barrier"); @Nullable public static final ItemType BAT_SPAWN_EGG = get("minecraft:bat_spawn_egg"); @Nullable public static final ItemType BEACON = get("minecraft:beacon"); @@ -59,6 +65,7 @@ public final class ItemTypes { @Nullable public static final ItemType BEETROOT = get("minecraft:beetroot"); @Nullable public static final ItemType BEETROOT_SEEDS = get("minecraft:beetroot_seeds"); @Nullable public static final ItemType BEETROOT_SOUP = get("minecraft:beetroot_soup"); + @Nullable public static final ItemType BELL = get("minecraft:bell"); @Nullable public static final ItemType BIRCH_BOAT = get("minecraft:birch_boat"); @Nullable public static final ItemType BIRCH_BUTTON = get("minecraft:birch_button"); @Nullable public static final ItemType BIRCH_DOOR = get("minecraft:birch_door"); @@ -69,6 +76,7 @@ public final class ItemTypes { @Nullable public static final ItemType BIRCH_PLANKS = get("minecraft:birch_planks"); @Nullable public static final ItemType BIRCH_PRESSURE_PLATE = get("minecraft:birch_pressure_plate"); @Nullable public static final ItemType BIRCH_SAPLING = get("minecraft:birch_sapling"); + @Nullable public static final ItemType BIRCH_SIGN = get("minecraft:birch_sign"); @Nullable public static final ItemType BIRCH_SLAB = get("minecraft:birch_slab"); @Nullable public static final ItemType BIRCH_STAIRS = get("minecraft:birch_stairs"); @Nullable public static final ItemType BIRCH_TRAPDOOR = get("minecraft:birch_trapdoor"); @@ -78,12 +86,14 @@ public final class ItemTypes { @Nullable public static final ItemType BLACK_CARPET = get("minecraft:black_carpet"); @Nullable public static final ItemType BLACK_CONCRETE = get("minecraft:black_concrete"); @Nullable public static final ItemType BLACK_CONCRETE_POWDER = get("minecraft:black_concrete_powder"); + @Nullable public static final ItemType BLACK_DYE = get("minecraft:black_dye"); @Nullable public static final ItemType BLACK_GLAZED_TERRACOTTA = get("minecraft:black_glazed_terracotta"); @Nullable public static final ItemType BLACK_SHULKER_BOX = get("minecraft:black_shulker_box"); @Nullable public static final ItemType BLACK_STAINED_GLASS = get("minecraft:black_stained_glass"); @Nullable public static final ItemType BLACK_STAINED_GLASS_PANE = get("minecraft:black_stained_glass_pane"); @Nullable public static final ItemType BLACK_TERRACOTTA = get("minecraft:black_terracotta"); @Nullable public static final ItemType BLACK_WOOL = get("minecraft:black_wool"); + @Nullable public static final ItemType BLAST_FURNACE = get("minecraft:blast_furnace"); @Nullable public static final ItemType BLAZE_POWDER = get("minecraft:blaze_powder"); @Nullable public static final ItemType BLAZE_ROD = get("minecraft:blaze_rod"); @Nullable public static final ItemType BLAZE_SPAWN_EGG = get("minecraft:blaze_spawn_egg"); @@ -92,6 +102,7 @@ public final class ItemTypes { @Nullable public static final ItemType BLUE_CARPET = get("minecraft:blue_carpet"); @Nullable public static final ItemType BLUE_CONCRETE = get("minecraft:blue_concrete"); @Nullable public static final ItemType BLUE_CONCRETE_POWDER = get("minecraft:blue_concrete_powder"); + @Nullable public static final ItemType BLUE_DYE = get("minecraft:blue_dye"); @Nullable public static final ItemType BLUE_GLAZED_TERRACOTTA = get("minecraft:blue_glazed_terracotta"); @Nullable public static final ItemType BLUE_ICE = get("minecraft:blue_ice"); @Nullable public static final ItemType BLUE_ORCHID = get("minecraft:blue_orchid"); @@ -115,12 +126,14 @@ public final class ItemTypes { @Nullable public static final ItemType BRICK = get("minecraft:brick"); @Nullable public static final ItemType BRICK_SLAB = get("minecraft:brick_slab"); @Nullable public static final ItemType BRICK_STAIRS = get("minecraft:brick_stairs"); + @Nullable public static final ItemType BRICK_WALL = get("minecraft:brick_wall"); @Nullable public static final ItemType BRICKS = get("minecraft:bricks"); @Nullable public static final ItemType BROWN_BANNER = get("minecraft:brown_banner"); @Nullable public static final ItemType BROWN_BED = get("minecraft:brown_bed"); @Nullable public static final ItemType BROWN_CARPET = get("minecraft:brown_carpet"); @Nullable public static final ItemType BROWN_CONCRETE = get("minecraft:brown_concrete"); @Nullable public static final ItemType BROWN_CONCRETE_POWDER = get("minecraft:brown_concrete_powder"); + @Nullable public static final ItemType BROWN_DYE = get("minecraft:brown_dye"); @Nullable public static final ItemType BROWN_GLAZED_TERRACOTTA = get("minecraft:brown_glazed_terracotta"); @Nullable public static final ItemType BROWN_MUSHROOM = get("minecraft:brown_mushroom"); @Nullable public static final ItemType BROWN_MUSHROOM_BLOCK = get("minecraft:brown_mushroom_block"); @@ -134,11 +147,14 @@ public final class ItemTypes { @Nullable public static final ItemType BUBBLE_CORAL_FAN = get("minecraft:bubble_coral_fan"); @Nullable public static final ItemType BUCKET = get("minecraft:bucket"); @Nullable public static final ItemType CACTUS = get("minecraft:cactus"); - @Nullable public static final ItemType CACTUS_GREEN = get("minecraft:cactus_green"); + @Deprecated @Nullable public static final ItemType CACTUS_GREEN = Optional.ofNullable(get("minecraft:cactus_green")).orElseGet(() -> get("minecraft:green_dye")); @Nullable public static final ItemType CAKE = get("minecraft:cake"); + @Nullable public static final ItemType CAMPFIRE = get("minecraft:campfire"); @Nullable public static final ItemType CARROT = get("minecraft:carrot"); @Nullable public static final ItemType CARROT_ON_A_STICK = get("minecraft:carrot_on_a_stick"); + @Nullable public static final ItemType CARTOGRAPHY_TABLE = get("minecraft:cartography_table"); @Nullable public static final ItemType CARVED_PUMPKIN = get("minecraft:carved_pumpkin"); + @Nullable public static final ItemType CAT_SPAWN_EGG = get("minecraft:cat_spawn_egg"); @Nullable public static final ItemType CAULDRON = get("minecraft:cauldron"); @Nullable public static final ItemType CAVE_SPIDER_SPAWN_EGG = get("minecraft:cave_spider_spawn_egg"); @Nullable public static final ItemType CHAIN_COMMAND_BLOCK = get("minecraft:chain_command_block"); @@ -179,6 +195,7 @@ public final class ItemTypes { @Nullable public static final ItemType COMMAND_BLOCK_MINECART = get("minecraft:command_block_minecart"); @Nullable public static final ItemType COMPARATOR = get("minecraft:comparator"); @Nullable public static final ItemType COMPASS = get("minecraft:compass"); + @Nullable public static final ItemType COMPOSTER = get("minecraft:composter"); @Nullable public static final ItemType CONDUIT = get("minecraft:conduit"); @Nullable public static final ItemType COOKED_BEEF = get("minecraft:cooked_beef"); @Nullable public static final ItemType COOKED_CHICKEN = get("minecraft:cooked_chicken"); @@ -188,13 +205,18 @@ public final class ItemTypes { @Nullable public static final ItemType COOKED_RABBIT = get("minecraft:cooked_rabbit"); @Nullable public static final ItemType COOKED_SALMON = get("minecraft:cooked_salmon"); @Nullable public static final ItemType COOKIE = get("minecraft:cookie"); + @Nullable public static final ItemType CORNFLOWER = get("minecraft:cornflower"); @Nullable public static final ItemType COW_SPAWN_EGG = get("minecraft:cow_spawn_egg"); @Nullable public static final ItemType CRACKED_STONE_BRICKS = get("minecraft:cracked_stone_bricks"); @Nullable public static final ItemType CRAFTING_TABLE = get("minecraft:crafting_table"); + @Nullable public static final ItemType CREEPER_BANNER_PATTERN = get("minecraft:creeper_banner_pattern"); @Nullable public static final ItemType CREEPER_HEAD = get("minecraft:creeper_head"); @Nullable public static final ItemType CREEPER_SPAWN_EGG = get("minecraft:creeper_spawn_egg"); + @Nullable public static final ItemType CROSSBOW = get("minecraft:crossbow"); @Nullable public static final ItemType CUT_RED_SANDSTONE = get("minecraft:cut_red_sandstone"); + @Nullable public static final ItemType CUT_RED_SANDSTONE_SLAB = get("minecraft:cut_red_sandstone_slab"); @Nullable public static final ItemType CUT_SANDSTONE = get("minecraft:cut_sandstone"); + @Nullable public static final ItemType CUT_SANDSTONE_SLAB = get("minecraft:cut_sandstone_slab"); @Nullable public static final ItemType CYAN_BANNER = get("minecraft:cyan_banner"); @Nullable public static final ItemType CYAN_BED = get("minecraft:cyan_bed"); @Nullable public static final ItemType CYAN_CARPET = get("minecraft:cyan_carpet"); @@ -209,7 +231,7 @@ public final class ItemTypes { @Nullable public static final ItemType CYAN_WOOL = get("minecraft:cyan_wool"); @Nullable public static final ItemType DAMAGED_ANVIL = get("minecraft:damaged_anvil"); @Nullable public static final ItemType DANDELION = get("minecraft:dandelion"); - @Nullable public static final ItemType DANDELION_YELLOW = get("minecraft:dandelion_yellow"); + @Deprecated @Nullable public static final ItemType DANDELION_YELLOW = Optional.ofNullable(get("minecraft:dandelion_yellow")).orElseGet(() -> (get("minecraft:yellow_dye"))); @Nullable public static final ItemType DARK_OAK_BOAT = get("minecraft:dark_oak_boat"); @Nullable public static final ItemType DARK_OAK_BUTTON = get("minecraft:dark_oak_button"); @Nullable public static final ItemType DARK_OAK_DOOR = get("minecraft:dark_oak_door"); @@ -220,6 +242,7 @@ public final class ItemTypes { @Nullable public static final ItemType DARK_OAK_PLANKS = get("minecraft:dark_oak_planks"); @Nullable public static final ItemType DARK_OAK_PRESSURE_PLATE = get("minecraft:dark_oak_pressure_plate"); @Nullable public static final ItemType DARK_OAK_SAPLING = get("minecraft:dark_oak_sapling"); + @Nullable public static final ItemType DARK_OAK_SIGN = get("minecraft:dark_oak_sign"); @Nullable public static final ItemType DARK_OAK_SLAB = get("minecraft:dark_oak_slab"); @Nullable public static final ItemType DARK_OAK_STAIRS = get("minecraft:dark_oak_stairs"); @Nullable public static final ItemType DARK_OAK_TRAPDOOR = get("minecraft:dark_oak_trapdoor"); @@ -260,6 +283,9 @@ public final class ItemTypes { @Nullable public static final ItemType DIAMOND_SHOVEL = get("minecraft:diamond_shovel"); @Nullable public static final ItemType DIAMOND_SWORD = get("minecraft:diamond_sword"); @Nullable public static final ItemType DIORITE = get("minecraft:diorite"); + @Nullable public static final ItemType DIORITE_SLAB = get("minecraft:diorite_slab"); + @Nullable public static final ItemType DIORITE_STAIRS = get("minecraft:diorite_stairs"); + @Nullable public static final ItemType DIORITE_WALL = get("minecraft:diorite_wall"); @Nullable public static final ItemType DIRT = get("minecraft:dirt"); @Nullable public static final ItemType DISPENSER = get("minecraft:dispenser"); @Nullable public static final ItemType DOLPHIN_SPAWN_EGG = get("minecraft:dolphin_spawn_egg"); @@ -284,6 +310,9 @@ public final class ItemTypes { @Nullable public static final ItemType END_PORTAL_FRAME = get("minecraft:end_portal_frame"); @Nullable public static final ItemType END_ROD = get("minecraft:end_rod"); @Nullable public static final ItemType END_STONE = get("minecraft:end_stone"); + @Nullable public static final ItemType END_STONE_BRICK_SLAB = get("minecraft:end_stone_brick_slab"); + @Nullable public static final ItemType END_STONE_BRICK_STAIRS = get("minecraft:end_stone_brick_stairs"); + @Nullable public static final ItemType END_STONE_BRICK_WALL = get("minecraft:end_stone_brick_wall"); @Nullable public static final ItemType END_STONE_BRICKS = get("minecraft:end_stone_bricks"); @Nullable public static final ItemType ENDER_CHEST = get("minecraft:ender_chest"); @Nullable public static final ItemType ENDER_EYE = get("minecraft:ender_eye"); @@ -304,9 +333,12 @@ public final class ItemTypes { @Nullable public static final ItemType FIREWORK_ROCKET = get("minecraft:firework_rocket"); @Nullable public static final ItemType FIREWORK_STAR = get("minecraft:firework_star"); @Nullable public static final ItemType FISHING_ROD = get("minecraft:fishing_rod"); + @Nullable public static final ItemType FLETCHING_TABLE = get("minecraft:fletching_table"); @Nullable public static final ItemType FLINT = get("minecraft:flint"); @Nullable public static final ItemType FLINT_AND_STEEL = get("minecraft:flint_and_steel"); + @Nullable public static final ItemType FLOWER_BANNER_PATTERN = get("minecraft:flower_banner_pattern"); @Nullable public static final ItemType FLOWER_POT = get("minecraft:flower_pot"); + @Nullable public static final ItemType FOX_SPAWN_EGG = get("minecraft:fox_spawn_egg"); @Nullable public static final ItemType FURNACE = get("minecraft:furnace"); @Nullable public static final ItemType FURNACE_MINECART = get("minecraft:furnace_minecart"); @Nullable public static final ItemType GHAST_SPAWN_EGG = get("minecraft:ghast_spawn_egg"); @@ -315,6 +347,7 @@ public final class ItemTypes { @Nullable public static final ItemType GLASS_BOTTLE = get("minecraft:glass_bottle"); @Nullable public static final ItemType GLASS_PANE = get("minecraft:glass_pane"); @Nullable public static final ItemType GLISTERING_MELON_SLICE = get("minecraft:glistering_melon_slice"); + @Nullable public static final ItemType GLOBE_BANNER_PATTERN = get("minecraft:globe_banner_pattern"); @Nullable public static final ItemType GLOWSTONE = get("minecraft:glowstone"); @Nullable public static final ItemType GLOWSTONE_DUST = get("minecraft:glowstone_dust"); @Nullable public static final ItemType GOLD_BLOCK = get("minecraft:gold_block"); @@ -334,6 +367,9 @@ public final class ItemTypes { @Nullable public static final ItemType GOLDEN_SHOVEL = get("minecraft:golden_shovel"); @Nullable public static final ItemType GOLDEN_SWORD = get("minecraft:golden_sword"); @Nullable public static final ItemType GRANITE = get("minecraft:granite"); + @Nullable public static final ItemType GRANITE_SLAB = get("minecraft:granite_slab"); + @Nullable public static final ItemType GRANITE_STAIRS = get("minecraft:granite_stairs"); + @Nullable public static final ItemType GRANITE_WALL = get("minecraft:granite_wall"); @Nullable public static final ItemType GRASS = get("minecraft:grass"); @Nullable public static final ItemType GRASS_BLOCK = get("minecraft:grass_block"); @Nullable public static final ItemType GRASS_PATH = get("minecraft:grass_path"); @@ -355,12 +391,14 @@ public final class ItemTypes { @Nullable public static final ItemType GREEN_CARPET = get("minecraft:green_carpet"); @Nullable public static final ItemType GREEN_CONCRETE = get("minecraft:green_concrete"); @Nullable public static final ItemType GREEN_CONCRETE_POWDER = get("minecraft:green_concrete_powder"); + @Nullable public static final ItemType GREEN_DYE = get("minecraft:green_dye"); @Nullable public static final ItemType GREEN_GLAZED_TERRACOTTA = get("minecraft:green_glazed_terracotta"); @Nullable public static final ItemType GREEN_SHULKER_BOX = get("minecraft:green_shulker_box"); @Nullable public static final ItemType GREEN_STAINED_GLASS = get("minecraft:green_stained_glass"); @Nullable public static final ItemType GREEN_STAINED_GLASS_PANE = get("minecraft:green_stained_glass_pane"); @Nullable public static final ItemType GREEN_TERRACOTTA = get("minecraft:green_terracotta"); @Nullable public static final ItemType GREEN_WOOL = get("minecraft:green_wool"); + @Nullable public static final ItemType GRINDSTONE = get("minecraft:grindstone"); @Nullable public static final ItemType GUARDIAN_SPAWN_EGG = get("minecraft:guardian_spawn_egg"); @Nullable public static final ItemType GUNPOWDER = get("minecraft:gunpowder"); @Nullable public static final ItemType HAY_BLOCK = get("minecraft:hay_block"); @@ -400,6 +438,7 @@ public final class ItemTypes { @Nullable public static final ItemType IRON_TRAPDOOR = get("minecraft:iron_trapdoor"); @Nullable public static final ItemType ITEM_FRAME = get("minecraft:item_frame"); @Nullable public static final ItemType JACK_O_LANTERN = get("minecraft:jack_o_lantern"); + @Nullable public static final ItemType JIGSAW = get("minecraft:jigsaw"); @Nullable public static final ItemType JUKEBOX = get("minecraft:jukebox"); @Nullable public static final ItemType JUNGLE_BOAT = get("minecraft:jungle_boat"); @Nullable public static final ItemType JUNGLE_BUTTON = get("minecraft:jungle_button"); @@ -411,6 +450,7 @@ public final class ItemTypes { @Nullable public static final ItemType JUNGLE_PLANKS = get("minecraft:jungle_planks"); @Nullable public static final ItemType JUNGLE_PRESSURE_PLATE = get("minecraft:jungle_pressure_plate"); @Nullable public static final ItemType JUNGLE_SAPLING = get("minecraft:jungle_sapling"); + @Nullable public static final ItemType JUNGLE_SIGN = get("minecraft:jungle_sign"); @Nullable public static final ItemType JUNGLE_SLAB = get("minecraft:jungle_slab"); @Nullable public static final ItemType JUNGLE_STAIRS = get("minecraft:jungle_stairs"); @Nullable public static final ItemType JUNGLE_TRAPDOOR = get("minecraft:jungle_trapdoor"); @@ -418,6 +458,7 @@ public final class ItemTypes { @Nullable public static final ItemType KELP = get("minecraft:kelp"); @Nullable public static final ItemType KNOWLEDGE_BOOK = get("minecraft:knowledge_book"); @Nullable public static final ItemType LADDER = get("minecraft:ladder"); + @Nullable public static final ItemType LANTERN = get("minecraft:lantern"); @Nullable public static final ItemType LAPIS_BLOCK = get("minecraft:lapis_block"); @Nullable public static final ItemType LAPIS_LAZULI = get("minecraft:lapis_lazuli"); @Nullable public static final ItemType LAPIS_ORE = get("minecraft:lapis_ore"); @@ -428,7 +469,9 @@ public final class ItemTypes { @Nullable public static final ItemType LEATHER_BOOTS = get("minecraft:leather_boots"); @Nullable public static final ItemType LEATHER_CHESTPLATE = get("minecraft:leather_chestplate"); @Nullable public static final ItemType LEATHER_HELMET = get("minecraft:leather_helmet"); + @Nullable public static final ItemType LEATHER_HORSE_ARMOR = get("minecraft:leather_horse_armor"); @Nullable public static final ItemType LEATHER_LEGGINGS = get("minecraft:leather_leggings"); + @Nullable public static final ItemType LECTERN = get("minecraft:lectern"); @Nullable public static final ItemType LEVER = get("minecraft:lever"); @Nullable public static final ItemType LIGHT_BLUE_BANNER = get("minecraft:light_blue_banner"); @Nullable public static final ItemType LIGHT_BLUE_BED = get("minecraft:light_blue_bed"); @@ -456,6 +499,7 @@ public final class ItemTypes { @Nullable public static final ItemType LIGHT_GRAY_WOOL = get("minecraft:light_gray_wool"); @Nullable public static final ItemType LIGHT_WEIGHTED_PRESSURE_PLATE = get("minecraft:light_weighted_pressure_plate"); @Nullable public static final ItemType LILAC = get("minecraft:lilac"); + @Nullable public static final ItemType LILY_OF_THE_VALLEY = get("minecraft:lily_of_the_valley"); @Nullable public static final ItemType LILY_PAD = get("minecraft:lily_pad"); @Nullable public static final ItemType LIME_BANNER = get("minecraft:lime_banner"); @Nullable public static final ItemType LIME_BED = get("minecraft:lime_bed"); @@ -471,6 +515,7 @@ public final class ItemTypes { @Nullable public static final ItemType LIME_WOOL = get("minecraft:lime_wool"); @Nullable public static final ItemType LINGERING_POTION = get("minecraft:lingering_potion"); @Nullable public static final ItemType LLAMA_SPAWN_EGG = get("minecraft:llama_spawn_egg"); + @Nullable public static final ItemType LOOM = get("minecraft:loom"); @Nullable public static final ItemType MAGENTA_BANNER = get("minecraft:magenta_banner"); @Nullable public static final ItemType MAGENTA_BED = get("minecraft:magenta_bed"); @Nullable public static final ItemType MAGENTA_CARPET = get("minecraft:magenta_carpet"); @@ -492,9 +537,15 @@ public final class ItemTypes { @Nullable public static final ItemType MELON_SLICE = get("minecraft:melon_slice"); @Nullable public static final ItemType MILK_BUCKET = get("minecraft:milk_bucket"); @Nullable public static final ItemType MINECART = get("minecraft:minecart"); + @Nullable public static final ItemType MOJANG_BANNER_PATTERN = get("minecraft:mojang_banner_pattern"); @Nullable public static final ItemType MOOSHROOM_SPAWN_EGG = get("minecraft:mooshroom_spawn_egg"); @Nullable public static final ItemType MOSSY_COBBLESTONE = get("minecraft:mossy_cobblestone"); + @Nullable public static final ItemType MOSSY_COBBLESTONE_SLAB = get("minecraft:mossy_cobblestone_slab"); + @Nullable public static final ItemType MOSSY_COBBLESTONE_STAIRS = get("minecraft:mossy_cobblestone_stairs"); @Nullable public static final ItemType MOSSY_COBBLESTONE_WALL = get("minecraft:mossy_cobblestone_wall"); + @Nullable public static final ItemType MOSSY_STONE_BRICK_SLAB = get("minecraft:mossy_stone_brick_slab"); + @Nullable public static final ItemType MOSSY_STONE_BRICK_STAIRS = get("minecraft:mossy_stone_brick_stairs"); + @Nullable public static final ItemType MOSSY_STONE_BRICK_WALL = get("minecraft:mossy_stone_brick_wall"); @Nullable public static final ItemType MOSSY_STONE_BRICKS = get("minecraft:mossy_stone_bricks"); @Nullable public static final ItemType MULE_SPAWN_EGG = get("minecraft:mule_spawn_egg"); @Nullable public static final ItemType MUSHROOM_STEM = get("minecraft:mushroom_stem"); @@ -519,6 +570,7 @@ public final class ItemTypes { @Nullable public static final ItemType NETHER_BRICK_FENCE = get("minecraft:nether_brick_fence"); @Nullable public static final ItemType NETHER_BRICK_SLAB = get("minecraft:nether_brick_slab"); @Nullable public static final ItemType NETHER_BRICK_STAIRS = get("minecraft:nether_brick_stairs"); + @Nullable public static final ItemType NETHER_BRICK_WALL = get("minecraft:nether_brick_wall"); @Nullable public static final ItemType NETHER_BRICKS = get("minecraft:nether_bricks"); @Nullable public static final ItemType NETHER_QUARTZ_ORE = get("minecraft:nether_quartz_ore"); @Nullable public static final ItemType NETHER_STAR = get("minecraft:nether_star"); @@ -536,6 +588,7 @@ public final class ItemTypes { @Nullable public static final ItemType OAK_PLANKS = get("minecraft:oak_planks"); @Nullable public static final ItemType OAK_PRESSURE_PLATE = get("minecraft:oak_pressure_plate"); @Nullable public static final ItemType OAK_SAPLING = get("minecraft:oak_sapling"); + @Nullable public static final ItemType OAK_SIGN = get("minecraft:oak_sign"); @Nullable public static final ItemType OAK_SLAB = get("minecraft:oak_slab"); @Nullable public static final ItemType OAK_STAIRS = get("minecraft:oak_stairs"); @Nullable public static final ItemType OAK_TRAPDOOR = get("minecraft:oak_trapdoor"); @@ -559,6 +612,7 @@ public final class ItemTypes { @Nullable public static final ItemType OXEYE_DAISY = get("minecraft:oxeye_daisy"); @Nullable public static final ItemType PACKED_ICE = get("minecraft:packed_ice"); @Nullable public static final ItemType PAINTING = get("minecraft:painting"); + @Nullable public static final ItemType PANDA_SPAWN_EGG = get("minecraft:panda_spawn_egg"); @Nullable public static final ItemType PAPER = get("minecraft:paper"); @Nullable public static final ItemType PARROT_SPAWN_EGG = get("minecraft:parrot_spawn_egg"); @Nullable public static final ItemType PEONY = get("minecraft:peony"); @@ -566,6 +620,7 @@ public final class ItemTypes { @Nullable public static final ItemType PHANTOM_MEMBRANE = get("minecraft:phantom_membrane"); @Nullable public static final ItemType PHANTOM_SPAWN_EGG = get("minecraft:phantom_spawn_egg"); @Nullable public static final ItemType PIG_SPAWN_EGG = get("minecraft:pig_spawn_egg"); + @Nullable public static final ItemType PILLAGER_SPAWN_EGG = get("minecraft:pillager_spawn_egg"); @Nullable public static final ItemType PINK_BANNER = get("minecraft:pink_banner"); @Nullable public static final ItemType PINK_BED = get("minecraft:pink_bed"); @Nullable public static final ItemType PINK_CARPET = get("minecraft:pink_carpet"); @@ -585,8 +640,14 @@ public final class ItemTypes { @Nullable public static final ItemType POISONOUS_POTATO = get("minecraft:poisonous_potato"); @Nullable public static final ItemType POLAR_BEAR_SPAWN_EGG = get("minecraft:polar_bear_spawn_egg"); @Nullable public static final ItemType POLISHED_ANDESITE = get("minecraft:polished_andesite"); + @Nullable public static final ItemType POLISHED_ANDESITE_SLAB = get("minecraft:polished_andesite_slab"); + @Nullable public static final ItemType POLISHED_ANDESITE_STAIRS = get("minecraft:polished_andesite_stairs"); @Nullable public static final ItemType POLISHED_DIORITE = get("minecraft:polished_diorite"); + @Nullable public static final ItemType POLISHED_DIORITE_SLAB = get("minecraft:polished_diorite_slab"); + @Nullable public static final ItemType POLISHED_DIORITE_STAIRS = get("minecraft:polished_diorite_stairs"); @Nullable public static final ItemType POLISHED_GRANITE = get("minecraft:polished_granite"); + @Nullable public static final ItemType POLISHED_GRANITE_SLAB = get("minecraft:polished_granite_slab"); + @Nullable public static final ItemType POLISHED_GRANITE_STAIRS = get("minecraft:polished_granite_stairs"); @Nullable public static final ItemType POPPED_CHORUS_FRUIT = get("minecraft:popped_chorus_fruit"); @Nullable public static final ItemType POPPY = get("minecraft:poppy"); @Nullable public static final ItemType PORKCHOP = get("minecraft:porkchop"); @@ -601,6 +662,7 @@ public final class ItemTypes { @Nullable public static final ItemType PRISMARINE_SHARD = get("minecraft:prismarine_shard"); @Nullable public static final ItemType PRISMARINE_SLAB = get("minecraft:prismarine_slab"); @Nullable public static final ItemType PRISMARINE_STAIRS = get("minecraft:prismarine_stairs"); + @Nullable public static final ItemType PRISMARINE_WALL = get("minecraft:prismarine_wall"); @Nullable public static final ItemType PUFFERFISH = get("minecraft:pufferfish"); @Nullable public static final ItemType PUFFERFISH_BUCKET = get("minecraft:pufferfish_bucket"); @Nullable public static final ItemType PUFFERFISH_SPAWN_EGG = get("minecraft:pufferfish_spawn_egg"); @@ -634,19 +696,25 @@ public final class ItemTypes { @Nullable public static final ItemType RABBIT_SPAWN_EGG = get("minecraft:rabbit_spawn_egg"); @Nullable public static final ItemType RABBIT_STEW = get("minecraft:rabbit_stew"); @Nullable public static final ItemType RAIL = get("minecraft:rail"); + @Nullable public static final ItemType RAVAGER_SPAWN_EGG = get("minecraft:ravager_spawn_egg"); @Nullable public static final ItemType RED_BANNER = get("minecraft:red_banner"); @Nullable public static final ItemType RED_BED = get("minecraft:red_bed"); @Nullable public static final ItemType RED_CARPET = get("minecraft:red_carpet"); @Nullable public static final ItemType RED_CONCRETE = get("minecraft:red_concrete"); @Nullable public static final ItemType RED_CONCRETE_POWDER = get("minecraft:red_concrete_powder"); + @Nullable public static final ItemType RED_DYE = get("minecraft:red_dye"); @Nullable public static final ItemType RED_GLAZED_TERRACOTTA = get("minecraft:red_glazed_terracotta"); @Nullable public static final ItemType RED_MUSHROOM = get("minecraft:red_mushroom"); @Nullable public static final ItemType RED_MUSHROOM_BLOCK = get("minecraft:red_mushroom_block"); + @Nullable public static final ItemType RED_NETHER_BRICK_SLAB = get("minecraft:red_nether_brick_slab"); + @Nullable public static final ItemType RED_NETHER_BRICK_STAIRS = get("minecraft:red_nether_brick_stairs"); + @Nullable public static final ItemType RED_NETHER_BRICK_WALL = get("minecraft:red_nether_brick_wall"); @Nullable public static final ItemType RED_NETHER_BRICKS = get("minecraft:red_nether_bricks"); @Nullable public static final ItemType RED_SAND = get("minecraft:red_sand"); @Nullable public static final ItemType RED_SANDSTONE = get("minecraft:red_sandstone"); @Nullable public static final ItemType RED_SANDSTONE_SLAB = get("minecraft:red_sandstone_slab"); @Nullable public static final ItemType RED_SANDSTONE_STAIRS = get("minecraft:red_sandstone_stairs"); + @Nullable public static final ItemType RED_SANDSTONE_WALL = get("minecraft:red_sandstone_wall"); @Nullable public static final ItemType RED_SHULKER_BOX = get("minecraft:red_shulker_box"); @Nullable public static final ItemType RED_STAINED_GLASS = get("minecraft:red_stained_glass"); @Nullable public static final ItemType RED_STAINED_GLASS_PANE = get("minecraft:red_stained_glass_pane"); @@ -661,7 +729,7 @@ public final class ItemTypes { @Nullable public static final ItemType REPEATER = get("minecraft:repeater"); @Nullable public static final ItemType REPEATING_COMMAND_BLOCK = get("minecraft:repeating_command_block"); @Nullable public static final ItemType ROSE_BUSH = get("minecraft:rose_bush"); - @Nullable public static final ItemType ROSE_RED = get("minecraft:rose_red"); + @Deprecated @Nullable public static final ItemType ROSE_RED = Optional.ofNullable(get("minecraft:rose_red")).orElseGet(() -> (get("minecraft:red_dye"))); @Nullable public static final ItemType ROTTEN_FLESH = get("minecraft:rotten_flesh"); @Nullable public static final ItemType SADDLE = get("minecraft:saddle"); @Nullable public static final ItemType SALMON = get("minecraft:salmon"); @@ -671,6 +739,8 @@ public final class ItemTypes { @Nullable public static final ItemType SANDSTONE = get("minecraft:sandstone"); @Nullable public static final ItemType SANDSTONE_SLAB = get("minecraft:sandstone_slab"); @Nullable public static final ItemType SANDSTONE_STAIRS = get("minecraft:sandstone_stairs"); + @Nullable public static final ItemType SANDSTONE_WALL = get("minecraft:sandstone_wall"); + @Nullable public static final ItemType SCAFFOLDING = get("minecraft:scaffolding"); @Nullable public static final ItemType SCUTE = get("minecraft:scute"); @Nullable public static final ItemType SEA_LANTERN = get("minecraft:sea_lantern"); @Nullable public static final ItemType SEA_PICKLE = get("minecraft:sea_pickle"); @@ -681,18 +751,28 @@ public final class ItemTypes { @Nullable public static final ItemType SHULKER_BOX = get("minecraft:shulker_box"); @Nullable public static final ItemType SHULKER_SHELL = get("minecraft:shulker_shell"); @Nullable public static final ItemType SHULKER_SPAWN_EGG = get("minecraft:shulker_spawn_egg"); - @Nullable public static final ItemType SIGN = get("minecraft:sign"); + @Deprecated @Nullable public static final ItemType SIGN = Optional.ofNullable(get("minecraft:sign")).orElseGet(() -> (get("minecraft:oak_sign"))); @Nullable public static final ItemType SILVERFISH_SPAWN_EGG = get("minecraft:silverfish_spawn_egg"); @Nullable public static final ItemType SKELETON_HORSE_SPAWN_EGG = get("minecraft:skeleton_horse_spawn_egg"); @Nullable public static final ItemType SKELETON_SKULL = get("minecraft:skeleton_skull"); @Nullable public static final ItemType SKELETON_SPAWN_EGG = get("minecraft:skeleton_spawn_egg"); + @Nullable public static final ItemType SKULL_BANNER_PATTERN = get("minecraft:skull_banner_pattern"); @Nullable public static final ItemType SLIME_BALL = get("minecraft:slime_ball"); @Nullable public static final ItemType SLIME_BLOCK = get("minecraft:slime_block"); @Nullable public static final ItemType SLIME_SPAWN_EGG = get("minecraft:slime_spawn_egg"); + @Nullable public static final ItemType SMITHING_TABLE = get("minecraft:smithing_table"); + @Nullable public static final ItemType SMOKER = get("minecraft:smoker"); @Nullable public static final ItemType SMOOTH_QUARTZ = get("minecraft:smooth_quartz"); + @Nullable public static final ItemType SMOOTH_QUARTZ_SLAB = get("minecraft:smooth_quartz_slab"); + @Nullable public static final ItemType SMOOTH_QUARTZ_STAIRS = get("minecraft:smooth_quartz_stairs"); @Nullable public static final ItemType SMOOTH_RED_SANDSTONE = get("minecraft:smooth_red_sandstone"); + @Nullable public static final ItemType SMOOTH_RED_SANDSTONE_SLAB = get("minecraft:smooth_red_sandstone_slab"); + @Nullable public static final ItemType SMOOTH_RED_SANDSTONE_STAIRS = get("minecraft:smooth_red_sandstone_stairs"); @Nullable public static final ItemType SMOOTH_SANDSTONE = get("minecraft:smooth_sandstone"); + @Nullable public static final ItemType SMOOTH_SANDSTONE_SLAB = get("minecraft:smooth_sandstone_slab"); + @Nullable public static final ItemType SMOOTH_SANDSTONE_STAIRS = get("minecraft:smooth_sandstone_stairs"); @Nullable public static final ItemType SMOOTH_STONE = get("minecraft:smooth_stone"); + @Nullable public static final ItemType SMOOTH_STONE_SLAB = get("minecraft:smooth_stone_slab"); @Nullable public static final ItemType SNOW = get("minecraft:snow"); @Nullable public static final ItemType SNOW_BLOCK = get("minecraft:snow_block"); @Nullable public static final ItemType SNOWBALL = get("minecraft:snowball"); @@ -713,6 +793,7 @@ public final class ItemTypes { @Nullable public static final ItemType SPRUCE_PLANKS = get("minecraft:spruce_planks"); @Nullable public static final ItemType SPRUCE_PRESSURE_PLATE = get("minecraft:spruce_pressure_plate"); @Nullable public static final ItemType SPRUCE_SAPLING = get("minecraft:spruce_sapling"); + @Nullable public static final ItemType SPRUCE_SIGN = get("minecraft:spruce_sign"); @Nullable public static final ItemType SPRUCE_SLAB = get("minecraft:spruce_slab"); @Nullable public static final ItemType SPRUCE_STAIRS = get("minecraft:spruce_stairs"); @Nullable public static final ItemType SPRUCE_TRAPDOOR = get("minecraft:spruce_trapdoor"); @@ -724,6 +805,7 @@ public final class ItemTypes { @Nullable public static final ItemType STONE_AXE = get("minecraft:stone_axe"); @Nullable public static final ItemType STONE_BRICK_SLAB = get("minecraft:stone_brick_slab"); @Nullable public static final ItemType STONE_BRICK_STAIRS = get("minecraft:stone_brick_stairs"); + @Nullable public static final ItemType STONE_BRICK_WALL = get("minecraft:stone_brick_wall"); @Nullable public static final ItemType STONE_BRICKS = get("minecraft:stone_bricks"); @Nullable public static final ItemType STONE_BUTTON = get("minecraft:stone_button"); @Nullable public static final ItemType STONE_HOE = get("minecraft:stone_hoe"); @@ -731,7 +813,9 @@ public final class ItemTypes { @Nullable public static final ItemType STONE_PRESSURE_PLATE = get("minecraft:stone_pressure_plate"); @Nullable public static final ItemType STONE_SHOVEL = get("minecraft:stone_shovel"); @Nullable public static final ItemType STONE_SLAB = get("minecraft:stone_slab"); + @Nullable public static final ItemType STONE_STAIRS = get("minecraft:stone_stairs"); @Nullable public static final ItemType STONE_SWORD = get("minecraft:stone_sword"); + @Nullable public static final ItemType STONECUTTER = get("minecraft:stonecutter"); @Nullable public static final ItemType STRAY_SPAWN_EGG = get("minecraft:stray_spawn_egg"); @Nullable public static final ItemType STRING = get("minecraft:string"); @Nullable public static final ItemType STRIPPED_ACACIA_LOG = get("minecraft:stripped_acacia_log"); @@ -751,6 +835,8 @@ public final class ItemTypes { @Nullable public static final ItemType SUGAR = get("minecraft:sugar"); @Nullable public static final ItemType SUGAR_CANE = get("minecraft:sugar_cane"); @Nullable public static final ItemType SUNFLOWER = get("minecraft:sunflower"); + @Nullable public static final ItemType SUSPICIOUS_STEW = get("minecraft:suspicious_stew"); + @Nullable public static final ItemType SWEET_BERRIES = get("minecraft:sweet_berries"); @Nullable public static final ItemType TALL_GRASS = get("minecraft:tall_grass"); @Nullable public static final ItemType TERRACOTTA = get("minecraft:terracotta"); @Nullable public static final ItemType TIPPED_ARROW = get("minecraft:tipped_arrow"); @@ -758,6 +844,7 @@ public final class ItemTypes { @Nullable public static final ItemType TNT_MINECART = get("minecraft:tnt_minecart"); @Nullable public static final ItemType TORCH = get("minecraft:torch"); @Nullable public static final ItemType TOTEM_OF_UNDYING = get("minecraft:totem_of_undying"); + @Nullable public static final ItemType TRADER_LLAMA_SPAWN_EGG = get("minecraft:trader_llama_spawn_egg"); @Nullable public static final ItemType TRAPPED_CHEST = get("minecraft:trapped_chest"); @Nullable public static final ItemType TRIDENT = get("minecraft:trident"); @Nullable public static final ItemType TRIPWIRE_HOOK = get("minecraft:tripwire_hook"); @@ -774,6 +861,7 @@ public final class ItemTypes { @Nullable public static final ItemType VILLAGER_SPAWN_EGG = get("minecraft:villager_spawn_egg"); @Nullable public static final ItemType VINDICATOR_SPAWN_EGG = get("minecraft:vindicator_spawn_egg"); @Nullable public static final ItemType VINE = get("minecraft:vine"); + @Nullable public static final ItemType WANDERING_TRADER_SPAWN_EGG = get("minecraft:wandering_trader_spawn_egg"); @Nullable public static final ItemType WATER_BUCKET = get("minecraft:water_bucket"); @Nullable public static final ItemType WET_SPONGE = get("minecraft:wet_sponge"); @Nullable public static final ItemType WHEAT = get("minecraft:wheat"); @@ -783,6 +871,7 @@ public final class ItemTypes { @Nullable public static final ItemType WHITE_CARPET = get("minecraft:white_carpet"); @Nullable public static final ItemType WHITE_CONCRETE = get("minecraft:white_concrete"); @Nullable public static final ItemType WHITE_CONCRETE_POWDER = get("minecraft:white_concrete_powder"); + @Nullable public static final ItemType WHITE_DYE = get("minecraft:white_dye"); @Nullable public static final ItemType WHITE_GLAZED_TERRACOTTA = get("minecraft:white_glazed_terracotta"); @Nullable public static final ItemType WHITE_SHULKER_BOX = get("minecraft:white_shulker_box"); @Nullable public static final ItemType WHITE_STAINED_GLASS = get("minecraft:white_stained_glass"); @@ -791,6 +880,7 @@ public final class ItemTypes { @Nullable public static final ItemType WHITE_TULIP = get("minecraft:white_tulip"); @Nullable public static final ItemType WHITE_WOOL = get("minecraft:white_wool"); @Nullable public static final ItemType WITCH_SPAWN_EGG = get("minecraft:witch_spawn_egg"); + @Nullable public static final ItemType WITHER_ROSE = get("minecraft:wither_rose"); @Nullable public static final ItemType WITHER_SKELETON_SKULL = get("minecraft:wither_skeleton_skull"); @Nullable public static final ItemType WITHER_SKELETON_SPAWN_EGG = get("minecraft:wither_skeleton_spawn_egg"); @Nullable public static final ItemType WOLF_SPAWN_EGG = get("minecraft:wolf_spawn_egg"); @@ -806,6 +896,7 @@ public final class ItemTypes { @Nullable public static final ItemType YELLOW_CARPET = get("minecraft:yellow_carpet"); @Nullable public static final ItemType YELLOW_CONCRETE = get("minecraft:yellow_concrete"); @Nullable public static final ItemType YELLOW_CONCRETE_POWDER = get("minecraft:yellow_concrete_powder"); + @Nullable public static final ItemType YELLOW_DYE = get("minecraft:yellow_dye"); @Nullable public static final ItemType YELLOW_GLAZED_TERRACOTTA = get("minecraft:yellow_glazed_terracotta"); @Nullable public static final ItemType YELLOW_SHULKER_BOX = get("minecraft:yellow_shulker_box"); @Nullable public static final ItemType YELLOW_STAINED_GLASS = get("minecraft:yellow_stained_glass"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java index 02e9a66ff..1e8f1ab2c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java @@ -23,6 +23,8 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; +import java.util.OptionalInt; + import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; @@ -64,6 +66,14 @@ public interface BlockRegistry { */ Map> getProperties(BlockType blockType); + /** + * Retrieve the internal ID for a given state, if possible. + * + * @param state The block state + * @return the internal ID of the state + */ + OptionalInt getInternalBlockStateId(BlockState state); + /** * Register all blocks */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java index 3d318ef01..8da5e576b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java @@ -20,16 +20,19 @@ package com.sk89q.worldedit.world.registry; import com.google.common.io.Resources; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.gson.VectorAdapter; +import com.sk89q.worldedit.util.io.ResourceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.io.IOException; -import java.lang.reflect.Type; import java.net.URL; import java.nio.charset.Charset; import java.util.HashMap; @@ -47,7 +50,7 @@ import java.util.Map; * reading fails (which occurs when this class is first instantiated), then * the methods will return {@code null}s for all blocks.

*/ -public class BundledBlockData { +public final class BundledBlockData { private static final Logger log = LoggerFactory.getLogger(BundledBlockData.class); private static BundledBlockData INSTANCE; @@ -73,20 +76,19 @@ public class BundledBlockData { private void loadFromResource() throws IOException { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Vector3.class, new VectorAdapter()); - gsonBuilder.registerTypeAdapter(int.class, (JsonDeserializer) (json, typeOfT, context) -> { - JsonPrimitive primitive = (JsonPrimitive) json; - if (primitive.isString()) { - String value = primitive.getAsString(); - if (value.charAt(0) == '#') return Integer.parseInt(value.substring(1), 16); - return Integer.parseInt(value); - } - return primitive.getAsInt(); - }); Gson gson = gsonBuilder.create(); - URL url = BundledBlockData.class.getResource("blocks.json"); + URL url = null; + final int dataVersion = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion(); + if (dataVersion > 1900) { // > MC 1.13 + url = ResourceLoader.getResource(BundledBlockData.class, "blocks.114.json"); + } + if (url == null) { + url = ResourceLoader.getResource(BundledBlockData.class, "blocks.json"); + } if (url == null) { throw new IOException("Could not find blocks.json"); } + log.debug("Using {} for bundled block data.", url); String data = Resources.toString(url, Charset.defaultCharset()); List entries = gson.fromJson(data, new TypeToken>() {}.getType()); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java index 62c8b5c3a..96739d6fb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockRegistry.java @@ -20,12 +20,13 @@ package com.sk89q.worldedit.world.registry; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; +import javax.annotation.Nullable; import java.util.Collections; import java.util.Map; - -import javax.annotation.Nullable; +import java.util.OptionalInt; /** * A block registry that uses {@link BundledBlockData} to serve information @@ -52,4 +53,9 @@ public class BundledBlockRegistry implements BlockRegistry { return Collections.emptyMap(); // Oof } + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + return OptionalInt.empty(); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemData.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemData.java index 4bdac0c8a..28c6c6aa7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemData.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemData.java @@ -23,8 +23,11 @@ import com.google.common.io.Resources; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.gson.VectorAdapter; +import com.sk89q.worldedit.util.io.ResourceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +50,7 @@ import java.util.Map; * reading fails (which occurs when this class is first instantiated), then * the methods will return {@code null}s for all items.

*/ -public class BundledItemData { +public final class BundledItemData { private static final Logger log = LoggerFactory.getLogger(BundledItemData.class); private static BundledItemData INSTANCE; @@ -74,10 +77,18 @@ public class BundledItemData { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Vector3.class, new VectorAdapter()); Gson gson = gsonBuilder.create(); - URL url = BundledItemData.class.getResource("items.json"); + URL url = null; + final int dataVersion = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion(); + if (dataVersion > 1900) { // > MC 1.13 + url = ResourceLoader.getResource(BundledBlockData.class, "items.114.json"); + } + if (url == null) { + url = ResourceLoader.getResource(BundledBlockData.class, "items.json"); + } if (url == null) { throw new IOException("Could not find items.json"); } + log.debug("Using {} for bundled item data.", url); String data = Resources.toString(url, Charset.defaultCharset()); List entries = gson.fromJson(data, new TypeToken>() {}.getType()); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/CategoryRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/CategoryRegistry.java index 496893821..0de14a17c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/CategoryRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/CategoryRegistry.java @@ -20,13 +20,14 @@ package com.sk89q.worldedit.world.registry; import com.sk89q.worldedit.registry.Category; +import com.sk89q.worldedit.registry.Keyed; import java.util.Set; /** * A registry of categories. Minecraft internally calls these 'Tags'. */ -public interface CategoryRegistry { +public interface CategoryRegistry { /** * Gets a set of values with a given category. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/EntityRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/EntityRegistry.java index ff879c6b8..c522451ac 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/EntityRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/EntityRegistry.java @@ -19,13 +19,9 @@ package com.sk89q.worldedit.world.registry; -import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; -import com.sk89q.worldedit.world.item.ItemType; -import com.sk89q.worldedit.world.item.ItemTypes; import javax.annotation.Nullable; import java.util.Collection; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java index fb7b9a83b..538927977 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java @@ -28,10 +28,15 @@ import com.google.common.io.Resources; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.registry.state.PropertyKey; import com.sk89q.worldedit.util.gson.VectorAdapter; +import com.sk89q.worldedit.util.io.ResourceLoader; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -49,7 +54,7 @@ import java.net.URL; import java.nio.charset.Charset; import java.util.Map; -public class LegacyMapper { +public final class LegacyMapper { private static final Logger log = LoggerFactory.getLogger(LegacyMapper.class); private static LegacyMapper INSTANCE; @@ -66,7 +71,6 @@ public class LegacyMapper { try { loadFromResource(); } catch (Throwable e) { - e.printStackTrace(); log.warn("Failed to load the built-in legacy id registry", e); } } @@ -80,12 +84,14 @@ public class LegacyMapper { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Vector3.class, new VectorAdapter()); Gson gson = gsonBuilder.disableHtmlEscaping().create(); - URL url = LegacyMapper.class.getResource("legacy.json"); + URL url = ResourceLoader.getResource(LegacyMapper.class, "legacy.json"); if (url == null) { throw new IOException("Could not find legacy.json"); } String source = Resources.toString(url, Charset.defaultCharset()); LegacyDataFile dataFile = gson.fromJson(source, new TypeToken() {}.getType()); + + DataFixer fixer = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataFixer(); ParserContext parserContext = new ParserContext(); parserContext.setPreferringWildcard(false); parserContext.setRestricted(false); @@ -93,7 +99,7 @@ public class LegacyMapper { for (Map.Entry blockEntry : dataFile.blocks.entrySet()) { try { - BlockStateHolder blockState = BlockState.get(null, blockEntry.getValue()); + BlockState blockState = BlockState.get(null, blockEntry.getValue()); BlockType type = blockState.getBlockType(); if (type.hasProperty(PropertyKey.WATERLOGGED)) { blockState = blockState.with(PropertyKey.WATERLOGGED, false); @@ -103,7 +109,7 @@ public class LegacyMapper { blockStateToLegacyId4Data.put(blockState.getInternalId(), (Integer) combinedId); blockStateToLegacyId4Data.putIfAbsent(blockState.getInternalBlockTypeId(), combinedId); - } catch (Exception e) { + } catch (InputParseException e) { log.warn("Unknown block: " + blockEntry.getValue()); } } @@ -143,7 +149,12 @@ public class LegacyMapper { public BlockState getBlockFromLegacy(String input) { if (input.startsWith("minecraft:")) input = input.substring(10); - return BlockState.getFromInternalId(blockArr[getCombinedId(input)]); + try { + return BlockState.getFromInternalId(blockArr[getCombinedId(input)]); + } catch (InputParseException e) { + e.printStackTrace(); + } + return null; } @Nullable @@ -186,7 +197,11 @@ public class LegacyMapper { try { int internalId = blockArr[combinedId]; if (internalId == 0) return null; - return BlockState.getFromInternalId(internalId); + try { + return BlockState.getFromInternalId(internalId); + } catch (InputParseException e) { + e.printStackTrace(); + } } catch (IndexOutOfBoundsException ignore) { return null; } @@ -196,14 +211,18 @@ public class LegacyMapper { extra = extraId4DataToStateId.get(combinedId & 0xFF0); } if (extra != null) { - return BlockState.getFromInternalId(extra); + try { + return BlockState.getFromInternalId(extra); + } catch (InputParseException e) { + e.printStackTrace(); + } } return null; } public void register(int id, int data, BlockStateHolder state) { int combinedId = ((id << 4) + data); - extraId4DataToStateId.put((int) combinedId, (Integer) state.getInternalId()); + extraId4DataToStateId.put(combinedId, (Integer) state.getInternalId()); blockStateToLegacyId4Data.putIfAbsent(state.getInternalId(), combinedId); } @@ -229,7 +248,7 @@ public class LegacyMapper { if(plotBlock instanceof StringPlotBlock) { try { return BlockTypes.get(plotBlock.toString()).getDefaultState().toBaseBlock(); - }catch(Throwable failed) { + } catch (Throwable failed) { log.error("Unable to convert StringPlotBlock " + plotBlock + " to BaseBlock!"); failed.printStackTrace(); return null; @@ -237,7 +256,7 @@ public class LegacyMapper { }else if(plotBlock instanceof LegacyPlotBlock) { try { return BaseBlock.getState(((LegacyPlotBlock)plotBlock).getId(), ((LegacyPlotBlock)plotBlock).getData()).toBaseBlock(); - }catch(Throwable failed) { + } catch (Throwable failed) { log.error("Unable to convert LegacyPlotBlock " + plotBlock + " to BaseBlock!"); failed.printStackTrace(); return null; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java index 17436f1f3..3edea1dd0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java @@ -178,7 +178,7 @@ public class PassthroughBlockMaterial implements BlockMaterial { if (blockMaterial == null) { return true; } else { - return blockMaterial.isOpaque(); + return blockMaterial.isBurnable(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java index 6a8b2b378..b863b9cb3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java @@ -45,7 +45,7 @@ class SimpleBlockMaterial implements BlockMaterial { @Override public boolean isAir() { - return isAir; + return this.isAir; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/Snapshot.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/Snapshot.java index 8dc7735b1..7ee097d94 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/Snapshot.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/Snapshot.java @@ -34,7 +34,7 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; -import java.util.Calendar; +import java.time.ZonedDateTime; import java.util.Locale; import java.util.zip.ZipFile; @@ -47,7 +47,7 @@ public class Snapshot implements Comparable { protected File file; protected String name; - protected Calendar date; + protected ZonedDateTime date; /** * Construct a snapshot restoration operation. @@ -188,7 +188,7 @@ public class Snapshot implements Comparable { * * @return date for the snapshot */ - public Calendar getDate() { + public ZonedDateTime getDate() { return date; } @@ -197,7 +197,7 @@ public class Snapshot implements Comparable { * * @param date the date of the snapshot */ - public void setDate(Calendar date) { + public void setDate(ZonedDateTime date) { this.date = date; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java index 72650a408..e6fe6aec7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java @@ -23,14 +23,16 @@ package com.sk89q.worldedit.world.snapshot; import com.sk89q.worldedit.world.storage.MissingWorldException; +import javax.annotation.Nullable; import java.io.File; import java.io.FilenameFilter; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.List; - -import javax.annotation.Nullable; +import java.util.Locale; /** * A repository contains zero or more snapshots. @@ -115,12 +117,12 @@ public class SnapshotRepository { * @return a snapshot or null */ @Nullable - public Snapshot getSnapshotAfter(Calendar date, String world) throws MissingWorldException { + public Snapshot getSnapshotAfter(ZonedDateTime date, String world) throws MissingWorldException { List snapshots = getSnapshots(true, world); Snapshot last = null; for (Snapshot snapshot : snapshots) { - if (snapshot.getDate() != null && snapshot.getDate().before(date)) { + if (snapshot.getDate() != null && snapshot.getDate().compareTo(date) < 0) { return last; } @@ -137,12 +139,12 @@ public class SnapshotRepository { * @return a snapshot or null */ @Nullable - public Snapshot getSnapshotBefore(Calendar date, String world) throws MissingWorldException { + public Snapshot getSnapshotBefore(ZonedDateTime date, String world) throws MissingWorldException { List snapshots = getSnapshots(false, world); Snapshot last = null; for (Snapshot snapshot : snapshots) { - if (snapshot.getDate().after(date)) { + if (snapshot.getDate().compareTo(date) > 0) { return last; } @@ -161,7 +163,7 @@ public class SnapshotRepository { for (SnapshotDateParser parser : dateParsers) { Calendar date = parser.detectDate(snapshot.getFile()); if (date != null) { - snapshot.setDate(date); + snapshot.setDate(date.toInstant().atZone(ZoneOffset.UTC)); return; } } @@ -207,11 +209,17 @@ public class SnapshotRepository { return false; } - return (file.isDirectory() && (new File(file, "level.dat")).exists()) - || (file.isFile() && (file.getName().toLowerCase().endsWith(".zip") - || file.getName().toLowerCase().endsWith(".tar.bz2") - || file.getName().toLowerCase().endsWith(".tar.gz") - || file.getName().toLowerCase().endsWith(".tar"))); + if (file.isDirectory() && new File(file, "level.dat").exists()) { + return true; + } + if (file.isFile()) { + String lowerCaseFileName = file.getName().toLowerCase(Locale.ROOT); + return lowerCaseFileName.endsWith(".zip") + || lowerCaseFileName.endsWith(".tar.bz2") + || lowerCaseFileName.endsWith(".tar.gz") + || lowerCaseFileName.endsWith(".tar"); + } + return false; } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/YYMMDDHHIISSParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/YYMMDDHHIISSParser.java index 3356f71f6..51a8fa393 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/YYMMDDHHIISSParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/YYMMDDHHIISSParser.java @@ -27,13 +27,13 @@ import java.util.regex.Pattern; public class YYMMDDHHIISSParser implements SnapshotDateParser { - protected Pattern patt = + private Pattern datePattern = Pattern.compile("([0-9]+)[^0-9]?([0-9]+)[^0-9]?([0-9]+)[^0-9]?" - + "([0-9]+)[^0-9]?([0-9]+)[^0-9]?([0-9]+)"); + + "([0-9]+)[^0-9]?([0-9]+)[^0-9]?([0-9]+)(\\..*)?"); @Override public Calendar detectDate(File file) { - Matcher matcher = patt.matcher(file.getName()); + Matcher matcher = datePattern.matcher(file.getName()); if (matcher.matches()) { int year = Integer.parseInt(matcher.group(1)); int month = Integer.parseInt(matcher.group(2)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java index 319503740..1b89c8b09 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java @@ -21,9 +21,13 @@ package com.sk89q.worldedit.world.storage; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.DataException; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.chunk.AnvilChunk; import com.sk89q.worldedit.world.chunk.AnvilChunk13; @@ -42,7 +46,7 @@ public abstract class ChunkStore implements Closeable { /** * The DataVersion for Minecraft 1.13 */ - public static final int DATA_VERSION_MC_1_13 = 1519; + private static final int DATA_VERSION_MC_1_13 = 1519; /** * {@code >>} - to chunk @@ -102,6 +106,15 @@ public abstract class ChunkStore implements Closeable { } int dataVersion = rootTag.getInt("DataVersion"); + if (dataVersion == 0) dataVersion = -1; + final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING); + final int currentDataVersion = platform.getDataVersion(); + if (tag.getValue().containsKey("Sections") && dataVersion < currentDataVersion) { // only fix up MCA format, DFU doesn't support MCR chunks + final DataFixer dataFixer = platform.getDataFixer(); + if (dataFixer != null) { + return new AnvilChunk13((CompoundTag) dataFixer.fixUp(DataFixer.FixTypes.CHUNK, rootTag, dataVersion).getValue().get("Level")); + } + } if (dataVersion >= DATA_VERSION_MC_1_13) { return new AnvilChunk13(tag); } @@ -114,6 +127,7 @@ public abstract class ChunkStore implements Closeable { return new OldChunk(world, tag); } + @Override public void close() throws IOException { } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionChunkStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionChunkStore.java index 5c4eabfd9..b8b82c314 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionChunkStore.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionChunkStore.java @@ -36,7 +36,7 @@ public abstract class McRegionChunkStore extends ChunkStore { /** * Get the filename of a region file. - * + * * @param position chunk position * @return the filename */ @@ -78,13 +78,13 @@ public abstract class McRegionChunkStore extends ChunkStore { throw new ChunkStoreException("CompoundTag expected for chunk; got " + tag.getClass().getName()); } - return (CompoundTag)tag; + return (CompoundTag) tag; } } /** * Get the input stream for a chunk file. - * + * * @param name the name of the chunk file * @param worldName the world name * @return an input stream diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java index e991216ae..6e7b1766f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java @@ -85,7 +85,7 @@ public class McRegionReader { /** * Construct the reader. - * + * * @param stream the stream * @throws DataException * @throws IOException @@ -99,11 +99,10 @@ public class McRegionReader { /** * Read the header. - * - * @throws DataException + * * @throws IOException */ - private void readHeader() throws DataException, IOException { + private void readHeader() throws IOException { offsets = new int[SECTOR_INTS]; for (int i = 0; i < SECTOR_INTS; ++i) { @@ -114,7 +113,7 @@ public class McRegionReader { /** * Gets the uncompressed data input stream for a chunk. - * + * * @param position chunk position * @return an input stream * @throws IOException @@ -138,7 +137,7 @@ public class McRegionReader { int sectorNumber = offset >> 8; int numSectors = offset & 0xFF; - stream.seek(sectorNumber * SECTOR_BYTES); + stream.seek((long) sectorNumber * SECTOR_BYTES); int length = dataStream.readInt(); if (length > SECTOR_BYTES * numSectors) { @@ -170,7 +169,7 @@ public class McRegionReader { /** * Get the offset for a chunk. May return 0 if it doesn't exist. - * + * * @param x the X coordinate * @param z the Z coordinate * @return the offset diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java index 6b61bddf9..3da5c0047 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java @@ -52,7 +52,7 @@ public final class NBTConversions { return new Location( extent, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), - (float) directionTag.asDouble(0), (float) directionTag.asDouble(1)); + directionTag.getFloat(0), directionTag.getFloat(1)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/TrueZipMcRegionChunkStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/TrueZipMcRegionChunkStore.java index 8d7c03e93..a885de689 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/TrueZipMcRegionChunkStore.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/TrueZipMcRegionChunkStore.java @@ -50,7 +50,7 @@ public class TrueZipMcRegionChunkStore extends McRegionChunkStore { * @param zipFile the ZIP file * @param folder the folder to look into * @throws IOException - * @throws ZipException + * @throws ZipException */ public TrueZipMcRegionChunkStore(File zipFile, String folder) throws IOException, ZipException { this.zipFile = zipFile; @@ -101,6 +101,7 @@ public class TrueZipMcRegionChunkStore extends McRegionChunkStore { // Check for file if (pattern.matcher(testEntry.getName()).matches()) { folder = testEntry.getName().substring(0, testEntry.getName().lastIndexOf('/')); + if (folder.endsWith("poi")) continue; name = folder + "/" + name; break; } @@ -115,7 +116,14 @@ public class TrueZipMcRegionChunkStore extends McRegionChunkStore { ZipEntry entry = getEntry(name); if (entry == null) { - throw new MissingChunkException(); + if (name.endsWith(".mca")) { // try old mcr format + entry = getEntry(name.replace(".mca", ".mcr")); + if (entry == null) { + throw new MissingChunkException(); + } + } else { + throw new MissingChunkException(); + } } try { return zip.getInputStream(entry); @@ -126,7 +134,7 @@ public class TrueZipMcRegionChunkStore extends McRegionChunkStore { /** * Get an entry from the ZIP, trying both types of slashes. - * + * * @param file the file * @return an entry */ @@ -150,7 +158,7 @@ public class TrueZipMcRegionChunkStore extends McRegionChunkStore { ZipEntry testEntry = e.nextElement(); - if (testEntry.getName().matches(".*\\.mcr$") || testEntry.getName().matches(".*\\.mca$")) { // TODO: does this need a separate class? + if (testEntry.getName().matches(".*\\.mcr$") || testEntry.getName().matches(".*\\.mca$")) { return true; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ZippedMcRegionChunkStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ZippedMcRegionChunkStore.java index 581686ea4..ca3854bcd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ZippedMcRegionChunkStore.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ZippedMcRegionChunkStore.java @@ -87,6 +87,7 @@ public class ZippedMcRegionChunkStore extends McRegionChunkStore { if (testEntry.getName().startsWith(worldName + "/")) { if (pattern.matcher(testEntry.getName()).matches()) { // does entry end in .mca folder = testEntry.getName().substring(0, testEntry.getName().lastIndexOf('/')); + if (folder.endsWith("poi")) continue; name = folder + "/" + name; break; } @@ -102,7 +103,14 @@ public class ZippedMcRegionChunkStore extends McRegionChunkStore { ZipEntry entry = getEntry(name); if (entry == null) { - throw new MissingChunkException(); + if (name.endsWith(".mca")) { // try old mcr format + entry = getEntry(name.replace(".mca", ".mcr")); + if (entry == null) { + throw new MissingChunkException(); + } + } else { + throw new MissingChunkException(); + } } try { return zip.getInputStream(entry); @@ -113,7 +121,7 @@ public class ZippedMcRegionChunkStore extends McRegionChunkStore { /** * Get an entry from the ZIP, trying both types of slashes. - * + * * @param file the file * @return a ZIP entry */ @@ -136,7 +144,7 @@ public class ZippedMcRegionChunkStore extends McRegionChunkStore { ZipEntry testEntry = e.nextElement(); - if (testEntry.getName().matches(".*\\.mcr$") || testEntry.getName().matches(".*\\.mca$")) { // TODO: does this need a separate class? + if (testEntry.getName().matches(".*\\.mcr$") || testEntry.getName().matches(".*\\.mca$")) { return true; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherType.java index c44392df0..43f689d8c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherType.java @@ -19,9 +19,10 @@ package com.sk89q.worldedit.world.weather; +import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.Registry; -public class WeatherType { +public class WeatherType implements Keyed { public static final Registry REGISTRY = new Registry<>("weather type"); @@ -31,6 +32,7 @@ public class WeatherType { this.id = id; } + @Override public String getId() { return this.id; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherTypes.java index d7450610d..dd0f07e27 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/weather/WeatherTypes.java @@ -21,23 +21,25 @@ package com.sk89q.worldedit.world.weather; import javax.annotation.Nullable; -public class WeatherTypes { +public final class WeatherTypes { - static { - // This isn't really a proper registry - so inject these before they're obtained. - WeatherType.REGISTRY.register("clear", new WeatherType("clear")); - WeatherType.REGISTRY.register("rain", new WeatherType("rain")); - WeatherType.REGISTRY.register("thunder_storm", new WeatherType("thunder_storm")); - } - - @Nullable public static final WeatherType CLEAR = get("clear"); - @Nullable public static final WeatherType RAIN = get("rain"); - @Nullable public static final WeatherType THUNDER_STORM = get("thunder_storm"); + public static final WeatherType CLEAR = register("clear"); + public static final WeatherType RAIN = register("rain"); + public static final WeatherType THUNDER_STORM = register("thunder_storm"); private WeatherTypes() { } - public static @Nullable WeatherType get(final String id) { + private static WeatherType register(String id) { + return register(new WeatherType(id)); + } + + public static WeatherType register(WeatherType weather) { + return WeatherType.REGISTRY.register(weather.getId(), weather); + } + + @Nullable + public static WeatherType get(final String id) { return WeatherType.REGISTRY.get(id); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java index 45d4169e4..052f19683 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java @@ -98,8 +98,6 @@ public class ConfigurateConfiguration extends LocalConfiguration { superPickaxeDrop = node.getNode("super-pickaxe", "drop-items").getBoolean(superPickaxeDrop); superPickaxeManyDrop = node.getNode("super-pickaxe", "many-drop-items").getBoolean(superPickaxeManyDrop); - noDoubleSlash = node.getNode("no-double-slash").getBoolean(noDoubleSlash); - useInventory = node.getNode("use-inventory", "enable").getBoolean(useInventory); useInventoryOverride = node.getNode("use-inventory", "allow-override").getBoolean(useInventoryOverride); useInventoryCreativeOverride = node.getNode("use-inventory", "creative-mode-overrides").getBoolean(useInventoryCreativeOverride); @@ -131,4 +129,4 @@ public class ConfigurateConfiguration extends LocalConfiguration { String type = node.getNode("shell-save-type").getString("").trim(); shellSaveType = type.equals("") ? null : type; } -} \ No newline at end of file +}