From 60759934a3c06e22733bae52859d026491c5fa0b Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Wed, 20 Nov 2019 00:11:54 +0000 Subject: [PATCH] Fix some merge issues --- worldedit-bukkit/build.gradle.kts | 2 + .../sk89q/worldedit/bukkit/BukkitPlayer.java | 16 +- worldedit-core/build.gradle.kts | 3 +- .../fawe/object/brush/BrushSettings.java | 13 +- .../object/pattern/ExpressionPattern.java | 4 +- .../plotquared/FaweSchematicHandler.java | 4 +- .../fawe/util/DocumentationPrinter.java | 287 ------------ .../sk89q/jnbt/CompressedSchematicTag.java | 4 +- .../java/com/sk89q/worldedit/EditSession.java | 62 ++- .../com/sk89q/worldedit/LocalSession.java | 6 +- .../java/com/sk89q/worldedit/WorldEdit.java | 6 +- .../worldedit/command/ApplyBrushCommands.java | 2 +- .../worldedit/command/BiomeCommands.java | 1 + .../worldedit/command/BrushCommands.java | 26 +- .../worldedit/command/ClipboardCommands.java | 8 +- .../worldedit/command/GeneralCommands.java | 4 +- .../worldedit/command/GenerationCommands.java | 21 +- .../worldedit/command/HistoryCommands.java | 44 +- .../sk89q/worldedit/command/MaskCommands.java | 2 +- .../worldedit/command/PaintBrushCommands.java | 2 +- .../worldedit/command/PatternCommands.java | 4 +- .../worldedit/command/RegionCommands.java | 38 +- .../worldedit/command/SchematicCommands.java | 81 ++-- .../worldedit/command/ScriptingCommands.java | 9 - .../worldedit/command/SelectionCommands.java | 5 +- .../command/SnapshotUtilCommands.java | 34 +- .../worldedit/command/ToolUtilCommands.java | 4 +- .../worldedit/command/UtilityCommands.java | 15 +- .../command/argument/ExpressionConverter.java | 21 +- .../command/factory/TreeGeneratorFactory.java | 44 ++ .../command/tool/RecursivePickaxe.java | 3 + .../command/tool/brush/GravityBrush.java | 9 +- .../factory/parser/DefaultBlockParser.java | 22 +- .../platform/AbstractNonPlayerActor.java | 64 +++ .../platform/PlatformCommandManager.java | 4 +- .../extension/platform/PlatformManager.java | 2 - .../platform/binding/AnnotatedBindings.java | 46 -- .../platform/binding/ConsumeBindings.java | 1 - .../platform/binding/PrimitiveBindings.java | 25 +- .../clipboard/io/BuiltInClipboardFormat.java | 8 +- .../clipboard/io/FastSchematicReader.java | 343 ++++++++++++++ .../clipboard/io/FastSchematicWriter.java | 341 ++++++++++++++ .../clipboard/io/MCEditSchematicReader.java | 1 + .../clipboard/io/SpongeSchematicReader.java | 417 +++++++----------- .../clipboard/io/SpongeSchematicWriter.java | 317 ++++--------- .../function/mask/ExpressionMask.java | 7 +- .../function/operation/ForwardExtentCopy.java | 1 + .../WorldEditExceptionConverter.java | 1 - .../internal/expression/Expression.java | 104 ++--- .../com/sk89q/worldedit/math/BitMath.java | 52 +++ .../sk89q/worldedit/math/BlockVector3.java | 2 +- .../sk89q/worldedit/util/auth/Subject.java | 10 +- .../worldedit/world/block/BlockType.java | 5 +- .../world/registry/LegacyMapper.java | 26 +- 54 files changed, 1403 insertions(+), 1180 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/boydti/fawe/util/DocumentationPrinter.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java delete mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/AnnotatedBindings.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/math/BitMath.java diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 09b6db88c..6c44e67ab 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -21,6 +21,7 @@ repositories { maven { url = uri("http://repo.dmulloy2.net/content/groups/public/") } maven { url = uri("http://ci.ender.zone/plugin/repository/everything/") } maven { url = uri("https://repo.inventivetalent.org/content/groups/public/")} + flatDir {dir(File("lib"))} } configurations.all { @@ -34,6 +35,7 @@ dependencies { "api"(project(":worldedit-core")) "api"(project(":worldedit-libs:core")) "api"(project(":worldedit-libs:bukkit")) + "compile"(":worldedit-adapters:") "compile"("it.unimi.dsi:fastutil:8.2.1") "api"("com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT") { exclude("junit", "junit") 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 8cd06e0ca..389b081f3 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 @@ -230,16 +230,6 @@ public class BukkitPlayer extends AbstractPlayerActor { || plugin.getPermissionsResolver().hasPermission(player.getWorld().getName(), player, perm); } - @Override public boolean togglePermission(String permission) { - if (this.hasPermission(permission)) { - player.addAttachment(plugin).setPermission(permission, false); - return false; - } else { - player.addAttachment(plugin).setPermission(permission, true); - return true; - } - } - @Override public boolean isAllowedToFly() { return player.getAllowFlight(); @@ -257,13 +247,13 @@ public class BukkitPlayer extends AbstractPlayerActor { * - The `/wea` command will give/remove the required bypass permission */ if (Fawe.imp().getVault() == null || Fawe. imp().getVault().permission == null) { - player.addAttachment(Fawe. imp().getPlugin()).setPermission(permission, value); + player.addAttachment(plugin).setPermission(permission, value); } else if (value) { if (!Fawe. imp().getVault().permission.playerAdd(player, permission)) { - player.addAttachment(Fawe. imp().getPlugin()).setPermission(permission, value); + player.addAttachment(plugin).setPermission(permission, value); } } else if (!Fawe.imp().getVault().permission.playerRemove(player, permission)) { - player.addAttachment(Fawe.imp().getPlugin()).setPermission(permission, value); + player.addAttachment(plugin).setPermission(permission, value); } } diff --git a/worldedit-core/build.gradle.kts b/worldedit-core/build.gradle.kts index dfe9f42b6..645bf6fbe 100644 --- a/worldedit-core/build.gradle.kts +++ b/worldedit-core/build.gradle.kts @@ -10,6 +10,7 @@ plugins { repositories { maven { url = uri("http://ci.athion.net/job/PlotSquared-breaking/ws/mvn/") } + } applyPlatformAndCoreConfiguration() @@ -23,7 +24,7 @@ configurations.all { dependencies { "compile"(project(":worldedit-libs:core")) "compile"("de.schlichtherle:truezip:6.8.3") - "compile"("rhino:js:1.7.11") + "compile"("org.mozilla:rhino:1.7.11") "compile"("org.yaml:snakeyaml:1.23") "compile"("com.google.guava:guava:21.0") "compile"("com.google.code.findbugs:jsr305:3.0.2") 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 6e855f10b..13ef9dc66 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 @@ -16,10 +16,9 @@ import com.sk89q.worldedit.extension.platform.Actor; 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.EvaluationException; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; -import com.sk89q.worldedit.internal.expression.runtime.Constant; -import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -39,6 +38,8 @@ public class BrushSettings { SCROLL_ACTION, } + private static final Expression DEFAULT_SIZE = Expression.compile("1"); + private final Map constructor = new ConcurrentHashMap<>(); private Brush brush; @@ -46,7 +47,7 @@ public class BrushSettings { private Mask sourceMask; private ResettableExtent transform; private Pattern material; - private Expression size = new Expression(1); + private Expression size = DEFAULT_SIZE; private Set permissions; private Scroll scrollAction; private String lastWorld; @@ -135,7 +136,7 @@ public class BrushSettings { transform = null; material = null; scrollAction = null; - size = new Expression(1); + size = DEFAULT_SIZE; permissions.clear(); constructor.clear(); return this; @@ -182,7 +183,7 @@ public class BrushSettings { public BrushSettings setSize(Expression size) { checkNotNull(size); this.size = size; - if (size.getRoot() instanceof Constant && ((Constant) size.getRoot()).getValue() == -1) { + if (size == DEFAULT_SIZE) { constructor.remove(SettingType.SIZE); } else { constructor.put(SettingType.SIZE, size.toString()); @@ -191,7 +192,7 @@ public class BrushSettings { } public BrushSettings setSize(double size) { - return setSize(new Expression(size)); + return setSize(Expression.compile(Double.toString(size))); } public BrushSettings setScrollAction(Scroll scrollAction) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java index 300da2a5e..ee1e054ce 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java @@ -3,9 +3,9 @@ package com.boydti.fawe.object.pattern; import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.function.pattern.AbstractPattern; +import com.sk89q.worldedit.internal.expression.EvaluationException; 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.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.world.block.BaseBlock; @@ -49,7 +49,7 @@ public class ExpressionPattern extends AbstractPattern { ((WorldEditExpressionEnvironment) expression.getEnvironment()).setCurrentBlock(vector.toVector3()); } double combined = expression.evaluate(vector.getX(), vector.getY(), vector.getZ()); - return BlockState.getFromInternalId((int) combined).toBaseBlock(); + return BlockState.getFromOrdinal((int) combined).toBaseBlock(); } catch (EvaluationException e) { e.printStackTrace(); return BlockTypes.AIR.getDefaultState().toBaseBlock(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweSchematicHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweSchematicHandler.java index 6cde9f23a..3a3eec8ff 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweSchematicHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/general/integrations/plotquared/FaweSchematicHandler.java @@ -22,7 +22,7 @@ import com.sk89q.worldedit.EditSession; 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.SpongeSchematicWriter; +import com.sk89q.worldedit.extent.clipboard.io.FastSchematicWriter; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import java.io.BufferedOutputStream; @@ -78,7 +78,7 @@ public class FaweSchematicHandler extends SchematicHandler { if (cTag instanceof CompressedSchematicTag) { Clipboard clipboard = (Clipboard) cTag.getSource(); try (OutputStream stream = new FileOutputStream(tmp); NBTOutputStream output = new NBTOutputStream(new BufferedOutputStream(new PGZIPOutputStream(stream)))) { - new SpongeSchematicWriter(output).write(clipboard); + new FastSchematicWriter(output).write(clipboard); } } else { try (OutputStream stream = new FileOutputStream(tmp); BufferedOutputStream output = new BufferedOutputStream(new PGZIPOutputStream(stream))) { 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 deleted file mode 100644 index f86b28db0..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/DocumentationPrinter.java +++ /dev/null @@ -1,287 +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.boydti.fawe.util; - -import com.boydti.fawe.command.AnvilCommands; -import com.boydti.fawe.command.CFICommands; -import com.sk89q.minecraft.util.commands.Step; -import com.sk89q.worldedit.command.ToolUtilCommands; -import com.sk89q.worldedit.internal.annotation.Range; -import org.enginehub.piston.annotation.Command; -import com.sk89q.worldedit.command.util.CommandPermissions; -import com.sk89q.minecraft.util.commands.NestedCommand; -import com.sk89q.worldedit.command.BiomeCommands; -import com.sk89q.worldedit.command.BrushCommands; -import com.sk89q.worldedit.command.ChunkCommands; -import com.sk89q.worldedit.command.ClipboardCommands; -import com.sk89q.worldedit.command.GenerationCommands; -import com.sk89q.worldedit.command.HistoryCommands; -//import com.sk89q.worldedit.command.MaskCommands; -import com.sk89q.worldedit.command.NavigationCommands; -//import com.sk89q.worldedit.command.PatternCommands; -import com.sk89q.worldedit.command.RegionCommands; -import com.sk89q.worldedit.command.SchematicCommands; -import com.sk89q.worldedit.command.ScriptingCommands; -import com.sk89q.worldedit.command.SelectionCommands; -import com.sk89q.worldedit.command.SnapshotCommands; -import com.sk89q.worldedit.command.SnapshotUtilCommands; -import com.sk89q.worldedit.command.SuperPickaxeCommands; -import com.sk89q.worldedit.command.ToolCommands; -//import com.sk89q.worldedit.command.TransformCommands; -import com.sk89q.worldedit.command.UtilityCommands; -import com.sk89q.worldedit.command.WorldEditCommands; -import org.enginehub.piston.annotation.param.Arg; -import org.enginehub.piston.annotation.param.ArgFlag; -import org.enginehub.piston.annotation.param.Switch; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@SuppressWarnings("UseOfSystemOutOrSystemErr") -public final class DocumentationPrinter { - - private DocumentationPrinter() { - } - - /** - * Generates documentation. - * - * @param args arguments - * @throws IOException thrown on I/O error - */ - public static void main(String[] args) throws IOException { - writePermissionsWikiTable(); - } - - private static void writePermissionsWikiTable() - throws IOException { - try (FileOutputStream fos = new FileOutputStream("wiki_permissions.md")) { - PrintStream stream = new PrintStream(fos); - - stream.print("## Overview\n"); - stream.print("This page is generated from the source. " + - "Click one of the edit buttons below to modify a command class. " + - "You will need to find the parts which correspond to the documentation. " + - "Command documentation will be consistent with what is available ingame"); - stream.println(); - stream.println(); - stream.print("To view this information ingame use `//help [category|command]`\n"); - stream.print("## Command Syntax \n"); - stream.print(" - `` - A required parameter \n"); - stream.print(" - `[arg]` - An optional parameter \n"); - stream.print(" - `` - Multiple parameters options \n"); - stream.print(" - `` - Default or suggested value \n"); - stream.print(" - `-a` - A command flag e.g., `// -a [flag-value]`"); - stream.println(); - stream.print("## See also\n"); - stream.print(" - [Masks](https://github.com/boy0001/FastAsyncWorldedit/wiki/WorldEdit---FAWE-mask-list)\n"); - stream.print(" - [Patterns](https://github.com/boy0001/FastAsyncWorldedit/wiki/WorldEdit-and-FAWE-patterns)\n"); - stream.print(" - [Transforms](https://github.com/boy0001/FastAsyncWorldedit/wiki/Transforms)\n"); - stream.println(); - stream.print("## Content"); - stream.println(); - stream.print("Click on a category to go to the list of commands, or `More Info` for detailed descriptions "); - stream.println(); - StringBuilder builder = new StringBuilder(); - writePermissionsWikiTable(stream, builder, "/we ", WorldEditCommands.class); - writePermissionsWikiTable(stream, builder, "/", UtilityCommands.class); - writePermissionsWikiTable(stream, builder, "/", RegionCommands.class); - writePermissionsWikiTable(stream, builder, "/", SelectionCommands.class); - writePermissionsWikiTable(stream, builder, "/", HistoryCommands.class); - writePermissionsWikiTable(stream, builder, "/schematic ", SchematicCommands.class); - writePermissionsWikiTable(stream, builder, "/", ClipboardCommands.class); - writePermissionsWikiTable(stream, builder, "/", GenerationCommands.class); - writePermissionsWikiTable(stream, builder, "/", BiomeCommands.class); - writePermissionsWikiTable(stream, builder, "/anvil ", AnvilCommands.class); - writePermissionsWikiTable(stream, builder, "/sp ", SuperPickaxeCommands.class); - writePermissionsWikiTable(stream, builder, "/", NavigationCommands.class); - writePermissionsWikiTable(stream, builder, "/snapshot", SnapshotCommands.class); - writePermissionsWikiTable(stream, builder, "/", SnapshotUtilCommands.class); - writePermissionsWikiTable(stream, builder, "/", ScriptingCommands.class); - writePermissionsWikiTable(stream, builder, "/", ChunkCommands.class); - writePermissionsWikiTable(stream, builder, "/", ToolUtilCommands.class); - writePermissionsWikiTable(stream, builder, "/tool ", ToolCommands.class); - writePermissionsWikiTable(stream, builder, "/brush ", BrushCommands.class); - //writePermissionsWikiTable(stream, builder, "", MaskCommands.class, "/Masks"); - //writePermissionsWikiTable(stream, builder, "", PatternCommands.class, "/Patterns"); - //writePermissionsWikiTable(stream, builder, "", TransformCommands.class, "/Transforms"); - writePermissionsWikiTable(stream, builder, "/cfi ", CFICommands.class, "Create From Image"); - stream.println(); - stream.print("#### Uncategorized\n"); - stream.append("| Aliases | Permission | flags | Usage |\n"); - stream.append("| --- | --- | --- | --- |\n"); - stream.append("| //cancel | fawe.cancel | | Cancels your current operations |\n"); - stream.append("| /plot replaceall | plots.replaceall | | Replace all blocks in the plot world |\n"); -// stream.append("| /plot createfromimage | plots.createfromimage | | Starts world creation from a heightmap image: [More Info](https://github.com/boy0001/FastAsyncWorldedit/wiki/CreateFromImage) |\n"); - stream.print("\n---\n"); - - stream.print(builder); - } - } - - private static void writePermissionsWikiTable(PrintStream stream, StringBuilder content, String prefix, Class cls) { - writePermissionsWikiTable(stream, content, prefix, cls, getName(cls)); - } - - public static String getName(Class cls) { - return cls.getSimpleName().replaceAll("(\\p{Ll})(\\p{Lu})", "$1 $2"); - } - - private static void writePermissionsWikiTable(PrintStream stream, StringBuilder content, String prefix, Class cls, String name) { - stream.print(" - [`" + name + "`](#" + name.replaceAll(" ", "-").replaceAll("/", "").toLowerCase() + "-edittop) "); - Command cmd = cls.getAnnotation(Command.class); - if (cmd != null) { - stream.print(" (" + cmd.desc() + ")"); - } - stream.println(); - writePermissionsWikiTable(content, prefix, cls, name, true); - } - - private static void writePermissionsWikiTable(StringBuilder stream, String prefix, Class cls, String name, boolean title) { - if (title) { - String path = "https://github.com/boy0001/FastAsyncWorldedit/edit/master/core/src/main/java/" + cls.getName().replaceAll("\\.", "/") + ".java"; - stream.append("### **" + name + "** `[`[`edit`](" + path + ")`|`[`top`](#overview)`]`"); - stream.append("\n"); - Command cmd = cls.getAnnotation(Command.class); - if (cmd != null) { - if (!cmd.desc().isEmpty()) { - stream.append("> (" + (cmd.desc()) + ") \n"); - } - if (!cmd.descFooter().isEmpty()) { - stream.append("" + (cmd.descFooter()) + " \n"); - } - } - stream.append("\n"); - stream.append("---"); - stream.append("\n"); - stream.append("\n"); - } - for (Method method : cls.getMethods()) { - if (!method.isAnnotationPresent(Command.class)) { - continue; - } - Command cmd = method.getAnnotation(Command.class); - String[] aliases = cmd.aliases(); - String usage = prefix + aliases[0] + " " + getUsage(cmd, method); - - stream.append("#### `" + usage + "`\n"); - if (method.isAnnotationPresent(CommandPermissions.class)) { - CommandPermissions perms = method.getAnnotation(CommandPermissions.class); - stream.append("**Perm**: `" + StringMan.join(perms.value(), "`, `") + "` \n"); - } - String help = getDesc(cmd, method); - stream.append("**Desc**: " + help.trim().replaceAll("\n", "
") + " \n"); - - if (method.isAnnotationPresent(NestedCommand.class)) { - NestedCommand nested = - method.getAnnotation(NestedCommand.class); - - Class[] nestedClasses = nested.value(); - for (Class clazz : nestedClasses) { - writePermissionsWikiTable(stream, prefix + cmd.aliases()[0] + " ", clazz, getName(clazz), false); - } - } - } - stream.append("\n"); - if (title) stream.append("---"); - stream.append("\n"); - stream.append("\n"); - } - - public static String getDesc(Command command, Method method) { - Parameter[] params = method.getParameters(); - List desc = new ArrayList<>(); - for (Parameter param : params) { - String[] info = getParamInfo(param); - if (info != null) { - desc.add(info[0].replace("%s0", info[1]) + " - " + info[2] + ": " + info[3]); - } - } - String footer = command.descFooter(); - if (!footer.isEmpty()) footer += "\n"; - return footer + StringMan.join(desc, "\n"); - } - - public static String getUsage(Command command, Method method) { - Parameter[] params = method.getParameters(); - List usage = new ArrayList<>(); - for (Parameter param : params) { - String[] info = getParamInfo(param); - if (info != null) { - usage.add(info[0].replace("%s0", info[1])); - } - } - return StringMan.join(usage, " "); - } - - /* - Return format, name, type, description - */ - public static String[] getParamInfo(Parameter param) { - Switch switchAnn = param.getAnnotation(Switch.class); - Arg argAnn = param.getAnnotation(Arg.class); - Range rangeAnn = param.getAnnotation(Range.class); - Step stepAnn = param.getAnnotation(Step.class); - if (switchAnn != null || argAnn != null || rangeAnn != null || stepAnn != null) { - String[] result = new String[] { "[%s0]", param.getName(), param.getType().getSimpleName(), ""}; - boolean optional = argAnn != null && argAnn.def().length != 0; - if (optional) { - result[0] = "<%s0>"; - } - if (argAnn != null) result[1] = argAnn.name(); - if (argAnn != null) { - if (argAnn.def().length != 0) { - result[0] = result[0].replace("%s0", "%s0=" + argAnn.def()); - } - result[3] = argAnn.desc(); - } else if (switchAnn != null) { - result[0] = result[0].replace("%s0", "-" + switchAnn.name() + " %s0"); - } - if (switchAnn != null) result[3] = switchAnn.desc(); - if (rangeAnn != null) { - String step; - String min = rangeAnn.min() == Double.MIN_VALUE ? "(-∞" : ("[" + rangeAnn.min()); - String max = rangeAnn.max() == Double.MAX_VALUE ? "∞)" : (rangeAnn.max() + "]"); - result[0] += min + "," + max; - } - if (stepAnn != null) { - result[0] += "⦧" + stepAnn.value(); - } - return result; - } - return null; - } - - public static Collection getFlags(Command command, Method method) { - Parameter[] params = method.getParameters(); - List flags = new ArrayList<>(); - for (Parameter param : params) { - ArgFlag flagAnn = param.getAnnotation(ArgFlag.class); - if (flagAnn != null) flags.add(flagAnn); - } - return flags; - } -} 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 8aa4b74b8..189b2bb2b 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/CompressedSchematicTag.java @@ -3,7 +3,7 @@ package com.sk89q.jnbt; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.object.io.FastByteArraysInputStream; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicWriter; +import com.sk89q.worldedit.extent.clipboard.io.FastSchematicWriter; import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; @@ -19,7 +19,7 @@ public class CompressedSchematicTag extends CompressedCompoundTag { FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream(); try (LZ4BlockOutputStream lz4out = new LZ4BlockOutputStream(blocksOut)) { NBTOutputStream nbtOut = new NBTOutputStream(lz4out); - new SpongeSchematicWriter(nbtOut).write(getSource()); + new FastSchematicWriter(nbtOut).write(getSource()); } catch (IOException e) { throw new RuntimeException(e); } 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 b8e9fd545..23c21d7e6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -135,6 +135,7 @@ 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 org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -1427,8 +1428,22 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } /** - * Stack a cuboid region. For compatibility, entities are copied but biomes are not. - * Use {@link #stackCuboidRegion(Region, BlockVector3, int, boolean, boolean, boolean)} to fine tune. + * Stack a cuboid region. For compatibility, entities are copied by biomes are not. + * Use {@link #stackCuboidRegion(Region, BlockVector3, int, boolean, boolean, Mask)} to fine tune. + * + * @param region the region to stack + * @param dir the direction to stack + * @param count the number of times to stack + * @param copyAir true to also copy air blocks + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean copyAir) throws MaxChangedBlocksException { + return stackCuboidRegion(region, dir, count, true, false, copyAir ? null : new ExistingBlockMask(this)); + } + + /** + * Stack a cuboid region. * * @param region the region to stack * @param dir the direction to stack @@ -1439,7 +1454,8 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean copyAir, boolean copyEntities, boolean copyBiomes) throws MaxChangedBlocksException { + public int stackCuboidRegion(Region region, BlockVector3 dir, int count, + boolean copyEntities, boolean copyBiomes, Mask mask) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(dir); checkArgument(count >= 1, "count >= 1 required"); @@ -1451,14 +1467,10 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { copy.setCopyingBiomes(copyBiomes); copy.setRepetitions(count); copy.setTransform(new AffineTransform().translate(dir.multiply(size))); - Mask sourceMask = getSourceMask(); - if (sourceMask != null) { - new MaskTraverser(sourceMask).reset(EditSession.this); - copy.setSourceMask(sourceMask); + mask = MaskIntersection.of(getSourceMask(), mask).optimize(); + if (mask != Masks.alwaysTrue()) { setSourceMask(null); - } - if (!copyAir) { - copy.setSourceMask(new ExistingBlockMask(this)); + copy.setSourceMask(mask); } Operations.completeBlindly(copy); return this.changes = copy.getAffected(); @@ -2327,15 +2339,15 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @throws MaxChangedBlocksException */ public int makeShape(final Region region, final Vector3 zero, final Vector3 unit, - final Pattern pattern, final String expressionString, final boolean hollow, final int timeout) + final Pattern pattern, final String expressionString, final boolean hollow, final int timeout) throws ExpressionException, MaxChangedBlocksException { final Expression expression = Expression.compile(expressionString, "x", "y", "z", "type", "data"); expression.optimize(); final Variable typeVariable = expression.getSlots().getVariable("type") - .orElseThrow(IllegalStateException::new); + .orElseThrow(IllegalStateException::new); final Variable dataVariable = expression.getSlots().getVariable("data") - .orElseThrow(IllegalStateException::new); + .orElseThrow(IllegalStateException::new); final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero); expression.setEnvironment(environment); @@ -2349,12 +2361,26 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { final Vector3 scaled = current.subtract(zero).divide(unit); try { - if (expression.evaluateTimeout(timeout, scaled.getX(), scaled.getY(), scaled.getZ(), defaultMaterial.getBlockType().getInternalId(), defaultMaterial.getInternalPropertiesId()) <= 0) { - // TODO data + int[] legacy = LegacyMapper.getInstance().getLegacyFromBlock(defaultMaterial.toImmutableState()); + int typeVar = 0; + int dataVar = 0; + if (legacy != null) { + typeVar = legacy[0]; + if (legacy.length > 1) { + dataVar = legacy[1]; + } + } + if (expression.evaluate(new double[]{scaled.getX(), scaled.getY(), scaled.getZ(), typeVar, dataVar}, timeout) <= 0) { return null; } - - return BlockTypes.get((int) typeVariable.getValue()).withPropertyId((int) dataVariable.getValue()).toBaseBlock(); + int newType = (int) typeVariable.getValue(); + int newData = (int) dataVariable.getValue(); + if (newType != typeVar || newData != dataVar) { + BlockState state = LegacyMapper.getInstance().getBlockFromLegacy(newType, newData); + return state == null ? defaultMaterial : state.toBaseBlock(); + } else { + return defaultMaterial; + } } catch (ExpressionTimeoutException e) { timedOut[0] = timedOut[0] + 1; return null; @@ -2404,7 +2430,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { final Vector3 scaled = position.toVector3().subtract(zero).divide(unit); // transform - expression.evaluateTimeout(timeout, scaled.getX(), scaled.getY(), scaled.getZ()); + expression.evaluate(new double[]{scaled.getX(), scaled.getY(), scaled.getZ()}, timeout); int xv = (int) (x.getValue() * unit.getX() + zero2.getX()); int yv = (int) (y.getValue() * unit.getY() + zero2.getY()); int zv = (int) (z.getValue() * unit.getZ() + zero2.getZ()); 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 c6855b4a7..ab2032c9f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -78,6 +78,7 @@ 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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.io.File; @@ -147,7 +148,6 @@ public class LocalSession implements TextureHolder { private transient ResettableExtent transform = null; private transient ZoneId timezone = ZoneId.systemDefault(); private transient World currentWorld; - private transient boolean tickingWatchdog = false; private transient UUID uuid; private transient volatile long historySize = 0; @@ -992,6 +992,8 @@ public class LocalSession implements TextureHolder { return getTool(item, player); } + private transient boolean loadDefaults = true; + public Tool getTool(BaseItem item, Player player) { if (Settings.IMP.EXPERIMENTAL.PERSISTENT_BRUSHES && item.getNativeItem() != null) { BrushTool tool = BrushCache.getTool(player, this, item); @@ -1612,7 +1614,9 @@ public class LocalSession implements TextureHolder { private void prepareEditingExtents(EditSession editSession, Actor actor) { editSession.setFastMode(fastMode); + /* editSession.setReorderMode(reorderMode); + */ if (editSession.getSurvivalExtent() != null) { editSession.getSurvivalExtent().setStripNbt(!actor.hasPermission("worldedit.setnbt")); } 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 4111cc83c..a372eb9a8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -43,8 +43,6 @@ import com.sk89q.worldedit.extent.inventory.BlockBag; 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.internal.expression.runtime.Constant; -import com.sk89q.worldedit.internal.expression.runtime.RValue; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.scripting.CraftScriptContext; import com.sk89q.worldedit.scripting.CraftScriptEngine; @@ -397,8 +395,8 @@ public final class WorldEdit { public void checkMaxBrushRadius(Expression radius) throws MaxBrushRadiusException { if (getConfiguration().maxBrushRadius > 0) { - RValue r = radius.getRoot(); - if (r instanceof Constant && ((Constant) r).getValue() > getConfiguration().maxBrushRadius) { + double val = radius.evaluate(); + if (val > getConfiguration().maxBrushRadius) { throw new MaxBrushRadiusException(); } } 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 index 217fcd133..03c0650fb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java @@ -97,7 +97,7 @@ public class ApplyBrushCommands { 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, new Expression(radius), + BrushCommands.setOperationBasedBrush(player, localSession, Expression.compile(Double.toString(radius)), new Apply(generatorFactory), regionFactory, "worldedit.brush.apply"); } 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 b1e11cbd9..dfc17d63f 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 @@ -42,6 +42,7 @@ 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; 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 a681da433..3aa230beb 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 @@ -98,9 +98,8 @@ import com.sk89q.worldedit.function.mask.SingleBlockTypeMask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.annotation.ClipboardMask; -import com.sk89q.worldedit.internal.annotation.Range; +import com.sk89q.worldedit.internal.expression.EvaluationException; import com.sk89q.worldedit.internal.expression.Expression; -import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.factory.RegionFactory; @@ -133,6 +132,7 @@ import org.enginehub.piston.annotation.param.ArgFlag; import org.enginehub.piston.annotation.param.Switch; import org.enginehub.piston.inject.InjectedValueAccess; import org.enginehub.piston.inject.Key; +import org.jetbrains.annotations.Range; import static com.google.common.base.Preconditions.checkNotNull; @@ -308,7 +308,7 @@ public class BrushCommands { ) @CommandPermissions("worldedit.brush.spline") public void catenaryBrush(InjectedValueAccess context, @Arg(desc = "Pattern") Pattern fill, - @Arg(def = "1.2", desc = "Length of wire compared to distance between points") @Range(min = 1) double lengthFactor, + @Arg(def = "1.2", desc = "Length of wire compared to distance between points") @Range(from = 1, to=Integer.MAX_VALUE) double lengthFactor, @Arg(desc = "The radius to sample for blending", def = "0") Expression radius, @Switch(name = 'h', desc = "Create only a shell") @@ -439,7 +439,7 @@ public class BrushCommands { public void stencilBrush(LocalSession session, InjectedValueAccess context, @Arg(desc = "Pattern") Pattern fill, @Arg(desc = "Expression", def = "5") Expression radius, @Arg(desc = "String", def = "") String image, - @Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation, + @Arg(def = "0", desc = "rotation") @Range(from=0, to=360) int rotation, @Arg(desc = "double", def = "1") double yscale, @Switch(name = 'w', desc = "Apply at maximum saturation") boolean onlyWhite, @Switch(name = 'r', desc = "Apply random rotation") boolean randomRotate) throws WorldEditException, FileNotFoundException { @@ -470,7 +470,7 @@ public class BrushCommands { @Arg(desc = "Expression", def = "5") Expression radius, ProvideBindings.ImageUri imageUri, - @Arg(def = "1", desc = "scale height") @Range(min = Double.MIN_NORMAL) + @Arg(def = "1", desc = "scale height") double yscale, @Switch(name = 'a', desc = "Use image Alpha") boolean alpha, @@ -660,13 +660,14 @@ public class BrushCommands { 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 = 'e', desc = "Skip paste entities if available") + boolean skipEntities, @Switch(name = 'b', desc = "Paste biomes if available") boolean pasteBiomes, @ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard", def = "") @ClipboardMask - Mask sourceMask) throws WorldEditException { + Mask sourceMask, + InjectedValueAccess context) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); Clipboard clipboard = holder.getClipboard(); @@ -695,7 +696,8 @@ public class BrushCommands { @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 maskOpt) throws WorldEditException { + Mask maskOpt, + InjectedValueAccess context) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); FaweLimit limit = Settings.IMP.getLimit(player); @@ -752,7 +754,7 @@ public class BrushCommands { "Snow Pic: https://i.imgur.com/Hrzn0I4.png" ) @CommandPermissions("worldedit.brush.height") - public void heightBrush(LocalSession session, @Arg(desc = "Expression", def = "5") Expression radius, @Arg(desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation, @Arg(desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "TODO") boolean randomRotate, @Switch(name = 'l', desc = "TODO") boolean layers, @Switch(name = 's', desc = "TODO") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException { + public void heightBrush(LocalSession session, @Arg(desc = "Expression", def = "5") Expression radius, @Arg(desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Range(from = 0, to = 360) int rotation, @Arg(desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "TODO") boolean randomRotate, @Switch(name = 'l', desc = "TODO") boolean layers, @Switch(name = 's', desc = "TODO") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException { terrainBrush(session, radius, image, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context); } @@ -768,7 +770,7 @@ public class BrushCommands { Expression radius, @Arg(desc = "String", def = "") String image, - @Arg(def = "0", desc = "rotation") @Step(90) @Range(min = 0, max = 360) + @Arg(def = "0", desc = "rotation") @Step(90) @Range(from = 0, to = 360) int rotation, @Arg(desc = "double", def = "1") double yscale, @@ -787,7 +789,7 @@ public class BrushCommands { desc = "This brush raises or lowers land towards the clicked point" ) @CommandPermissions("worldedit.brush.height") - public void flattenBrush(LocalSession session, @Arg(desc = "Expression", def = "5") Expression radius, @Arg(desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Step(90) @Range(min = 0, max = 360) int rotation, @Arg(desc = "double", def = "1") double yscale, + public void flattenBrush(LocalSession session, @Arg(desc = "Expression", def = "5") Expression radius, @Arg(desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Step(90) @Range(from = 0, to = 360) int rotation, @Arg(desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "Enables random off-axis rotation") boolean randomRotate, @Switch(name = 'l', desc = "Will work on snow layers") 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 054cc7055..ff2efac91 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 @@ -39,10 +39,12 @@ import com.boydti.fawe.util.MaskTraverser; 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.event.extent.PasteEvent; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.Extent; @@ -314,8 +316,8 @@ public class ClipboardCommands { } } - final LocalConfiguration config = this.worldEdit.getConfiguration(); - final File working = this.worldEdit.getWorkingDirectoryFile(config.saveDir).getAbsoluteFile(); + final LocalConfiguration config = WorldEdit.getInstance().getConfiguration(); + final File working = WorldEdit.getInstance().getWorkingDirectoryFile(config.saveDir).getAbsoluteFile(); url = MainUtil.upload(null, null, "zip", new RunnableVal() { @Override @@ -484,7 +486,7 @@ public class ClipboardCommands { uri = ((URIClipboardHolder) holder).getURI(clipboard); } PasteEvent event = new PasteEvent(player, clipboard, uri, editSession, to); - worldEdit.getEventBus().post(event); + WorldEdit.getInstance().getEventBus().post(event); if (event.isCancelled()) { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MANUAL); } 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 index 98c851b17..3c84c5c15 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -342,7 +342,7 @@ public class GeneralCommands { desc = "Set the global mask" ) @CommandPermissions("worldedit.global-texture") - public void gtexture(Player player, World world, LocalSession session, EditSession editSession, @Arg(name = "context", desc = "InjectedValueAccess", def = "") List arguments) throws WorldEditException, FileNotFoundException { + public void gtexture(Player player, World worldArg, LocalSession session, EditSession editSession, @Arg(name = "context", desc = "InjectedValueAccess", def = "") List arguments) throws WorldEditException, FileNotFoundException { // gtexture // TODO NOT IMPLEMENTED convert this to an ArgumentConverter if (arguments.isEmpty()) { @@ -375,7 +375,7 @@ public class GeneralCommands { } else { ParserContext parserContext = new ParserContext(); parserContext.setActor(player); - parserContext.setWorld(world); + parserContext.setWorld(worldArg); parserContext.setSession(session); parserContext.setExtent(editSession); Mask mask = worldEdit.getMaskFactory().parseFromInput(arg, parserContext); 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 6c32f8c1c..0f3f5a9df 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 @@ -47,8 +47,8 @@ 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.Range; import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -69,6 +69,7 @@ import org.enginehub.piston.annotation.param.Switch; import static com.google.common.base.Preconditions.checkNotNull; import org.enginehub.piston.inject.InjectedValueAccess; +import org.jetbrains.annotations.Range; /** * Commands for the generation of shapes and other objects. @@ -173,7 +174,7 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.ore") @Logging(PLACEMENT) - public void ore(Actor actor, LocalSession session, EditSession editSession, @Selection Region region, @Arg(desc = "Mask") Mask mask, @Arg(desc = "Pattern") Pattern material, @Arg(desc="Ore vein size") @Range(min = 0) int size, int freq, @Range(min = 0, max = 100) int rarity, @Range(min = 0, max = 255) int minY, @Range(min = 0, max = 255) int maxY, InjectedValueAccess context) throws WorldEditException { + public void ore(Actor actor, LocalSession session, EditSession editSession, @Selection Region region, @Arg(desc = "Mask") Mask mask, @Arg(desc = "Pattern") Pattern material, @Arg(desc="Ore vein size") @Range(from = 0, to=Integer.MAX_VALUE) int size, int freq, @Range(from=0, to=100) int rarity, @Range(from=0, to=255) int minY, @Range(from=0, to=255) int maxY, InjectedValueAccess context) throws WorldEditException { actor.checkConfirmationRegion(() -> { editSession.addOre(region, mask, material, size, freq, rarity, minY, maxY); BBC.VISITOR_BLOCK.send(actor, editSession.getBlockChangeCount()); @@ -186,15 +187,14 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) - public int hcyl(Actor actor, LocalSession session, EditSession editSession, + public void hcyl(Actor actor, LocalSession session, EditSession editSession, InjectedValueAccess context, @Arg(desc = "The pattern of blocks to generate") Pattern pattern, @Arg(desc = "The radii of the cylinder. 1st is N/S, 2nd is E/W") - @Radii(2) - List radii, + BlockVector2 radii, @Arg(desc = "The height of the cylinder", def = "1") int height) throws WorldEditException { - return cyl(actor, session, editSession, pattern, radii, height, true); + cyl(actor, session, editSession, pattern, radii, height, true, context); } @Command( @@ -206,7 +206,7 @@ public class GenerationCommands { public void cyl(Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The pattern of blocks to generate") Pattern pattern, - @Arg(desc = "The radii of the cylinder. Order is N/S, E/W") BlockVector2 radius, + @Arg(desc = "The radii of the cylinder. Order is N/S, E/W") BlockVector2 radius, @Arg(desc = "The height of the cylinder", def = "1") int height, @Switch(name = 'h', desc = "Make a hollow cylinder") @@ -293,9 +293,12 @@ public class GenerationCommands { @Logging(POSITION) public int pumpkins(Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The size of the patch", def = "10") - int size) throws WorldEditException { + int size, + @Arg(desc = "//TODO ", def = "0.02") + double density) throws WorldEditException { + checkCommandArgument(0 <= density && density <= 100, "Density must be between 0 and 100"); worldEdit.checkMaxRadius(size); - int affected = editSession.makePumpkinPatches(session.getPlacementPosition(actor), size); + int affected = editSession.makePumpkinPatches(session.getPlacementPosition(actor), size, density); actor.print(affected + " pumpkin patches created."); return affected; } 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 0da8c3d74..2f7ac5526 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 @@ -40,7 +40,6 @@ import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.internal.annotation.Range; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; @@ -54,6 +53,7 @@ import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; import org.enginehub.piston.annotation.param.Switch; import org.enginehub.piston.inject.InjectedValueAccess; +import org.jetbrains.annotations.Range; import static com.google.common.base.Preconditions.checkNotNull; @@ -83,7 +83,7 @@ public class HistoryCommands { " - Import from disk: /frb #import" ) @CommandPermissions("worldedit.history.rollback") - public void faweRollback(Player player, LocalSession session, @Arg(desc = "String user") String user, @Arg(def = "0", desc = "radius") @Range(min = 0) int radius, @Arg(name = "time", desc = "String", def = "0") String time, @Switch(name = 'r', desc = "TODO") boolean restore) throws WorldEditException { + public void faweRollback(Player player, LocalSession session, @Arg(desc = "String user") String user, @Arg(def = "0", desc = "radius") @Range(from = 0, to=Integer.MAX_VALUE) int radius, @Arg(name = "time", desc = "String", def = "0") String time, @Switch(name = 'r', desc = "TODO") boolean restore) throws WorldEditException { if (!Settings.IMP.HISTORY.USE_DATABASE) { BBC.SETTING_DISABLE.send(player, "history.use-database (Import with /frb #import )"); return; @@ -214,32 +214,22 @@ public class HistoryCommands { " - Import from disk: /frb #import" ) @CommandPermissions("worldedit.history.rollback") - public void restore(Player player, LocalSession session, @Arg(desc = "String user") String user, @Arg(def = "0", desc = "radius") @Range(min = 0) int radius, @Arg(name = "time", desc = "String", def = "0") String time) throws WorldEditException { + public void restore(Player player, LocalSession session, @Arg(desc = "String user") String user, @Arg(def = "0", desc = "radius") int radius, @Arg(name = "time", desc = "String", def = "0") String time) throws WorldEditException { faweRollback(player, session, user, radius, time, true); } @Command( - name = "/undo", - aliases = { "/un", "/ud", "undo" }, - desc = "Undoes the last action (from history)" + name = "/undo", + aliases = { "/un", "/ud", "undo" }, + desc = "Undoes the last action (from history)" ) - } else { - undoSession = session; - } - int finalTimes = times; - player.checkConfirmation(() -> { - EditSession undone = null; - int i = 0; - for (; i < finalTimes; ++i) { - undone = undoSession.undo(undoSession.getBlockBag(player), player); - if (undone == null) break; @CommandPermissions({"worldedit.history.undo", "worldedit.history.undo.self"}) public void undo(Player player, LocalSession session, - @Range(min = 1) @Arg(desc = "Number of undoes to perform", def = "1") - int times, - @Arg(name = "player", desc = "Undo this player's operations", def = "") - String playerName, - InjectedValueAccess context) throws WorldEditException { + @Arg(desc = "Number of undoes to perform", def = "1") + int times, + @Arg(name = "player", desc = "Undo this player's operations", def = "") + String playerName, + InjectedValueAccess context) throws WorldEditException { times = Math.max(1, times); LocalSession undoSession; if (session.hasFastMode()) { @@ -253,6 +243,16 @@ public class HistoryCommands { BBC.COMMAND_HISTORY_OTHER_ERROR.send(player, playerName); return; } + } else { + undoSession = session; + } + int finalTimes = times; + player.checkConfirmation(() -> { + EditSession undone = null; + int i = 0; + for (; i < finalTimes; ++i) { + undone = undoSession.undo(undoSession.getBlockBag(player), player); + if (undone == null) break; worldEdit.flushBlockBag(player, undone); } if (undone == null) i--; @@ -272,7 +272,7 @@ public class HistoryCommands { ) @CommandPermissions({"worldedit.history.redo", "worldedit.history.redo.self"}) public void redo(Player player, LocalSession session, - @Range(min = 1) @Arg(desc = "Number of redoes to perform", def = "1") + @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 { 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 b8a125601..3c0fdfaa1 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 @@ -39,8 +39,8 @@ import com.sk89q.worldedit.function.mask.OffsetMask; import com.sk89q.worldedit.function.mask.RegionMask; import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.ExpressionEnvironment; import com.sk89q.worldedit.internal.expression.ExpressionException; -import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; 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 index 0cda0303b..b833f075b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java @@ -104,7 +104,7 @@ public class PaintBrushCommands { 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, new Expression(radius), + BrushCommands.setOperationBasedBrush(player, localSession, Expression.compile(Double.toString(radius)), new Paint(generatorFactory, density), regionFactory, "worldedit.brush.paint"); } 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 41b77e521..5215ee8b9 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 @@ -46,7 +46,6 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.ClipboardPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.RandomPattern; -import com.sk89q.worldedit.internal.annotation.Range; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.Vector3; @@ -61,6 +60,7 @@ import java.util.Set; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; +import org.jetbrains.annotations.Range; //@Command(aliases = {"patterns"}, // desc = "Help for the various patterns. [More Info](https://git.io/vSPmA)", @@ -226,7 +226,7 @@ public class PatternCommands { descFooter = "Use the pattern's id and the existing blocks data with the provided mask\n" + " - Use to replace slabs or where the data values needs to be shifted instead of set" ) - public Pattern iddatamask(Extent extent, @Range(min = 0, max = 15) @Arg(desc = "bit mask") int bitmask, @Arg(desc = "Pattern")Pattern pattern) { + public Pattern iddatamask(Extent extent, @Range(from = 0, to = 15) @Arg(desc = "bit mask") int bitmask, @Arg(desc = "Pattern")Pattern pattern) { return new IdDataMaskPattern(extent, pattern, bitmask); } 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 203485780..1b0cfda34 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 @@ -54,7 +54,6 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; import com.sk89q.worldedit.function.visitor.LayerVisitor; import com.sk89q.worldedit.internal.annotation.Direction; -import com.sk89q.worldedit.internal.annotation.Range; import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.BlockVector2; @@ -83,6 +82,7 @@ import org.enginehub.piston.annotation.param.Arg; import org.enginehub.piston.annotation.param.ArgFlag; import org.enginehub.piston.annotation.param.Switch; import org.enginehub.piston.inject.InjectedValueAccess; +import org.jetbrains.annotations.Range; import java.util.ArrayList; import java.util.Collections; @@ -254,7 +254,7 @@ public class RegionCommands { desc = "Set block lighting in a selection" ) @CommandPermissions("worldedit.light.set") - public void setlighting(Player player, EditSession editSession, @Selection Region region, @Range(min = 0, max = 15) int value) { + public void setlighting(Player player, EditSession editSession, @Selection Region region, @Range(from = 0, to = 15) int value) { // TODO NOT IMPLEMENTED } @@ -263,7 +263,7 @@ public class RegionCommands { 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) { + public void setskylighting(Player player, @Selection Region region, @Range(from = 0, to= 15) int value) { // TODO NOT IMPLEMENTED } @@ -314,7 +314,7 @@ public class RegionCommands { boolean shell, InjectedValueAccess context) throws WorldEditException { if (!(region instanceof ConvexPolyhedralRegion)) { actor.printError("//curve only works with convex polyhedral selections"); - return 0; + return; } checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); @@ -523,22 +523,24 @@ public class RegionCommands { @CommandPermissions("worldedit.region.move") @Logging(ORIENTATION_REGION) public void move(Actor actor, World world, EditSession editSession, LocalSession session, - @Selection Region region, + @Selection Region region, @Arg(desc = "# of blocks to move", def = "1") - int count, + int count, @Arg(desc = "The direction to move", def = Direction.AIM) @Direction(includeDiagonals = true) - BlockVector3 direction, + BlockVector3 direction, @Arg(desc = "The pattern of blocks to leave", def = "air") - Pattern replace, + Pattern replace, @Switch(name = 's', desc = "Shift the selection to the target location") - boolean moveSelection, + boolean moveSelection, @Switch(name = 'a', desc = "Ignore air blocks") - boolean ignoreAirBlocks, - @Switch(name = 'e', desc = "Ignore entities") - boolean skipEntities, + boolean ignoreAirBlocks, + @Switch(name = 'e', desc = "Skip copy entities") + boolean skipEntities, @Switch(name = 'b', desc = "Also copy biomes") - boolean copyBiomes, + boolean copyBiomes, + @ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air", def = "") + Mask mask, InjectedValueAccess context) throws WorldEditException { checkCommandArgument(count >= 1, "Count must be >= 1"); @@ -554,7 +556,7 @@ public class RegionCommands { } actor.checkConfirmationRegion(() -> { - int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, !skipEntities, copyBiomes, combinedMask, replace); + int affected = editSession.moveRegion(region, direction, count, !skipEntities, copyBiomes, combinedMask, replace); if (moveSelection) { try { @@ -612,7 +614,7 @@ public class RegionCommands { @Switch(name = 'b', desc = "Also copy biomes") boolean copyBiomes, @ArgFlag(name = 'm', desc = "Source mask", def="") - Mask sourceMask, + Mask mask, InjectedValueAccess context) throws WorldEditException { Mask combinedMask; @@ -627,9 +629,6 @@ public class RegionCommands { } actor.checkConfirmationStack(() -> { - if (sourceMask != null) { - editSession.addSourceMask(sourceMask); - } int affected = editSession.stackCuboidRegion(region, direction, count, !skipEntities, copyBiomes, combinedMask); if (moveSelection) { @@ -751,7 +750,7 @@ public class RegionCommands { @Logging(REGION) public void hollow(Actor actor, EditSession editSession, @Selection Region region, - @Range(min = 0) @Arg(desc = "Thickness of the shell to leave", def = "0") + @Range(from=0, to=Integer.MAX_VALUE) @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, @@ -799,6 +798,7 @@ public class RegionCommands { visitor.setMask(new NoiseFilter2D(new RandomNoise(), density / 100)); Operations.completeLegacy(visitor); + int affected = ground.getAffected(); actor.print(affected + " flora created."); }, "/flora", region, context); } 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 0c148fcb8..a1ddc9396 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 @@ -429,49 +429,6 @@ public class SchematicCommands { } } - @Command( - name = "delete", - aliases = {"d"}, - desc = "Delete a saved schematic" - ) - @CommandPermissions({"worldedit.schematic.delete", "worldedit.schematic.delete.other"}) - 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<>(); - - if (filename.equalsIgnoreCase("*")) { - files.addAll(getFiles(session.getClipboard())); - } else { - File f = MainUtil.resolveRelative(new File(dir, filename)); - files.add(f); - } - - if (files.isEmpty()) { - actor.printError(BBC.SCHEMATIC_NONE.s()); - return; - } - for (File f : files) { - if (!MainUtil.isInSubDirectory(working, f) || !f.exists()) { - 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) && !actor.hasPermission("worldedit.schematic.delete.other")) { - BBC.NO_PERM.send(actor, "worldedit.schematic.delete.other"); - continue; - } - if (!delete(f)) { - actor.printError("Deletion of " + filename + " failed! Maybe it is read-only."); - continue; - } - BBC.FILE_DELETED.send(actor, filename); - } - } - private List getFiles(ClipboardHolder clipboard) { Collection uris = Collections.emptyList(); if (clipboard instanceof URIClipboardHolder) { @@ -853,17 +810,43 @@ public class SchematicCommands { ) @CommandPermissions("worldedit.schematic.delete") public void delete(Actor actor, + LocalSession session, @Arg(desc = "File name.") - String filename) throws WorldEditException { + String filename) throws WorldEditException, IOException { LocalConfiguration config = worldEdit.getConfiguration(); - File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); + 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<>(); - File f = worldEdit.getSafeOpenFile(actor instanceof Player ? ((Player) actor) : null, - dir, filename, "schematic", ClipboardFormats.getFileExtensionArray()); + if (filename.equalsIgnoreCase("*")) { + files.addAll(getFiles(session.getClipboard())); + } else { + File f = MainUtil.resolveRelative(new File(dir, filename)); + files.add(f); + } - if (!f.exists()) { - actor.printError("Schematic " + filename + " does not exist!"); + if (files.isEmpty()) { + actor.printError(BBC.SCHEMATIC_NONE.s()); return; + } + for (File f : files) { + if (!MainUtil.isInSubDirectory(working, f) || !f.exists()) { + 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) && !actor.hasPermission("worldedit.schematic.delete.other")) { + BBC.NO_PERM.send(actor, "worldedit.schematic.delete.other"); + continue; + } + if (!delete(f)) { + actor.printError("Deletion of " + filename + " failed! Maybe it is read-only."); + continue; + } + BBC.FILE_DELETED.send(actor, filename); + } + } + private boolean delete(File file) { if (file.delete()) { new File(file.getParentFile(), "." + file.getName() + ".cached").delete(); 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 dbbaf47c2..6301df717 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 @@ -58,15 +58,6 @@ public class ScriptingCommands { this.worldEdit = worldEdit; } - @Command( - name = "setupdispatcher", - desc = "" - ) - @CommandPermissions("fawe.setupdispatcher") - public void setupdispatcher(Player player, LocalSession session, InjectedValueAccess args) throws WorldEditException { - PlatformCommandManager.getInstance().registerAllCommands(); - } - @Command( name = "cs", desc = "Execute a CraftScript" 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 6a3349890..24248bb55 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 @@ -74,6 +74,7 @@ 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.block.BlockState; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.storage.ChunkStore; @@ -108,6 +109,7 @@ public class SelectionCommands { @Command( name = "/pos1", + aliases = "/1", desc = "Set position 1" ) @Logging(POSITION) @@ -136,6 +138,7 @@ public class SelectionCommands { @Command( name = "/pos2", + aliases = "/2", desc = "Set position 2" ) @Logging(POSITION) @@ -544,7 +547,7 @@ public class SelectionCommands { if (distribution.isEmpty()) { // *Should* always be false - actor.printError("No blocks counted."); + player.printError("No blocks counted."); return; } 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 45a516218..45468ffdb 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,9 +19,6 @@ package com.sk89q.worldedit.command; -import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; - -import com.boydti.fawe.config.BBC; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; @@ -39,12 +36,15 @@ 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 java.io.File; +import java.io.IOException; + +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; + @CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class SnapshotUtilCommands { @@ -55,15 +55,15 @@ public class SnapshotUtilCommands { } @Command( - name = "restore", - aliases = { "/restore" }, - desc = "Restore the selection from a snapshot" + name = "restore", + aliases = { "/restore" }, + desc = "Restore the selection from a snapshot" ) @Logging(REGION) @CommandPermissions("worldedit.snapshots.restore") public void restore(Actor actor, World world, LocalSession session, EditSession editSession, @Arg(name = "snapshot", desc = "The snapshot to restore", def = "") - String snapshotName) throws WorldEditException { + String snapshotName) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -98,10 +98,10 @@ public class SnapshotUtilCommands { File dir = config.snapshotRepo.getDirectory(); try { - WorldEdit.logger.info("FAWE found no snapshots: looked in: " + WorldEdit.logger.info("WorldEdit found no snapshots: looked in: " + dir.getCanonicalPath()); } catch (IOException e) { - WorldEdit.logger.info("FAWE found no snapshots: looked in " + WorldEdit.logger.info("WorldEdit found no snapshots: looked in " + "(NON-RESOLVABLE PATH - does it exist?): " + dir.getPath()); } @@ -125,6 +125,7 @@ public class SnapshotUtilCommands { return; } + try { // Restore snapshot SnapshotRestore restore = new SnapshotRestore(chunkStore, editSession, region); //player.print(restore.getChunksAffected() + " chunk(s) will be loaded."); @@ -143,12 +144,15 @@ public class SnapshotUtilCommands { } } else { actor.print(String.format("Restored; %d " - + "missing chunks and %d other errors.", + + "missing chunks and %d other errors.", restore.getMissingChunks().size(), restore.getErrorChunks().size())); } - } catch (DataException | IOException e) { - actor.printError("Failed to load snapshot: " + e.getMessage()); + } finally { + try { + chunkStore.close(); + } catch (IOException ignored) { } } -} + } +} \ No newline at end of file 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 7f8881815..39b5e502c 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 @@ -42,13 +42,13 @@ import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.internal.annotation.Range; import com.sk89q.worldedit.internal.command.CommandArgParser; import com.sk89q.worldedit.util.HandSide; 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 org.jetbrains.annotations.Range; import java.util.List; @@ -253,7 +253,7 @@ public class ToolUtilCommands { ) @CommandPermissions("worldedit.brush.visualize") public void visual(Player player, LocalSession session, - @Arg(name = "mode", desc = "int", def = "0") @Range(min = 0, max = 2) + @Arg(name = "mode", desc = "int", def = "0") @Range(from = 0, to = 2) int mode) throws WorldEditException { BrushTool tool = session.getBrushTool(player, false); 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 0008b8b78..f74aafad6 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 @@ -45,6 +45,7 @@ import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.PrintCommandHelp; import com.sk89q.worldedit.command.util.SkipQueue; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; @@ -57,7 +58,7 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.mask.BlockTypeMask; import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.internal.annotation.Direction; -import com.sk89q.worldedit.internal.annotation.Range; +import com.sk89q.worldedit.internal.expression.EvaluationException; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; @@ -95,15 +96,13 @@ 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.jetbrains.annotations.Range; /** * Utility commands. */ @CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) -public class UtilityCommands { -// CommandQueuedConditionGenerator.Registration.class, - CommandPermissionsConditionGenerator.Registration.class // TODO NOT IMPLEMENTED - Piston doesn't seem to work with multiple conditions??? -}) public class UtilityCommands { private final WorldEdit we; @@ -201,9 +200,9 @@ public class UtilityCommands { public int fill(Actor actor, LocalSession session, EditSession editSession, @Arg(desc = "The blocks to fill with") Pattern pattern, - @Range(min=1) @Arg(desc = "The radius to fill in") + @Range(from=1, to=Integer.MAX_VALUE) @Arg(desc = "The radius to fill in") Expression radiusExp, - @Range(min=1) @Arg(desc = "The depth to fill", def = "1") + @Range(from=1, to=Integer.MAX_VALUE) @Arg(desc = "The depth to fill", def = "1") int depth, @Arg(desc = "The direction to move", def = "down") @Direction BlockVector3 direction) throws WorldEditException, EvaluationException { @@ -745,7 +744,7 @@ public class UtilityCommands { } try { if (!MainUtil.isInSubDirectory(root, file)) { - throw new RuntimeException(new CommandException("Invalid path")); + throw new RuntimeException(new StopExecutionException(TextComponent.of("Invalid path"))); } } catch (IOException ignore) { } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpressionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpressionConverter.java index 86317f616..e39ca6c19 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpressionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpressionConverter.java @@ -1,18 +1,12 @@ package com.sk89q.worldedit.command.argument; -import com.google.common.collect.ImmutableSetMultimap; 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.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.MultiKeyConverter; -import org.enginehub.piston.converter.SimpleArgumentConverter; import org.enginehub.piston.converter.SuccessfulConversion; import org.enginehub.piston.inject.InjectedValueAccess; import org.enginehub.piston.inject.Key; @@ -32,16 +26,11 @@ public class ExpressionConverter implements ArgumentConverter { public ConversionResult convert(String s, InjectedValueAccess injectedValueAccess) { Expression expression; try { - expression = new Expression(Double.parseDouble(s)); - } catch (NumberFormatException e1) { - try { - expression = Expression.compile(s); - expression.optimize(); - return SuccessfulConversion.fromSingle(expression); - } catch (Exception e) { - return FailedConversion.from(e); - } + expression = Expression.compile(s); + expression.optimize(); + return SuccessfulConversion.fromSingle(expression); + } catch (Exception e) { + return FailedConversion.from(e); } - return SuccessfulConversion.fromSingle(expression); } } 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..a0b684539 --- /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; + } +} \ No newline at end of file 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 c5c8ecf6a..d2d869176 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 @@ -23,6 +23,7 @@ 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; @@ -35,6 +36,8 @@ import com.sk89q.worldedit.world.World; 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. 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 2901baa66..69b64a037 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 @@ -29,6 +29,7 @@ import java.util.LinkedHashSet; import java.util.Set; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.util.collection.LocatedBlockList; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; public class GravityBrush implements Brush { @@ -53,15 +54,11 @@ public class GravityBrush implements Brush { if (y != freeSpot) { editSession.setBlock((int)x, (int)y, (int)z, BlockTypes.AIR.getDefaultState()); editSession.setBlock((int)x, (int)freeSpot, (int)z, block); - } + } freeSpot = y + 1; - } } } - - column.clear(); - removedBlocks.clear(); } } - + } } 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 175b68449..cfa4ec00a 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 @@ -62,9 +62,12 @@ import com.sk89q.worldedit.world.block.FuzzyBlockState; import com.sk89q.worldedit.world.registry.LegacyMapper; import java.util.Arrays; +import java.util.HashMap; import java.util.Locale; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; + +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -367,7 +370,7 @@ public class DefaultBlockParser extends InputParser { } } // this should be impossible but IntelliJ isn't that smart - if (blockType == null) { + if (state == null) { throw new NoMatchException("Does not match a valid block type: '" + input + "'"); } @@ -398,23 +401,10 @@ public class DefaultBlockParser extends InputParser { // 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(Locale.ROOT))) { - mobName = mobType.getName(); - break; - } - } - mobName = ent.getId(); - if (!worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS).isValidMobType(mobName)) { - String finalMobName = mobName.toLowerCase(Locale.ROOT); - throw new SuggestInputParseException("Unknown mob type '" + mobName + "'", mobName, () -> Stream.of(MobType.values()) - .map(m -> m.getName().toLowerCase(Locale.ROOT)) - .filter(s -> s.startsWith(finalMobName)) - .collect(Collectors.toList())); - } + EntityType mobType = EntityTypes.parse(mobName); return validate(context, new MobSpawnerBlock(state, mobName)); } else { - return validate(context, new MobSpawnerBlock(state, MobType.PIG.getName())); + return validate(context, new MobSpawnerBlock(state, EntityTypes.PIG.getId())); } } else if (blockType == BlockTypes.PLAYER_HEAD || blockType == BlockTypes.PLAYER_WALL_HEAD) { // allow setting type/player/rotation diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java index 8262d0696..355d4db8f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java @@ -19,9 +19,16 @@ package com.sk89q.worldedit.extension.platform; +import com.boydti.fawe.object.exception.FaweException; +import com.boydti.fawe.object.task.SimpleAsyncNotifyQueue; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.internal.cui.CUIEvent; import java.io.File; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; public abstract class AbstractNonPlayerActor implements Actor { @@ -48,4 +55,61 @@ public abstract class AbstractNonPlayerActor implements Actor { @Override public void dispatchCUIEvent(CUIEvent event) { } + + private final ConcurrentHashMap meta = new ConcurrentHashMap<>(); + + @Override + public Map getRawMeta() { + return meta; + } + + // Queue for async tasks + private AtomicInteger runningCount = new AtomicInteger(); + private SimpleAsyncNotifyQueue asyncNotifyQueue = new SimpleAsyncNotifyQueue( + (thread, throwable) -> { + while (throwable.getCause() != null) { + throwable = throwable.getCause(); + } + if (throwable instanceof WorldEditException) { + printError(throwable.getLocalizedMessage()); + } else { + FaweException fe = FaweException.get(throwable); + if (fe != null) { + printError(fe.getMessage()); + } else { + throwable.printStackTrace(); + } + } + }); + + /** + * Run a task either async, or on the current thread + * + * @param ifFree + * @param checkFree Whether to first check if a task is running + * @param async + * @return false if the task was ran or queued + */ + @Override + public boolean runAction(Runnable ifFree, boolean checkFree, boolean async) { + if (checkFree) { + if (runningCount.get() != 0) { + return false; + } + } + Runnable wrapped = () -> { + try { + runningCount.addAndGet(1); + ifFree.run(); + } finally { + runningCount.decrementAndGet(); + } + }; + if (async) { + asyncNotifyQueue.queue(wrapped); + } else { + TaskManager.IMP.taskNow(wrapped, false); + } + return true; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 2d10a8bb6..564d67b5d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -498,7 +498,7 @@ public final class PlatformCommandManager { this.registration.register( commandManager, ClipboardCommandsRegistration.builder(), - new ClipboardCommands(worldEdit) + new ClipboardCommands() ); this.registration.register( commandManager, @@ -523,7 +523,7 @@ public final class PlatformCommandManager { this.registration.register( commandManager, RegionCommandsRegistration.builder(), - new RegionCommands(worldEdit) + new RegionCommands() ); this.registration.register( 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 637991687..e73e0985b 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 @@ -415,7 +415,6 @@ public class PlatformManager { player.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session)); event.setCancelled(true); - } return; } @@ -429,7 +428,6 @@ public class PlatformManager { player.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session), false, true); event.setCancelled(true); - } return; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/AnnotatedBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/AnnotatedBindings.java deleted file mode 100644 index 6626d4b35..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/AnnotatedBindings.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sk89q.worldedit.extension.platform.binding; - -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.input.InputParseException; -import com.sk89q.worldedit.internal.annotation.Validate; - -import java.lang.annotation.Annotation; - -public class AnnotatedBindings extends Bindings { - - public AnnotatedBindings(WorldEdit worldEdit) { - super(worldEdit); - } - - @Validate() - public String getText(String argument, Validate modifier) { - return validate(argument, modifier); - } - - /** - * Validate a string value using relevant modifiers. - * - * @param string the string - * @param modifiers the list of modifiers to scan - * @throws InputParseException on a validation error - */ - private static String validate(String string, Annotation... modifiers) { - if (string != null) { - for (Annotation modifier : modifiers) { - if (modifier instanceof Validate) { - Validate validate = (Validate) modifier; - - if (!validate.value().isEmpty()) { - if (!string.matches(validate.value())) { - throw new InputParseException( - String.format( - "The given text doesn't match the right format (technically speaking, the 'format' is %s)", - validate.value())); - } - } - } - } - } - return string; - } -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java index e95e41af8..8a99ff387 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/ConsumeBindings.java @@ -14,7 +14,6 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.annotation.Direction; -import com.sk89q.worldedit.internal.annotation.Validate; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.World; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java index 397843b8a..aefda48f0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/binding/PrimitiveBindings.java @@ -10,10 +10,9 @@ 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.internal.annotation.Range; +import com.sk89q.worldedit.internal.expression.EvaluationException; 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.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector2; @@ -38,19 +37,15 @@ public class PrimitiveBindings extends Bindings { @Binding public Expression getExpression(String argument) throws ExpressionException { try { - return new Expression(Double.parseDouble(argument)); - } catch (NumberFormatException e1) { - try { - Expression expression = Expression.compile(argument); - expression.optimize(); - return expression; - } catch (EvaluationException e) { - throw new InputParseException(String.format( - "Expected '%s' to be a valid number (or a valid mathematical expression)", argument)); - } catch (ExpressionException e) { - throw new InputParseException(String.format( - "Expected '%s' to be a number or valid math expression (error: %s)", argument, e.getMessage())); - } + Expression expression = Expression.compile(argument); + expression.optimize(); + return expression; + } catch (EvaluationException e) { + throw new InputParseException(String.format( + "Expected '%s' to be a valid number (or a valid mathematical expression)", argument)); + } catch (ExpressionException e) { + throw new InputParseException(String.format( + "Expected '%s' to be a number or valid math expression (error: %s)", argument, e.getMessage())); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java index 5ff717636..9baaa2b21 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java @@ -25,11 +25,8 @@ import com.boydti.fawe.object.schematic.PNGWriter; import com.boydti.fawe.object.schematic.MinecraftStructure; import com.google.common.collect.ImmutableSet; -import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; -import com.sk89q.jnbt.NamedTag; -import com.sk89q.jnbt.Tag; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -38,7 +35,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -95,7 +91,7 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { } BufferedInputStream buffered = new BufferedInputStream(inputStream); NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(buffered))); - return new SpongeSchematicReader(nbtStream); + return new FastSchematicReader(nbtStream); } @Override @@ -108,7 +104,7 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { gzip = new PGZIPOutputStream(outputStream); } NBTOutputStream nbtStream = new NBTOutputStream(new BufferedOutputStream(gzip)); - return new SpongeSchematicWriter(nbtStream); + return new FastSchematicWriter(nbtStream); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java new file mode 100644 index 000000000..96ca59e05 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java @@ -0,0 +1,343 @@ +/* + * 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; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.jnbt.streamer.IntValueReader; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.clipboard.LinearClipboard; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; +import com.boydti.fawe.object.io.FastByteArraysInputStream; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector3; +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.BlockTypesCache; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.entity.EntityTypes; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.function.Function; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Reads schematic files using the Sponge Schematic Specification. + */ +public class FastSchematicReader extends NBTSchematicReader { + + private static final Logger log = LoggerFactory.getLogger(FastSchematicReader.class); + private final NBTInputStream inputStream; + private DataFixer fixer = null; + private int dataVersion = -1; + + private FastByteArrayOutputStream blocksOut; + private FaweOutputStream blocks; + + private FastByteArrayOutputStream biomesOut; + private FaweOutputStream biomes; + + private List> tiles; + private List> entities; + + private int width, height, length; + private int offsetX, offsetY, offsetZ; + private char[] palette, biomePalette; + private BlockVector3 min = BlockVector3.ZERO; + + + /** + * Create a new instance. + * + * @param inputStream the input stream to read from + */ + public FastSchematicReader(NBTInputStream inputStream) { + checkNotNull(inputStream); + this.inputStream = inputStream; + } + + private String fix(String palettePart) { + if (fixer == null || dataVersion == -1) return palettePart; + return fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); + } + + private CompoundTag fixBlockEntity(CompoundTag tag) { + if (fixer == null || dataVersion == -1) return tag; + return fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, tag, dataVersion); + } + + private CompoundTag fixEntity(CompoundTag tag) { + if (fixer == null || dataVersion == -1) return tag; + return fixer.fixUp(DataFixer.FixTypes.ENTITY, tag, dataVersion); + } + + public StreamDelegate createDelegate() { + StreamDelegate root = new StreamDelegate(); + StreamDelegate schematic = root.add("Schematic"); + schematic.add("DataVersion").withInt((i, v) -> dataVersion = v); + schematic.add("Width").withInt((i, v) -> width = v); + schematic.add("Height").withInt((i, v) -> height = v); + schematic.add("Length").withInt((i, v) -> length = v); + schematic.add("Offset").withValue((ValueReader) (index, v) -> min = BlockVector3.at(v[0], v[1], v[2])); + + StreamDelegate metadata = schematic.add("Metadata"); + metadata.add("WEOffsetX").withInt((i, v) -> offsetX = v); + metadata.add("WEOffsetY").withInt((i, v) -> offsetY = v); + metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v); + + StreamDelegate paletteDelegate = schematic.add("Palette"); + paletteDelegate.withValue((ValueReader>) (ignore, v) -> { + palette = new char[v.size()]; + for (Entry entry : v.entrySet()) { + BlockState state = null; + try { + String palettePart = fix(entry.getKey()); + state = BlockState.get(palettePart); + } catch (InputParseException e) { + e.printStackTrace(); + } + int index = (int) entry.getValue(); + palette[index] = (char) state.getOrdinal(); + } + }); + StreamDelegate blockData = schematic.add("BlockData"); + blockData.withInfo((length, type) -> { + blocksOut = new FastByteArrayOutputStream(); + blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); + }); + blockData.withInt((index, value) -> blocks.writeVarInt(value)); + StreamDelegate tilesDelegate = schematic.add("TileEntities"); + tilesDelegate.withInfo((length, type) -> tiles = new ArrayList<>(length)); + tilesDelegate.withElem((ValueReader>) (index, tile) -> tiles.add(tile)); + + StreamDelegate entitiesDelegate = schematic.add("Entities"); + entitiesDelegate.withInfo((length, type) -> entities = new ArrayList<>(length)); + entitiesDelegate.withElem((ValueReader>) (index, entity) -> entities.add(entity)); + StreamDelegate biomeData = schematic.add("BiomeData"); + biomeData.withInfo((length, type) -> { + biomesOut = new FastByteArrayOutputStream(); + biomes = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); + }); + biomeData.withElem((IntValueReader) (index, value) -> { + try { + biomes.write(value); // byte of varInt + } catch (IOException e) { + e.printStackTrace(); + } + }); + StreamDelegate biomePaletteDelegate = schematic.add("BiomePalette"); + biomePaletteDelegate.withInfo((length, type) -> biomePalette = new char[length]); + biomePaletteDelegate.withElem(new ValueReader>() { + @Override + public void apply(int index, Map.Entry palettePart) { + String key = palettePart.getKey(); + if (fixer != null) { + key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion); + } + BiomeType biome = BiomeTypes.get(key); + if (biome == null) { + System. out.println("Unknown biome " + key); + biome = BiomeTypes.FOREST; + } + int paletteIndex = palettePart.getValue().intValue(); + biomePalette[paletteIndex] = (char) biome.getInternalId(); + } + }); + return root; + } + + private BlockState getBlockState(int id) { + return BlockTypesCache.states[palette[id]]; + } + + private BiomeType getBiomeType(FaweInputStream fis) throws IOException { + char biomeId = biomePalette[fis.readVarInt()]; + BiomeType biome = BiomeTypes.get(biomeId); + return biome; + } + + @Override + public Clipboard read(UUID uuid, Function createOutput) throws IOException { + StreamDelegate root = createDelegate(); + inputStream.readNamedTagLazy(root); + if (blocks != null) blocks.close(); + if (biomes != null) biomes.close(); + blocks = null; + biomes = null; + + BlockVector3 dimensions = BlockVector3.at(width, height, length); + BlockVector3 origin = BlockVector3.ZERO; + if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { + origin = BlockVector3.at(-offsetX, -offsetY, -offsetZ); + } + + Clipboard clipboard = createOutput.apply(dimensions); + + if (blocksOut != null && blocksOut.getSize() != 0) { + try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(blocksOut.toByteArrays())))) { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + int volume = width * height * length; + if (palette.length < 128) { + for (int index = 0; index < volume; index++) { + linear.setBlock(index, getBlockState(fis.read())); + } + } else { + for (int index = 0; index < volume; index++) { + linear.setBlock(index, getBlockState(fis.readVarInt())); + } + } + } else { + if (palette.length < 128) { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + clipboard.setBlock(x, y, z, getBlockState(fis.read())); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + clipboard.setBlock(x, y, z, getBlockState(fis.readVarInt())); + } + } + } + } + } + } + } + if (biomesOut != null && biomesOut.getSize() != 0) { + try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays())))) { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + int volume = width * length; + for (int index = 0; index < volume; index++) { + linear.setBiome(index, getBiomeType(fis)); + } + } else { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + clipboard.setBiome(x, 0, z, getBiomeType(fis)); + } + } + } + } + } + // tiles + if (tiles != null && !tiles.isEmpty()) { + for (Map tileRaw : tiles) { + CompoundTag tile = FaweCache.IMP.asTag(tileRaw); + + int[] pos = tile.getIntArray("Pos"); + int x,y,z; + if (pos.length != 3) { + if (!tile.containsKey("x") || !tile.containsKey("y") || !tile.containsKey("z")) { + return null; + } + x = tile.getInt("x"); + y = tile.getInt("y"); + z = tile.getInt("z"); + } else { + x = pos[0]; + y = pos[1]; + z = pos[2]; + } + Map values = tile.getValue(); + Tag id = values.get("Id"); + if (id != null) { + values.put("x", new IntTag(x)); + values.put("y", new IntTag(y)); + values.put("z", new IntTag(z)); + values.put("id", id); + } + values.remove("Id"); + values.remove("Pos"); + + tile = fixBlockEntity(tile); + clipboard.setTile(x, y, z, tile); + } + } + + // entities + if (entities != null && !entities.isEmpty()) { + for (Map entRaw : entities) { + CompoundTag ent = FaweCache.IMP.asTag(entRaw); + + Map value = ent.getValue(); + StringTag id = (StringTag) value.get("Id"); + if (id == null) { + id = (StringTag) value.get("id"); + if (id == null) { + return null; + } + } + value.put("id", id); + value.remove("Id"); + + EntityType type = EntityTypes.parse(id.getValue()); + if (type != null) { + ent = fixEntity(ent); + BaseEntity state = new BaseEntity(type, ent); + Location loc = ent.getEntityLocation(clipboard); + clipboard.createEntity(loc, state); + } else { + log.debug("Invalid entity: " + id); + } + } + } + clipboard.setOrigin(origin); + + if (!min.equals(BlockVector3.ZERO)) { + new BlockArrayClipboard(clipboard, min); + } + + return clipboard; + } + + @Override + public void close() throws IOException { + inputStream.close(); + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java new file mode 100644 index 000000000..b57a50448 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicWriter.java @@ -0,0 +1,341 @@ +/* + * 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; + +import com.boydti.fawe.jnbt.streamer.IntValueReader; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.clipboard.LinearClipboard; +import com.boydti.fawe.util.IOUtil; +import com.google.common.collect.Maps; +import com.sk89q.jnbt.CompoundTag; +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.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.visitor.Order; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Writes schematic files using the Sponge schematic format. + */ +public class FastSchematicWriter 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; + + /** + * Create a new schematic writer. + * + * @param outputStream the output stream to write to + */ + public FastSchematicWriter(NBTOutputStream outputStream) { + checkNotNull(outputStream); + this.outputStream = outputStream; + } + + @Override + public void write(Clipboard clipboard) throws IOException { + // For now always write the latest version. Maybe provide support for earlier if more appear. + write2(clipboard); + } + + /** + * Writes a version 2 schematic file. + * + * @param clipboard The clipboard + */ + private void write2(Clipboard clipboard) throws IOException { + Region region = clipboard.getRegion(); + BlockVector3 origin = clipboard.getOrigin(); + BlockVector3 min = region.getMinimumPoint(); + BlockVector3 offset = min.subtract(origin); + int width = region.getWidth(); + int height = region.getHeight(); + int length = region.getLength(); + + if (width > MAX_SIZE) { + throw new IllegalArgumentException("Width of region too large for a .schematic"); + } + if (height > MAX_SIZE) { + throw new IllegalArgumentException("Height of region too large for a .schematic"); + } + if (length > MAX_SIZE) { + throw new IllegalArgumentException("Length of region too large for a .schematic"); + } + + final DataOutput rawStream = outputStream.getOutputStream(); + outputStream.writeLazyCompoundTag("Schematic", out -> { + out.writeNamedTag("DataVersion", WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion()); + out.writeNamedTag("Version", CURRENT_VERSION); + out.writeNamedTag("Width", (short) width); + out.writeNamedTag("Height", (short) height); + out.writeNamedTag("Length", (short) length); + + // The Sponge format Offset refers to the 'min' points location in the world. That's our 'Origin' + out.writeNamedTag("Offset", new int[]{ + min.getBlockX(), + min.getBlockY(), + min.getBlockZ(), + }); + + out.writeLazyCompoundTag("Metadata", out1 -> { + out1.writeNamedTag("WEOffsetX", offset.getBlockX()); + out1.writeNamedTag("WEOffsetY", offset.getBlockY()); + out1.writeNamedTag("WEOffsetZ", offset.getBlockZ()); + }); + + ByteArrayOutputStream blocksCompressed = new ByteArrayOutputStream(); + FaweOutputStream blocksOut = new FaweOutputStream(new DataOutputStream(new LZ4BlockOutputStream(blocksCompressed))); + + ByteArrayOutputStream tilesCompressed = new ByteArrayOutputStream(); + NBTOutputStream tilesOut = new NBTOutputStream(new LZ4BlockOutputStream(tilesCompressed)); + + List paletteList = new ArrayList<>(); + char[] palette = new char[BlockTypesCache.states.length]; + Arrays.fill(palette, Character.MAX_VALUE); + int paletteMax = 0; + int numTiles = 0; + Clipboard finalClipboard; + if (clipboard instanceof BlockArrayClipboard) { + finalClipboard = ((BlockArrayClipboard) clipboard).getParent(); + } else { + finalClipboard = clipboard; + } + Iterator iterator = finalClipboard.iterator(Order.YZX); + while (iterator.hasNext()) { + BlockVector3 pos = iterator.next(); + BaseBlock block = pos.getFullBlock(finalClipboard); + CompoundTag nbt = block.getNbtData(); + if (nbt != null) { + Map values = nbt.getValue(); + + values.remove("id"); // Remove 'id' if it exists. We want 'Id' + + // Positions are kept in NBT, we don't want that. + values.remove("x"); + values.remove("y"); + values.remove("z"); + if (!values.containsKey("Id")) { + values.put("Id", new StringTag(block.getNbtId())); + } + values.put("Pos", new IntArrayTag(new int[]{ + pos.getX(), + pos.getY(), + pos.getZ() + })); + numTiles++; + tilesOut.writeTagPayload(block.getNbtData()); + } + + int ordinal = block.getOrdinal(); + char value = palette[ordinal]; + if (value == Character.MAX_VALUE) { + int size = paletteMax++; + palette[ordinal] = value = (char) size; + paletteList.add(ordinal); + } + blocksOut.writeVarInt(value); + } + // close + tilesOut.close(); + blocksOut.close(); + + out.writeNamedTag("PaletteMax", paletteMax); + + out.writeLazyCompoundTag("Palette", out12 -> { + for (int i = 0; i < paletteList.size(); i++) { + int stateOrdinal = paletteList.get(i); + BlockState state = BlockTypesCache.states[stateOrdinal]; + out12.writeNamedTag(state.getAsString(), i); + } + }); + + out.writeNamedTagName("BlockData", NBTConstants.TYPE_BYTE_ARRAY); + rawStream.writeInt(blocksOut.size()); + try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(blocksCompressed.toByteArray()))) { + IOUtil.copy(in, rawStream); + } + + if (numTiles != 0) { + out.writeNamedTagName("TileEntities", NBTConstants.TYPE_LIST); + rawStream.write(NBTConstants.TYPE_COMPOUND); + rawStream.writeInt(numTiles); + try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(tilesCompressed.toByteArray()))) { + IOUtil.copy(in, rawStream); + } + } else { + out.writeNamedEmptyList("TileEntities"); + } + + if (finalClipboard.hasBiomes()) { + writeBiomes(finalClipboard, out); + } + + List entities = new ArrayList<>(); + for (Entity entity : finalClipboard.getEntities()) { + BaseEntity state = entity.getState(); + + if (state != null) { + Map values = new HashMap<>(); + + // Put NBT provided data + CompoundTag rawTag = state.getNbtData(); + if (rawTag != null) { + values.putAll(rawTag.getValue()); + } + + // Store our location data, overwriting any + values.remove("id"); + values.put("Id", new StringTag(state.getType().getId())); + values.put("Pos", writeVector(entity.getLocation())); + values.put("Rotation", writeRotation(entity.getLocation())); + + CompoundTag entityTag = new CompoundTag(values); + entities.add(entityTag); + } + } + if (entities.isEmpty()) { + out.writeNamedEmptyList("Entities"); + } else { + out.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities)); + } + }); + } + + private void writeBiomes(Clipboard clipboard, NBTOutputStream out) throws IOException { + ByteArrayOutputStream biomesCompressed = new ByteArrayOutputStream(); + DataOutputStream biomesOut = new DataOutputStream(new LZ4BlockOutputStream(biomesCompressed)); + + List paletteList = new ArrayList<>(); + int[] palette = new int[BiomeTypes.getMaxId() + 1]; + Arrays.fill(palette, Integer.MAX_VALUE); + int[] paletteMax = {0}; + IntValueReader task = new IntValueReader() { + @Override + public void applyInt(int index, int ordinal) { + try { + int value = palette[ordinal]; + if (value == Integer.MAX_VALUE) { + int size = paletteMax[0]++; + palette[ordinal] = value = size; + paletteList.add(ordinal); + } + IOUtil.writeVarInt(biomesOut, value); + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + BlockVector3 min = clipboard.getMinimumPoint(); + int width = clipboard.getRegion().getWidth(); + int length = clipboard.getRegion().getLength(); + for (int z = 0, i = 0; z < length; z++) { + int z0 = min.getBlockZ() + z; + for (int x = 0; x < width; x++, i++) { + int x0 = min.getBlockX() + x; + BlockVector2 pt = BlockVector2.at(x0, z0); + BiomeType biome = clipboard.getBiome(pt); + task.applyInt(i, biome.getInternalId()); + } + } + biomesOut.close(); + + out.writeNamedTag("BiomePaletteMax", paletteMax[0]); + + out.writeLazyCompoundTag("BiomePalette", out12 -> { + for (int i = 0; i < paletteList.size(); i++) { + int ordinal = paletteList.get(i); + BiomeType state = BiomeTypes.get(ordinal); + out12.writeNamedTag(state.getId(), i); + } + }); + + out.writeNamedTagName("BiomeData", NBTConstants.TYPE_BYTE_ARRAY); + out.writeInt(biomesOut.size()); + try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(biomesCompressed.toByteArray()))) { + IOUtil.copy(in, (DataOutput) out); + } + } + + 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)); + } + + @Override + public void close() throws IOException { + outputStream.close(); + } +} \ No newline at end of file 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 8fd0d9303..83b086c61 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 @@ -56,6 +56,7 @@ 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; 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 0f069e689..3b81f03fd 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 @@ -19,88 +19,63 @@ package com.sk89q.worldedit.extent.clipboard.io; -import com.boydti.fawe.Fawe; -import com.boydti.fawe.FaweCache; -import com.boydti.fawe.config.Settings; -import com.boydti.fawe.jnbt.streamer.InfoReader; -import com.boydti.fawe.jnbt.streamer.IntValueReader; -import com.boydti.fawe.jnbt.streamer.StreamDelegate; -import com.boydti.fawe.jnbt.streamer.ValueReader; -import com.boydti.fawe.object.FaweInputStream; -import com.boydti.fawe.object.FaweOutputStream; -import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard; -import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; -import com.boydti.fawe.object.clipboard.LinearClipboard; -import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; -import com.boydti.fawe.object.io.FastByteArrayOutputStream; -import com.boydti.fawe.object.io.FastByteArraysInputStream; +import com.google.common.collect.Maps; 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.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.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.block.BlockTypesCache; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; -import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.extension.platform.Capability; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; +import com.sk89q.worldedit.world.storage.NBTConversions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.DataInputStream; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.UUID; -import java.util.function.Function; +import java.util.OptionalInt; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; -import com.sk89q.worldedit.world.storage.NBTConversions; -import java.util.OptionalInt; /** * Reads schematic files using the Sponge Schematic Specification. */ +@Deprecated // High mem usage + slow public class SpongeSchematicReader extends NBTSchematicReader { private static final Logger log = LoggerFactory.getLogger(SpongeSchematicReader.class); private final NBTInputStream inputStream; private DataFixer fixer = null; - private int dataVersion = -1; - - private FastByteArrayOutputStream blocksOut; - private FaweOutputStream blocks; - - private FastByteArrayOutputStream biomesOut; - private FaweOutputStream biomes; - - private List> tiles; - private List> entities; - - private int width, height, length; - private int offsetX, offsetY, offsetZ; - private char[] palette, biomePalette; - private BlockVector3 min = BlockVector3.ZERO; private int schematicVersion = -1; - + private int dataVersion = -1; /** * Create a new instance. @@ -112,107 +87,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { this.inputStream = inputStream; } - private String fix(String palettePart) { - if (fixer == null || dataVersion == -1) return palettePart; - return fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); - } - - private CompoundTag fixBlockEntity(CompoundTag tag) { - if (fixer == null || dataVersion == -1) return tag; - return fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, tag, dataVersion); - } - - private CompoundTag fixEntity(CompoundTag tag) { - if (fixer == null || dataVersion == -1) return tag; - return fixer.fixUp(DataFixer.FixTypes.ENTITY, tag, dataVersion); - } - - public StreamDelegate createDelegate() { - StreamDelegate root = new StreamDelegate(); - StreamDelegate schematic = root.add("Schematic"); - schematic.add("DataVersion").withInt((i, v) -> dataVersion = v); - schematic.add("Width").withInt((i, v) -> width = v); - schematic.add("Height").withInt((i, v) -> height = v); - schematic.add("Length").withInt((i, v) -> length = v); - schematic.add("Offset").withValue((ValueReader) (index, v) -> min = BlockVector3.at(v[0], v[1], v[2])); - - StreamDelegate metadata = schematic.add("Metadata"); - metadata.add("WEOffsetX").withInt((i, v) -> offsetX = v); - metadata.add("WEOffsetY").withInt((i, v) -> offsetY = v); - metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v); - - StreamDelegate paletteDelegate = schematic.add("Palette"); - paletteDelegate.withValue((ValueReader>) (ignore, v) -> { - palette = new char[v.size()]; - for (Entry entry : v.entrySet()) { - BlockState state = null; - try { - String palettePart = fix(entry.getKey()); - System.out.println("Read " + palettePart); - state = BlockState.get(palettePart); - } catch (InputParseException e) { - e.printStackTrace(); - } - int index = (int) entry.getValue(); - palette[index] = (char) state.getOrdinal(); - } - }); - StreamDelegate blockData = schematic.add("BlockData"); - blockData.withInfo((length, type) -> { - blocksOut = new FastByteArrayOutputStream(); - blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); - }); - blockData.withInt((index, value) -> blocks.writeVarInt(value)); - StreamDelegate tilesDelegate = schematic.add("TileEntities"); - tilesDelegate.withInfo((length, type) -> tiles = new ArrayList<>(length)); - tilesDelegate.withElem((ValueReader>) (index, tile) -> tiles.add(tile)); - - StreamDelegate entitiesDelegate = schematic.add("Entities"); - entitiesDelegate.withInfo((length, type) -> entities = new ArrayList<>(length)); - entitiesDelegate.withElem((ValueReader>) (index, entity) -> entities.add(entity)); - StreamDelegate biomeData = schematic.add("BiomeData"); - biomeData.withInfo((length, type) -> { - biomesOut = new FastByteArrayOutputStream(); - biomes = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); - }); - biomeData.withElem((IntValueReader) (index, value) -> { - try { - biomes.write(value); // byte of varInt - } catch (IOException e) { - e.printStackTrace(); - } - }); - StreamDelegate biomePaletteDelegate = schematic.add("BiomePalette"); - biomePaletteDelegate.withInfo((length, type) -> biomePalette = new char[length]); - biomePaletteDelegate.withElem(new ValueReader>() { - @Override - public void apply(int index, Map.Entry palettePart) { - String key = palettePart.getKey(); - if (fixer != null) { - key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion); - } - BiomeType biome = BiomeTypes.get(key); - if (biome == null) { - System. out.println("Unknown biome " + key); - biome = BiomeTypes.FOREST; - } - int paletteIndex = palettePart.getValue().intValue(); - biomePalette[paletteIndex] = (char) biome.getInternalId(); - } - }); - return root; - } - - private BlockState getBlockState(int id) { - return BlockTypesCache.states[palette[id]]; - } - - private BiomeType getBiomeType(FaweInputStream fis) throws IOException { - char biomeId = biomePalette[fis.readVarInt()]; - BiomeType biome = BiomeTypes.get(biomeId); - return biome; - } - @Override public Clipboard read() throws IOException { CompoundTag schematicTag = getBaseTag(); @@ -264,141 +138,156 @@ public class SpongeSchematicReader extends NBTSchematicReader { } } - @Override - public Clipboard getBaseTag(UUID uuid, Function createOutput) throws IOException { - StreamDelegate root = createDelegate(); - inputStream.readNamedTagLazy(root); - if (blocks != null) blocks.close(); - if (biomes != null) biomes.close(); - blocks = null; - biomes = null; - - BlockVector3 dimensions = BlockVector3.at(width, height, length); - BlockVector3 origin = BlockVector3.ZERO; - if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { - origin = BlockVector3.at(-offsetX, -offsetY, -offsetZ); + private CompoundTag getBaseTag() 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(); - Clipboard clipboard = createOutput.apply(dimensions); + // Check + Map schematic = schematicTag.getValue(); - if (blocksOut != null && blocksOut.getSize() != 0) { - try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(blocksOut.toByteArrays())))) { - if (clipboard instanceof LinearClipboard) { - LinearClipboard linear = (LinearClipboard) clipboard; - int volume = width * height * length; - if (palette.length < 128) { - for (int index = 0; index < volume; index++) { - linear.setBlock(index, getBlockState(fis.read())); - } - } else { - for (int index = 0; index < volume; index++) { - linear.setBlock(index, getBlockState(fis.readVarInt())); - } - } - } else { - if (palette.length < 128) { - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++) { - clipboard.setBlock(x, y, z, getBlockState(fis.read())); - } - } - } - } else { - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++) { - clipboard.setBlock(x, y, z, getBlockState(fis.readVarInt())); - } - } - } - } - } + schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue(); + return schematicTag; + } + + private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOException { + BlockVector3 origin; + Region region; + Map schematic = schematicTag.getValue(); + + int width = requireTag(schematic, "Width", ShortTag.class).getValue(); + int height = requireTag(schematic, "Height", ShortTag.class).getValue(); + int length = requireTag(schematic, "Length", ShortTag.class).getValue(); + + IntArrayTag offsetTag = getTag(schematic, "Offset", IntArrayTag.class); + int[] offsetParts; + if (offsetTag != null) { + offsetParts = offsetTag.getValue(); + if (offsetParts.length != 3) { + throw new IOException("Invalid offset specified in schematic."); } + } else { + offsetParts = new int[] {0, 0, 0}; } - if (biomesOut != null && biomesOut.getSize() != 0) { - try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays())))) { - if (clipboard instanceof LinearClipboard) { - LinearClipboard linear = (LinearClipboard) clipboard; - int volume = width * length; - for (int index = 0; index < volume; index++) { - linear.setBiome(index, getBiomeType(fis)); - } - } else { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++) { - clipboard.setBiome(x, 0, z, getBiomeType(fis)); - } - } - } - } - } - // tiles - if (tiles != null && !tiles.isEmpty()) { - for (Map tileRaw : tiles) { - CompoundTag tile = FaweCache.IMP.asTag(tileRaw); - int[] pos = tile.getIntArray("Pos"); - int x,y,z; - if (pos.length != 3) { - if (!tile.containsKey("x") || !tile.containsKey("y") || !tile.containsKey("z")) { - return null; - } - x = tile.getInt("x"); - y = tile.getInt("y"); - z = tile.getInt("z"); - } else { - x = pos[0]; - y = pos[1]; - z = pos[2]; - } - Map values = tile.getValue(); - Tag id = values.get("Id"); - if (id != null) { - values.put("x", new IntTag(x)); - values.put("y", new IntTag(y)); - values.put("z", new IntTag(z)); - values.put("id", id); - } + BlockVector3 min = BlockVector3.at(offsetParts[0], offsetParts[1], offsetParts[2]); + + CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class); + if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) { + // We appear to have WorldEdit Metadata + Map metadata = metadataTag.getValue(); + int offsetX = requireTag(metadata, "WEOffsetX", IntTag.class).getValue(); + int offsetY = requireTag(metadata, "WEOffsetY", IntTag.class).getValue(); + int offsetZ = requireTag(metadata, "WEOffsetZ", IntTag.class).getValue(); + BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); + origin = min.subtract(offset); + region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); + } else { + origin = min; + region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE)); + } + + IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class); + Map paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue(); + if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue()) { + throw new IOException("Block palette size does not match expected size."); + } + + Map palette = new HashMap<>(); + + ParserContext parserContext = new ParserContext(); + parserContext.setRestricted(false); + parserContext.setTryLegacy(false); + parserContext.setPreferringWildcard(false); + + for (String palettePart : paletteObject.keySet()) { + int id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); + if (fixer != null) { + palettePart = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); + } + BlockState state; + try { + state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState(); + } catch (InputParseException e) { + log.warn("Invalid BlockState in palette: " + palettePart + ". Block will be replaced with air."); + state = BlockTypes.AIR.getDefaultState(); + } + palette.put(id, state); + } + + byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue(); + + Map> tileEntitiesMap = new HashMap<>(); + ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class); + if (tileEntities == null) { + tileEntities = getTag(schematic, "TileEntities", ListTag.class); + } + if (tileEntities != null) { + List> tileEntityTags = tileEntities.getValue().stream() + .map(tag -> (CompoundTag) tag) + .map(CompoundTag::getValue) + .collect(Collectors.toList()); + + for (Map tileEntity : tileEntityTags) { + int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); + final BlockVector3 pt = BlockVector3.at(pos[0], pos[1], pos[2]); + Map values = Maps.newHashMap(tileEntity); + values.put("x", new IntTag(pt.getBlockX())); + values.put("y", new IntTag(pt.getBlockY())); + values.put("z", new IntTag(pt.getBlockZ())); + values.put("id", values.get("Id")); values.remove("Id"); values.remove("Pos"); - - tile = fixBlockEntity(tile); - clipboard.setTile(x, y, z, tile); - } - } - - // entities - if (entities != null && !entities.isEmpty()) { - for (Map entRaw : entities) { - CompoundTag ent = FaweCache.IMP.asTag(entRaw); - - Map value = ent.getValue(); - StringTag id = (StringTag) value.get("Id"); - if (id == null) { - id = (StringTag) value.get("id"); - if (id == null) { - return null; - } - } - value.put("id", id); - value.remove("Id"); - - EntityType type = EntityTypes.parse(id.getValue()); - if (type != null) { - ent = fixEntity(ent); - BaseEntity state = new BaseEntity(type, ent); - Location loc = ent.getEntityLocation(clipboard); - clipboard.createEntity(loc, state); + if (fixer != null) { + tileEntity = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, new CompoundTag(values), dataVersion).getValue(); } else { - log.debug("Invalid entity: " + id); + tileEntity = values; } + tileEntitiesMap.put(pt, tileEntity); } } + + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); clipboard.setOrigin(origin); - if (!min.equals(BlockVector3.ZERO)) { - new BlockArrayClipboard(clipboard, min); + int index = 0; + int i = 0; + int value; + int varintLength; + while (i < blocks.length) { + value = 0; + varintLength = 0; + + while (true) { + value |= (blocks[i] & 127) << (varintLength++ * 7); + if (varintLength > 5) { + throw new IOException("VarInt too big (probably corrupted data)"); + } + if ((blocks[i] & 128) != 128) { + i++; + break; + } + i++; + } + // index = (y * length * width) + (z * width) + x + int y = index / (width * length); + int z = (index % (width * length)) / width; + int x = (index % (width * length)) % width; + BlockState state = palette.get(value); + BlockVector3 pt = BlockVector3.at(x, y, z); + try { + if (tileEntitiesMap.containsKey(pt)) { + clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt)))); + } else { + clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state); + } + } catch (WorldEditException e) { + throw new IOException("Failed to load a block in the schematic"); + } + + index++; } return clipboard; @@ -508,4 +397,4 @@ public class SpongeSchematicReader extends NBTSchematicReader { public void close() throws IOException { inputStream.close(); } -} +} \ No newline at end of file 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 3ea7ed88a..a461fbcc6 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 @@ -19,61 +19,45 @@ package com.sk89q.worldedit.extent.clipboard.io; -import com.boydti.fawe.jnbt.streamer.IntValueReader; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.Maps; -import com.boydti.fawe.object.FaweOutputStream; -import com.boydti.fawe.object.clipboard.LinearClipboard; -import com.boydti.fawe.util.IOUtil; +import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.IntArrayTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.FloatTag; import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntArrayTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.function.visitor.Order; 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.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.block.BlockTypesCache; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.DataOutput; -import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; -import com.sk89q.worldedit.math.Vector3; import java.util.Objects; -import com.sk89q.worldedit.util.Location; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Writes schematic files using the Sponge schematic format. */ +@Deprecated // High mem usage + slow public class SpongeSchematicWriter implements ClipboardWriter { private static final int CURRENT_VERSION = 2; @@ -94,15 +78,16 @@ public class SpongeSchematicWriter implements ClipboardWriter { @Override public void write(Clipboard clipboard) throws IOException { // For now always write the latest version. Maybe provide support for earlier if more appear. - write2(clipboard); + outputStream.writeNamedTag("Schematic", new CompoundTag(write2(clipboard))); } /** * Writes a version 2 schematic file. * * @param clipboard The clipboard + * @return The schematic map */ - private void write2(Clipboard clipboard) throws IOException { + private Map write2(Clipboard clipboard) { Region region = clipboard.getRegion(); BlockVector3 origin = clipboard.getOrigin(); BlockVector3 min = region.getMinimumPoint(); @@ -121,222 +106,102 @@ public class SpongeSchematicWriter implements ClipboardWriter { throw new IllegalArgumentException("Length of region too large for a .schematic"); } - final DataOutput rawStream = outputStream.getOutputStream(); - outputStream.writeLazyCompoundTag("Schematic", out -> { - out.writeNamedTag("DataVersion", WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion()); - out.writeNamedTag("Version", CURRENT_VERSION); - out.writeNamedTag("Width", (short) width); - out.writeNamedTag("Height", (short) height); - out.writeNamedTag("Length", (short) length); + Map schematic = new HashMap<>(); + schematic.put("Version", new IntTag(CURRENT_VERSION)); + schematic.put("DataVersion", new IntTag( + WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion())); - // The Sponge format Offset refers to the 'min' points location in the world. That's our 'Origin' - out.writeNamedTag("Offset", new int[]{ - min.getBlockX(), - min.getBlockY(), - min.getBlockZ(), - }); + Map metadata = new HashMap<>(); + metadata.put("WEOffsetX", new IntTag(offset.getBlockX())); + metadata.put("WEOffsetY", new IntTag(offset.getBlockY())); + metadata.put("WEOffsetZ", new IntTag(offset.getBlockZ())); - out.writeLazyCompoundTag("Metadata", out1 -> { - out1.writeNamedTag("WEOffsetX", offset.getBlockX()); - out1.writeNamedTag("WEOffsetY", offset.getBlockY()); - out1.writeNamedTag("WEOffsetZ", offset.getBlockZ()); - }); + schematic.put("Metadata", new CompoundTag(metadata)); - ByteArrayOutputStream blocksCompressed = new ByteArrayOutputStream(); - FaweOutputStream blocksOut = new FaweOutputStream(new DataOutputStream(new LZ4BlockOutputStream(blocksCompressed))); + schematic.put("Width", new ShortTag((short) width)); + schematic.put("Height", new ShortTag((short) height)); + schematic.put("Length", new ShortTag((short) length)); - ByteArrayOutputStream tilesCompressed = new ByteArrayOutputStream(); - NBTOutputStream tilesOut = new NBTOutputStream(new LZ4BlockOutputStream(tilesCompressed)); + // The Sponge format Offset refers to the 'min' points location in the world. That's our 'Origin' + schematic.put("Offset", new IntArrayTag(new int[]{ + min.getBlockX(), + min.getBlockY(), + min.getBlockZ(), + })); - List paletteList = new ArrayList<>(); - char[] palette = new char[BlockTypesCache.states.length]; - Arrays.fill(palette, Character.MAX_VALUE); - int paletteMax = 0; - int numTiles = 0; - Clipboard finalClipboard; - if (clipboard instanceof BlockArrayClipboard) { - finalClipboard = ((BlockArrayClipboard) clipboard).getParent(); - } else { - finalClipboard = clipboard; - } - Iterator iterator = finalClipboard.iterator(Order.YZX); - while (iterator.hasNext()) { - BlockVector3 pos = iterator.next(); - BaseBlock block = pos.getFullBlock(finalClipboard); - CompoundTag nbt = block.getNbtData(); - if (nbt != null) { - Map values = nbt.getValue(); + int paletteMax = 0; + Map palette = new HashMap<>(); - values.remove("id"); // Remove 'id' if it exists. We want 'Id' + List tileEntities = new ArrayList<>(); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * height * length); + + for (int y = 0; y < height; y++) { + int y0 = min.getBlockY() + y; + for (int z = 0; z < length; z++) { + int z0 = min.getBlockZ() + z; + for (int x = 0; x < width; x++) { + int x0 = min.getBlockX() + x; + BlockVector3 point = BlockVector3.at(x0, y0, z0); + BaseBlock block = clipboard.getFullBlock(point); + if (block.getNbtData() != null) { + Map values = new HashMap<>(block.getNbtData().getValue()); + + values.remove("id"); // Remove 'id' if it exists. We want 'Id' + + // Positions are kept in NBT, we don't want that. + values.remove("x"); + values.remove("y"); + values.remove("z"); - // Positions are kept in NBT, we don't want that. - values.remove("x"); - values.remove("y"); - values.remove("z"); - if (!values.containsKey("Id")) { values.put("Id", new StringTag(block.getNbtId())); - } - values.put("Pos", new IntArrayTag(new int[]{ - pos.getX(), - pos.getY(), - pos.getZ() - })); - numTiles++; - tilesOut.writeTagPayload(block.getNbtData()); - } + values.put("Pos", new IntArrayTag(new int[]{ + x, + y, + z + })); - int ordinal = block.getOrdinal(); - char value = palette[ordinal]; - if (value == Character.MAX_VALUE) { - int size = paletteMax++; - palette[ordinal] = value = (char) size; - paletteList.add(ordinal); - } - blocksOut.writeVarInt(value); - } - // close - tilesOut.close(); - blocksOut.close(); - - out.writeNamedTag("PaletteMax", paletteMax); - - out.writeLazyCompoundTag("Palette", out12 -> { - for (int i = 0; i < paletteList.size(); i++) { - int stateOrdinal = paletteList.get(i); - BlockState state = BlockTypesCache.states[stateOrdinal]; - out12.writeNamedTag(state.getAsString(), i); - } - }); - - out.writeNamedTagName("BlockData", NBTConstants.TYPE_BYTE_ARRAY); - rawStream.writeInt(blocksOut.size()); - try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(blocksCompressed.toByteArray()))) { - IOUtil.copy(in, rawStream); - } - - if (numTiles != 0) { - out.writeNamedTagName("TileEntities", NBTConstants.TYPE_LIST); - rawStream.write(NBTConstants.TYPE_COMPOUND); - rawStream.writeInt(numTiles); - try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(tilesCompressed.toByteArray()))) { - IOUtil.copy(in, rawStream); - } - } else { - out.writeNamedEmptyList("TileEntities"); - } - - if (finalClipboard.hasBiomes()) { - writeBiomes(finalClipboard, out); - } - - List entities = new ArrayList<>(); - for (Entity entity : finalClipboard.getEntities()) { - BaseEntity state = entity.getState(); - - if (state != null) { - Map values = new HashMap<>(); - - // Put NBT provided data - CompoundTag rawTag = state.getNbtData(); - if (rawTag != null) { - values.putAll(rawTag.getValue()); + tileEntities.add(new CompoundTag(values)); } - // Store our location data, overwriting any - values.remove("id"); - values.put("Id", new StringTag(state.getType().getId())); - values.put("Pos", writeVector(entity.getLocation())); - values.put("Rotation", writeRotation(entity.getLocation())); - - CompoundTag entityTag = new CompoundTag(values); - entities.add(entityTag); - } - } - if (entities.isEmpty()) { - out.writeNamedEmptyList("Entities"); - } else { - out.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities)); - } - }); - } - - private void writeBiomes(Clipboard clipboard, NBTOutputStream out) throws IOException { - ByteArrayOutputStream biomesCompressed = new ByteArrayOutputStream(); - DataOutputStream biomesOut = new DataOutputStream(new LZ4BlockOutputStream(biomesCompressed)); - - List paletteList = new ArrayList<>(); - int[] palette = new int[BiomeTypes.getMaxId() + 1]; - Arrays.fill(palette, Integer.MAX_VALUE); - int[] paletteMax = {0}; - IntValueReader task = new IntValueReader() { - @Override - public void applyInt(int index, int ordinal) { - try { - int value = palette[ordinal]; - if (value == Integer.MAX_VALUE) { - int size = paletteMax[0]++; - palette[ordinal] = value = size; - paletteList.add(ordinal); + String blockKey = block.toImmutableState().getAsString(); + int blockId; + if (palette.containsKey(blockKey)) { + blockId = palette.get(blockKey); + } else { + blockId = paletteMax; + palette.put(blockKey, blockId); + paletteMax++; } - IOUtil.writeVarInt(biomesOut, value); - } catch (IOException e) { - e.printStackTrace(); + + while ((blockId & -128) != 0) { + buffer.write(blockId & 127 | 128); + blockId >>>= 7; + } + buffer.write(blockId); } } - }; - BlockVector3 min = clipboard.getMinimumPoint(); - int width = clipboard.getRegion().getWidth(); - int length = clipboard.getRegion().getLength(); - for (int z = 0, i = 0; z < length; z++) { - int z0 = min.getBlockZ() + z; - for (int x = 0; x < width; x++, i++) { - int x0 = min.getBlockX() + x; - BlockVector2 pt = BlockVector2.at(x0, z0); - BiomeType biome = clipboard.getBiome(pt); - task.applyInt(i, biome.getInternalId()); - } } - biomesOut.close(); - out.writeNamedTag("BiomePaletteMax", paletteMax[0]); + schematic.put("PaletteMax", new IntTag(paletteMax)); - out.writeLazyCompoundTag("BiomePalette", out12 -> { - for (int i = 0; i < paletteList.size(); i++) { - int ordinal = paletteList.get(i); - BiomeType state = BiomeTypes.get(ordinal); - out12.writeNamedTag(state.getId(), i); - } - }); + Map paletteTag = new HashMap<>(); + palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value))); - out.writeNamedTagName("BiomeData", NBTConstants.TYPE_BYTE_ARRAY); - out.writeInt(biomesOut.size()); - try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(biomesCompressed.toByteArray()))) { - IOUtil.copy(in, (DataOutput) out); + schematic.put("Palette", new CompoundTag(paletteTag)); + schematic.put("BlockData", new ByteArrayTag(buffer.toByteArray())); + schematic.put("BlockEntities", new ListTag(CompoundTag.class, tileEntities)); + + // version 2 stuff + if (clipboard.hasBiomes()) { + writeBiomes(clipboard, schematic); } - } - 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; + if (!clipboard.getEntities().isEmpty()) { + writeEntities(clipboard, schematic); } - schematic.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities)); + + return schematic; } private void writeBiomes(Clipboard clipboard, Map schematic) { @@ -408,7 +273,7 @@ public class SpongeSchematicWriter implements ClipboardWriter { schematic.put("Entities", new ListTag(CompoundTag.class, entities)); } - private Tag writeVector(Vector3 vector) { + public Tag writeVector(Vector3 vector) { List list = new ArrayList<>(); list.add(new DoubleTag(vector.getX())); list.add(new DoubleTag(vector.getY())); @@ -416,7 +281,7 @@ public class SpongeSchematicWriter implements ClipboardWriter { return new ListTag(DoubleTag.class, list); } - private Tag writeRotation(Location location) { + public Tag writeRotation(Location location) { List list = new ArrayList<>(); list.add(new FloatTag(location.getYaw())); list.add(new FloatTag(location.getPitch())); @@ -427,4 +292,4 @@ public class SpongeSchematicWriter implements ClipboardWriter { public void close() throws IOException { outputStream.close(); } -} +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java index 7f850d5e2..d540a41af 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java @@ -22,8 +22,8 @@ package com.sk89q.worldedit.function.mask; import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.internal.expression.Expression; -import com.sk89q.worldedit.internal.expression.EvaluationException; import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.internal.expression.EvaluationException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; @@ -75,7 +75,8 @@ public class ExpressionMask extends AbstractMask { if (timeout == null) { return expression.evaluate(vector.getX(), vector.getY(), vector.getZ()) > 0; } else { - return expression.evaluateTimeout(timeout.getAsInt(), vector.getX(), vector.getY(), vector.getZ()) > 0; + return expression.evaluate(new double[]{vector.getX(), vector.getY(), vector.getZ()}, + timeout.getAsInt()) > 0; } } catch (EvaluationException e) { return false; @@ -88,4 +89,4 @@ public class ExpressionMask extends AbstractMask { return new ExpressionMask2D(expression, timeout); } -} +} \ No newline at end of file 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 b9e490d11..d728bf920 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 @@ -411,6 +411,7 @@ public class ForwardExtentCopy implements Operation { } } + int affected; affected = region.getArea(); return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java index 7033b5828..07e4e5648 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java @@ -40,7 +40,6 @@ 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 com.sk89q.worldedit.world.storage.MissingWorldException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.enginehub.piston.exception.CommandException; 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 14a3edd0f..17efef064 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 @@ -22,25 +22,23 @@ package com.sk89q.worldedit.internal.expression; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.SetMultimap; - import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.antlr.ExpressionLexer; import com.sk89q.worldedit.antlr.ExpressionParser; +import com.sk89q.worldedit.session.request.Request; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.misc.ParseCancellationException; -import com.sk89q.worldedit.session.request.Request; import org.antlr.v4.runtime.tree.ParseTreeWalker; -import java.util.ArrayDeque; - import java.lang.invoke.MethodHandle; import java.util.List; import java.util.Objects; -import java.util.concurrent.ExecutionException; +import java.util.Stack; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -75,17 +73,16 @@ import java.util.concurrent.TimeoutException; */ public class Expression { - private static final ThreadLocal> instance = ThreadLocal.withInitial(ArrayDeque::new); + private static final ThreadLocal> instance = new ThreadLocal<>(); private static final ExecutorService evalThread = Executors.newFixedThreadPool( - Runtime.getRuntime().availableProcessors(), - new ThreadFactoryBuilder() - .setDaemon(true) - .setNameFormat("worldedit-expression-eval-%d") - .build()); + Runtime.getRuntime().availableProcessors(), + new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("worldedit-expression-eval-%d") + .build()); private final SlotTable slots = new SlotTable(); private final List providedSlots; - private Variable[] variableArray; private ExpressionParser.AllStatementsContext root; private final SetMultimap functions = Functions.getFunctionMap(); private ExpressionEnvironment environment; @@ -102,8 +99,8 @@ public class Expression { for (String variableName : variableNames) { slots.initVariable(variableName) - .orElseThrow(() -> new ExpressionException(-1, - "Tried to overwrite identifier '" + variableName + "'")); + .orElseThrow(() -> new ExpressionException(-1, + "Tried to overwrite identifier '" + variableName + "'")); } this.providedSlots = ImmutableList.copyOf(variableNames); @@ -124,48 +121,16 @@ public class Expression { ParseTreeWalker.DEFAULT.walk(new ExpressionValidator(slots.keySet(), functions), root); } - public Expression(double constant) { - root = new Constant(0, constant); - } - - public double evaluate(double x, double y, double z) throws EvaluationException { - return evaluateTimeout(WorldEdit.getInstance().getConfiguration().calculationTimeout, x, y, z); - } - - public double evaluate() throws EvaluationException { - return evaluateFinal(WorldEdit.getInstance().getConfiguration().calculationTimeout); - } - public double evaluate(double... values) throws EvaluationException { - return evaluateTimeout(WorldEdit.getInstance().getConfiguration().calculationTimeout, values); - } - - private double evaluateTimeout(int timeout, double x, double y, double z) throws EvaluationException { - if (root instanceof Constant) return root.getValue(); - variableArray[0].value = x; - variableArray[1].value = y; - variableArray[2].value = z; - return evaluateFinal(timeout); - } - - public double evaluateTimeout(int timeout, double... values) throws EvaluationException { - if (root instanceof Constant) return root.getValue(); - for (int i = 0; i < values.length; ++i) { - final Variable var = variableArray[i]; - var.value = values[i]; - } - return evaluateFinal(timeout); + return evaluate(values, WorldEdit.getInstance().getConfiguration().calculationTimeout); } public double evaluate(double[] values, int timeout) throws EvaluationException { - if (root instanceof Constant) { - return root.getValue(); - } for (int i = 0; i < values.length; ++i) { String slotName = providedSlots.get(i); LocalSlot.Variable slot = slots.getVariable(slotName) - .orElseThrow(() -> new EvaluationException(-1, - "Tried to assign to non-variable " + slotName + ".")); + .orElseThrow(() -> new EvaluationException(-1, + "Tried to assign to non-variable " + slotName + ".")); slot.setValue(values[i]); } @@ -177,17 +142,6 @@ public class Expression { return evaluateRootTimed(timeout); } - private double evaluateFinal(int timeout) throws EvaluationException { - try { - if (timeout < 0) { - return evaluateRoot(); - } - return evaluateRootTimed(timeout); - } catch (ReturnException e) { - return e.getValue(); - } // other evaluation exceptions are thrown out of this method - } - private double evaluateRootTimed(int timeout) throws EvaluationException { CountDownLatch startLatch = new CountDownLatch(1); Request request = Request.request(); @@ -233,32 +187,36 @@ public class Expression { // TODO optimizing } - public SlotTable getSlots() { - return slots; - } - - public RValue getRoot() { - return root; - } - @Override public String toString() { return root.toString(); } + public SlotTable getSlots() { + return slots; + } + public static Expression getInstance() { return instance.get().peek(); } private void pushInstance() { - ArrayDeque foo = instance.get(); - foo.push(this); + Stack threadLocalExprStack = instance.get(); + if (threadLocalExprStack == null) { + instance.set(threadLocalExprStack = new Stack<>()); + } + + threadLocalExprStack.push(this); } private void popInstance() { - ArrayDeque foo = instance.get(); + Stack threadLocalExprStack = instance.get(); - foo.pop(); + threadLocalExprStack.pop(); + + if (threadLocalExprStack.isEmpty()) { + instance.set(null); + } } public ExpressionEnvironment getEnvironment() { @@ -269,4 +227,4 @@ public class Expression { this.environment = environment; } -} +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BitMath.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BitMath.java new file mode 100644 index 000000000..33e94ffd2 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BitMath.java @@ -0,0 +1,52 @@ +/* + * 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.math; + +public final class BitMath { + + public static int mask(int bits) { + return ~(~0 << bits); + } + + public static int unpackX(long packed) { + return extractSigned(packed, 0, 26); + } + + public static int unpackZ(long packed) { + return extractSigned(packed, 26, 26); + } + + public static int unpackY(long packed) { + return extractSigned(packed, 26 + 26, 12); + } + + public static int extractSigned(long i, int shift, int bits) { + return fixSign((int) (i >> shift) & mask(bits), bits); + } + + public static int fixSign(int i, int bits) { + // Using https://stackoverflow.com/a/29266331/436524 + return i << (32 - bits) >> (32 - bits); + } + + private BitMath() { + } + +} \ 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 0e57cc00c..bb9448e8a 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 @@ -124,7 +124,7 @@ public abstract class BlockVector3 { public long toLongPackedForm() { checkLongPackable(this); - return (x & BITS_26) | ((z & BITS_26) << 26) | (((y & (long) BITS_12) << (26 + 26))); + return (getX() & BITS_26) | ((getZ() & BITS_26) << 26) | (((getY() & (long) BITS_12) << (26 + 26))); } public MutableBlockVector3 mutX(double x) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/auth/Subject.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/auth/Subject.java index 09f577cca..11945b202 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/auth/Subject.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/auth/Subject.java @@ -54,7 +54,15 @@ public interface Subject { * @param permission the permission * @return false if the permission was removed, true if this subject has permission */ - boolean togglePermission(String permission); + default boolean togglePermission(String permission) { + if (this.hasPermission(permission)) { + setPermission(permission, false); + return false; + } else { + setPermission(permission, true); + return true; + } + } void setPermission(String permission, boolean value); } 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 5517a1b06..f917a39f8 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 @@ -103,15 +103,16 @@ public class BlockType implements FawePattern, Keyed { return name; } } - + /* private BlockState computeDefaultState() { + BlockState defaultState = Iterables.getFirst(getBlockStatesMap().values(), null); if (values != null) { defaultState = values.apply(defaultState); } return defaultState; } - + */ @Deprecated public BlockState withPropertyId(int propertyId) { if (settings.stateOrdinals == null) return settings.defaultState; 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 a032921e5..9117d4d18 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 @@ -102,7 +102,7 @@ public final class LegacyMapper { Integer combinedId = getCombinedId(blockEntry.getKey()); final String value = blockEntry.getValue(); blockEntries.put(id, value); - BlockState blockState; + BlockState blockState = null; try { blockState = BlockState.get(null, blockEntry.getValue()); BlockType type = blockState.getBlockType(); @@ -114,10 +114,10 @@ public final class LegacyMapper { String newEntry = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, value, 1631); try { blockState = WorldEdit.getInstance().getBlockFactory().parseFromInput(newEntry, parserContext).toImmutableState(); - } catch (InputParseException ignored) { - log.warn("Unknown block: " + value); - continue; - } + } catch (InputParseException ignored) {} + } + if (blockState == null) { + log.warn("Unknown block: " + value); } } blockArr[combinedId] = blockState.getInternalId(); @@ -135,9 +135,21 @@ public final class LegacyMapper { } for (Map.Entry itemEntry : dataFile.items.entrySet()) { - try { - itemMap.put(getCombinedId(itemEntry.getKey()), ItemTypes.get(itemEntry.getValue())); + String id = itemEntry.getKey(); + String value = itemEntry.getValue(); + ItemType type = ItemTypes.get(value); + if (type == null && fixer != null) { + value = fixer.fixUp(DataFixer.FixTypes.ITEM_TYPE, value, 1631); + type = ItemTypes.get(value); } + if (type != null) { + try { + itemMap.put(getCombinedId(id), type); + continue; + } catch (Exception e) { + } + } + log.warn("Unknown item: " + value); } }