diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 5f4bc4a8a..1d3389b09 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -1,3 +1,5 @@
+import java.util.Properties
+
plugins {
`kotlin-dsl`
kotlin("jvm") version embeddedKotlinVersion
@@ -6,6 +8,18 @@ plugins {
repositories {
jcenter()
gradlePluginPortal()
+ maven {
+ name = "Forge Maven"
+ url = uri("https://files.minecraftforge.net/maven")
+ }
+ maven {
+ name = "Fabric"
+ url = uri("https://maven.fabricmc.net/")
+ }
+ maven {
+ name = "sponge"
+ url = uri("https://repo.spongepowered.org/maven")
+ }
}
configurations.all {
@@ -19,10 +33,24 @@ configurations.all {
}
}
+val properties = Properties().also { props ->
+ project.projectDir.resolveSibling("gradle.properties").bufferedReader().use {
+ props.load(it)
+ }
+}
+val loomVersion: String = properties.getProperty("loom.version")
+val mixinVersion: String = properties.getProperty("mixin.version")
+
dependencies {
implementation(gradleApi())
+ implementation("gradle.plugin.net.minecrell:licenser:0.4.1")
implementation("org.ajoberstar.grgit:grgit-gradle:3.1.1")
implementation("com.github.jengelman.gradle.plugins:shadow:5.1.0")
implementation("net.ltgt.apt-eclipse:net.ltgt.apt-eclipse.gradle.plugin:0.21")
implementation("net.ltgt.apt-idea:net.ltgt.apt-idea.gradle.plugin:0.21")
+ implementation("org.jfrog.buildinfo:build-info-extractor-gradle:4.9.7")
+ implementation("gradle.plugin.org.spongepowered:spongegradle:0.9.0")
+ implementation("net.minecraftforge.gradle:ForgeGradle:3.0.143")
+ implementation("net.fabricmc:fabric-loom:$loomVersion")
+ implementation("net.fabricmc:sponge-mixin:$mixinVersion")
}
diff --git a/buildSrc/src/main/kotlin/ArtifactoryConfig.kt b/buildSrc/src/main/kotlin/ArtifactoryConfig.kt
new file mode 100644
index 000000000..d19f35238
--- /dev/null
+++ b/buildSrc/src/main/kotlin/ArtifactoryConfig.kt
@@ -0,0 +1,40 @@
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.apply
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.named
+import org.jfrog.gradle.plugin.artifactory.dsl.ArtifactoryPluginConvention
+import org.jfrog.gradle.plugin.artifactory.task.ArtifactoryTask
+
+private const val ARTIFACTORY_CONTEXT_URL = "artifactory_contextUrl"
+private const val ARTIFACTORY_USER = "artifactory_user"
+private const val ARTIFACTORY_PASSWORD = "artifactory_password"
+
+fun Project.applyRootArtifactoryConfig() {
+ if (!project.hasProperty(ARTIFACTORY_CONTEXT_URL)) ext[ARTIFACTORY_CONTEXT_URL] = "http://localhost"
+ if (!project.hasProperty(ARTIFACTORY_USER)) ext[ARTIFACTORY_USER] = "guest"
+ if (!project.hasProperty(ARTIFACTORY_PASSWORD)) ext[ARTIFACTORY_PASSWORD] = ""
+
+ apply(plugin = "com.jfrog.artifactory")
+ configure {
+ setContextUrl("${project.property(ARTIFACTORY_CONTEXT_URL)}")
+ clientConfig.publisher.run {
+ repoKey = when {
+ "${project.version}".contains("SNAPSHOT") -> "libs-snapshot-local"
+ else -> "libs-release-local"
+ }
+ username = "${project.property(ARTIFACTORY_USER)}"
+ password = "${project.property(ARTIFACTORY_PASSWORD)}"
+ isMaven = true
+ isIvy = false
+ }
+ }
+ tasks.named("artifactoryPublish") {
+ isSkip = true
+ }
+}
+
+fun Project.applyCommonArtifactoryConfig() {
+ tasks.named("artifactoryPublish") {
+ publishConfigs("archives")
+ }
+}
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 0ec4b175d..d1b42cac8 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -1,3 +1,5 @@
+import org.gradle.api.Project
+
object Versions {
const val TEXT = "3.0.1"
const val TEXT_EXTRAS = "3.0.2"
@@ -6,3 +8,12 @@ object Versions {
const val JUNIT = "5.5.0"
const val MOCKITO = "3.0.0"
}
+
+// Properties that need a project reference to resolve:
+class ProjectVersions(project: Project) {
+ val loom = project.rootProject.property("loom.version")
+ val mixin = project.rootProject.property("mixin.version")
+}
+
+val Project.versions
+ get() = ProjectVersions(this)
diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml
index dc8dace40..bc0081b37 100644
--- a/config/checkstyle/import-control.xml
+++ b/config/checkstyle/import-control.xml
@@ -44,12 +44,21 @@
+
+
+
+
+
+
+
+
+
@@ -63,6 +72,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contrib/craftscripts/README.txt b/contrib/craftscripts/README.txt
index 61dd01128..c44072d8f 100644
--- a/contrib/craftscripts/README.txt
+++ b/contrib/craftscripts/README.txt
@@ -11,4 +11,4 @@ that you have installed WorldEdit.
In order to be able to use CraftScripts, you must install the Rhino JavaScript library.
The installation page linked above has information about that. More information
about scripts in general can be found at
-https://worldedit.enginehub.org/en/latest/usage/other/craftscripts/
+https://worldedit.enginehub.org/en/latest/usage/other/craftscripts/
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 911076493..15c5a2370 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,4 +6,4 @@ org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true
-org.gradle.caching=true
\ No newline at end of file
+org.gradle.caching=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 0d4a95168..5c2d1cf01 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradlew b/gradlew
index cccdd3d51..83f2acfdc 100755
--- a/gradlew
+++ b/gradlew
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
diff --git a/gradlew.bat b/gradlew.bat
index e95643d6a..24467a141 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java
index d54a9c986..c0f66fd83 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java
@@ -31,6 +31,7 @@ import com.sk89q.worldedit.bukkit.adapter.IBukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.SimpleBukkitAdapter;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.extension.platform.PlayerProxy;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
@@ -51,15 +52,431 @@ import com.sk89q.worldedit.world.item.ItemTypes;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Objects;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
+
+import java.util.EnumMap;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
+/**
+ * Adapts between Bukkit and WorldEdit equivalent objects.
+ */
+public class BukkitAdapter {
+
+ private BukkitAdapter() {
+ }
+
+ private static final ParserContext TO_BLOCK_CONTEXT = new ParserContext();
+
+ static {
+ TO_BLOCK_CONTEXT.setRestricted(false);
+ }
+
+ /**
+ * Checks equality between a WorldEdit BlockType and a Bukkit Material
+ *
+ * @param blockType The WorldEdit BlockType
+ * @param type The Bukkit Material
+ * @return If they are equal
+ */
+ public static boolean equals(BlockType blockType, Material type) {
+ return Objects.equals(blockType.getId(), type.getKey().toString());
+ }
+
+ /**
+ * Convert any WorldEdit world into an equivalent wrapped Bukkit world.
+ *
+ *
If a matching world cannot be found, a {@link RuntimeException}
+ * will be thrown.
+ *
+ * @param world the world
+ * @return a wrapped Bukkit world
+ */
+ public static BukkitWorld asBukkitWorld(World world) {
+ if (world instanceof BukkitWorld) {
+ return (BukkitWorld) world;
+ } else {
+ BukkitWorld bukkitWorld = WorldEditPlugin.getInstance().getInternalPlatform().matchWorld(world);
+ if (bukkitWorld == null) {
+ throw new RuntimeException("World '" + world.getName() + "' has no matching version in Bukkit");
+ }
+ return bukkitWorld;
+ }
+ }
+
+ /**
+ * Create a WorldEdit world from a Bukkit world.
+ *
+ * @param world the Bukkit world
+ * @return a WorldEdit world
+ */
+ public static World adapt(org.bukkit.World world) {
+ checkNotNull(world);
+ return new BukkitWorld(world);
+ }
+
+ /**
+ * Create a WorldEdit Player from a Bukkit Player.
+ *
+ * @param player The Bukkit player
+ * @return The WorldEdit player
+ */
+ public static BukkitPlayer adapt(Player player) {
+ return WorldEditPlugin.getInstance().wrapPlayer(player);
+ }
+
+ /**
+ * Create a Bukkit Player from a WorldEdit Player.
+ *
+ * @param player The WorldEdit player
+ * @return The Bukkit player
+ */
+ public static Player adapt(com.sk89q.worldedit.entity.Player player) {
+ return ((BukkitPlayer) player).getPlayer();
+ }
+
+ /**
+ * Create a Bukkit world from a WorldEdit world.
+ *
+ * @param world the WorldEdit world
+ * @return a Bukkit world
+ */
+ public static org.bukkit.World adapt(World world) {
+ checkNotNull(world);
+ if (world instanceof BukkitWorld) {
+ return ((BukkitWorld) world).getWorld();
+ } else {
+ org.bukkit.World match = Bukkit.getServer().getWorld(world.getName());
+ if (match != null) {
+ return match;
+ } else {
+ throw new IllegalArgumentException("Can't find a Bukkit world for " + world.getName());
+ }
+ }
+ }
+
+ /**
+ * Create a WorldEdit location from a Bukkit location.
+ *
+ * @param location the Bukkit location
+ * @return a WorldEdit location
+ */
+ public static Location adapt(org.bukkit.Location location) {
+ checkNotNull(location);
+ Vector3 position = asVector(location);
+ return new com.sk89q.worldedit.util.Location(
+ adapt(location.getWorld()),
+ position,
+ location.getYaw(),
+ location.getPitch());
+ }
+
+ /**
+ * Create a Bukkit location from a WorldEdit location.
+ *
+ * @param location the WorldEdit location
+ * @return a Bukkit location
+ */
+ public static org.bukkit.Location adapt(Location location) {
+ checkNotNull(location);
+ Vector3 position = location.toVector();
+ return new org.bukkit.Location(
+ adapt((World) location.getExtent()),
+ position.getX(), position.getY(), position.getZ(),
+ location.getYaw(),
+ location.getPitch());
+ }
+
+ /**
+ * Create a Bukkit location from a WorldEdit position with a Bukkit world.
+ *
+ * @param world the Bukkit world
+ * @param position the WorldEdit position
+ * @return a Bukkit location
+ */
+ public static org.bukkit.Location adapt(org.bukkit.World world, Vector3 position) {
+ checkNotNull(world);
+ checkNotNull(position);
+ return new org.bukkit.Location(
+ world,
+ position.getX(), position.getY(), position.getZ());
+ }
+
+ /**
+ * Create a Bukkit location from a WorldEdit position with a Bukkit world.
+ *
+ * @param world the Bukkit world
+ * @param position the WorldEdit position
+ * @return a Bukkit location
+ */
+ public static org.bukkit.Location adapt(org.bukkit.World world, BlockVector3 position) {
+ checkNotNull(world);
+ checkNotNull(position);
+ return new org.bukkit.Location(
+ world,
+ position.getX(), position.getY(), position.getZ());
+ }
+
+ /**
+ * Create a Bukkit location from a WorldEdit location with a Bukkit world.
+ *
+ * @param world the Bukkit world
+ * @param location the WorldEdit location
+ * @return a Bukkit location
+ */
+ public static org.bukkit.Location adapt(org.bukkit.World world, Location location) {
+ checkNotNull(world);
+ checkNotNull(location);
+ return new org.bukkit.Location(
+ world,
+ location.getX(), location.getY(), location.getZ(),
+ location.getYaw(),
+ location.getPitch());
+ }
+
+ /**
+ * Create a WorldEdit Vector from a Bukkit location.
+ *
+ * @param location The Bukkit location
+ * @return a WorldEdit vector
+ */
+ public static Vector3 asVector(org.bukkit.Location location) {
+ checkNotNull(location);
+ return Vector3.at(location.getX(), location.getY(), location.getZ());
+ }
+
+ /**
+ * Create a WorldEdit BlockVector from a Bukkit location.
+ *
+ * @param location The Bukkit location
+ * @return a WorldEdit vector
+ */
+ public static BlockVector3 asBlockVector(org.bukkit.Location location) {
+ checkNotNull(location);
+ return BlockVector3.at(location.getX(), location.getY(), location.getZ());
+ }
+
+ /**
+ * Create a WorldEdit entity from a Bukkit entity.
+ *
+ * @param entity the Bukkit entity
+ * @return a WorldEdit entity
+ */
+ public static Entity adapt(org.bukkit.entity.Entity entity) {
+ checkNotNull(entity);
+ return new BukkitEntity(entity);
+ }
+
+ /**
+ * Create a Bukkit Material form a WorldEdit ItemType
+ *
+ * @param itemType The WorldEdit ItemType
+ * @return The Bukkit Material
+ */
+ public static Material adapt(ItemType itemType) {
+ checkNotNull(itemType);
+ if (!itemType.getId().startsWith("minecraft:")) {
+ throw new IllegalArgumentException("Bukkit only supports Minecraft items");
+ }
+ return Material.getMaterial(itemType.getId().substring(10).toUpperCase(Locale.ROOT));
+ }
+
+ /**
+ * Create a Bukkit Material form a WorldEdit BlockType
+ *
+ * @param blockType The WorldEdit BlockType
+ * @return The Bukkit Material
+ */
+ public static Material adapt(BlockType blockType) {
+ checkNotNull(blockType);
+ if (!blockType.getId().startsWith("minecraft:")) {
+ throw new IllegalArgumentException("Bukkit only supports Minecraft blocks");
+ }
+ return Material.getMaterial(blockType.getId().substring(10).toUpperCase(Locale.ROOT));
+ }
+
+ /**
+ * Create a WorldEdit GameMode from a Bukkit one.
+ *
+ * @param gameMode Bukkit GameMode
+ * @return WorldEdit GameMode
+ */
+ public static GameMode adapt(org.bukkit.GameMode gameMode) {
+ checkNotNull(gameMode);
+ return GameModes.get(gameMode.name().toLowerCase(Locale.ROOT));
+ }
+
+ /**
+ * Create a WorldEdit BiomeType from a Bukkit one.
+ *
+ * @param biome Bukkit Biome
+ * @return WorldEdit BiomeType
+ */
+ public static BiomeType adapt(Biome biome) {
+ return BiomeTypes.get(biome.name().toLowerCase(Locale.ROOT));
+ }
+
+ public static Biome adapt(BiomeType biomeType) {
+ if (!biomeType.getId().startsWith("minecraft:")) {
+ throw new IllegalArgumentException("Bukkit only supports vanilla biomes");
+ }
+ try {
+ return Biome.valueOf(biomeType.getId().substring(10).toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Create a WorldEdit EntityType from a Bukkit one.
+ *
+ * @param entityType Bukkit EntityType
+ * @return WorldEdit EntityType
+ */
+ public static EntityType adapt(org.bukkit.entity.EntityType entityType) {
+ final String name = entityType.getName();
+ if (name == null) {
+ return null;
+ }
+ return EntityTypes.get(name.toLowerCase(Locale.ROOT));
+ }
+
+ public static org.bukkit.entity.EntityType adapt(EntityType entityType) {
+ if (!entityType.getId().startsWith("minecraft:")) {
+ throw new IllegalArgumentException("Bukkit only supports vanilla entities");
+ }
+ return org.bukkit.entity.EntityType.fromName(entityType.getId().substring(10));
+ }
+
+ private static EnumMap materialBlockTypeCache = new EnumMap<>(Material.class);
+ private static EnumMap materialItemTypeCache = new EnumMap<>(Material.class);
+
+ /**
+ * Converts a Material to a BlockType
+ *
+ * @param material The material
+ * @return The blocktype
+ */
+ @Nullable
+ public static BlockType asBlockType(Material material) {
+ checkNotNull(material);
+ return materialBlockTypeCache.computeIfAbsent(material, input -> BlockTypes.get(material.getKey().toString()));
+ }
+
+ /**
+ * Converts a Material to a ItemType
+ *
+ * @param material The material
+ * @return The itemtype
+ */
+ @Nullable
+ public static ItemType asItemType(Material material) {
+ checkNotNull(material);
+ return materialItemTypeCache.computeIfAbsent(material, input -> ItemTypes.get(material.getKey().toString()));
+ }
+
+ private static Int2ObjectMap blockStateCache = new Int2ObjectOpenHashMap<>();
+ private static Map blockStateStringCache = new HashMap<>();
+
+ /**
+ * Create a WorldEdit BlockState from a Bukkit BlockData
+ *
+ * @param blockData The Bukkit BlockData
+ * @return The WorldEdit BlockState
+ */
+ public static BlockState adapt(BlockData blockData) {
+ checkNotNull(blockData);
+
+ if (WorldEditPlugin.getInstance().getBukkitImplAdapter() == null) {
+ return blockStateStringCache.computeIfAbsent(blockData.getAsString(), input -> {
+ try {
+ return WorldEdit.getInstance().getBlockFactory().parseFromInput(input, TO_BLOCK_CONTEXT).toImmutableState();
+ } catch (InputParseException e) {
+ e.printStackTrace();
+ return null;
+ }
+ });
+ } else {
+ return blockStateCache.computeIfAbsent(
+ WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBlockStateId(blockData).orElseGet(
+ () -> blockData.getAsString().hashCode()
+ ), input -> {
+ try {
+ return WorldEdit.getInstance().getBlockFactory().parseFromInput(blockData.getAsString(), TO_BLOCK_CONTEXT).toImmutableState();
+ } catch (InputParseException e) {
+ e.printStackTrace();
+ return null;
+ }
+ });
+ }
+ }
+
+ private static Int2ObjectMap blockDataCache = new Int2ObjectOpenHashMap<>();
+
+ /**
+ * Create a Bukkit BlockData from a WorldEdit BlockStateHolder
+ *
+ * @param block The WorldEdit BlockStateHolder
+ * @return The Bukkit BlockData
+ */
+ public static > BlockData adapt(B block) {
+ checkNotNull(block);
+ // Should never not have an ID for this BlockState.
+ int cacheKey = BlockStateIdAccess.getBlockStateId(block.toImmutableState()).orElseGet(block::hashCode);
+ return blockDataCache.computeIfAbsent(cacheKey, input -> Bukkit.createBlockData(block.getAsString())).clone();
+ }
+
+ /**
+ * Create a WorldEdit BlockState from a Bukkit ItemStack
+ *
+ * @param itemStack The Bukkit ItemStack
+ * @return The WorldEdit BlockState
+ */
+ public static BlockState asBlockState(ItemStack itemStack) throws WorldEditException {
+ checkNotNull(itemStack);
+ if (itemStack.getType().isBlock()) {
+ return adapt(itemStack.getType().createBlockData());
+ } else {
+ throw new NotABlockException();
+ }
+ }
+
+ /**
+ * Create a WorldEdit BaseItemStack from a Bukkit ItemStack
+ *
+ * @param itemStack The Bukkit ItemStack
+ * @return The WorldEdit BaseItemStack
+ */
+ public static BaseItemStack adapt(ItemStack itemStack) {
+ checkNotNull(itemStack);
+ if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) {
+ return WorldEditPlugin.getInstance().getBukkitImplAdapter().adapt(itemStack);
+ }
+ return new BaseItemStack(ItemTypes.get(itemStack.getType().getKey().toString()), itemStack.getAmount());
+ }
+
+ /**
+ * Create a Bukkit ItemStack from a WorldEdit BaseItemStack
+ *
+ * @param item The WorldEdit BaseItemStack
+ * @return The Bukkit ItemStack
+ */
+ public static ItemStack adapt(BaseItemStack item) {
+ checkNotNull(item);
+ if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) {
+ return WorldEditPlugin.getInstance().getBukkitImplAdapter().adapt(item);
+ }
+ return new ItemStack(adapt(item.getType()), item.getAmount());
+ }
+}
+
/**
* Adapts between Bukkit and WorldEdit equivalent objects.
*/
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCategoryRegistry.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCategoryRegistry.java
index cecec0691..c6d6fd9b0 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCategoryRegistry.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCategoryRegistry.java
@@ -19,7 +19,6 @@
package com.sk89q.worldedit.bukkit;
-import com.sk89q.worldedit.registry.Category;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.registry.BlockCategoryRegistry;
import org.bukkit.Bukkit;
@@ -44,9 +43,4 @@ public class BukkitBlockCategoryRegistry implements BlockCategoryRegistry {
Tag tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, new NamespacedKey(namespace, key), Material.class);
return getFromBukkitTag(tag);
}
-
- @Override
- public Set getAll(Category category) {
- return getCategorisedByName(category.getId());
- }
}
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java
index 0736ee066..d290fb96e 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java
@@ -75,6 +75,13 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
return super.getMaterial(state);
}
+ @Override
+ public OptionalInt getInternalBlockStateId(BlockState state) {
+ if (WorldEditPlugin.getInstance().getBukkitImplAdapter() != null) {
+ return WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBlockStateId(state);
+ }
+ return OptionalInt.empty();
+ }
@Nullable
@Override
public Map> getProperties(BlockType blockType) {
@@ -137,9 +144,4 @@ public class BukkitBlockRegistry extends BundledBlockRegistry {
}
return blocks;
}
-
- @Override
- public OptionalInt getInternalBlockStateId(BlockState state) {
- return WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBlockStateId(state);
- }
}
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java
index ffb0037d5..f1422b95c 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java
@@ -106,10 +106,6 @@ public class BukkitCommandSender extends AbstractNonPlayerActor {
return true;
}
- @Override public boolean togglePermission(String permission) {
- return false;
- }
-
@Override public void setPermission(String permission, boolean value) {
}
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitItemCategoryRegistry.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitItemCategoryRegistry.java
index c75c56eb2..2beb469e5 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitItemCategoryRegistry.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitItemCategoryRegistry.java
@@ -19,7 +19,6 @@
package com.sk89q.worldedit.bukkit;
-import com.sk89q.worldedit.registry.Category;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.ItemCategoryRegistry;
import org.bukkit.Bukkit;
@@ -44,9 +43,4 @@ public class BukkitItemCategoryRegistry implements ItemCategoryRegistry {
Tag tag = Bukkit.getTag(Tag.REGISTRY_ITEMS, new NamespacedKey(namespace, key), Material.class);
return getFromBukkitTag(tag);
}
-
- @Override
- public Set getAll(Category category) {
- return getCategorisedByName(category.getId());
- }
}
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 dfb1678fd..0cd61ca33 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
@@ -44,6 +44,8 @@ import com.sk89q.worldedit.util.formatting.text.adapter.bukkit.TextAdapter;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
+
+import java.util.Locale;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.gamemode.GameMode;
import com.sk89q.worldedit.world.gamemode.GameModes;
@@ -59,7 +61,6 @@ import org.bukkit.inventory.PlayerInventory;
import javax.annotation.Nullable;
import java.util.HashMap;
-import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -215,12 +216,12 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public GameMode getGameMode() {
- return GameModes.get(getPlayer().getGameMode().name().toLowerCase(Locale.ROOT));
+ return GameModes.get(player.getGameMode().name().toLowerCase(Locale.ROOT));
}
@Override
public void setGameMode(GameMode gameMode) {
- getPlayer().setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT)));
+ player.setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT)));
}
@Override
@@ -239,6 +240,16 @@ public class BukkitPlayer extends AbstractPlayerActor {
}
}
+ @Override
+ public boolean isAllowedToFly() {
+ return player.getAllowFlight();
+ }
+
+ @Override
+ public void setFlying(boolean flying) {
+ player.setFlying(flying);
+ }
+
@Override
public void setPermission(String permission, boolean value) {
/*
@@ -281,16 +292,6 @@ public class BukkitPlayer extends AbstractPlayerActor {
return player;
}
- @Override
- public boolean isAllowedToFly() {
- return getPlayer().getAllowFlight();
- }
-
- @Override
- public void setFlying(boolean flying) {
- getPlayer().setFlying(flying);
- }
-
@Override
public BaseEntity getState() {
throw new UnsupportedOperationException("Cannot create a state from this object");
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java
index 82888e308..d20d04da6 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java
@@ -41,8 +41,8 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import javax.annotation.Nullable;
import org.bukkit.Bukkit;
+import javax.annotation.Nullable;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
index 0fc65b847..eac42c71c 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
@@ -34,6 +34,7 @@ import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
@@ -43,6 +44,7 @@ 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.Direction;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.AbstractWorld;
import com.sk89q.worldedit.world.biome.BiomeType;
@@ -328,19 +330,27 @@ public class BukkitWorld extends AbstractWorld {
@Override
public void checkLoadedChunk(BlockVector3 pt) {
World world = getWorld();
- int X = pt.getBlockX() >> 4;
- int Z = pt.getBlockZ() >> 4;
- if (Fawe.isMainThread()) {
- world.getChunkAt(X, Z);
- } else if (!world.isChunkLoaded(X, Z)) {
- if (PaperLib.isPaper()) {
- world.getChunkAtAsync(X, Z, true);
- } else {
- Fawe.get().getQueueHandler().sync(() -> {
- world.getChunkAt(X, Z);
- });
- }
+
+ world.getChunkAt(pt.getBlockX() >> 4, pt.getBlockZ() >> 4);
+
+ if (!world.isChunkLoaded(pt.getBlockX() >> 4, pt.getBlockZ() >> 4)) {
+ world.loadChunk(pt.getBlockX() >> 4, pt.getBlockZ() >> 4);
}
+ if (!world.isChunkLoaded(pt.getBlockX() >> 4, pt.getBlockZ() >> 4)) {
+ int X = pt.getBlockX() >> 4;
+ int Z = pt.getBlockZ() >> 4;
+ if (Fawe.isMainThread()) {
+ world.getChunkAt(X, Z);
+ } else if (!world.isChunkLoaded(X, Z)) {
+ if (PaperLib.isPaper()) {
+ world.getChunkAtAsync(X, Z, true);
+ } else {
+ Fawe.get().getQueueHandler().sync(() -> {
+ world.getChunkAt(X, Z);
+ });
+ }
+ }
+ }
}
@Override
@@ -552,4 +562,14 @@ public class BukkitWorld extends AbstractWorld {
org.bukkit.entity.Player bukkitPlayer = BukkitAdapter.adapt(player);
WorldEditPlugin.getInstance().getBukkitImplAdapter().sendFakeChunk(getWorld(), bukkitPlayer, packet);
}
+
+ @Override
+ public boolean useItem(BlockVector3 position, BaseItem item, Direction face) {
+ BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
+ if (adapter != null) {
+ return adapter.simulateItemUse(getWorld(), position, item, face);
+ }
+
+ return false;
+ }
}
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java
index d7d97f8c0..f32ca1ac5 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java
@@ -26,7 +26,6 @@ import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
-import java.util.Optional;
import org.bukkit.block.Block;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
@@ -42,6 +41,8 @@ import org.enginehub.piston.inject.InjectedValueStore;
import org.enginehub.piston.inject.Key;
import org.enginehub.piston.inject.MapBackedValueStore;
+import java.util.Optional;
+
/**
* Handles all events thrown in relation to a Player
*/
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
index 1273e17c0..572a7fe4a 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
@@ -183,8 +183,8 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter
if (Files.exists(delChunks)) {
ChunkDeleter.runFromFile(delChunks, true);
}
-
- fail(() -> PermissionsResolverManager.initialize(INSTANCE), "Failed to initialize permissions resolver");
+
+ fail(() -> PermissionsResolverManager.initialize(INSTANCE), "Failed to initialize permissions resolver");
}
/**
@@ -341,6 +341,20 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter
}
}
+ @Override
+ public List onTabComplete(CommandSender sender, Command cmd, String commandLabel, String[] args) {
+ // Add the command to the array because the underlying command handling
+ // code of WorldEdit expects it
+ String[] split = new String[args.length + 1];
+ System.arraycopy(args, 0, split, 1, args.length);
+ split[0] = "/" + commandLabel;
+
+ String arguments = Joiner.on(" ").join(split);
+ CommandSuggestionEvent event = new CommandSuggestionEvent(wrapCommandSender(sender), arguments);
+ getWorldEdit().getEventBus().post(event);
+ return CommandUtil.fixSuggestions(arguments, event.getSuggestions());
+ }
+
private void fail(Runnable run, String message) {
try {
run.run();
diff --git a/worldedit-bukkit/src/main/resources/defaults/config-legacy.yml b/worldedit-bukkit/src/main/resources/defaults/config-legacy.yml
index ef7a2e6ad..524a2a8e8 100644
--- a/worldedit-bukkit/src/main/resources/defaults/config-legacy.yml
+++ b/worldedit-bukkit/src/main/resources/defaults/config-legacy.yml
@@ -90,3 +90,4 @@ no-op-permissions: false
debug: false
show-help-on-first-use: true
server-side-cui: true
+command-block-support: false
diff --git a/worldedit-bukkit/src/main/resources/worldedit-adapters.jar b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar
new file mode 100644
index 000000000..43a6d5009
Binary files /dev/null and b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar differ
diff --git a/worldedit-cli/build.gradle.kts b/worldedit-cli/build.gradle.kts
new file mode 100644
index 000000000..ab563127c
--- /dev/null
+++ b/worldedit-cli/build.gradle.kts
@@ -0,0 +1,33 @@
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+
+applyPlatformAndCoreConfiguration()
+applyShadowConfiguration()
+
+dependencies {
+ "compile"(project(":worldedit-core"))
+ "compile"("org.apache.logging.log4j:log4j-core:2.8.1")
+ "compile"("org.apache.logging.log4j:log4j-slf4j-impl:2.8.1")
+ "compile"("commons-cli:commons-cli:1.4")
+}
+
+tasks.named("jar") {
+ manifest {
+ attributes(
+ "Implementation-Version" to project.version,
+ "Main-Class" to "com.sk89q.worldedit.cli.CLIWorldEdit"
+ )
+ }
+}
+
+tasks.named("shadowJar") {
+ dependencies {
+ include { true }
+ }
+ minimize {
+ exclude(dependency("org.apache.logging.log4j:log4j-core"))
+ }
+}
+
+tasks.named("assemble").configure {
+ dependsOn("shadowJar")
+}
diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIBlockCategoryRegistry.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIBlockCategoryRegistry.java
new file mode 100644
index 000000000..697e9e521
--- /dev/null
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIBlockCategoryRegistry.java
@@ -0,0 +1,37 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.cli;
+
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.registry.BlockCategoryRegistry;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class CLIBlockCategoryRegistry implements BlockCategoryRegistry {
+
+ @Override
+ public Set getCategorisedByName(String category) {
+ return CLIWorldEdit.inst.getFileRegistries().getDataFile().blocktags.getOrDefault(category, Collections.emptyList()).stream()
+ .map(BlockType.REGISTRY::get)
+ .collect(Collectors.toSet());
+ }
+}
diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIBlockRegistry.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIBlockRegistry.java
new file mode 100644
index 000000000..7cc74f45a
--- /dev/null
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIBlockRegistry.java
@@ -0,0 +1,73 @@
+/*
+ * 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.cli;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.sk89q.worldedit.cli.data.FileRegistries;
+import com.sk89q.worldedit.registry.state.BooleanProperty;
+import com.sk89q.worldedit.registry.state.DirectionalProperty;
+import com.sk89q.worldedit.registry.state.EnumProperty;
+import com.sk89q.worldedit.registry.state.IntegerProperty;
+import com.sk89q.worldedit.registry.state.Property;
+import com.sk89q.worldedit.util.Direction;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.registry.BundledBlockRegistry;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+public class CLIBlockRegistry extends BundledBlockRegistry {
+
+ private Property> createProperty(String type, String key, List values) {
+ switch (type) {
+ case "int": {
+ List fixedValues = values.stream().map(Integer::parseInt).collect(Collectors.toList());
+ return new IntegerProperty(key, fixedValues);
+ }
+ case "bool": {
+ List fixedValues = values.stream().map(Boolean::parseBoolean).collect(Collectors.toList());
+ return new BooleanProperty(key, fixedValues);
+ }
+ case "enum": {
+ return new EnumProperty(key, values);
+ }
+ case "direction": {
+ List fixedValues = values.stream().map(String::toUpperCase).map(Direction::valueOf).collect(Collectors.toList());
+ return new DirectionalProperty(key, fixedValues);
+ }
+ default:
+ throw new RuntimeException("Failed to create property");
+ }
+ }
+
+ @Nullable
+ @Override
+ public Map> getProperties(BlockType blockType) {
+ Map properties =
+ CLIWorldEdit.inst.getFileRegistries().getDataFile().blocks.get(blockType.getId()).properties;
+ return ImmutableMap.copyOf(Maps.transformEntries(properties,
+ (Maps.EntryTransformer>)
+ (key, value) -> createProperty(value.type, key, value.values)));
+ }
+}
diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLICommandSender.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLICommandSender.java
new file mode 100644
index 000000000..970c11df5
--- /dev/null
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLICommandSender.java
@@ -0,0 +1,165 @@
+/*
+ * 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.cli;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+import com.sk89q.worldedit.session.SessionKey;
+import com.sk89q.worldedit.util.FileDialogUtil;
+import com.sk89q.worldedit.util.auth.AuthorizationException;
+import com.sk89q.worldedit.util.formatting.WorldEditText;
+import com.sk89q.worldedit.util.formatting.text.Component;
+import com.sk89q.worldedit.util.formatting.text.serializer.plain.PlainComponentSerializer;
+import org.slf4j.Logger;
+
+import java.io.File;
+import java.util.UUID;
+
+public class CLICommandSender implements Actor {
+
+ /**
+ * One time generated ID.
+ */
+ private static final UUID DEFAULT_ID = UUID.fromString("a233eb4b-4cab-42cd-9fd9-7e7b9a3f74be");
+
+ private final CLIWorldEdit app;
+ private final Logger sender;
+
+ public CLICommandSender(CLIWorldEdit app, Logger sender) {
+ checkNotNull(app);
+ checkNotNull(sender);
+
+ this.app = app;
+ this.sender = sender;
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return DEFAULT_ID;
+ }
+
+ @Override
+ public String getName() {
+ return "Console";
+ }
+
+ @Override
+ public void printRaw(String msg) {
+ for (String part : msg.split("\n")) {
+ sender.info(part);
+ }
+ }
+
+ private static final String ANSI_PURPLE = "\u001B[35m";
+ private static final String ANSI_RED = "\u001B[31m";
+ private static final String ANSI_GREEN = "\u001B[32m";
+ private static final String ANSI_RESET = "\u001B[0m";
+
+ @Override
+ public void print(String msg) {
+ for (String part : msg.split("\n")) {
+ sender.info(ANSI_PURPLE + part + ANSI_RESET);
+ }
+ }
+
+ @Override
+ public void printDebug(String msg) {
+ for (String part : msg.split("\n")) {
+ sender.debug(ANSI_GREEN + part + ANSI_RESET);
+ }
+ }
+
+ @Override
+ public void printError(String msg) {
+ for (String part : msg.split("\n")) {
+ sender.error(ANSI_RED + part + ANSI_RESET);
+ }
+ }
+
+ @Override
+ public void print(Component component) {
+ print(PlainComponentSerializer.INSTANCE.serialize(WorldEditText.format(component)));
+ }
+
+ @Override
+ public boolean canDestroyBedrock() {
+ return true;
+ }
+
+ @Override
+ public String[] getGroups() {
+ return new String[0];
+ }
+
+ @Override
+ public boolean hasPermission(String perm) {
+ return true;
+ }
+
+ @Override
+ public void checkPermission(String permission) throws AuthorizationException {
+ }
+
+ @Override
+ public boolean isPlayer() {
+ return false;
+ }
+
+ @Override
+ public File openFileOpenDialog(String[] extensions) {
+ return FileDialogUtil.showOpenDialog(extensions);
+ }
+
+ @Override
+ public File openFileSaveDialog(String[] extensions) {
+ return FileDialogUtil.showSaveDialog(extensions);
+ }
+
+ @Override
+ public void dispatchCUIEvent(CUIEvent event) {
+ }
+
+ @Override
+ public SessionKey getSessionKey() {
+ return new SessionKey() {
+ @Override
+ public String getName() {
+ return "Console";
+ }
+
+ @Override
+ public boolean isActive() {
+ return true;
+ }
+
+ @Override
+ public boolean isPersistent() {
+ return true;
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return DEFAULT_ID;
+ }
+ };
+ }
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/OperatorToken.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIConfiguration.java
similarity index 66%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/OperatorToken.java
rename to worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIConfiguration.java
index c4b70e475..95adb23aa 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/OperatorToken.java
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIConfiguration.java
@@ -17,28 +17,24 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.lexer.tokens;
+package com.sk89q.worldedit.cli;
-/**
- * A unary or binary operator.
- */
-public class OperatorToken extends Token {
+import com.sk89q.worldedit.util.PropertiesConfiguration;
- public final String operator;
+import java.io.File;
- public OperatorToken(int position, String operator) {
- super(position);
- this.operator = operator;
+public class CLIConfiguration extends PropertiesConfiguration {
+
+ public CLIConfiguration(CLIWorldEdit app) {
+ super(app.getWorkingDir().resolve("worldedit.properties").toFile());
}
@Override
- public char id() {
- return 'o';
+ protected void loadExtra() {
}
@Override
- public String toString() {
- return "OperatorToken(" + operator + ")";
+ public File getWorkingDirectory() {
+ return CLIWorldEdit.inst.getWorkingDir().toFile();
}
-
-}
+}
\ No newline at end of file
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/CharacterToken.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIItemCategoryRegistry.java
similarity index 61%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/CharacterToken.java
rename to worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIItemCategoryRegistry.java
index 51bf757da..40b99b59a 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/CharacterToken.java
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIItemCategoryRegistry.java
@@ -17,28 +17,20 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.lexer.tokens;
+package com.sk89q.worldedit.cli;
-/**
- * A single character that doesn't fit any of the other token categories.
- */
-public class CharacterToken extends Token {
+import com.sk89q.worldedit.world.item.ItemType;
+import com.sk89q.worldedit.world.registry.ItemCategoryRegistry;
- public final char character;
+import java.util.Set;
+import java.util.stream.Collectors;
- public CharacterToken(int position, char character) {
- super(position);
- this.character = character;
- }
+public class CLIItemCategoryRegistry implements ItemCategoryRegistry {
@Override
- public char id() {
- return character;
+ public Set getCategorisedByName(String category) {
+ return CLIWorldEdit.inst.getFileRegistries().getDataFile().itemtags.get(category).stream()
+ .map(ItemType.REGISTRY::get)
+ .collect(Collectors.toSet());
}
-
- @Override
- public String toString() {
- return "CharacterToken(" + character + ")";
- }
-
}
diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java
new file mode 100644
index 000000000..c2e9eaba9
--- /dev/null
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIPlatform.java
@@ -0,0 +1,159 @@
+/*
+ * 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.cli;
+
+import com.sk89q.worldedit.entity.Player;
+import com.sk89q.worldedit.extension.platform.AbstractPlatform;
+import com.sk89q.worldedit.extension.platform.Capability;
+import com.sk89q.worldedit.extension.platform.Preference;
+import com.sk89q.worldedit.world.DataFixer;
+import com.sk89q.worldedit.world.World;
+import com.sk89q.worldedit.world.entity.EntityTypes;
+import com.sk89q.worldedit.world.registry.Registries;
+import org.enginehub.piston.CommandManager;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.annotation.Nullable;
+
+class CLIPlatform extends AbstractPlatform {
+
+ private final CLIWorldEdit app;
+ private int dataVersion = -1;
+
+ private final List worlds = new ArrayList<>();
+ private final Timer timer = new Timer();
+ private int lastTimerId = 0;
+
+ CLIPlatform(CLIWorldEdit app) {
+ this.app = app;
+ }
+
+ @Override
+ public Registries getRegistries() {
+ return CLIRegistries.getInstance();
+ }
+
+ @Override
+ public int getDataVersion() {
+ return this.dataVersion;
+ }
+
+ public void setDataVersion(int dataVersion) {
+ this.dataVersion = dataVersion;
+ }
+
+ @Override
+ public DataFixer getDataFixer() {
+ return null;
+ }
+
+ @Override
+ public boolean isValidMobType(String type) {
+ return EntityTypes.get(type) != null;
+ }
+
+ @Override
+ public void reload() {
+ getConfiguration().load();
+ }
+
+ @Override
+ public int schedule(long delay, long period, Runnable task) {
+ this.timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ task.run();
+ if (period >= 0) {
+ timer.schedule(this, period);
+ }
+ }
+ }, delay);
+ return this.lastTimerId++;
+ }
+
+ @Override
+ public List extends World> getWorlds() {
+ return this.worlds;
+ }
+
+ @Nullable
+ @Override
+ public Player matchPlayer(Player player) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public World matchWorld(World world) {
+ return this.worlds.stream()
+ .filter(w -> w.getId().equals(world.getId()))
+ .findAny()
+ .orElse(null);
+ }
+
+ @Override
+ public void registerCommands(CommandManager manager) {
+ }
+
+ @Override
+ public void registerGameHooks() {
+ }
+
+ @Override
+ public CLIConfiguration getConfiguration() {
+ return app.getConfig();
+ }
+
+ @Override
+ public String getVersion() {
+ return app.getInternalVersion();
+ }
+
+ @Override
+ public String getPlatformName() {
+ return "CLI-Official";
+ }
+
+ @Override
+ public String getPlatformVersion() {
+ return app.getInternalVersion();
+ }
+
+ @Override
+ public Map getCapabilities() {
+ Map capabilities = new EnumMap<>(Capability.class);
+ capabilities.put(Capability.CONFIGURATION, Preference.PREFER_OTHERS);
+ capabilities.put(Capability.GAME_HOOKS, Preference.NORMAL);
+ capabilities.put(Capability.PERMISSIONS, Preference.NORMAL);
+ capabilities.put(Capability.USER_COMMANDS, Preference.NORMAL);
+ capabilities.put(Capability.WORLD_EDITING, Preference.PREFERRED);
+ return capabilities;
+ }
+
+ public void addWorld(World world) {
+ worlds.add(world);
+ }
+}
diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIRegistries.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIRegistries.java
new file mode 100644
index 000000000..82e7119d6
--- /dev/null
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIRegistries.java
@@ -0,0 +1,64 @@
+/*
+ * 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.cli;
+
+import com.sk89q.worldedit.world.registry.BlockCategoryRegistry;
+import com.sk89q.worldedit.world.registry.BlockRegistry;
+import com.sk89q.worldedit.world.registry.BundledRegistries;
+import com.sk89q.worldedit.world.registry.ItemCategoryRegistry;
+
+public class CLIRegistries extends BundledRegistries {
+
+ private static final CLIRegistries INSTANCE = new CLIRegistries();
+ private final BlockRegistry blockRegistry = new CLIBlockRegistry();
+ private final BlockCategoryRegistry blockCategoryRegistry = new CLIBlockCategoryRegistry();
+ private final ItemCategoryRegistry itemCategoryRegistry = new CLIItemCategoryRegistry();
+
+ /**
+ * Create a new instance.
+ */
+ private CLIRegistries() {
+ }
+
+ @Override
+ public BlockRegistry getBlockRegistry() {
+ return blockRegistry;
+ }
+
+ @Override
+ public BlockCategoryRegistry getBlockCategoryRegistry() {
+ return blockCategoryRegistry;
+ }
+
+ @Override
+ public ItemCategoryRegistry getItemCategoryRegistry() {
+ return itemCategoryRegistry;
+ }
+
+ /**
+ * Get a static instance.
+ *
+ * @return an instance
+ */
+ public static CLIRegistries getInstance() {
+ return INSTANCE;
+ }
+
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/KeywordToken.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIWorld.java
similarity index 65%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/KeywordToken.java
rename to worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIWorld.java
index 208e7280c..7df65f333 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/KeywordToken.java
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIWorld.java
@@ -17,28 +17,28 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.lexer.tokens;
+package com.sk89q.worldedit.cli;
-/**
- * A keyword.
- */
-public class KeywordToken extends Token {
+public interface CLIWorld {
- public final String value;
+ /**
+ * Saves this world back to file if dirty or forced.
+ *
+ * @param force Force a save
+ */
+ void save(boolean force);
- public KeywordToken(int position, String value) {
- super(position);
- this.value = value;
- }
-
- @Override
- public char id() {
- return 'k';
- }
-
- @Override
- public String toString() {
- return "KeywordToken(" + value + ")";
- }
+ /**
+ * Gets whether the world is dirty.
+ *
+ * @return If it's dirty
+ */
+ boolean isDirty();
+ /**
+ * Set the world's dirty status
+ *
+ * @param dirty if dirty
+ */
+ void setDirty(boolean dirty);
}
diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIWorldEdit.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIWorldEdit.java
new file mode 100644
index 000000000..ba77fb1f2
--- /dev/null
+++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIWorldEdit.java
@@ -0,0 +1,335 @@
+/*
+ * 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.cli;
+
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.cli.data.FileRegistries;
+import com.sk89q.worldedit.cli.schematic.ClipboardWorld;
+import com.sk89q.worldedit.event.platform.CommandEvent;
+import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.extension.platform.Platform;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
+import com.sk89q.worldedit.registry.state.Property;
+import com.sk89q.worldedit.world.biome.BiomeType;
+import com.sk89q.worldedit.world.block.BlockCategory;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.block.FuzzyBlockState;
+import com.sk89q.worldedit.world.entity.EntityType;
+import com.sk89q.worldedit.world.item.ItemCategory;
+import com.sk89q.worldedit.world.item.ItemType;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Options;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * The CLI implementation of WorldEdit.
+ */
+public class CLIWorldEdit {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CLIWorldEdit.class);
+
+ public static CLIWorldEdit inst;
+
+ private CLIPlatform platform;
+ private CLIConfiguration config;
+ private Path workingDir;
+ private String version;
+
+ private Actor commandSender;
+
+ private FileRegistries fileRegistries;
+
+ public CLIWorldEdit() {
+ inst = this;
+ }
+
+ private void setupPlatform() {
+ this.fileRegistries = new FileRegistries(this);
+ this.fileRegistries.loadDataFiles();
+
+ WorldEdit.getInstance().getPlatformManager().register(platform);
+ }
+
+ public void setupRegistries() {
+ // Blocks
+ for (Map.Entry manifestEntry : fileRegistries.getDataFile().blocks.entrySet()) {
+ if (BlockType.REGISTRY.get(manifestEntry.getKey()) == null) {
+ BlockType.REGISTRY.register(manifestEntry.getKey(), new BlockType(manifestEntry.getKey(), input -> {
+ ParserContext context = new ParserContext();
+ context.setPreferringWildcard(true);
+ context.setTryLegacy(false);
+ context.setRestricted(false);
+ try {
+ FuzzyBlockState state = (FuzzyBlockState) WorldEdit.getInstance().getBlockFactory().parseFromInput(
+ manifestEntry.getValue().defaultstate,
+ context
+ ).toImmutableState();
+ BlockState defaultState = input.getBlockType().getAllStates().get(0);
+ for (Map.Entry, Object> propertyObjectEntry : state.getStates().entrySet()) {
+ @SuppressWarnings("unchecked")
+ Property
- *
- *
Variables are also supported and can be set either by passing values
- * to {@link #evaluate(double...)}.
+ * pass values for all slots specified while compiling.
+ * To query slots after evaluation, you can use the {@linkplain #getSlots() slot table}.
*/
public class Expression {
private static final ThreadLocal> instance = ThreadLocal.withInitial(ArrayDeque::new);
- private static final ExecutorService evalThread = Executors.newCachedThreadPool(
- new ThreadFactoryBuilder()
- .setDaemon(true)
- .setNameFormat("worldedit-expression-eval-%d")
- .build());
+ private static final ExecutorService evalThread = Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors(),
+ new ThreadFactoryBuilder()
+ .setDaemon(true)
+ .setNameFormat("worldedit-expression-eval-%d")
+ .build());
- private final Map variables = new HashMap<>();
+ private final SlotTable slots = new SlotTable();
+ private final List providedSlots;
private Variable[] variableArray;
- private RValue root;
- private final Functions functions = new Functions();
+ private ExpressionParser.AllStatementsContext root;
+ private final SetMultimap functions = Functions.getFunctionMap();
private ExpressionEnvironment environment;
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
return new Expression(expression, variableNames);
}
+ private Expression(String expression, String... variableNames) throws ExpressionException {
+ slots.putSlot("e", new LocalSlot.Constant(Math.E));
+ slots.putSlot("pi", new LocalSlot.Constant(Math.PI));
+ slots.putSlot("true", new LocalSlot.Constant(1));
+ slots.putSlot("false", new LocalSlot.Constant(0));
+
+ for (String variableName : variableNames) {
+ slots.initVariable(variableName)
+ .orElseThrow(() -> new ExpressionException(-1,
+ "Tried to overwrite identifier '" + variableName + "'"));
+ }
+ this.providedSlots = ImmutableList.copyOf(variableNames);
+
+ CharStream cs = CharStreams.fromString(expression, "");
+ ExpressionLexer lexer = new ExpressionLexer(cs);
+ lexer.removeErrorListeners();
+ lexer.addErrorListener(new LexerErrorListener());
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
+ ExpressionParser parser = new ExpressionParser(tokens);
+ parser.removeErrorListeners();
+ parser.addErrorListener(new ParserErrorListener());
+ try {
+ root = parser.allStatements();
+ Objects.requireNonNull(root, "Unable to parse root, but no exceptions?");
+ } catch (ParseCancellationException e) {
+ throw new ParserException(parser.getState(), e);
+ }
+ ParseTreeWalker.DEFAULT.walk(new ExpressionValidator(slots.keySet(), functions), root);
+ }
+
public Expression(double constant) {
root = new Constant(0, constant);
}
- private Expression(String expression, String... variableNames) throws ExpressionException {
- this(Lexer.tokenize(expression), variableNames);
- }
-
- private Expression(List tokens, String... variableNames) throws ExpressionException {
-
- variables.put("e", new Constant(-1, Math.E));
- variables.put("pi", new Constant(-1, Math.PI));
- variables.put("true", new Constant(-1, 1));
- variables.put("false", new Constant(-1, 0));
-
- variableArray = new Variable[variableNames.length];
- for (int i = 0; i < variableNames.length; i++) {
- if (variables.containsKey(variableNames[i])) {
- throw new ExpressionException(-1, "Tried to overwrite identifier '" + variableNames[i] + "'");
- }
- Variable var = new Variable(0);
- variables.put(variableNames[i], var);
- variableArray[i] = var;
- }
-
- root = Parser.parse(tokens, this);
- }
-
public double evaluate(double x, double y, double z) throws EvaluationException {
return evaluateTimeout(WorldEdit.getInstance().getConfiguration().calculationTimeout, x, y, z);
}
@@ -157,10 +162,19 @@ public class Expression {
return root.getValue();
}
for (int i = 0; i < values.length; ++i) {
- Variable var = variableArray[i];
- var.value = values[i];
+ String slotName = providedSlots.get(i);
+ LocalSlot.Variable slot = slots.getVariable(slotName)
+ .orElseThrow(() -> new EvaluationException(-1,
+ "Tried to assign to non-variable " + slotName + "."));
+
+ slot.setValue(values[i]);
}
- return evaluateFinal(timeout);
+
+ // evaluation exceptions are thrown out of this method
+ if (timeout < 0) {
+ return evaluateRoot();
+ }
+ return evaluateRootTimed(timeout);
}
private double evaluateFinal(int timeout) throws EvaluationException {
@@ -175,6 +189,7 @@ public class Expression {
}
private double evaluateRootTimed(int timeout) throws EvaluationException {
+ CountDownLatch startLatch = new CountDownLatch(1);
Request request = Request.request();
Future result = evalThread.submit(() -> {
Request local = Request.request();
@@ -182,12 +197,14 @@ public class Expression {
local.setWorld(request.getWorld());
local.setEditSession(request.getEditSession());
try {
+ startLatch.countDown();
return Expression.this.evaluateRoot();
} finally {
Request.reset();
}
});
try {
+ startLatch.await();
return result.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -197,12 +214,8 @@ public class Expression {
throw new ExpressionTimeoutException("Calculations exceeded time limit.");
} catch (ExecutionException e) {
Throwable cause = e.getCause();
- if (cause instanceof EvaluationException) {
- throw (EvaluationException) cause;
- }
- if (cause instanceof RuntimeException) {
- throw (RuntimeException) cause;
- }
+ Throwables.throwIfInstanceOf(cause, EvaluationException.class);
+ Throwables.throwIfUnchecked(cause);
throw new RuntimeException(cause);
}
}
@@ -210,14 +223,18 @@ public class Expression {
private Double evaluateRoot() throws EvaluationException {
pushInstance();
try {
- return root.getValue();
+ return root.accept(new EvaluatingVisitor(slots, functions));
} finally {
popInstance();
}
}
- public void optimize() throws EvaluationException {
- root = root.optimize();
+ public void optimize() {
+ // TODO optimizing
+ }
+
+ public SlotTable getSlots() {
+ return slots;
}
public RValue getRoot() {
@@ -229,15 +246,6 @@ public class Expression {
return root.toString();
}
- public RValue getVariable(String name, boolean create) {
- RValue variable = variables.get(name);
- if (variable == null && create) {
- variables.put(name, variable = new Variable(0));
- }
-
- return variable;
- }
-
public static Expression getInstance() {
return instance.get().peek();
}
@@ -253,10 +261,6 @@ public class Expression {
foo.pop();
}
- public Functions getFunctions() {
- return functions;
- }
-
public ExpressionEnvironment getEnvironment() {
return environment;
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionEnvironment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionEnvironment.java
similarity index 95%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionEnvironment.java
rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionEnvironment.java
index 1a9a57d4a..9fa11e923 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionEnvironment.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionEnvironment.java
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.runtime;
+package com.sk89q.worldedit.internal.expression;
/**
* Represents a way to access blocks in a world. Has to accept non-rounded coordinates.
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionException.java
index 2320ad99c..41c6f8863 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionException.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionException.java
@@ -23,7 +23,7 @@ package com.sk89q.worldedit.internal.expression;
* Thrown when there's a problem during any stage of the expression
* compilation or evaluation.
*/
-public class ExpressionException extends Exception {
+public class ExpressionException extends RuntimeException {
private final int position;
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionHelper.java
new file mode 100644
index 000000000..f1689f78e
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionHelper.java
@@ -0,0 +1,149 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+import com.google.common.collect.SetMultimap;
+import com.sk89q.worldedit.antlr.ExpressionParser;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.Token;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.sk89q.worldedit.antlr.ExpressionLexer.ID;
+
+class ExpressionHelper {
+
+ static void check(boolean condition, ParserRuleContext ctx, String message) {
+ if (!condition) {
+ throw evalException(ctx, message);
+ }
+ }
+
+ static EvaluationException evalException(ParserRuleContext ctx, String message) {
+ return new EvaluationException(
+ ctx.getStart().getCharPositionInLine(),
+ message
+ );
+ }
+
+ static void checkIterations(int iterations, ParserRuleContext ctx) {
+ check(iterations <= 256, ctx, "Loop exceeded 256 iterations");
+ }
+
+ static void checkTimeout() {
+ if (Thread.interrupted()) {
+ throw new ExpressionTimeoutException("Calculations exceeded time limit.");
+ }
+ }
+
+ static MethodHandle resolveFunction(SetMultimap functions,
+ ExpressionParser.FunctionCallContext ctx) {
+ String fnName = ctx.name.getText();
+ Set matchingFns = functions.get(fnName);
+ check(!matchingFns.isEmpty(), ctx, "Unknown function '" + fnName + "'");
+ for (MethodHandle function : matchingFns) {
+ MethodType type = function.type();
+ // Validate argc if not varargs
+ if (!function.isVarargsCollector() && type.parameterCount() != ctx.args.size()) {
+ // skip non-matching function
+ continue;
+ }
+ for (int i = 0; i < ctx.args.size(); i++) {
+ ExpressionParser.ExpressionContext arg = ctx.args.get(i);
+ getArgumentHandleName(fnName, type, i, arg);
+ }
+ // good match!
+ return function;
+ }
+ // We matched no function, fail with appropriate message.
+ String possibleCounts = matchingFns.stream()
+ .map(mh -> mh.isVarargsCollector()
+ ? (mh.type().parameterCount() - 1) + "+"
+ : String.valueOf(mh.type().parameterCount()))
+ .collect(Collectors.joining("/"));
+ throw evalException(ctx, "Incorrect number of arguments for function '" + fnName + "', " +
+ "expected " + possibleCounts + ", " +
+ "got " + ctx.args.size());
+ }
+
+ // Special argument handle names
+ /**
+ * The argument should be wrapped in a {@link LocalSlot.Constant} before being passed.
+ */
+ static final String WRAPPED_CONSTANT = "";
+
+ /**
+ * If this argument needs a handle, returns the name of the handle needed. Otherwise, returns
+ * {@code null}. If {@code arg} isn't a valid handle reference, throws.
+ */
+ static String getArgumentHandleName(String fnName, MethodType type, int i,
+ ParserRuleContext arg) {
+ // Pass variable handle in for modification?
+ Class> pType = type.parameterType(i);
+ Optional id = tryResolveId(arg);
+ if (pType == LocalSlot.Variable.class) {
+ // MUST be an id
+ check(id.isPresent(), arg,
+ "Function '" + fnName + "' requires a variable in parameter " + i);
+ return id.get();
+ } else if (pType == LocalSlot.class) {
+ return id.orElse(WRAPPED_CONSTANT);
+ }
+ return null;
+ }
+
+ private static Optional tryResolveId(ParserRuleContext arg) {
+ Optional wrappedExprContext =
+ tryAs(arg, ExpressionParser.WrappedExprContext.class);
+ if (wrappedExprContext.isPresent()) {
+ return tryResolveId(wrappedExprContext.get().expression());
+ }
+ Token token = arg.start;
+ int tokenType = token.getType();
+ boolean isId = arg.start == arg.stop && tokenType == ID;
+ return isId ? Optional.of(token.getText()) : Optional.empty();
+ }
+
+ private static Optional tryAs(
+ ParserRuleContext ctx,
+ Class rule
+ ) {
+ if (rule.isInstance(ctx)) {
+ return Optional.of(rule.cast(ctx));
+ }
+ if (ctx.children.size() != 1) {
+ return Optional.empty();
+ }
+ List ctxs = ctx.getRuleContexts(ParserRuleContext.class);
+ if (ctxs.size() != 1) {
+ return Optional.empty();
+ }
+ return tryAs(ctxs.get(0), rule);
+ }
+
+ private ExpressionHelper() {
+ }
+
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionTimeoutException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionTimeoutException.java
similarity index 94%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionTimeoutException.java
rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionTimeoutException.java
index ce7d55140..e0395cd7a 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionTimeoutException.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionTimeoutException.java
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.runtime;
+package com.sk89q.worldedit.internal.expression;
/**
* Thrown when an evaluation exceeds the timeout time.
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionValidator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionValidator.java
new file mode 100644
index 000000000..c823df306
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionValidator.java
@@ -0,0 +1,70 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+import com.google.common.collect.SetMultimap;
+import com.sk89q.worldedit.antlr.ExpressionBaseListener;
+import com.sk89q.worldedit.antlr.ExpressionParser;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.sk89q.worldedit.internal.expression.ExpressionHelper.check;
+import static com.sk89q.worldedit.internal.expression.ExpressionHelper.resolveFunction;
+
+class ExpressionValidator extends ExpressionBaseListener {
+
+ private final Set variableNames = new HashSet<>();
+ private final SetMultimap functions;
+
+ ExpressionValidator(Collection variableNames,
+ SetMultimap functions) {
+ this.variableNames.addAll(variableNames);
+ this.functions = functions;
+ }
+
+ private void bindVariable(String name) {
+ variableNames.add(name);
+ }
+
+ @Override
+ public void enterAssignment(ExpressionParser.AssignmentContext ctx) {
+ bindVariable(ctx.target.getText());
+ }
+
+ @Override
+ public void enterSimpleForStatement(ExpressionParser.SimpleForStatementContext ctx) {
+ bindVariable(ctx.counter.getText());
+ }
+
+ @Override
+ public void enterIdExpr(ExpressionParser.IdExprContext ctx) {
+ String text = ctx.source.getText();
+ check(variableNames.contains(text), ctx,
+ "Variable '" + text + "' is not bound");
+ }
+
+ @Override
+ public void enterFunctionCall(ExpressionParser.FunctionCallContext ctx) {
+ resolveFunction(functions, ctx);
+ }
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java
new file mode 100644
index 000000000..e97f96ade
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java
@@ -0,0 +1,337 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import com.google.common.primitives.Doubles;
+import com.sk89q.worldedit.internal.expression.LocalSlot.Variable;
+import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.noise.PerlinNoise;
+import com.sk89q.worldedit.math.noise.RidgedMultiFractalNoise;
+import com.sk89q.worldedit.math.noise.VoronoiNoise;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static java.lang.invoke.MethodType.methodType;
+
+/**
+ * Contains all functions that can be used in expressions.
+ */
+final class Functions {
+
+ static SetMultimap getFunctionMap() {
+ SetMultimap map = HashMultimap.create();
+ Functions functions = new Functions();
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ try {
+ addMathHandles(map, lookup);
+ addStaticFunctionHandles(map, lookup);
+ functions.addInstanceFunctionHandles(map, lookup);
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+
+ return ImmutableSetMultimap.copyOf(map);
+ }
+
+ private static void addMathHandles(
+ SetMultimap map,
+ MethodHandles.Lookup lookup
+ ) throws NoSuchMethodException, IllegalAccessException {
+ // double (double) functions
+ for (String name : ImmutableList.of(
+ "sin", "cos", "tan", "asin", "acos", "atan",
+ "sinh", "cosh", "tanh", "sqrt", "cbrt", "abs",
+ "ceil", "floor", "rint", "exp", "log", "log10"
+ )) {
+ map.put(name, lookup.findStatic(Math.class, name,
+ methodType(double.class, double.class)));
+ }
+ // Alias ln -> log
+ map.put("ln", lookup.findStatic(Math.class, "log",
+ methodType(double.class, double.class)));
+ map.put("round", lookup.findStatic(Math.class, "round",
+ methodType(long.class, double.class)));
+
+ map.put("atan2", lookup.findStatic(Math.class, "atan2",
+ methodType(double.class, double.class, double.class)));
+
+ // Special cases: we accept varargs for these
+ map.put("min", lookup.findStatic(Doubles.class, "min",
+ methodType(double.class, double[].class))
+ .asVarargsCollector(double[].class));
+ map.put("max", lookup.findStatic(Doubles.class, "max",
+ methodType(double.class, double[].class))
+ .asVarargsCollector(double[].class));
+ }
+
+ private static void addStaticFunctionHandles(
+ SetMultimap map,
+ MethodHandles.Lookup lookup
+ ) throws NoSuchMethodException, IllegalAccessException {
+ map.put("rotate", lookup.findStatic(Functions.class, "rotate",
+ methodType(double.class, Variable.class, Variable.class, double.class)));
+ map.put("swap", lookup.findStatic(Functions.class, "swap",
+ methodType(double.class, Variable.class, Variable.class)));
+ map.put("gmegabuf", lookup.findStatic(Functions.class, "gmegabuf",
+ methodType(double.class, double.class)));
+ map.put("gmegabuf", lookup.findStatic(Functions.class, "gmegabuf",
+ methodType(double.class, double.class, double.class)));
+ map.put("gclosest", lookup.findStatic(Functions.class, "gclosest",
+ methodType(double.class, double.class, double.class, double.class, double.class,
+ double.class, double.class)));
+ map.put("random", lookup.findStatic(Functions.class, "random",
+ methodType(double.class)));
+ map.put("randint", lookup.findStatic(Functions.class, "randint",
+ methodType(double.class, double.class)));
+ map.put("perlin", lookup.findStatic(Functions.class, "perlin",
+ methodType(double.class, double.class, double.class, double.class, double.class,
+ double.class, double.class, double.class)));
+ map.put("voronoi", lookup.findStatic(Functions.class, "voronoi",
+ methodType(double.class, double.class, double.class, double.class, double.class,
+ double.class)));
+ map.put("ridgedmulti", lookup.findStatic(Functions.class, "ridgedmulti",
+ methodType(double.class, double.class, double.class, double.class, double.class,
+ double.class, double.class)));
+ map.put("query", lookup.findStatic(Functions.class, "query",
+ methodType(double.class, double.class, double.class, double.class, LocalSlot.class,
+ LocalSlot.class)));
+ map.put("queryAbs", lookup.findStatic(Functions.class, "queryAbs",
+ methodType(double.class, double.class, double.class, double.class, LocalSlot.class,
+ LocalSlot.class)));
+ map.put("queryRel", lookup.findStatic(Functions.class, "queryRel",
+ methodType(double.class, double.class, double.class, double.class, LocalSlot.class,
+ LocalSlot.class)));
+ }
+
+ private void addInstanceFunctionHandles(
+ SetMultimap map,
+ MethodHandles.Lookup lookup
+ ) throws NoSuchMethodException, IllegalAccessException {
+ map.put("megabuf", lookup.findSpecial(Functions.class, "megabuf",
+ methodType(double.class, double.class), Functions.class)
+ .bindTo(this));
+ map.put("megabuf", lookup.findSpecial(Functions.class, "megabuf",
+ methodType(double.class, double.class, double.class), Functions.class)
+ .bindTo(this));
+ map.put("closest", lookup.findSpecial(Functions.class, "closest",
+ methodType(double.class, double.class, double.class, double.class, double.class,
+ double.class, double.class), Functions.class)
+ .bindTo(this));
+ }
+
+ private static double rotate(Variable x, Variable y, double angle) {
+ final double cosF = Math.cos(angle);
+ final double sinF = Math.sin(angle);
+
+ final double xOld = x.getValue();
+ final double yOld = y.getValue();
+
+ x.setValue(xOld * cosF - yOld * sinF);
+ y.setValue(xOld * sinF + yOld * cosF);
+
+ return 0.0;
+ }
+
+ private static double swap(Variable x, Variable y) {
+ final double tmp = x.getValue();
+
+ x.setValue(y.getValue());
+ y.setValue(tmp);
+
+ return 0.0;
+ }
+
+
+ private static final Int2ObjectMap globalMegaBuffer = new Int2ObjectOpenHashMap<>();
+ private final Int2ObjectMap megaBuffer = new Int2ObjectOpenHashMap<>();
+
+ private static double[] getSubBuffer(Int2ObjectMap megabuf, int key) {
+ return megabuf.computeIfAbsent(key, k -> new double[1024]);
+ }
+
+ private static double getBufferItem(final Int2ObjectMap megabuf, final int index) {
+ return getSubBuffer(megabuf, index & ~1023)[index & 1023];
+ }
+
+ private static double setBufferItem(final Int2ObjectMap megabuf, final int index, double value) {
+ return getSubBuffer(megabuf, index & ~1023)[index & 1023] = value;
+ }
+
+ private static double gmegabuf(double index) {
+ return getBufferItem(globalMegaBuffer, (int) index);
+ }
+
+ private static double gmegabuf(double index, double value) {
+ return setBufferItem(globalMegaBuffer, (int) index, value);
+ }
+
+ private double megabuf(double index) {
+ return getBufferItem(megaBuffer, (int) index);
+ }
+
+ private double megabuf(double index, double value) {
+ return setBufferItem(megaBuffer, (int) index, value);
+ }
+
+ private double closest(double x, double y, double z, double index, double count, double stride) {
+ return findClosest(
+ megaBuffer, x, y, z, (int) index, (int) count, (int) stride
+ );
+ }
+
+ private static double gclosest(double x, double y, double z, double index, double count, double stride) {
+ return findClosest(
+ globalMegaBuffer, x, y, z, (int) index, (int) count, (int) stride
+ );
+ }
+
+ private static double findClosest(Int2ObjectMap megabuf, double x, double y, double z, int index, int count, int stride) {
+ int closestIndex = -1;
+ double minDistanceSquared = Double.MAX_VALUE;
+
+ for (int i = 0; i < count; ++i) {
+ double currentX = getBufferItem(megabuf, index) - x;
+ double currentY = getBufferItem(megabuf, index+1) - y;
+ double currentZ = getBufferItem(megabuf, index+2) - z;
+
+ double currentDistanceSquared = currentX*currentX + currentY*currentY + currentZ*currentZ;
+
+ if (currentDistanceSquared < minDistanceSquared) {
+ minDistanceSquared = currentDistanceSquared;
+ closestIndex = index;
+ }
+
+ index += stride;
+ }
+
+ return closestIndex;
+ }
+
+ private static double random() {
+ return ThreadLocalRandom.current().nextDouble();
+ }
+
+ private static double randint(double max) {
+ return ThreadLocalRandom.current().nextInt((int) Math.floor(max));
+ }
+
+ private static final ThreadLocal localPerlin = ThreadLocal.withInitial(PerlinNoise::new);
+
+ private static double perlin(double seed, double x, double y, double z,
+ double frequency, double octaves, double persistence) {
+ PerlinNoise perlin = localPerlin.get();
+ try {
+ perlin.setSeed((int) seed);
+ perlin.setFrequency(frequency);
+ perlin.setOctaveCount((int) octaves);
+ perlin.setPersistence(persistence);
+ } catch (IllegalArgumentException e) {
+ throw new EvaluationException(0, "Perlin noise error: " + e.getMessage());
+ }
+ return perlin.noise(Vector3.at(x, y, z));
+ }
+
+ private static final ThreadLocal localVoronoi = ThreadLocal.withInitial(VoronoiNoise::new);
+
+ private static double voronoi(double seed, double x, double y, double z, double frequency) {
+ VoronoiNoise voronoi = localVoronoi.get();
+ try {
+ voronoi.setSeed((int) seed);
+ voronoi.setFrequency(frequency);
+ } catch (IllegalArgumentException e) {
+ throw new EvaluationException(0, "Voronoi error: " + e.getMessage());
+ }
+ return voronoi.noise(Vector3.at(x, y, z));
+ }
+
+ private static final ThreadLocal localRidgedMulti = ThreadLocal.withInitial(RidgedMultiFractalNoise::new);
+
+ private static double ridgedmulti(double seed, double x, double y, double z,
+ double frequency, double octaves) {
+ RidgedMultiFractalNoise ridgedMulti = localRidgedMulti.get();
+ try {
+ ridgedMulti.setSeed((int) seed);
+ ridgedMulti.setFrequency(frequency);
+ ridgedMulti.setOctaveCount((int) octaves);
+ } catch (IllegalArgumentException e) {
+ throw new EvaluationException(0, "Ridged multi error: " + e.getMessage());
+ }
+ return ridgedMulti.noise(Vector3.at(x, y, z));
+ }
+
+ private static double queryInternal(LocalSlot type, LocalSlot data, double typeId, double dataValue) {
+ // Compare to input values and determine return value
+ // -1 is a wildcard, always true
+ double ret = ((type.getValue() == -1 || typeId == type.getValue())
+ && (data.getValue() == -1 || dataValue == data.getValue())) ? 1.0 : 0.0;
+
+ if (type instanceof Variable) {
+ ((Variable) type).setValue(typeId);
+ }
+ if (data instanceof Variable) {
+ ((Variable) data).setValue(dataValue);
+ }
+
+ return ret;
+ }
+
+ private static double query(double x, double y, double z, LocalSlot type, LocalSlot data) {
+ final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
+
+ // Read values from world
+ final double typeId = environment.getBlockType(x, y, z);
+ final double dataValue = environment.getBlockData(x, y, z);
+
+ return queryInternal(type, data, typeId, dataValue);
+ }
+
+ private static double queryAbs(double x, double y, double z, LocalSlot type, LocalSlot data) {
+ final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
+
+ // Read values from world
+ final double typeId = environment.getBlockTypeAbs(x, y, z);
+ final double dataValue = environment.getBlockDataAbs(x, y, z);
+
+ return queryInternal(type, data, typeId, dataValue);
+ }
+
+ private static double queryRel(double x, double y, double z, LocalSlot type, LocalSlot data) {
+ final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
+
+ // Read values from world
+ final double typeId = environment.getBlockTypeRel(x, y, z);
+ final double dataValue = environment.getBlockDataRel(x, y, z);
+
+ return queryInternal(type, data, typeId, dataValue);
+ }
+
+ private Functions() {
+ }
+
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/IdentifierToken.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LexerErrorListener.java
similarity index 65%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/IdentifierToken.java
rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LexerErrorListener.java
index 4a840f754..cb4b41ce5 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/IdentifierToken.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LexerErrorListener.java
@@ -17,28 +17,15 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.lexer.tokens;
+package com.sk89q.worldedit.internal.expression;
-/**
- * An identifier.
- */
-public class IdentifierToken extends Token {
-
- public final String value;
-
- public IdentifierToken(int position, String value) {
- super(position);
- this.value = value;
- }
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+class LexerErrorListener extends BaseErrorListener {
@Override
- public char id() {
- return 'i';
+ public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
+ throw new LexerException(charPositionInLine, msg);
}
-
- @Override
- public String toString() {
- return "IdentifierToken(" + value + ")";
- }
-
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/LexerException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LexerException.java
similarity index 92%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/LexerException.java
rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LexerException.java
index 3e08b2732..ede88dee8 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/LexerException.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LexerException.java
@@ -17,9 +17,7 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.lexer;
-
-import com.sk89q.worldedit.internal.expression.ExpressionException;
+package com.sk89q.worldedit.internal.expression;
/**
* Thrown when the lexer encounters a problem.
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LocalSlot.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LocalSlot.java
new file mode 100644
index 000000000..7bfe5517c
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/LocalSlot.java
@@ -0,0 +1,69 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+/**
+ * Represents the metadata for a named local slot.
+ */
+public interface LocalSlot {
+
+ final class Constant implements LocalSlot {
+ private final double value;
+
+ public Constant(double value) {
+ this.value = value;
+ }
+
+ @Override
+ public double getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ final class Variable implements LocalSlot {
+ private double value;
+
+ public Variable(double value) {
+ this.value = value;
+ }
+
+ public void setValue(double value) {
+ this.value = value;
+ }
+
+ @Override
+ public double getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ double getValue();
+
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ParserErrorListener.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ParserErrorListener.java
new file mode 100644
index 000000000..937c00154
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ParserErrorListener.java
@@ -0,0 +1,31 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+
+class ParserErrorListener extends BaseErrorListener {
+ @Override
+ public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
+ throw new ParserException(charPositionInLine, msg);
+ }
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/ParserException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ParserException.java
similarity index 92%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/ParserException.java
rename to worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ParserException.java
index 9a04fc914..30a3eace8 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/ParserException.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ParserException.java
@@ -17,9 +17,7 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.parser;
-
-import com.sk89q.worldedit.internal.expression.ExpressionException;
+package com.sk89q.worldedit.internal.expression;
/**
* Thrown when the parser encounters a problem.
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/SlotTable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/SlotTable.java
new file mode 100644
index 000000000..e47d4200e
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/SlotTable.java
@@ -0,0 +1,64 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.Set;
+
+public class SlotTable {
+
+ private final Map slots = new HashMap<>();
+
+ public Set keySet() {
+ return slots.keySet();
+ }
+
+ public void putSlot(String name, LocalSlot slot) {
+ slots.put(name, slot);
+ }
+
+ public boolean containsSlot(String name) {
+ return slots.containsKey(name);
+ }
+
+ public Optional initVariable(String name) {
+ slots.computeIfAbsent(name, n -> new LocalSlot.Variable(0));
+ return getVariable(name);
+ }
+
+ public Optional getSlot(String name) {
+ return Optional.ofNullable(slots.get(name));
+ }
+
+ public Optional getVariable(String name) {
+ return getSlot(name)
+ .filter(LocalSlot.Variable.class::isInstance)
+ .map(LocalSlot.Variable.class::cast);
+ }
+
+ public OptionalDouble getSlotValue(String name) {
+ LocalSlot slot = slots.get(name);
+ return slot == null ? OptionalDouble.empty() : OptionalDouble.of(slot.getValue());
+ }
+
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/Lexer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/Lexer.java
deleted file mode 100644
index 8e5b3bbec..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/Lexer.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.lexer;
-
-import com.sk89q.worldedit.internal.expression.lexer.tokens.CharacterToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.IdentifierToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.KeywordToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.NumberToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.OperatorToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.Token;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Processes a string into a list of tokens.
- *
- *
Tokens can be numbers, identifiers, operators and assorted other
- * characters.
- */
-public class Lexer {
-
- private final String expression;
- private int position = 0;
-
- private Lexer(String expression) {
- this.expression = expression;
- }
-
- public static List tokenize(String expression) throws LexerException {
- return new Lexer(expression).tokenize();
- }
-
- private final DecisionTree operatorTree = new DecisionTree(null,
- '+', new DecisionTree("+",
- '=', new DecisionTree("+="),
- '+', new DecisionTree("++")
- ),
- '-', new DecisionTree("-",
- '=', new DecisionTree("-="),
- '-', new DecisionTree("--")
- ),
- '*', new DecisionTree("*",
- '=', new DecisionTree("*="),
- '*', new DecisionTree("**")
- ),
- '/', new DecisionTree("/",
- '=', new DecisionTree("/=")
- ),
- '%', new DecisionTree("%",
- '=', new DecisionTree("%=")
- ),
- '^', new DecisionTree("^",
- '=', new DecisionTree("^=")
- ),
- '=', new DecisionTree("=",
- '=', new DecisionTree("==")
- ),
- '!', new DecisionTree("!",
- '=', new DecisionTree("!=")
- ),
- '<', new DecisionTree("<",
- '<', new DecisionTree("<<"),
- '=', new DecisionTree("<=")
- ),
- '>', new DecisionTree(">",
- '>', new DecisionTree(">>"),
- '=', new DecisionTree(">=")
- ),
- '&', new DecisionTree(null, // not implemented
- '&', new DecisionTree("&&")
- ),
- '|', new DecisionTree(null, // not implemented
- '|', new DecisionTree("||")
- ),
- '~', new DecisionTree("~",
- '=', new DecisionTree("~=")
- )
- );
-
- private static final Set characterTokens = new HashSet<>();
- static {
- characterTokens.add(',');
- characterTokens.add('(');
- characterTokens.add(')');
- characterTokens.add('{');
- characterTokens.add('}');
- characterTokens.add(';');
- characterTokens.add('?');
- characterTokens.add(':');
- }
-
- private static final Set keywords =
- new HashSet<>(Arrays.asList("if", "else", "while", "do", "for", "break", "continue", "return", "switch", "case", "default"));
-
- private static final Pattern numberPattern = Pattern.compile("^([0-9]*(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)");
- private static final Pattern identifierPattern = Pattern.compile("^([A-Za-z][0-9A-Za-z_]*)");
-
- private List tokenize() throws LexerException {
- List tokens = new ArrayList<>();
-
- do {
- skipWhitespace();
- if (position >= expression.length()) {
- break;
- }
-
- Token token = operatorTree.evaluate(position);
- if (token != null) {
- tokens.add(token);
- continue;
- }
-
- final char ch = peek();
-
- if (characterTokens.contains(ch)) {
- tokens.add(new CharacterToken(position++, ch));
- continue;
- }
-
- final Matcher numberMatcher = numberPattern.matcher(expression.substring(position));
- if (numberMatcher.lookingAt()) {
- String numberPart = numberMatcher.group(1);
- if (!numberPart.isEmpty()) {
- try {
- tokens.add(new NumberToken(position, Double.parseDouble(numberPart)));
- } catch (NumberFormatException e) {
- throw new LexerException(position, "Number parsing failed", e);
- }
-
- position += numberPart.length();
- continue;
- }
- }
-
- final Matcher identifierMatcher = identifierPattern.matcher(expression.substring(position));
- if (identifierMatcher.lookingAt()) {
- String identifierPart = identifierMatcher.group(1);
- if (!identifierPart.isEmpty()) {
- if (keywords.contains(identifierPart)) {
- tokens.add(new KeywordToken(position, identifierPart));
- } else {
- tokens.add(new IdentifierToken(position, identifierPart));
- }
-
- position += identifierPart.length();
- continue;
- }
- }
-
- throw new LexerException(position, "Unknown character '" + ch + "'");
- } while (position < expression.length());
-
- return tokens;
- }
-
- private char peek() {
- return expression.charAt(position);
- }
-
- private void skipWhitespace() {
- while (position < expression.length() && Character.isWhitespace(peek())) {
- ++position;
- }
- }
-
- public class DecisionTree {
- private final String tokenName;
- private final Map subTrees = new HashMap<>();
-
- private DecisionTree(String tokenName, Object... args) {
- this.tokenName = tokenName;
-
- if (args.length % 2 != 0) {
- throw new UnsupportedOperationException("You need to pass an even number of arguments.");
- }
-
- for (int i = 0; i < args.length; i += 2) {
- if (!(args[i] instanceof Character)) {
- throw new UnsupportedOperationException("Argument #" + i + " expected to be 'Character', not '" + args[i].getClass().getName() + "'.");
- }
- if (!(args[i + 1] instanceof DecisionTree)) {
- throw new UnsupportedOperationException("Argument #" + (i + 1) + " expected to be 'DecisionTree', not '" + args[i + 1].getClass().getName() + "'.");
- }
-
- Character next = (Character) args[i];
- DecisionTree subTree = (DecisionTree) args[i + 1];
-
- subTrees.put(next, subTree);
- }
- }
-
- private Token evaluate(int startPosition) throws LexerException {
- if (position < expression.length()) {
- final char next = peek();
-
- final DecisionTree subTree = subTrees.get(next);
- if (subTree != null) {
- ++position;
- final Token subTreeResult = subTree.evaluate(startPosition);
- if (subTreeResult != null) {
- return subTreeResult;
- }
- --position;
- }
- }
-
- if (tokenName == null) {
- return null;
- }
-
- return new OperatorToken(startPosition, tokenName);
- }
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/Token.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/Token.java
deleted file mode 100644
index c6427f0c5..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/Token.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.lexer.tokens;
-
-import com.sk89q.worldedit.internal.expression.Identifiable;
-
-/**
- * A token. The lexer generates these to make the parser's job easier.
- */
-public abstract class Token implements Identifiable {
-
- private final int position;
-
- public Token(int position) {
- this.position = position;
- }
-
- @Override
- public int getPosition() {
- return position;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/Parser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/Parser.java
deleted file mode 100644
index 24f3dafa9..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/Parser.java
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.parser;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.Identifiable;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.IdentifierToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.KeywordToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.NumberToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.OperatorToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.Token;
-import com.sk89q.worldedit.internal.expression.runtime.Break;
-import com.sk89q.worldedit.internal.expression.runtime.Conditional;
-import com.sk89q.worldedit.internal.expression.runtime.Constant;
-import com.sk89q.worldedit.internal.expression.runtime.For;
-import com.sk89q.worldedit.internal.expression.runtime.Function;
-import com.sk89q.worldedit.internal.expression.runtime.Functions;
-import com.sk89q.worldedit.internal.expression.runtime.LValue;
-import com.sk89q.worldedit.internal.expression.runtime.RValue;
-import com.sk89q.worldedit.internal.expression.runtime.Return;
-import com.sk89q.worldedit.internal.expression.runtime.Sequence;
-import com.sk89q.worldedit.internal.expression.runtime.SimpleFor;
-import com.sk89q.worldedit.internal.expression.runtime.Switch;
-import com.sk89q.worldedit.internal.expression.runtime.While;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Processes a list of tokens into an executable tree.
- *
- *
Tokens can be numbers, identifiers, operators and assorted other characters.
- */
-public class Parser {
- private final class NullToken extends Token {
- private NullToken(int position) {
- super(position);
- }
-
- @Override
- public char id() {
- return '\0';
- }
-
- @Override
- public String toString() {
- return "NullToken";
- }
- }
-
- private final List tokens;
- private int position = 0;
- private Expression expression;
-
- private Parser(List tokens, Expression expression) {
- this.tokens = tokens;
- this.expression = expression;
- }
-
- public static RValue parse(List tokens, Expression expression) throws ParserException {
- return new Parser(tokens, expression).parse();
- }
-
- private RValue parse() throws ParserException {
- final RValue ret = parseStatements(false);
- if (position < tokens.size()) {
- final Token token = peek();
- throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token);
- }
-
- ret.bindVariables(expression, false);
-
- return ret;
- }
-
- private RValue parseStatements(boolean singleStatement) throws ParserException {
- List statements = new ArrayList<>();
- loop: while (position < tokens.size()) {
- boolean expectSemicolon = false;
-
- final Token current = peek();
- switch (current.id()) {
- case '{':
- consumeCharacter('{');
-
- statements.add(parseStatements(false));
-
- consumeCharacter('}');
-
- break;
-
- case '}':
- break loop;
-
- case 'k':
- final String keyword = ((KeywordToken) current).value;
- switch (keyword.charAt(0)) {
- case 'i': { // if
- ++position;
- final RValue condition = parseBracket();
- final RValue truePart = parseStatements(true);
- final RValue falsePart;
-
- if (hasKeyword("else")) {
- ++position;
- falsePart = parseStatements(true);
- } else {
- falsePart = null;
- }
-
- statements.add(new Conditional(current.getPosition(), condition, truePart, falsePart));
- break;
- }
-
- case 'w': { // while
- ++position;
- final RValue condition = parseBracket();
- final RValue body = parseStatements(true);
-
- statements.add(new While(current.getPosition(), condition, body, false));
- break;
- }
-
- case 'd': { // do/default
- if (hasKeyword("default")) {
- break loop;
- }
-
- ++position;
- final RValue body = parseStatements(true);
-
- consumeKeyword("while");
-
- final RValue condition = parseBracket();
-
- statements.add(new While(current.getPosition(), condition, body, true));
-
- expectSemicolon = true;
- break;
- }
-
- case 'f': { // for
- ++position;
- consumeCharacter('(');
- int oldPosition = position;
- final RValue init = parseExpression(true);
- //if ((init instanceof LValue) && )
- if (peek().id() == ';') {
- ++position;
- final RValue condition = parseExpression(true);
- consumeCharacter(';');
- final RValue increment = parseExpression(true);
- consumeCharacter(')');
- final RValue body = parseStatements(true);
-
- statements.add(new For(current.getPosition(), init, condition, increment, body));
- } else {
- position = oldPosition;
-
- final Token variableToken = peek();
- if (!(variableToken instanceof IdentifierToken)) {
- throw new ParserException(variableToken.getPosition(), "Expected identifier");
- }
-
- RValue variable = expression.getVariable(((IdentifierToken) variableToken).value, true);
- if (!(variable instanceof LValue)) {
- throw new ParserException(variableToken.getPosition(), "Expected variable");
- }
- ++position;
-
- final Token equalsToken = peek();
- if (!(equalsToken instanceof OperatorToken) || !((OperatorToken) equalsToken).operator.equals("=")) {
- throw new ParserException(variableToken.getPosition(), "Expected '=' or a term and ';'");
- }
- ++position;
-
- final RValue first = parseExpression(true);
- consumeCharacter(',');
- final RValue last = parseExpression(true);
- consumeCharacter(')');
- final RValue body = parseStatements(true);
-
- statements.add(new SimpleFor(current.getPosition(), (LValue) variable, first, last, body));
- } // switch (keyword.charAt(0))
- break;
- }
-
- case 'b': // break
- ++position;
- statements.add(new Break(current.getPosition(), false));
- break;
-
- case 'c': // continue/case
- if (hasKeyword("case")) {
- break loop;
- }
-
- ++position;
- statements.add(new Break(current.getPosition(), true));
- break;
-
- case 'r': // return
- ++position;
- statements.add(new Return(current.getPosition(), parseExpression(true)));
-
- expectSemicolon = true;
- break;
-
- case 's': // switch
- ++position;
- final RValue parameter = parseBracket();
- final List values = new ArrayList<>();
- final List caseStatements = new ArrayList<>();
- RValue defaultCase = null;
-
- consumeCharacter('{');
- while (peek().id() != '}') {
- if (position >= tokens.size()) {
- throw new ParserException(current.getPosition(), "Expected '}' instead of EOF");
- }
- if (defaultCase != null) {
- throw new ParserException(current.getPosition(), "Expected '}' instead of " + peek());
- }
-
- if (hasKeyword("case")) {
- ++position;
-
- final Token valueToken = peek();
- if (!(valueToken instanceof NumberToken)) {
- throw new ParserException(current.getPosition(), "Expected number instead of " + peek());
- }
-
- ++position;
-
- values.add(((NumberToken) valueToken).value);
-
- consumeCharacter(':');
- caseStatements.add(parseStatements(false));
- } else if (hasKeyword("default")) {
- ++position;
-
- consumeCharacter(':');
- defaultCase = parseStatements(false);
- } else {
- throw new ParserException(current.getPosition(), "Expected 'case' or 'default' instead of " + peek());
- }
- }
- consumeCharacter('}');
-
- statements.add(new Switch(current.getPosition(), parameter, values, caseStatements, defaultCase));
- break;
-
- default:
- throw new ParserException(current.getPosition(), "Unexpected keyword '" + keyword + "'");
- }
-
- break;
-
- default:
- statements.add(parseExpression(true));
-
- expectSemicolon = true;
- } // switch (current.id())
-
- if (expectSemicolon) {
- if (peek().id() == ';') {
- ++position;
- } else {
- break;
- }
- }
-
- if (singleStatement) {
- break;
- }
- } // while (position < tokens.size())
-
- switch (statements.size()) {
- case 0:
- if (singleStatement) {
- throw new ParserException(peek().getPosition(), "Statement expected.");
- }
-
- return new Sequence(peek().getPosition());
-
- case 1:
- return statements.get(0);
-
- default:
- return new Sequence(peek().getPosition(), statements.toArray(new RValue[statements.size()]));
- }
- }
-
- private RValue parseExpression(boolean canBeEmpty) throws ParserException {
- LinkedList halfProcessed = new LinkedList<>();
-
- // process brackets, numbers, functions, variables and detect prefix operators
- boolean expressionStart = true;
- loop: while (position < tokens.size()) {
- final Token current = peek();
-
- switch (current.id()) {
- case '0':
- halfProcessed.add(new Constant(current.getPosition(), ((NumberToken) current).value));
- ++position;
- expressionStart = false;
- break;
-
- case 'i':
- final IdentifierToken identifierToken = (IdentifierToken) current;
- ++position;
-
- final Token next = peek();
- if (next.id() == '(') {
- halfProcessed.add(parseFunctionCall(identifierToken));
- } else {
- final RValue variable = expression.getVariable(identifierToken.value, false);
- if (variable == null) {
- halfProcessed.add(new UnboundVariable(identifierToken.getPosition(), identifierToken.value));
- } else {
- halfProcessed.add(variable);
- }
- }
- expressionStart = false;
- break;
-
- case '(':
- halfProcessed.add(parseBracket());
- expressionStart = false;
- break;
-
- case ',':
- case ')':
- case '}':
- case ';':
- break loop;
-
- case 'o':
- if (expressionStart) {
- // Preprocess prefix operators into unary operators
- halfProcessed.add(new UnaryOperator((OperatorToken) current));
- } else {
- halfProcessed.add(current);
- }
- ++position;
- expressionStart = true;
- break;
-
- default:
- halfProcessed.add(current);
- ++position;
- expressionStart = false;
- break;
- }
- }
-
- if (halfProcessed.isEmpty() && canBeEmpty) {
- return new Sequence(peek().getPosition());
- }
-
- return ParserProcessors.processExpression(halfProcessed);
- }
-
-
- private Token peek() {
- if (position >= tokens.size()) {
- return new NullToken(tokens.get(tokens.size() - 1).getPosition() + 1);
- }
-
- return tokens.get(position);
- }
-
- private Function parseFunctionCall(IdentifierToken identifierToken) throws ParserException {
- consumeCharacter('(');
-
- try {
- if (peek().id() == ')') {
- ++position;
- return Functions.getFunction(identifierToken.getPosition(), identifierToken.value);
- }
-
- List args = new ArrayList<>();
-
- loop: while (true) {
- args.add(parseExpression(false));
-
- final Token current = peek();
- ++position;
-
- switch (current.id()) {
- case ',':
- continue;
-
- case ')':
- break loop;
-
- default:
- throw new ParserException(current.getPosition(), "Unmatched opening bracket");
- }
- }
-
- return Functions.getFunction(identifierToken.getPosition(), identifierToken.value, args.toArray(new RValue[args.size()]));
- } catch (NoSuchMethodException e) {
- throw new ParserException(identifierToken.getPosition(), "Function '" + identifierToken.value + "' not found", e);
- }
- }
-
- private RValue parseBracket() throws ParserException {
- consumeCharacter('(');
-
- final RValue ret = parseExpression(false);
-
- consumeCharacter(')');
-
- return ret;
- }
-
- private boolean hasKeyword(String keyword) {
- final Token next = peek();
-
- return next instanceof KeywordToken && ((KeywordToken) next).value.equals(keyword);
- }
-
- private void assertCharacter(char character) throws ParserException {
- final Token next = peek();
- if (next.id() != character) {
- throw new ParserException(next.getPosition(), "Expected '" + character + "'");
- }
- }
-
- private void assertKeyword(String keyword) throws ParserException {
- if (!hasKeyword(keyword)) {
- throw new ParserException(peek().getPosition(), "Expected '" + keyword + "'");
- }
- }
-
- private void consumeCharacter(char character) throws ParserException {
- assertCharacter(character);
- ++position;
- }
-
- private void consumeKeyword(String keyword) throws ParserException {
- assertKeyword(keyword);
- ++position;
- }
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/ParserProcessors.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/ParserProcessors.java
deleted file mode 100644
index b50997cc6..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/ParserProcessors.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.parser;
-
-import com.sk89q.worldedit.internal.expression.Identifiable;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.OperatorToken;
-import com.sk89q.worldedit.internal.expression.lexer.tokens.Token;
-import com.sk89q.worldedit.internal.expression.runtime.Conditional;
-import com.sk89q.worldedit.internal.expression.runtime.Operators;
-import com.sk89q.worldedit.internal.expression.runtime.RValue;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-
-/**
- * Helper classfor Parser. Contains processors for statements and operators.
- */
-public final class ParserProcessors {
-
- private static final Map unaryOpMap = new HashMap<>();
-
- private static final Map[] binaryOpMapsLA;
- private static final Map[] binaryOpMapsRA;
-
- static {
- unaryOpMap.put("-", "neg");
- unaryOpMap.put("!", "not");
- unaryOpMap.put("~", "inv");
- unaryOpMap.put("++", "inc");
- unaryOpMap.put("--", "dec");
- unaryOpMap.put("x++", "postinc");
- unaryOpMap.put("x--", "postdec");
- unaryOpMap.put("x!", "fac");
-
- final Object[][][] binaryOpsLA = {
- {
- { "^", "pow" },
- { "**", "pow" },
- },
- {
- { "*", "mul" },
- { "/", "div" },
- { "%", "mod" },
- },
- {
- { "+", "add" },
- { "-", "sub" },
- },
- {
- { "<<", "shl" },
- { ">>", "shr" },
- },
- {
- { "<", "lth" },
- { ">", "gth" },
- { "<=", "leq" },
- { ">=", "geq" },
- },
- {
- { "==", "equ" },
- { "!=", "neq" },
- { "~=", "near" },
- },
- {
- { "&&", "and" },
- },
- {
- { "||", "or" },
- },
- };
- final Object[][][] binaryOpsRA = {
- {
- { "=", "ass" },
- { "+=", "aadd" },
- { "-=", "asub" },
- { "*=", "amul" },
- { "/=", "adiv" },
- { "%=", "amod" },
- { "^=", "aexp" },
- },
- };
-
- @SuppressWarnings("unchecked")
- final Map[] lBinaryOpMapsLA = binaryOpMapsLA = new Map[binaryOpsLA.length];
- for (int i = 0; i < binaryOpsLA.length; ++i) {
- final Object[][] a = binaryOpsLA[i];
- switch (a.length) {
- case 0:
- lBinaryOpMapsLA[i] = Collections.emptyMap();
- break;
-
- case 1:
- final Object[] first = a[0];
- lBinaryOpMapsLA[i] = Collections.singletonMap((String) first[0], (String) first[1]);
- break;
-
- default:
- Map m = lBinaryOpMapsLA[i] = new HashMap<>();
- for (final Object[] element : a) {
- m.put((String) element[0], (String) element[1]);
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- final Map[] lBinaryOpMapsRA = binaryOpMapsRA = new Map[binaryOpsRA.length];
- for (int i = 0; i < binaryOpsRA.length; ++i) {
- final Object[][] a = binaryOpsRA[i];
- switch (a.length) {
- case 0:
- lBinaryOpMapsRA[i] = Collections.emptyMap();
- break;
-
- case 1:
- final Object[] first = a[0];
- lBinaryOpMapsRA[i] = Collections.singletonMap((String) first[0], (String) first[1]);
- break;
-
- default:
- Map m = lBinaryOpMapsRA[i] = new HashMap<>();
- for (final Object[] element : a) {
- m.put((String) element[0], (String) element[1]);
- }
- }
- }
- }
-
- private ParserProcessors() {
- }
-
- static RValue processExpression(LinkedList input) throws ParserException {
- return processBinaryOpsRA(input, binaryOpMapsRA.length - 1);
- }
-
- private static RValue processBinaryOpsLA(LinkedList input, int level) throws ParserException {
- if (level < 0) {
- return processUnaryOps(input);
- }
-
- LinkedList lhs = new LinkedList<>();
- LinkedList rhs = new LinkedList<>();
- String operator = null;
-
- for (Iterator it = input.descendingIterator(); it.hasNext();) {
- Identifiable identifiable = it.next();
- if (operator == null) {
- rhs.addFirst(identifiable);
-
- if (!(identifiable instanceof OperatorToken)) {
- continue;
- }
-
- operator = binaryOpMapsLA[level].get(((OperatorToken) identifiable).operator);
- if (operator == null) {
- continue;
- }
-
- rhs.removeFirst();
- } else {
- lhs.addFirst(identifiable);
- }
- }
-
- RValue rhsInvokable = processBinaryOpsLA(rhs, level - 1);
- if (operator == null) return rhsInvokable;
-
- RValue lhsInvokable = processBinaryOpsLA(lhs, level);
-
- try {
- return Operators.getOperator(input.get(0).getPosition(), operator, lhsInvokable, rhsInvokable);
- } catch (NoSuchMethodException e) {
- final Token operatorToken = (Token) input.get(lhs.size());
- throw new ParserException(operatorToken.getPosition(), "Couldn't find operator '" + operator + "'");
- }
- }
-
- private static RValue processBinaryOpsRA(LinkedList input, int level) throws ParserException {
- if (level < 0) {
- return processTernaryOps(input);
- }
-
- LinkedList lhs = new LinkedList<>();
- LinkedList rhs = new LinkedList<>();
- String operator = null;
-
- for (Identifiable identifiable : input) {
- if (operator == null) {
- lhs.addLast(identifiable);
-
- if (!(identifiable instanceof OperatorToken)) {
- continue;
- }
-
- operator = binaryOpMapsRA[level].get(((OperatorToken) identifiable).operator);
- if (operator == null) {
- continue;
- }
-
- lhs.removeLast();
- } else {
- rhs.addLast(identifiable);
- }
- }
-
- RValue lhsInvokable = processBinaryOpsRA(lhs, level - 1);
- if (operator == null) return lhsInvokable;
-
- RValue rhsInvokable = processBinaryOpsRA(rhs, level);
-
- try {
- return Operators.getOperator(input.get(0).getPosition(), operator, lhsInvokable, rhsInvokable);
- } catch (NoSuchMethodException e) {
- final Token operatorToken = (Token) input.get(lhs.size());
- throw new ParserException(operatorToken.getPosition(), "Couldn't find operator '" + operator + "'");
- }
- }
-
- private static RValue processTernaryOps(LinkedList input) throws ParserException {
- LinkedList lhs = new LinkedList<>();
- LinkedList mhs = new LinkedList<>();
- LinkedList rhs = new LinkedList<>();
-
- int partsFound = 0;
- int conditionalsFound = 0;
-
- for (Identifiable identifiable : input) {
- final char character = identifiable.id();
- switch (character) {
- case '?':
- ++conditionalsFound;
- break;
- case ':':
- --conditionalsFound;
- break;
- }
-
- if (conditionalsFound < 0) {
- throw new ParserException(identifiable.getPosition(), "Unexpected ':'");
- }
-
- switch (partsFound) {
- case 0:
- if (character == '?') {
- partsFound = 1;
- } else {
- lhs.addLast(identifiable);
- }
- break;
-
- case 1:
- if (conditionalsFound == 0 && character == ':') {
- partsFound = 2;
- } else {
- mhs.addLast(identifiable);
- }
- break;
-
- case 2:
- rhs.addLast(identifiable);
- }
- }
-
- if (partsFound < 2) {
- return processBinaryOpsLA(input, binaryOpMapsLA.length - 1);
- }
-
- RValue lhsInvokable = processBinaryOpsLA(lhs, binaryOpMapsLA.length - 1);
- RValue mhsInvokable = processTernaryOps(mhs);
- RValue rhsInvokable = processTernaryOps(rhs);
-
- return new Conditional(input.get(lhs.size()).getPosition(), lhsInvokable, mhsInvokable, rhsInvokable);
- }
-
- private static RValue processUnaryOps(LinkedList input) throws ParserException {
- // Preprocess postfix operators into unary operators
- final Identifiable center;
- LinkedList postfixes = new LinkedList<>();
- do {
- if (input.isEmpty()) {
- throw new ParserException(-1, "Expression missing.");
- }
-
- final Identifiable last = input.removeLast();
- if (last instanceof OperatorToken) {
- postfixes.addLast(new UnaryOperator(last.getPosition(), "x" + ((OperatorToken) last).operator));
- } else if (last instanceof UnaryOperator) {
- postfixes.addLast(new UnaryOperator(last.getPosition(), "x" + ((UnaryOperator) last).operator));
- } else {
- center = last;
- break;
- }
- } while (true);
-
- if (!(center instanceof RValue)) {
- throw new ParserException(center.getPosition(), "Expected expression, found " + center);
- }
-
- input.addAll(postfixes);
-
- RValue ret = (RValue) center;
- while (!input.isEmpty()) {
- final Identifiable last = input.removeLast();
- final int lastPosition = last.getPosition();
- if (last instanceof UnaryOperator) {
- final String operator = ((UnaryOperator) last).operator;
- if (operator.equals("+")) {
- continue;
- }
-
- String opName = unaryOpMap.get(operator);
- if (opName != null) {
- try {
- ret = Operators.getOperator(lastPosition, opName, ret);
- continue;
- } catch (NoSuchMethodException e) {
- throw new ParserException(lastPosition, "No such prefix operator: " + operator);
- }
- }
- }
-
- if (last instanceof Token) {
- throw new ParserException(lastPosition, "Extra token found in expression: " + last);
- } else if (last instanceof RValue) {
- throw new ParserException(lastPosition, "Extra expression found: " + last);
- } else {
- throw new ParserException(lastPosition, "Extra element found: " + last);
- }
- }
- return ret;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/PseudoToken.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/PseudoToken.java
deleted file mode 100644
index cee19e8b3..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/PseudoToken.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.parser;
-
-import com.sk89q.worldedit.internal.expression.Identifiable;
-
-/**
- * A pseudo-token, inserted by the parser instead of the lexer.
- */
-public abstract class PseudoToken implements Identifiable {
-
- private final int position;
-
- public PseudoToken(int position) {
- this.position = position;
- }
-
- @Override
- public abstract char id();
-
- @Override
- public int getPosition() {
- return position;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/UnaryOperator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/UnaryOperator.java
deleted file mode 100644
index d6a54b321..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/UnaryOperator.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.parser;
-
-import com.sk89q.worldedit.internal.expression.lexer.tokens.OperatorToken;
-
-/**
- * The parser uses this pseudo-token to mark operators as unary operators.
- */
-public class UnaryOperator extends PseudoToken {
-
- final String operator;
-
- public UnaryOperator(OperatorToken operatorToken) {
- this(operatorToken.getPosition(), operatorToken.operator);
- }
-
- public UnaryOperator(int position, String operator) {
- super(position);
- this.operator = operator;
- }
-
- @Override
- public char id() {
- return 'p';
- }
-
- @Override
- public String toString() {
- return "UnaryOperator(" + operator + ")";
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/UnboundVariable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/UnboundVariable.java
deleted file mode 100644
index a128bc866..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/parser/UnboundVariable.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.parser;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
-import com.sk89q.worldedit.internal.expression.runtime.LValue;
-import com.sk89q.worldedit.internal.expression.runtime.RValue;
-
-public class UnboundVariable extends PseudoToken implements LValue {
-
- public final String name;
-
- public UnboundVariable(int position, String name) {
- super(position);
- this.name = name;
- }
-
- @Override
- public char id() {
- return 'V';
- }
-
- @Override
- public String toString() {
- return "UnboundVariable(" + name + ")";
- }
-
- @Override
- public double getValue() throws EvaluationException {
- throw new EvaluationException(getPosition(), "Tried to evaluate unbound variable!");
- }
-
- @Override
- public LValue optimize() throws EvaluationException {
- throw new EvaluationException(getPosition(), "Tried to optimize unbound variable!");
- }
-
- @Override
- public double assign(double value) throws EvaluationException {
- throw new EvaluationException(getPosition(), "Tried to assign unbound variable!");
- }
-
- public RValue bind(Expression expression, boolean isLValue) throws ParserException {
- final RValue variable = expression.getVariable(name, isLValue);
- if (variable == null) {
- throw new ParserException(getPosition(), "Variable '" + name + "' not found");
- }
-
- return variable;
- }
-
- @Override
- public LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- final RValue variable = expression.getVariable(name, preferLValue);
- if (variable == null) {
- throw new ParserException(getPosition(), "Variable '" + name + "' not found");
- }
-
- return (LValue) variable;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Break.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Break.java
deleted file mode 100644
index 00bcf9936..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Break.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-/**
- * A break or continue statement.
- */
-public class Break extends Node {
-
- boolean doContinue;
-
- public Break(int position, boolean doContinue) {
- super(position);
-
- this.doContinue = doContinue;
- }
-
- @Override
- public double getValue() throws EvaluationException {
- throw new BreakException(doContinue);
- }
-
- @Override
- public char id() {
- return 'b';
- }
-
- @Override
- public String toString() {
- return doContinue ? "continue" : "break";
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Conditional.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Conditional.java
deleted file mode 100644
index 4579004af..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Conditional.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * An if/else statement or a ternary operator.
- */
-public class Conditional extends Node {
-
- private RValue condition;
- private RValue truePart;
- private RValue falsePart;
-
- public Conditional(int position, RValue condition, RValue truePart, RValue falsePart) {
- super(position);
-
- this.condition = condition;
- this.truePart = truePart;
- this.falsePart = falsePart;
- }
-
- @Override
- public double getValue() throws EvaluationException {
- if (condition.getValue() > 0.0) {
- return truePart.getValue();
- } else {
- return falsePart == null ? 0.0 : falsePart.getValue();
- }
- }
-
- @Override
- public char id() {
- return 'I';
- }
-
- @Override
- public String toString() {
- if (falsePart == null) {
- return "if (" + condition + ") { " + truePart + " }";
- } else if (truePart instanceof Sequence || falsePart instanceof Sequence) {
- return "if (" + condition + ") { " + truePart + " } else { " + falsePart + " }";
- } else {
- return "(" + condition + ") ? (" + truePart + ") : (" + falsePart + ")";
- }
- }
-
- @Override
- public RValue optimize() throws EvaluationException {
- final RValue newCondition = condition.optimize();
-
- if (newCondition instanceof Constant) {
- if (newCondition.getValue() > 0) {
- return truePart.optimize();
- } else {
- return falsePart == null ? new Constant(getPosition(), 0.0) : falsePart.optimize();
- }
- }
-
- return new Conditional(getPosition(), newCondition, truePart.optimize(), falsePart == null ? null : falsePart.optimize());
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- condition = condition.bindVariables(expression, false);
- truePart = truePart.bindVariables(expression, false);
- if (falsePart != null) {
- falsePart = falsePart.bindVariables(expression, false);
- }
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Constant.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Constant.java
deleted file mode 100644
index 5bdab9e1c..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Constant.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-/**
- * A constant.
- */
-public final class Constant extends Node {
-
- private final double value;
-
- public Constant(int position, double value) {
- super(position);
- this.value = value;
- }
-
- @Override
- public double getValue() {
- return value;
- }
-
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
- @Override
- public char id() {
- return 'c';
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/For.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/For.java
deleted file mode 100644
index 6334a7350..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/For.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A Java/C-style for loop.
- */
-public class For extends Node {
-
- RValue init;
- RValue condition;
- RValue increment;
- RValue body;
-
- public For(int position, RValue init, RValue condition, RValue increment, RValue body) {
- super(position);
-
- this.init = init;
- this.condition = condition;
- this.increment = increment;
- this.body = body;
- }
-
- @Override
- public double getValue() throws EvaluationException {
- int iterations = 0;
- double ret = 0.0;
-
- for (init.getValue(); condition.getValue() > 0; increment.getValue()) {
- if (iterations > 256) {
- throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations.");
- }
- if (Thread.interrupted()) {
- throw new EvaluationException(getPosition(), "Calculations exceeded time limit.");
- }
- ++iterations;
-
- try {
- ret = body.getValue();
- } catch (BreakException e) {
- if (e.doContinue) {
- //noinspection UnnecessaryContinue
- continue;
- } else {
- break;
- }
- }
- }
-
- return ret;
- }
-
- @Override
- public char id() {
- return 'F';
- }
-
- @Override
- public String toString() {
- return "for (" + init + "; " + condition + "; " + increment + ") { " + body + " }";
- }
-
- @Override
- public RValue optimize() throws EvaluationException {
- final RValue newCondition = condition.optimize();
-
- if (newCondition instanceof Constant && newCondition.getValue() <= 0) {
- // If the condition is always false, the loop can be flattened.
- // So we run the init part and then return 0.0.
- return new Sequence(getPosition(), init, new Constant(getPosition(), 0.0)).optimize();
- }
-
- //return new Sequence(getPosition(), init.optimize(), new While(getPosition(), condition, new Sequence(getPosition(), body, increment), false)).optimize();
- return new For(getPosition(), init.optimize(), newCondition, increment.optimize(), body.optimize());
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- init = init.bindVariables(expression, false);
- condition = condition.bindVariables(expression, false);
- increment = increment.bindVariables(expression, false);
- body = body.bindVariables(expression, false);
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java
deleted file mode 100644
index 09ec8df2e..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Wrapper for a Java method and its arguments (other Nodes).
- */
-public class Function extends Node {
-
- /**
- * Add this annotation on functions that don't always return the same value
- * for the same inputs and on functions with side-effects.
- */
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Dynamic { }
-
- public final Method method;
- public final RValue[] args;
-
- public Function(int position, Method method, RValue... args) {
- super(position);
- this.method = method;
- this.method.setAccessible(true);
- this.args = args;
- }
-
- @Override
- public final double getValue() throws EvaluationException {
- return invokeMethod(method, args);
- }
-
- protected static double invokeMethod(Method method, Object[] args) throws EvaluationException {
- try {
- return (Double) method.invoke(null, args);
- } catch (InvocationTargetException e) {
- if (e.getTargetException() instanceof EvaluationException) {
- throw (EvaluationException) e.getTargetException();
- }
- throw new EvaluationException(-1, "Exception caught while evaluating expression", e.getTargetException());
- } catch (IllegalAccessException e) {
- throw new EvaluationException(-1, "Internal error while evaluating expression", e);
- }
- }
-
- @Override
- public String toString() {
- final StringBuilder ret = new StringBuilder(method.getName()).append('(');
- boolean first = true;
- for (Object obj : args) {
- if (!first) {
- ret.append(", ");
- }
- first = false;
- ret.append(obj);
- }
- return ret.append(')').toString();
- }
-
- @Override
- public char id() {
- return 'f';
- }
-
- @Override
- public RValue optimize() throws EvaluationException {
- final RValue[] optimizedArgs = new RValue[args.length];
- boolean optimizable = !method.isAnnotationPresent(Dynamic.class);
- int position = getPosition();
- for (int i = 0; i < args.length; ++i) {
- final RValue optimized = optimizedArgs[i] = args[i].optimize();
-
- if (!(optimized instanceof Constant)) {
- optimizable = false;
- }
-
- if (optimized.getPosition() < position) {
- position = optimized.getPosition();
- }
- }
-
- if (optimizable) {
- return new Constant(position, invokeMethod(method, optimizedArgs));
- } else {
- return new Function(position, method, optimizedArgs);
- }
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- final Class>[] parameters = method.getParameterTypes();
- for (int i = 0; i < args.length; ++i) {
- final boolean argumentPrefersLValue = LValue.class.isAssignableFrom(parameters[i]);
- args[i] = args[i].bindVariables(expression, argumentPrefersLValue);
- }
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java
deleted file mode 100644
index d2aede416..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.runtime.Function.Dynamic;
-import com.sk89q.worldedit.math.Vector3;
-import com.sk89q.worldedit.math.noise.PerlinNoise;
-import com.sk89q.worldedit.math.noise.RidgedMultiFractalNoise;
-import com.sk89q.worldedit.math.noise.VoronoiNoise;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-
-/**
- * Contains all functions that can be used in expressions.
- */
-@SuppressWarnings("UnusedDeclaration")
-public final class Functions {
-
- private static class Overload {
- private final Method method;
- private final int mask;
- private final boolean isSetter;
-
- private Overload(Method method) throws IllegalArgumentException {
- this.method = method;
-
- boolean isSetter = false;
- int accum = 0;
- Class>[] parameters = method.getParameterTypes();
- for (Class> parameter : parameters) {
- if (isSetter) {
- throw new IllegalArgumentException("Method takes arguments that can't be cast to RValue.");
- }
-
- if (double.class.equals(parameter)) {
- isSetter = true;
- continue;
- }
-
- if (!RValue.class.isAssignableFrom(parameter)) {
- throw new IllegalArgumentException("Method takes arguments that can't be cast to RValue.");
- }
-
- accum <<= 2;
-
- if (LValue.class.isAssignableFrom(parameter)) {
- accum |= 3;
- } else {
- accum |= 1;
- }
- }
- mask = accum;
- this.isSetter = isSetter;
- }
-
- public boolean matches(boolean isSetter, RValue... args) {
- if (this.isSetter != isSetter) {
- return false;
- }
-
- if (this.method.getParameterTypes().length != args.length) { // TODO: optimize
- return false;
- }
-
- int accum = 0;
- for (RValue argument : args) {
- accum <<= 2;
-
- if (argument instanceof LValue) {
- accum |= 3;
- } else {
- accum |= 1;
- }
- }
-
- return (accum & mask) == mask;
- }
- }
-
- public static Function getFunction(int position, String name, RValue... args) throws NoSuchMethodException {
- final Method getter = getMethod(name, false, args);
- try {
- Method setter = getMethod(name, true, args);
- return new LValueFunction(position, getter, setter, args);
- } catch (NoSuchMethodException e) {
- return new Function(position, getter, args);
- }
- }
-
- private static Method getMethod(String name, boolean isSetter, RValue... args) throws NoSuchMethodException {
- final List overloads = functions.get(name);
- if (overloads != null) {
- for (Overload overload : overloads) {
- if (overload.matches(isSetter, args)) {
- return overload.method;
- }
- }
- }
-
- throw new NoSuchMethodException(); // TODO: return null (check for side-effects first)
- }
-
- private static final Map> functions = new HashMap<>();
- static {
- for (Method method : Functions.class.getMethods()) {
- try {
- addFunction(method);
- } catch (IllegalArgumentException ignored) { }
- }
- }
-
-
- public static void addFunction(Method method) throws IllegalArgumentException {
- final String methodName = method.getName();
-
- Overload overload = new Overload(method);
-
- List overloads = functions.computeIfAbsent(methodName, k -> new ArrayList<>());
-
- overloads.add(overload);
- }
-
-
- public static double sin(RValue x) throws EvaluationException {
- return Math.sin(x.getValue());
- }
-
- public static double cos(RValue x) throws EvaluationException {
- return Math.cos(x.getValue());
- }
-
- public static double tan(RValue x) throws EvaluationException {
- return Math.tan(x.getValue());
- }
-
-
- public static double asin(RValue x) throws EvaluationException {
- return Math.asin(x.getValue());
- }
-
- public static double acos(RValue x) throws EvaluationException {
- return Math.acos(x.getValue());
- }
-
- public static double atan(RValue x) throws EvaluationException {
- return Math.atan(x.getValue());
- }
-
- public static double atan2(RValue y, RValue x) throws EvaluationException {
- return Math.atan2(y.getValue(), x.getValue());
- }
-
-
- public static double sinh(RValue x) throws EvaluationException {
- return Math.sinh(x.getValue());
- }
-
- public static double cosh(RValue x) throws EvaluationException {
- return Math.cosh(x.getValue());
- }
-
- public static double tanh(RValue x) throws EvaluationException {
- return Math.tanh(x.getValue());
- }
-
-
- public static double sqrt(RValue x) throws EvaluationException {
- return Math.sqrt(x.getValue());
- }
-
- public static double cbrt(RValue x) throws EvaluationException {
- return Math.cbrt(x.getValue());
- }
-
-
- public static double abs(RValue x) throws EvaluationException {
- return Math.abs(x.getValue());
- }
-
- public static double min(RValue a, RValue b) throws EvaluationException {
- return Math.min(a.getValue(), b.getValue());
- }
-
- public static double min(RValue a, RValue b, RValue c) throws EvaluationException {
- return Math.min(a.getValue(), Math.min(b.getValue(), c.getValue()));
- }
-
- public static double max(RValue a, RValue b) throws EvaluationException {
- return Math.max(a.getValue(), b.getValue());
- }
-
- public static double max(RValue a, RValue b, RValue c) throws EvaluationException {
- return Math.max(a.getValue(), Math.max(b.getValue(), c.getValue()));
- }
-
-
- public static double ceil(RValue x) throws EvaluationException {
- return Math.ceil(x.getValue());
- }
-
- public static double floor(RValue x) throws EvaluationException {
- return Math.floor(x.getValue());
- }
-
- public static double rint(RValue x) throws EvaluationException {
- return Math.rint(x.getValue());
- }
-
- public static double round(RValue x) throws EvaluationException {
- return Math.round(x.getValue());
- }
-
-
- public static double exp(RValue x) throws EvaluationException {
- return Math.exp(x.getValue());
- }
-
- public static double ln(RValue x) throws EvaluationException {
- return Math.log(x.getValue());
- }
-
- public static double log(RValue x) throws EvaluationException {
- return Math.log(x.getValue());
- }
-
- public static double log10(RValue x) throws EvaluationException {
- return Math.log10(x.getValue());
- }
-
-
- public static double rotate(LValue x, LValue y, RValue angle) throws EvaluationException {
- final double f = angle.getValue();
-
- final double cosF = Math.cos(f);
- final double sinF = Math.sin(f);
-
- final double xOld = x.getValue();
- final double yOld = y.getValue();
-
- x.assign(xOld * cosF - yOld * sinF);
- y.assign(xOld * sinF + yOld * cosF);
-
- return 0.0;
- }
-
- public static double swap(LValue x, LValue y) throws EvaluationException {
- final double tmp = x.getValue();
-
- x.assign(y.getValue());
- y.assign(tmp);
-
- return 0.0;
- }
-
-
- private static final Map gmegabuf = new HashMap<>();
- private final Map megabuf = new HashMap<>();
-
- public Map getMegabuf() {
- return megabuf;
- }
-
- private static double[] getSubBuffer(Map megabuf, Integer key) {
- double[] ret = megabuf.get(key);
- if (ret == null) {
- megabuf.put(key, ret = new double[1024]);
- }
- return ret;
- }
-
- private static double getBufferItem(final Map megabuf, final int index) {
- return getSubBuffer(megabuf, index & ~1023)[index & 1023];
- }
-
- private static double setBufferItem(final Map megabuf, final int index, double value) {
- return getSubBuffer(megabuf, index & ~1023)[index & 1023] = value;
- }
-
- @Dynamic
- public static double gmegabuf(RValue index) throws EvaluationException {
- return getBufferItem(gmegabuf, (int) index.getValue());
- }
-
- @Dynamic
- public static double gmegabuf(RValue index, double value) throws EvaluationException {
- return setBufferItem(gmegabuf, (int) index.getValue(), value);
- }
-
- @Dynamic
- public static double megabuf(RValue index) throws EvaluationException {
- return getBufferItem(Expression.getInstance().getFunctions().megabuf, (int) index.getValue());
- }
-
- @Dynamic
- public static double megabuf(RValue index, double value) throws EvaluationException {
- return setBufferItem(Expression.getInstance().getFunctions().megabuf, (int) index.getValue(), value);
- }
-
- @Dynamic
- public static double closest(RValue x, RValue y, RValue z, RValue index, RValue count, RValue stride) throws EvaluationException {
- return findClosest(
- Expression.getInstance().getFunctions().megabuf,
- x.getValue(),
- y.getValue(),
- z.getValue(),
- (int) index.getValue(),
- (int) count.getValue(),
- (int) stride.getValue()
- );
- }
-
- @Dynamic
- public static double gclosest(RValue x, RValue y, RValue z, RValue index, RValue count, RValue stride) throws EvaluationException {
- return findClosest(
- gmegabuf,
- x.getValue(),
- y.getValue(),
- z.getValue(),
- (int) index.getValue(),
- (int) count.getValue(),
- (int) stride.getValue()
- );
- }
-
- private static double findClosest(Map megabuf, double x, double y, double z, int index, int count, int stride) {
- int closestIndex = -1;
- double minDistanceSquared = Double.MAX_VALUE;
-
- for (int i = 0; i < count; ++i) {
- double currentX = getBufferItem(megabuf, index+0) - x;
- double currentY = getBufferItem(megabuf, index+1) - y;
- double currentZ = getBufferItem(megabuf, index+2) - z;
-
- double currentDistanceSquared = currentX*currentX + currentY*currentY + currentZ*currentZ;
-
- if (currentDistanceSquared < minDistanceSquared) {
- minDistanceSquared = currentDistanceSquared;
- closestIndex = index;
- }
-
- index += stride;
- }
-
- return closestIndex;
- }
-
-
- private static final Random random = new Random();
-
- @Dynamic
- public static double random() {
- return random.nextDouble();
- }
-
- @Dynamic
- public static double randint(RValue max) throws EvaluationException {
- return random.nextInt((int) Math.floor(max.getValue()));
- }
-
- private static final ThreadLocal localPerlin = ThreadLocal.withInitial(PerlinNoise::new);
-
- public static double perlin(RValue seed, RValue x, RValue y, RValue z, RValue frequency, RValue octaves, RValue persistence) throws EvaluationException {
- PerlinNoise perlin = localPerlin.get();
- try {
- perlin.setSeed((int) seed.getValue());
- perlin.setFrequency(frequency.getValue());
- perlin.setOctaveCount((int) octaves.getValue());
- perlin.setPersistence(persistence.getValue());
- } catch (IllegalArgumentException e) {
- throw new EvaluationException(0, "Perlin noise error: " + e.getMessage());
- }
- return perlin.noise(Vector3.at(x.getValue(), y.getValue(), z.getValue()));
- }
-
- private static final ThreadLocal localVoronoi = ThreadLocal.withInitial(VoronoiNoise::new);
-
- public static double voronoi(RValue seed, RValue x, RValue y, RValue z, RValue frequency) throws EvaluationException {
- VoronoiNoise voronoi = localVoronoi.get();
- try {
- voronoi.setSeed((int) seed.getValue());
- voronoi.setFrequency(frequency.getValue());
- } catch (IllegalArgumentException e) {
- throw new EvaluationException(0, "Voronoi error: " + e.getMessage());
- }
- return voronoi.noise(Vector3.at(x.getValue(), y.getValue(), z.getValue()));
- }
-
- private static final ThreadLocal localRidgedMulti = ThreadLocal.withInitial(RidgedMultiFractalNoise::new);
-
- public static double ridgedmulti(RValue seed, RValue x, RValue y, RValue z, RValue frequency, RValue octaves) throws EvaluationException {
- RidgedMultiFractalNoise ridgedMulti = localRidgedMulti.get();
- try {
- ridgedMulti.setSeed((int) seed.getValue());
- ridgedMulti.setFrequency(frequency.getValue());
- ridgedMulti.setOctaveCount((int) octaves.getValue());
- } catch (IllegalArgumentException e) {
- throw new EvaluationException(0, "Ridged multi error: " + e.getMessage());
- }
- return ridgedMulti.noise(Vector3.at(x.getValue(), y.getValue(), z.getValue()));
- }
-
- private static double queryInternal(RValue type, RValue data, double typeId, double dataValue) throws EvaluationException {
- // Compare to input values and determine return value
- // -1 is a wildcard, always true
- final double ret = ((type.getValue() == -1 || typeId == type.getValue())
- && (data.getValue() == -1 || dataValue == data.getValue())) ? 1.0 : 0.0;
-
- if (type instanceof LValue) {
- ((LValue) type).assign(typeId);
- }
-
- if (data instanceof LValue) {
- ((LValue) data).assign(dataValue);
- }
-
- return ret;
- }
-
- @Dynamic
- public static double query(RValue x, RValue y, RValue z, RValue type, RValue data) throws EvaluationException {
- final double xp = x.getValue();
- final double yp = y.getValue();
- final double zp = z.getValue();
-
- final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
-
- // Read values from world
- final double typeId = environment.getBlockType(xp, yp, zp);
- final double dataValue = environment.getBlockData(xp, yp, zp);
-
- return queryInternal(type, data, typeId, dataValue);
- }
-
- @Dynamic
- public static double queryAbs(RValue x, RValue y, RValue z, RValue type, RValue data) throws EvaluationException {
- final double xp = x.getValue();
- final double yp = y.getValue();
- final double zp = z.getValue();
-
- final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
-
- // Read values from world
- final double typeId = environment.getBlockTypeAbs(xp, yp, zp);
- final double dataValue = environment.getBlockDataAbs(xp, yp, zp);
-
- return queryInternal(type, data, typeId, dataValue);
- }
-
- @Dynamic
- public static double queryRel(RValue x, RValue y, RValue z, RValue type, RValue data) throws EvaluationException {
- final double xp = x.getValue();
- final double yp = y.getValue();
- final double zp = z.getValue();
-
- final ExpressionEnvironment environment = Expression.getInstance().getEnvironment();
-
- // Read values from world
- final double typeId = environment.getBlockTypeRel(xp, yp, zp);
- final double dataValue = environment.getBlockDataRel(xp, yp, zp);
-
- return queryInternal(type, data, typeId, dataValue);
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/LValue.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/LValue.java
deleted file mode 100644
index 338cc74b2..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/LValue.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A value that can be used on the left side of an assignment.
- */
-public interface LValue extends RValue {
-
- double assign(double value) throws EvaluationException;
-
- @Override
- LValue optimize() throws EvaluationException;
-
- @Override
- LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException;
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/LValueFunction.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/LValueFunction.java
deleted file mode 100644
index de145514a..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/LValueFunction.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-import java.lang.reflect.Method;
-
-/**
- * Wrapper for a pair of Java methods and their arguments (other Nodes),
- * forming an LValue.
- */
-public class LValueFunction extends Function implements LValue {
-
- private final Object[] setterArgs;
- private final Method setter;
-
- LValueFunction(int position, Method getter, Method setter, RValue... args) {
- super(position, getter, args);
- assert (getter.isAnnotationPresent(Dynamic.class));
-
- setterArgs = new Object[args.length + 1];
- System.arraycopy(args, 0, setterArgs, 0, args.length);
- this.setter = setter;
- }
-
- @Override
- public char id() {
- return 'l';
- }
-
- @Override
- public double assign(double value) throws EvaluationException {
- setterArgs[setterArgs.length - 1] = value;
- return invokeMethod(setter, setterArgs);
- }
-
- @Override
- public LValue optimize() throws EvaluationException {
- final RValue optimized = super.optimize();
- if (optimized == this) {
- return this;
- }
-
- if (optimized instanceof Function) {
- return new LValueFunction(optimized.getPosition(), method, setter, ((Function) optimized).args);
- }
-
- return (LValue) optimized;
- }
-
- @Override
- public LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- super.bindVariables(expression, preferLValue);
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Node.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Node.java
deleted file mode 100644
index 1c1836c4d..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Node.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A node in the execution tree of an expression.
- */
-public abstract class Node implements RValue {
-
- private final int position;
-
- public Node(int position) {
- this.position = position;
- }
-
- @Override
- public abstract String toString();
-
- @Override
- public RValue optimize() throws EvaluationException {
- return this;
- }
-
- @Override
- public final int getPosition() {
- return position;
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Operators.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Operators.java
deleted file mode 100644
index cb4f9d78b..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Operators.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-/**
- * Contains all unary and binary operators.
- */
-@SuppressWarnings("UnusedDeclaration")
-public final class Operators {
-
- private Operators() {
- }
-
- public static Function getOperator(int position, String name, RValue lhs, RValue rhs) throws NoSuchMethodException {
- if (lhs instanceof LValue) {
- try {
- return new Function(position, Operators.class.getMethod(name, LValue.class, RValue.class), lhs, rhs);
- } catch (NoSuchMethodException ignored) { }
- }
- return new Function(position, Operators.class.getMethod(name, RValue.class, RValue.class), lhs, rhs);
- }
-
- public static Function getOperator(int position, String name, RValue argument) throws NoSuchMethodException {
- if (argument instanceof LValue) {
- try {
- return new Function(position, Operators.class.getMethod(name, LValue.class), argument);
- } catch (NoSuchMethodException ignored) { }
- }
- return new Function(position, Operators.class.getMethod(name, RValue.class), argument);
- }
-
-
- public static double add(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() + rhs.getValue();
- }
-
- public static double sub(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() - rhs.getValue();
- }
-
- public static double mul(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() * rhs.getValue();
- }
-
- public static double div(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() / rhs.getValue();
- }
-
- public static double mod(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() % rhs.getValue();
- }
-
- public static double pow(RValue lhs, RValue rhs) throws EvaluationException {
- return Math.pow(lhs.getValue(), rhs.getValue());
- }
-
-
- public static double neg(RValue x) throws EvaluationException {
- return -x.getValue();
- }
-
- public static double not(RValue x) throws EvaluationException {
- return x.getValue() > 0.0 ? 0.0 : 1.0;
- }
-
- public static double inv(RValue x) throws EvaluationException {
- return ~(long) x.getValue();
- }
-
-
- public static double lth(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() < rhs.getValue() ? 1.0 : 0.0;
- }
-
- public static double gth(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() > rhs.getValue() ? 1.0 : 0.0;
- }
-
- public static double leq(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() <= rhs.getValue() ? 1.0 : 0.0;
- }
-
- public static double geq(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() >= rhs.getValue() ? 1.0 : 0.0;
- }
-
-
- public static double equ(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() == rhs.getValue() ? 1.0 : 0.0;
- }
-
- public static double neq(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() != rhs.getValue() ? 1.0 : 0.0;
- }
-
- public static double near(RValue lhs, RValue rhs) throws EvaluationException {
- return almostEqual2sComplement(lhs.getValue(), rhs.getValue(), 450359963L) ? 1.0 : 0.0;
- //return Math.abs(lhs.invoke() - rhs.invoke()) < 1e-7 ? 1.0 : 0.0;
- }
-
-
- public static double or(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() > 0.0 || rhs.getValue() > 0.0 ? 1.0 : 0.0;
- }
-
- public static double and(RValue lhs, RValue rhs) throws EvaluationException {
- return lhs.getValue() > 0.0 && rhs.getValue() > 0.0 ? 1.0 : 0.0;
- }
-
-
- public static double shl(RValue lhs, RValue rhs) throws EvaluationException {
- return (long) lhs.getValue() << (long) rhs.getValue();
- }
-
- public static double shr(RValue lhs, RValue rhs) throws EvaluationException {
- return (long) lhs.getValue() >> (long) rhs.getValue();
- }
-
-
- public static double ass(LValue lhs, RValue rhs) throws EvaluationException {
- return lhs.assign(rhs.getValue());
- }
-
- public static double aadd(LValue lhs, RValue rhs) throws EvaluationException {
- return lhs.assign(lhs.getValue() + rhs.getValue());
- }
-
- public static double asub(LValue lhs, RValue rhs) throws EvaluationException {
- return lhs.assign(lhs.getValue() - rhs.getValue());
- }
-
- public static double amul(LValue lhs, RValue rhs) throws EvaluationException {
- return lhs.assign(lhs.getValue() * rhs.getValue());
- }
-
- public static double adiv(LValue lhs, RValue rhs) throws EvaluationException {
- return lhs.assign(lhs.getValue() / rhs.getValue());
- }
-
- public static double amod(LValue lhs, RValue rhs) throws EvaluationException {
- return lhs.assign(lhs.getValue() % rhs.getValue());
- }
-
- public static double aexp(LValue lhs, RValue rhs) throws EvaluationException {
- return lhs.assign(Math.pow(lhs.getValue(), rhs.getValue()));
- }
-
-
- public static double inc(LValue x) throws EvaluationException {
- return x.assign(x.getValue() + 1);
- }
-
- public static double dec(LValue x) throws EvaluationException {
- return x.assign(x.getValue() - 1);
- }
-
- public static double postinc(LValue x) throws EvaluationException {
- final double oldValue = x.getValue();
- x.assign(oldValue + 1);
- return oldValue;
- }
-
- public static double postdec(LValue x) throws EvaluationException {
- final double oldValue = x.getValue();
- x.assign(oldValue - 1);
- return oldValue;
- }
-
-
- private static final double[] factorials = new double[171];
- static {
- double accum = 1;
- factorials[0] = 1;
- for (int i = 1; i < factorials.length; ++i) {
- factorials[i] = accum *= i;
- }
- }
-
- public static double fac(RValue x) throws EvaluationException {
- final int n = (int) x.getValue();
-
- if (n < 0) {
- return 0;
- }
-
- if (n >= factorials.length) {
- return Double.POSITIVE_INFINITY;
- }
-
- return factorials[n];
- }
-
- // Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
- private static boolean almostEqual2sComplement(double a, double b, long maxUlps) {
- // Make sure maxUlps is non-negative and small enough that the
- // default NAN won't compare as equal to anything.
- //assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); // this is for floats, not doubles
-
- long aLong = Double.doubleToRawLongBits(a);
- // Make aLong lexicographically ordered as a twos-complement long
- if (aLong < 0) aLong = 0x8000000000000000L - aLong;
-
- long bLong = Double.doubleToRawLongBits(b);
- // Make bLong lexicographically ordered as a twos-complement long
- if (bLong < 0) bLong = 0x8000000000000000L - bLong;
-
- final long longDiff = Math.abs(aLong - bLong);
- return longDiff <= maxUlps;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/RValue.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/RValue.java
deleted file mode 100644
index 45a654bcd..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/RValue.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.Identifiable;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A value that can be used on the right side of an assignment.
- */
-public interface RValue extends Identifiable {
-
- double getValue() throws EvaluationException;
-
- RValue optimize() throws EvaluationException;
-
- RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException;
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Return.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Return.java
deleted file mode 100644
index 455a09a3b..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Return.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A return statement.
- */
-public class Return extends Node {
-
- RValue value;
-
- public Return(int position, RValue value) {
- super(position);
-
- this.value = value;
- }
-
- @Override
- public double getValue() throws EvaluationException {
- throw new ReturnException(value.getValue());
- }
-
- @Override
- public char id() {
- return 'r';
- }
-
- @Override
- public String toString() {
- return "return " + value;
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- value = value.bindVariables(expression, false);
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ReturnException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ReturnException.java
deleted file mode 100644
index 84b60021b..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ReturnException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-/**
- * Thrown when a return statement is encountered.
- * {@link com.sk89q.worldedit.internal.expression.Expression#evaluate}
- * catches this exception and returns the enclosed value.
- */
-public class ReturnException extends EvaluationException {
-
- final double value;
-
- public ReturnException(double value) {
- super(-1);
-
- this.value = value;
- }
-
- public double getValue() {
- return value;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Sequence.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Sequence.java
deleted file mode 100644
index f6458acce..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Sequence.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A sequence of operations, usually separated by semicolons in the
- * input stream.
- */
-public class Sequence extends Node {
-
- final RValue[] sequence;
-
- public Sequence(int position, RValue... sequence) {
- super(position);
-
- this.sequence = sequence;
- }
-
- @Override
- public char id() {
- return 's';
- }
-
- @Override
- public double getValue() throws EvaluationException {
- double ret = 0;
- for (RValue invokable : sequence) {
- ret = invokable.getValue();
- }
- return ret;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("seq(");
- boolean first = true;
- for (RValue invokable : sequence) {
- if (!first) {
- sb.append(", ");
- }
- sb.append(invokable);
- first = false;
- }
-
- return sb.append(')').toString();
- }
-
- @Override
- public RValue optimize() throws EvaluationException {
- final List newSequence = new ArrayList<>();
-
- RValue droppedLast = null;
- for (RValue invokable : sequence) {
- droppedLast = null;
- invokable = invokable.optimize();
- if (invokable instanceof Sequence) {
- Collections.addAll(newSequence, ((Sequence) invokable).sequence);
- } else if (invokable instanceof Constant) {
- droppedLast = invokable;
- } else {
- newSequence.add(invokable);
- }
- }
-
- if (droppedLast != null) {
- newSequence.add(droppedLast);
- }
-
- if (newSequence.size() == 1) {
- return newSequence.get(0);
- }
-
- return new Sequence(getPosition(), newSequence.toArray(new RValue[newSequence.size()]));
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- for (int i = 0; i < sequence.length; ++i) {
- sequence[i] = sequence[i].bindVariables(expression, false);
- }
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/SimpleFor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/SimpleFor.java
deleted file mode 100644
index 1576c3484..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/SimpleFor.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A simple-style for loop.
- */
-public class SimpleFor extends Node {
-
- LValue counter;
- RValue first;
- RValue last;
- RValue body;
-
- public SimpleFor(int position, LValue counter, RValue first, RValue last, RValue body) {
- super(position);
-
- this.counter = counter;
- this.first = first;
- this.last = last;
- this.body = body;
- }
-
- @Override
- public double getValue() throws EvaluationException {
- int iterations = 0;
- double ret = 0.0;
-
- double firstValue = first.getValue();
- double lastValue = last.getValue();
-
- for (double i = firstValue; i <= lastValue; ++i) {
- if (iterations > 256) {
- throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations.");
- }
- if (Thread.interrupted()) {
- throw new EvaluationException(getPosition(), "Calculations exceeded time limit.");
- }
- ++iterations;
-
- try {
- counter.assign(i);
- ret = body.getValue();
- } catch (BreakException e) {
- if (e.doContinue) {
- //noinspection UnnecessaryContinue
- continue;
- } else {
- break;
- }
- }
- }
-
- return ret;
- }
-
- @Override
- public char id() {
- return 'S';
- }
-
- @Override
- public String toString() {
- return "for (" + counter + " = " + first + ", " + last + ") { " + body + " }";
- }
-
- @Override
- public RValue optimize() throws EvaluationException {
- // TODO: unroll small loops into Sequences
-
- return new SimpleFor(getPosition(), counter.optimize(), first.optimize(), last.optimize(), body.optimize());
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- counter = counter.bindVariables(expression, true);
- first = first.bindVariables(expression, false);
- last = last.bindVariables(expression, false);
- body = body.bindVariables(expression, false);
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Switch.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Switch.java
deleted file mode 100644
index 9f2f35764..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Switch.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * A switch/case construct.
- */
-public class Switch extends Node implements RValue {
-
- private RValue parameter;
- private final Map valueMap;
- private final RValue[] caseStatements;
- private RValue defaultCase;
-
- public Switch(int position, RValue parameter, List values, List caseStatements, RValue defaultCase) {
- this(position, parameter, invertList(values), caseStatements, defaultCase);
-
- }
-
- private static Map invertList(List values) {
- Map valueMap = new HashMap<>();
- for (int i = 0; i < values.size(); ++i) {
- valueMap.put(values.get(i), i);
- }
- return valueMap;
- }
-
- private Switch(int position, RValue parameter, Map valueMap, List caseStatements, RValue defaultCase) {
- super(position);
-
- this.parameter = parameter;
- this.valueMap = valueMap;
- this.caseStatements = caseStatements.toArray(new RValue[caseStatements.size()]);
- this.defaultCase = defaultCase;
- }
-
- @Override
- public char id() {
- return 'W';
- }
-
- @Override
- public double getValue() throws EvaluationException {
- final double parameter = this.parameter.getValue();
-
- try {
- double ret = 0.0;
-
- final Integer index = valueMap.get(parameter);
- if (index != null) {
- for (int i = index; i < caseStatements.length; ++i) {
- ret = caseStatements[i].getValue();
- }
- }
-
- return defaultCase == null ? ret : defaultCase.getValue();
- } catch (BreakException e) {
- if (e.doContinue) throw e;
-
- return 0.0;
- }
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
-
- sb.append("switch (");
- sb.append(parameter);
- sb.append(") { ");
-
- for (int i = 0; i < caseStatements.length; ++i) {
- RValue caseStatement = caseStatements[i];
- sb.append("case ");
- for (Entry entry : valueMap.entrySet()) {
- if (entry.getValue() == i) {
- sb.append(entry.getKey());
- break;
- }
- }
- sb.append(": ");
- sb.append(caseStatement);
- sb.append(' ');
- }
-
- if (defaultCase != null) {
- sb.append("default: ");
- sb.append(defaultCase);
- sb.append(' ');
- }
-
- sb.append("}");
-
- return sb.toString();
- }
-
- @Override
- public RValue optimize() throws EvaluationException {
- final RValue optimizedParameter = parameter.optimize();
- final List newSequence = new ArrayList<>();
-
- if (optimizedParameter instanceof Constant) {
- final double parameter = optimizedParameter.getValue();
-
- final Integer index = valueMap.get(parameter);
- if (index == null) {
- return defaultCase == null ? new Constant(getPosition(), 0.0) : defaultCase.optimize();
- }
-
- boolean breakDetected = false;
- for (int i = index; i < caseStatements.length && !breakDetected; ++i) {
- final RValue invokable = caseStatements[i].optimize();
-
- if (invokable instanceof Sequence) {
- for (RValue subInvokable : ((Sequence) invokable).sequence) {
- if (subInvokable instanceof Break) {
- breakDetected = true;
- break;
- }
-
- newSequence.add(subInvokable);
- }
- } else {
- newSequence.add(invokable);
- }
- }
-
- if (defaultCase != null && !breakDetected) {
- final RValue invokable = defaultCase.optimize();
-
- if (invokable instanceof Sequence) {
- Collections.addAll(newSequence, ((Sequence) invokable).sequence);
- } else {
- newSequence.add(invokable);
- }
- }
-
- return new Switch(getPosition(), optimizedParameter, Collections.singletonMap(parameter, 0), newSequence, null);
- }
-
- final Map newValueMap = new HashMap<>();
-
- Map backMap = new HashMap<>();
- for (Entry entry : valueMap.entrySet()) {
- backMap.put(entry.getValue(), entry.getKey());
- }
-
- for (int i = 0; i < caseStatements.length; ++i) {
- final RValue invokable = caseStatements[i].optimize();
-
- final Double caseValue = backMap.get(i);
- if (caseValue != null) {
- newValueMap.put(caseValue, newSequence.size());
- }
-
- if (invokable instanceof Sequence) {
- Collections.addAll(newSequence, ((Sequence) invokable).sequence);
- } else {
- newSequence.add(invokable);
- }
- }
-
- return new Switch(getPosition(), optimizedParameter, newValueMap, newSequence, defaultCase.optimize());
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- parameter = parameter.bindVariables(expression, false);
-
- for (int i = 0; i < caseStatements.length; ++i) {
- caseStatements[i] = caseStatements[i].bindVariables(expression, false);
- }
-
- defaultCase = defaultCase.bindVariables(expression, false);
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Variable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Variable.java
deleted file mode 100644
index 01fe6764f..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Variable.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A variable.
- */
-public final class Variable extends Node implements LValue {
-
- public double value;
-
- public Variable(double value) {
- super(-1);
- this.value = value;
- }
-
- @Override
- public double getValue() {
- return value;
- }
-
- @Override
- public String toString() {
- return "var";
- }
-
- @Override
- public char id() {
- return 'v';
- }
-
- @Override
- public double assign(double value) {
- return this.value = value;
- }
-
- @Override
- public LValue optimize() throws EvaluationException {
- return this;
- }
-
- @Override
- public LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/While.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/While.java
deleted file mode 100644
index 5da3dae01..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/While.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.expression.runtime;
-
-import com.sk89q.worldedit.internal.expression.Expression;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-
-/**
- * A while loop.
- */
-public class While extends Node {
-
- RValue condition;
- RValue body;
- boolean footChecked;
-
- public While(int position, RValue condition, RValue body, boolean footChecked) {
- super(position);
-
- this.condition = condition;
- this.body = body;
- this.footChecked = footChecked;
- }
-
- @Override
- public double getValue() throws EvaluationException {
- int iterations = 0;
- double ret = 0.0;
-
- if (footChecked) {
- do {
- if (iterations > 256) {
- throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations.");
- }
- if (Thread.interrupted()) {
- throw new EvaluationException(getPosition(), "Calculations exceeded time limit.");
- }
- ++iterations;
-
- try {
- ret = body.getValue();
- } catch (BreakException e) {
- if (e.doContinue) {
- continue;
- } else {
- break;
- }
- }
- } while (condition.getValue() > 0.0);
- } else {
- while (condition.getValue() > 0.0) {
- if (iterations > 256) {
- throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations.");
- }
- if (Thread.interrupted()) {
- throw new EvaluationException(getPosition(), "Calculations exceeded time limit.");
- }
- ++iterations;
-
- try {
- ret = body.getValue();
- } catch (BreakException e) {
- if (e.doContinue) {
- //noinspection UnnecessaryContinue
- continue;
- } else {
- break;
- }
- }
- }
- }
-
- return ret;
- }
-
- @Override
- public char id() {
- return 'w';
- }
-
- @Override
- public String toString() {
- if (footChecked) {
- return "do { " + body + " } while (" + condition + ")";
- } else {
- return "while (" + condition + ") { " + body + " }";
- }
- }
-
- @Override
- public RValue optimize() throws EvaluationException {
- final RValue newCondition = condition.optimize();
-
- if (newCondition instanceof Constant && newCondition.getValue() <= 0) {
- // If the condition is always false, the loop can be flattened.
- if (footChecked) {
- // Foot-checked loops run at least once.
- return body.optimize();
- } else {
- // Loops that never run always return 0.0.
- return new Constant(getPosition(), 0.0);
- }
- }
-
- return new While(getPosition(), newCondition, body.optimize(), footChecked);
- }
-
- @Override
- public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
- condition = condition.bindVariables(expression, false);
- body = body.bindVariables(expression, false);
-
- return this;
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java
index 3e490d8ca..febafacc8 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java
@@ -35,7 +35,7 @@ public abstract class InputParser {
protected final WorldEdit worldEdit;
- public InputParser(WorldEdit worldEdit) {
+ protected InputParser(WorldEdit worldEdit) {
this.worldEdit = worldEdit;
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java
index 5aa25daaa..aace1ee89 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java
@@ -34,7 +34,7 @@ import java.util.stream.Stream;
*/
public abstract class SimpleInputParser extends InputParser {
- public SimpleInputParser(WorldEdit worldEdit) {
+ protected SimpleInputParser(WorldEdit worldEdit) {
super(worldEdit);
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/DocumentationPrinter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/DocumentationPrinter.java
deleted file mode 100644
index 7be6b4be3..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/DocumentationPrinter.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.internal.util;
-
-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.*;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-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 {
- File commandsDir = new File(args[0]);
-
- List> commandClasses = getCommandClasses(commandsDir);
-
- System.out.println("Writing permissions wiki table...");
- writePermissionsWikiTable(commandClasses);
- System.out.println("Writing Bukkit plugin.yml...");
- writeBukkitYAML();
-
- System.out.println("Done!");
- }
-
- private static List> getCommandClasses(File dir) {
- List> classes = new ArrayList<>();
-
- classes.add(BiomeCommands.class);
- classes.add(ChunkCommands.class);
- classes.add(ClipboardCommands.class);
- classes.add(GenerationCommands.class);
- classes.add(HistoryCommands.class);
- classes.add(NavigationCommands.class);
- classes.add(RegionCommands.class);
- classes.add(ScriptingCommands.class);
- classes.add(SelectionCommands.class);
- classes.add(SnapshotUtilCommands.class);
- classes.add(ToolUtilCommands.class);
- classes.add(ToolCommands.class);
- classes.add(UtilityCommands.class);
-
- /*for (File f : dir.listFiles()) {
- if (!f.getName().matches("^.*\\.java$")) {
- continue;
- }
-
- String className = "com.sk89q.worldedit.commands."
- + f.getName().substring(0, f.getName().lastIndexOf("."));
-
- Class> cls;
- try {
- cls = Class.forName(className, true,
- Thread.currentThread().getContextClassLoader());
- } catch (ClassNotFoundException e) {
- continue;
- }
-
- classes.add(cls);
- }*/
-
- return classes;
- }
-
- private static void writePermissionsWikiTable(List> commandClasses)
- throws IOException {
- try (FileOutputStream stream = new FileOutputStream("wiki_permissions.txt")) {
- PrintStream print = new PrintStream(stream);
- writePermissionsWikiTable(print, commandClasses, "/");
- }
- }
-
- private static void writePermissionsWikiTable(PrintStream stream,
- List> commandClasses, String prefix) {
-
- for (Class> cls : commandClasses) {
- for (Method method : cls.getMethods()) {
- if (!method.isAnnotationPresent(Command.class)) {
- continue;
- }
-
- Command cmd = method.getAnnotation(Command.class);
-
- stream.println("|-");
- stream.print("| " + prefix + cmd.aliases()[0]);
- stream.print(" || ");
-
- if (method.isAnnotationPresent(CommandPermissions.class)) {
- CommandPermissions perms =
- method.getAnnotation(CommandPermissions.class);
-
- String[] permKeys = perms.value();
- for (int i = 0; i < permKeys.length; ++i) {
- if (i > 0) {
- stream.print(", ");
- }
- stream.print(permKeys[i]);
- }
- }
-
- stream.print(" || ");
-
- boolean firstAlias = true;
- if (cmd.aliases().length != 0) {
- for (String alias : cmd.aliases()) {
- if (!firstAlias) stream.print(" ");
- stream.print(prefix + alias);
- firstAlias = false;
- }
- }
-
- stream.print(" || ");
-
- if (cmd.desc() != null && !cmd.desc().isEmpty()) {
- stream.print(cmd.desc());
- }
-
- stream.println();
-
- if (method.isAnnotationPresent(NestedCommand.class)) {
- NestedCommand nested =
- method.getAnnotation(NestedCommand.class);
-
- Class>[] nestedClasses = nested.value();
- writePermissionsWikiTable(stream,
- Arrays.asList(nestedClasses),
- prefix + cmd.aliases()[0] + " ");
- }
- }
- }
- }
-
- private static void writeBukkitYAML()
- throws IOException {
- try (FileOutputStream stream = new FileOutputStream("plugin.yml")) {
- PrintStream print = new PrintStream(stream);
- writeBukkitYAML(print);
- }
- }
-
- private static void writeBukkitYAML(PrintStream stream) {
- stream.println("name: WorldEdit");
- stream.println("main: com.sk89q.worldedit.bukkit.WorldEditPlugin");
- stream.println("version: ${project.version}");
- stream.println("softdepend: [Spout] #hack to fix trove errors");
-
- stream.println();
- stream.println();
- stream.println("# Permissions aren't here. Read http://wiki.sk89q.com/wiki/WEPIF/DinnerPerms");
- stream.println("# for how WorldEdit permissions actually work.");
- }
-
-}
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
deleted file mode 100644
index 46fa66e73..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BitMath.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.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() {
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java
index 507293cd4..84fcace71 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java
@@ -599,5 +599,4 @@ public class BlockVector2 {
public String toString() {
return "(" + x + ", " + z + ")";
}
-
}
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 adf2d3f1c..0e57cc00c 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
@@ -18,8 +18,6 @@
*/
package com.sk89q.worldedit.math;
-
-import static com.google.common.base.Preconditions.checkArgument;
import static com.sk89q.worldedit.math.BitMath.mask;
import static com.sk89q.worldedit.math.BitMath.unpackX;
import static com.sk89q.worldedit.math.BitMath.unpackY;
@@ -28,6 +26,8 @@ import static com.sk89q.worldedit.math.BitMath.unpackZ;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.transform.AffineTransform;
+
+import static com.google.common.base.Preconditions.checkArgument;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
@@ -122,6 +122,11 @@ public abstract class BlockVector3 {
return new MutableBlockVector3(x, y, z);
}
+ public long toLongPackedForm() {
+ checkLongPackable(this);
+ return (x & BITS_26) | ((z & BITS_26) << 26) | (((y & (long) BITS_12) << (26 + 26)));
+ }
+
public MutableBlockVector3 mutX(double x) {
return new MutableBlockVector3((int) x, getY(), getZ());
}
@@ -150,11 +155,6 @@ public abstract class BlockVector3 {
return BlockVector3.at(getX(), getY(), getZ());
}
- public long toLongPackedForm() {
- checkLongPackable(this);
- return (getX() & BITS_26) | ((getZ() & BITS_26) << 26) | (((getY() & (long) BITS_12) << (26 + 26)));
- }
-
/**
* Get the X coordinate.
*
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java
index 534a772c1..cc6bdc320 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java
@@ -346,5 +346,13 @@ public class AffineTransform implements Transform, Serializable {
return String.format("Affine[%g %g %g %g, %g %g %g %g, %g %g %g %g]}", m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23);
}
+ /**
+ * Returns if this affine transform is representing a horizontal flip.
+ */
+ public boolean isHorizontalFlip() {
+ // use the determinant of the x-z submatrix to check if this is a horizontal flip
+ return m00 * m22 - m02 * m20 < 0;
+ }
+
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/NumberToken.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/AbstractFlatRegion.java
similarity index 69%
rename from worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/NumberToken.java
rename to worldedit-core/src/main/java/com/sk89q/worldedit/regions/AbstractFlatRegion.java
index 44cc70665..a2641d242 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/lexer/tokens/NumberToken.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/AbstractFlatRegion.java
@@ -17,28 +17,24 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.internal.expression.lexer.tokens;
+package com.sk89q.worldedit.regions;
-/**
- * A number.
- */
-public class NumberToken extends Token {
+import com.sk89q.worldedit.world.World;
- public final double value;
+public abstract class AbstractFlatRegion extends AbstractRegion implements FlatRegion {
- public NumberToken(int position, double value) {
- super(position);
- this.value = value;
+ protected AbstractFlatRegion(World world) {
+ super(world);
}
@Override
- public char id() {
- return '0';
+ public int getMinimumY() {
+ return getMinimumPoint().getBlockY();
}
@Override
- public String toString() {
- return "NumberToken(" + value + ")";
+ public int getMaximumY() {
+ return getMaximumPoint().getBlockY();
}
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java
index 893ea83b7..eb62a3b7b 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java
@@ -21,8 +21,8 @@ package com.sk89q.worldedit.regions.shape;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extent.Extent;
-import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment;
import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.internal.expression.ExpressionEnvironment;
import com.sk89q.worldedit.math.MutableVector3;
import com.sk89q.worldedit.math.Vector3;
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java
deleted file mode 100644
index bf64e4e12..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.scripting.java;
-
-import java.io.IOException;
-import java.io.Reader;
-
-import javax.script.AbstractScriptEngine;
-import javax.script.Bindings;
-import javax.script.ScriptContext;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
-import javax.script.ScriptException;
-import javax.script.SimpleBindings;
-
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.ImporterTopLevel;
-import org.mozilla.javascript.JavaScriptException;
-import org.mozilla.javascript.RhinoException;
-import org.mozilla.javascript.Scriptable;
-import org.mozilla.javascript.ScriptableObject;
-import com.sk89q.worldedit.scripting.RhinoContextFactory;
-
-public class RhinoScriptEngine extends AbstractScriptEngine {
- private ScriptEngineFactory factory;
- private Context cx;
-
- public RhinoScriptEngine() {
- RhinoContextFactory factory = new RhinoContextFactory(3000);
- factory.enterContext();
- }
-
- @Override
- public Bindings createBindings() {
- return new SimpleBindings();
- }
-
- @Override
- public Object eval(String script, ScriptContext context)
- throws ScriptException {
-
- Scriptable scope = setupScope(cx, context);
-
- String filename = (filename = (String) get(ScriptEngine.FILENAME)) == null
- ? "" : filename;
-
- try {
- return cx.evaluateString(scope, script, filename, 1, null);
- } catch (RhinoException e) {
- String msg;
- int line = (line = e.lineNumber()) == 0 ? -1 : line;
-
- if (e instanceof JavaScriptException) {
- msg = String.valueOf(((JavaScriptException) e).getValue());
- } else {
- msg = e.getMessage();
- }
-
- ScriptException scriptException =
- new ScriptException(msg, e.sourceName(), line);
- scriptException.initCause(e);
-
- throw scriptException;
- } finally {
- Context.exit();
- }
- }
-
- @Override
- public Object eval(Reader reader, ScriptContext context)
- throws ScriptException {
-
- Scriptable scope = setupScope(cx, context);
-
- String filename = (filename = (String) get(ScriptEngine.FILENAME)) == null
- ? "" : filename;
-
- try {
- return cx.evaluateReader(scope, reader, filename, 1, null);
- } catch (RhinoException e) {
- String msg;
- int line = (line = e.lineNumber()) == 0 ? -1 : line;
-
- if (e instanceof JavaScriptException) {
- msg = String.valueOf(((JavaScriptException) e).getValue());
- } else {
- msg = e.getMessage();
- }
-
- ScriptException scriptException =
- new ScriptException(msg, e.sourceName(), line);
- scriptException.initCause(e);
-
- throw scriptException;
- } catch (IOException e) {
- throw new ScriptException(e);
- } finally {
- Context.exit();
- }
- }
-
- @Override
- public ScriptEngineFactory getFactory() {
- if (factory != null) {
- return factory;
- } else {
- return new RhinoScriptEngineFactory();
- }
- }
-
- private Scriptable setupScope(Context cx, ScriptContext context) {
- ScriptableObject scriptable = new ImporterTopLevel(cx);
- Scriptable scope = cx.initStandardObjects(scriptable);
- //ScriptableObject.putProperty(scope, "argv", Context.javaToJS(args, scope));
- return scope;
- }
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngineFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngineFactory.java
deleted file mode 100644
index ee312229c..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/java/RhinoScriptEngineFactory.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.scripting.java;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
-
-public class RhinoScriptEngineFactory implements ScriptEngineFactory {
- private static List names;
- private static List mimeTypes;
- private static List extensions;
-
- static {
- names = new ArrayList<>(5);
- names.add("ECMAScript");
- names.add("ecmascript");
- names.add("JavaScript");
- names.add("javascript");
- names.add("js");
- names = Collections.unmodifiableList(names);
-
- mimeTypes = new ArrayList<>(4);
- mimeTypes.add("application/ecmascript");
- mimeTypes.add("text/ecmascript");
- mimeTypes.add("application/javascript");
- mimeTypes.add("text/javascript");
- mimeTypes = Collections.unmodifiableList(mimeTypes);
-
- extensions = new ArrayList<>(2);
- extensions.add("emcascript");
- extensions.add("js");
- extensions = Collections.unmodifiableList(extensions);
- }
-
- @Override
- public String getEngineName() {
- return "Rhino JavaScript Engine (SK)";
- }
-
- @Override
- public String getEngineVersion() {
- return "unknown";
- }
-
- @Override
- public List getExtensions() {
- return extensions;
- }
-
- @Override
- public String getLanguageName() {
- return "EMCAScript";
- }
-
- @Override
- public String getLanguageVersion() {
- return "1.8";
- }
-
- @Override
- public String getMethodCallSyntax(String obj, String m, String... args) {
- StringBuilder s = new StringBuilder();
- s.append(obj);
- s.append(".");
- s.append(m);
- s.append("(");
-
- for (int i = 0; i < args.length; ++i) {
- s.append(args[i]);
- if (i < args.length - 1) {
- s.append(",");
- }
- }
-
- s.append(")");
-
- return s.toString();
- }
-
- @Override
- public List getMimeTypes() {
- return mimeTypes;
- }
-
- @Override
- public List getNames() {
- return names;
- }
-
- @Override
- public String getOutputStatement(String str) {
- return "print(" + str.replace("\\", "\\\\")
- .replace("\"", "\\\\\"")
- .replace(";", "\\\\;") + ")";
- }
-
- @Override
- public Object getParameter(String key) {
- switch (key) {
- case ScriptEngine.ENGINE:
- return getEngineName();
- case ScriptEngine.ENGINE_VERSION:
- return getEngineVersion();
- case ScriptEngine.NAME:
- return getEngineName();
- case ScriptEngine.LANGUAGE:
- return getLanguageName();
- case ScriptEngine.LANGUAGE_VERSION:
- return getLanguageVersion();
- case "THREADING":
- return "MULTITHREADED";
- default:
- throw new IllegalArgumentException("Invalid key");
- }
- }
-
- @Override
- public String getProgram(String... statements) {
- StringBuilder s = new StringBuilder();
- for (String stmt : statements) {
- s.append(stmt);
- s.append(";");
- }
- return s.toString();
- }
-
- @Override
- public ScriptEngine getScriptEngine() {
- return new RhinoScriptEngine();
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java
index f91c34480..14450054e 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java
@@ -39,6 +39,7 @@ import com.sk89q.worldedit.session.storage.JsonFileSessionStore;
import com.sk89q.worldedit.session.storage.SessionStore;
import com.sk89q.worldedit.session.storage.VoidStore;
import com.sk89q.worldedit.util.concurrency.EvenMoreExecutors;
+import com.sk89q.worldedit.extension.platform.Locatable;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.world.gamemode.GameModes;
import com.sk89q.worldedit.world.item.ItemType;
@@ -200,6 +201,11 @@ public class SessionManager {
&& (owner.hasPermission("worldedit.inventory.unrestricted")
|| (config.useInventoryCreativeOverride && (!(owner instanceof Player) || ((Player) owner).getGameMode() == GameModes.CREATIVE)))));
+ // Force non-locatable actors to use placeAtPos1
+ if (!(owner instanceof Locatable)) {
+ session.setPlaceAtPos1(true);
+ }
+
return session;
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java
index ad8e422bf..aa2d579fd 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java
@@ -88,7 +88,15 @@ public class JsonFileSessionStore implements SessionStore {
try (Closer closer = Closer.create()) {
FileReader fr = closer.register(new FileReader(file));
BufferedReader br = closer.register(new BufferedReader(fr));
- return gson.fromJson(br, LocalSession.class);
+ LocalSession session = gson.fromJson(br, LocalSession.class);
+ if (session == null) {
+ log.warn("Loaded a null session from {}, creating new session", file);
+ if (!file.delete()) {
+ log.warn("Failed to delete corrupted session {}", file);
+ }
+ session = new LocalSession();
+ }
+ return session;
} catch (JsonParseException e) {
throw new IOException(e);
} catch (FileNotFoundException e) {
@@ -98,6 +106,7 @@ public class JsonFileSessionStore implements SessionStore {
@Override
public void save(UUID id, LocalSession session) throws IOException {
+ checkNotNull(session);
File finalFile = getPath(id);
File tempFile = new File(finalFile.getParentFile(), finalFile.getName() + ".tmp");
@@ -115,6 +124,10 @@ public class JsonFileSessionStore implements SessionStore {
}
}
+ if (tempFile.length() == 0) {
+ throw new IllegalStateException("Gson wrote zero bytes");
+ }
+
if (!tempFile.renameTo(finalFile)) {
log.warn("Failed to rename temporary session file to " + finalFile.getPath());
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/EventBus.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/EventBus.java
index 61cfb57a5..42d608b7d 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/EventBus.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/eventbus/EventBus.java
@@ -19,8 +19,9 @@
package com.sk89q.worldedit.util.eventbus;
-import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.HashMultimap;
+
+import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
import org.slf4j.Logger;
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java
index e990c4750..d1152e85f 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java
@@ -19,23 +19,24 @@
package com.sk89q.worldedit.util.formatting.component;
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.Iterables;
import static com.sk89q.worldedit.internal.command.CommandUtil.getSubCommands;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
import com.sk89q.worldedit.util.formatting.text.format.TextDecoration;
import java.util.List;
-import javax.annotation.Nullable;
import org.enginehub.piston.Command;
import org.enginehub.piston.CommandParameters;
import org.enginehub.piston.NoInputCommandParameters;
import org.enginehub.piston.config.ColorConfig;
import org.enginehub.piston.inject.InjectedValueAccess;
import org.enginehub.piston.part.CommandPart;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import javax.annotation.Nullable;
import org.enginehub.piston.util.HelpGenerator;
/**
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java
index 2ead3102f..6020b3b1b 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/ActorCallbackPaste.java
@@ -19,8 +19,9 @@
package com.sk89q.worldedit.util.paste;
-import com.boydti.fawe.util.IncendoPaster;
import com.sk89q.worldedit.command.util.AsyncCommandBuilder;
+
+import com.boydti.fawe.util.IncendoPaster;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.util.task.Supervisor;
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.java
deleted file mode 100644
index 5e01dee84..000000000
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.sk89q.worldedit.util.paste;
-
-import com.sk89q.worldedit.util.net.HttpRequest;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.concurrent.Callable;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Pastebin implements Paster {
-
- private static final Pattern URL_PATTERN = Pattern.compile("https?://pastebin.com/([^/]+)$");
-
- private boolean mungingLinks = true;
-
- public boolean isMungingLinks() {
- return mungingLinks;
- }
-
- public void setMungingLinks(boolean mungingLinks) {
- this.mungingLinks = mungingLinks;
- }
-
- @Override
- public Callable paste(String content) {
- if (mungingLinks) {
- content = content.replaceAll("http://", "http_//");
- }
-
- return new PasteTask(content);
- }
-
- private final class PasteTask implements Callable {
- private final String content;
-
- private PasteTask(String content) {
- this.content = content;
- }
-
- @Override
- public URL call() throws IOException, InterruptedException {
- HttpRequest.Form form = HttpRequest.Form.create();
- form.add("api_option", "paste");
- form.add("api_dev_key", "4867eae74c6990dbdef07c543cf8f805");
- form.add("api_paste_code", content);
- form.add("api_paste_private", "0");
- form.add("api_paste_name", "");
- form.add("api_paste_expire_date", "1W");
- form.add("api_paste_format", "text");
- form.add("api_user_key", "");
-
- URL url = HttpRequest.url("http://pastebin.com/api/api_post.php");
- String result = HttpRequest.post(url)
- .bodyForm(form)
- .execute()
- .expectResponseCode(200)
- .returnContent()
- .asString("UTF-8").trim();
-
- Matcher m = URL_PATTERN.matcher(result);
-
- if (m.matches()) {
- return new URL("http://pastebin.com/raw.php?i=" + m.group(1));
- } else if (result.matches("^https?://.+")) {
- return new URL(result);
- } else {
- throw new IOException("Failed to save paste; instead, got: " + result);
- }
- }
- }
-
-}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java
index 6a240e91a..019e57971 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java
@@ -33,13 +33,13 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.block.BlockStateHolder;
-import com.sk89q.worldedit.world.block.BlockType;
-import com.sk89q.worldedit.world.block.BlockTypes;
-
import com.sk89q.worldedit.world.weather.WeatherType;
+import com.sk89q.worldedit.world.block.BlockType;
+
+import java.nio.file.Path;
+import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.weather.WeatherTypes;
import javax.annotation.Nullable;
-import java.nio.file.Path;
import java.util.PriorityQueue;
/**
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java
index 1f0fd8850..a1d6e28d4 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockStateHolder.java
@@ -20,16 +20,18 @@
package com.sk89q.worldedit.world.block;
import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.blocks.TileEntityBlock;
import com.sk89q.worldedit.extent.OutputExtent;
import com.sk89q.worldedit.function.pattern.FawePattern;
+
+import java.util.Locale;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.registry.state.PropertyKey;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import javax.annotation.Nullable;
-import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
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 60ab33cfe..5517a1b06 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
@@ -19,9 +19,8 @@
package com.sk89q.worldedit.world.block;
-import static com.google.common.base.Preconditions.checkArgument;
-
import com.sk89q.worldedit.WorldEdit;
+import com.google.common.collect.Iterables;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extent.Extent;
@@ -35,6 +34,7 @@ import com.sk89q.worldedit.registry.state.AbstractProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.registry.state.PropertyKey;
import com.sk89q.worldedit.world.item.ItemType;
+import com.sk89q.worldedit.util.concurrency.LazyReference;
import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import com.sk89q.worldedit.world.registry.LegacyMapper;
@@ -43,6 +43,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
@@ -102,6 +104,14 @@ public class BlockType implements FawePattern, Keyed {
}
}
+ 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/block/BlockTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java
index 480753779..afaff434a 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java
@@ -50,6 +50,7 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.Optional;
/**
* Stores a list of common Block String IDs.
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java
index 3dd9bf599..6989f7731 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java
@@ -23,9 +23,8 @@ import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
-import java.util.OptionalInt;
-
import javax.annotation.Nullable;
+import java.util.OptionalInt;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
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 32bf52e73..a032921e5 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
@@ -50,12 +50,15 @@ import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;
+import java.util.HashMap;
public final class LegacyMapper {
private static final Logger log = LoggerFactory.getLogger(LegacyMapper.class);
private static LegacyMapper INSTANCE = new LegacyMapper();
+ private Map blockEntries = new HashMap<>();
+
private final Int2ObjectArrayMap blockStateToLegacyId4Data = new Int2ObjectArrayMap<>();
private final Int2ObjectArrayMap extraId4DataToStateId = new Int2ObjectArrayMap<>();
private final int[] blockArr = new int[4096];
@@ -95,20 +98,31 @@ public final class LegacyMapper {
parserContext.setTryLegacy(false); // This is legacy. Don't match itself.
for (Map.Entry blockEntry : dataFile.blocks.entrySet()) {
+ String id = blockEntry.getKey();
+ Integer combinedId = getCombinedId(blockEntry.getKey());
+ final String value = blockEntry.getValue();
+ blockEntries.put(id, value);
+ BlockState blockState;
try {
- BlockState blockState = BlockState.get(null, blockEntry.getValue());
+ blockState = BlockState.get(null, blockEntry.getValue());
BlockType type = blockState.getBlockType();
if (type.hasProperty(PropertyKey.WATERLOGGED)) {
blockState = blockState.with(PropertyKey.WATERLOGGED, false);
}
- Integer combinedId = getCombinedId(blockEntry.getKey());
- blockArr[combinedId] = blockState.getInternalId();
-
- blockStateToLegacyId4Data.put(blockState.getInternalId(), (Integer) combinedId);
- blockStateToLegacyId4Data.putIfAbsent(blockState.getInternalBlockTypeId(), combinedId);
} catch (InputParseException e) {
- log.warn("Unknown block: " + blockEntry.getValue());
+ if (fixer != null) {
+ 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;
+ }
+ }
}
+ blockArr[combinedId] = blockState.getInternalId();
+ blockStateToLegacyId4Data.put(blockState.getInternalId(), (Integer) combinedId);
+ blockStateToLegacyId4Data.putIfAbsent(blockState.getInternalBlockTypeId(), combinedId);
}
for (int id = 0; id < 256; id++) {
int combinedId = id << 4;
@@ -123,8 +137,6 @@ public final class LegacyMapper {
for (Map.Entry itemEntry : dataFile.items.entrySet()) {
try {
itemMap.put(getCombinedId(itemEntry.getKey()), ItemTypes.get(itemEntry.getValue()));
- } catch (Exception e) {
- log.warn("Unknown item: " + itemEntry.getValue());
}
}
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullBlockCategoryRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullBlockCategoryRegistry.java
index 4484d67d2..3e0322ca6 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullBlockCategoryRegistry.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullBlockCategoryRegistry.java
@@ -19,7 +19,6 @@
package com.sk89q.worldedit.world.registry;
-import com.sk89q.worldedit.registry.Category;
import com.sk89q.worldedit.world.block.BlockType;
import java.util.Collections;
@@ -31,9 +30,4 @@ public class NullBlockCategoryRegistry implements BlockCategoryRegistry {
public Set getCategorisedByName(String category) {
return Collections.emptySet();
}
-
- @Override
- public Set getAll(final Category category) {
- return Collections.emptySet();
- }
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullItemCategoryRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullItemCategoryRegistry.java
index 8eb5d9ee6..070e2fcfa 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullItemCategoryRegistry.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/NullItemCategoryRegistry.java
@@ -19,7 +19,6 @@
package com.sk89q.worldedit.world.registry;
-import com.sk89q.worldedit.registry.Category;
import com.sk89q.worldedit.world.item.ItemType;
import java.util.Collections;
@@ -31,9 +30,4 @@ public class NullItemCategoryRegistry implements ItemCategoryRegistry {
public Set getCategorisedByName(String category) {
return Collections.emptySet();
}
-
- @Override
- public Set getAll(final Category category) {
- return Collections.emptySet();
- }
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java
index d13663427..77424ddc0 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRepository.java
@@ -22,6 +22,7 @@
package com.sk89q.worldedit.world.snapshot;
import com.sk89q.worldedit.world.storage.MissingWorldException;
+import javax.annotation.Nullable;
import java.io.File;
import java.io.FilenameFilter;
import java.time.ZoneOffset;
@@ -31,7 +32,6 @@ import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
-import javax.annotation.Nullable;
/**
* A repository contains zero or more snapshots.
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java
index 6e7b1766f..ec03ba6b1 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java
@@ -123,10 +123,6 @@ public class McRegionReader {
int x = position.getBlockX() & 31;
int z = position.getBlockZ() & 31;
- if (x < 0 || x >= 32 || z < 0 || z >= 32) {
- throw new DataException("MCRegion file does not contain " + x + "," + z);
- }
-
int offset = getOffset(x, z);
// The chunk hasn't been generated
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/extent/transform/BlockTransformExtentTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/extent/transform/BlockTransformExtentTest.java
index 0c2fb8843..749451370 100644
--- a/worldedit-core/src/test/java/com/sk89q/worldedit/extent/transform/BlockTransformExtentTest.java
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/extent/transform/BlockTransformExtentTest.java
@@ -25,6 +25,8 @@ import com.sk89q.worldedit.world.block.BlockType;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java
new file mode 100644
index 000000000..20eec91ae
--- /dev/null
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java
@@ -0,0 +1,100 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+import com.sk89q.worldedit.LocalConfiguration;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.extension.platform.Platform;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Common setup code for expression tests.
+ */
+class BaseExpressionTest {
+
+ static double readSlot(Expression expr, String name) {
+ return expr.getSlots().getSlotValue(name).orElseThrow(IllegalStateException::new);
+ }
+
+ private Platform mockPlat = mock(Platform.class);
+
+ @BeforeEach
+ void setup() {
+ when(mockPlat.getConfiguration()).thenReturn(new LocalConfiguration() {
+ @Override
+ public void load() {
+ }
+ });
+ WorldEdit.getInstance().getPlatformManager().register(mockPlat);
+ }
+
+ double simpleEval(String expressionString) throws ExpressionException {
+ final Expression expression = compile(expressionString);
+
+ expression.setEnvironment(new ExpressionEnvironment() {
+ @Override
+ public int getBlockType(double x, double y, double z) {
+ return (int) x;
+ }
+
+ @Override
+ public int getBlockData(double x, double y, double z) {
+ return (int) y;
+ }
+
+ @Override
+ public int getBlockTypeAbs(double x, double y, double z) {
+ return (int) x*10;
+ }
+
+ @Override
+ public int getBlockDataAbs(double x, double y, double z) {
+ return (int) y*10;
+ }
+
+ @Override
+ public int getBlockTypeRel(double x, double y, double z) {
+ return (int) x*100;
+ }
+
+ @Override
+ public int getBlockDataRel(double x, double y, double z) {
+ return (int) y*100;
+ }
+ });
+
+ return expression.evaluate();
+ }
+
+ @AfterEach
+ void tearDown() {
+ WorldEdit.getInstance().getPlatformManager().unregister(mockPlat);
+ }
+
+ Expression compile(String expressionString, String... variableNames) throws ExpressionException {
+ final Expression expression = Expression.compile(expressionString, variableNames);
+ expression.optimize();
+ return expression;
+ }
+}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java
index de9cce3cc..73e747e6d 100644
--- a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java
@@ -19,17 +19,13 @@
package com.sk89q.worldedit.internal.expression;
-import com.sk89q.worldedit.LocalConfiguration;
-import com.sk89q.worldedit.WorldEdit;
-import com.sk89q.worldedit.extension.platform.Platform;
-import com.sk89q.worldedit.internal.expression.lexer.LexerException;
-import com.sk89q.worldedit.internal.expression.parser.ParserException;
-import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
-import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
+
+import java.time.Duration;
import com.sk89q.worldedit.internal.expression.runtime.ExpressionTimeoutException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
import static java.lang.Math.atan2;
import static java.lang.Math.sin;
@@ -40,20 +36,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-public class ExpressionTest {
+class ExpressionTest extends BaseExpressionTest {
private Platform mockPlat = mock(Platform.class);
- @BeforeEach
- public void setup() {
- when(mockPlat.getConfiguration()).thenReturn(new LocalConfiguration() {
- @Override
- public void load() {
- }
- });
- WorldEdit.getInstance().getPlatformManager().register(mockPlat);
- }
-
@AfterEach
public void tearDown() {
WorldEdit.getInstance().getPlatformManager().unregister(mockPlat);
@@ -77,73 +63,78 @@ public class ExpressionTest {
assertEquals(8, compile("foo+bar", "foo", "bar").evaluate(5D, 3D), 0);
}
+ @Test
+ void testTightTokenization() {
+ assertEquals(4, simpleEval("3+1"), 0);
+ }
+
@Test
public void testErrors() {
- assertAll(
- // test lexer errors
- () -> {
- LexerException e = assertThrows(LexerException.class,
- () -> compile("#"));
- assertEquals(0, e.getPosition(), "Error position");
- },
- // test parser errors
- () -> {
- ParserException e = assertThrows(ParserException.class,
- () -> compile("x"));
- assertEquals(0, e.getPosition(), "Error position");
- },
- () -> {
- ParserException e = assertThrows(ParserException.class,
- () -> compile("x()"));
- assertEquals(0, e.getPosition(), "Error position");
- },
- () -> assertThrows(ParserException.class,
- () -> compile("(")),
- () -> assertThrows(ParserException.class,
- () -> compile("x(")),
- // test overloader errors
- () -> {
- ParserException e = assertThrows(ParserException.class,
- () -> compile("atan2(1)"));
- assertEquals(0, e.getPosition(), "Error position");
- },
- () -> {
- ParserException e = assertThrows(ParserException.class,
- () -> compile("atan2(1, 2, 3)"));
- assertEquals(0, e.getPosition(), "Error position");
- },
- () -> {
- ParserException e = assertThrows(ParserException.class,
- () -> compile("rotate(1, 2, 3)"));
- assertEquals(0, e.getPosition(), "Error position");
- }
- );
+ // test lexer errors
+ {
+ ExpressionException e = assertThrows(ExpressionException.class,
+ () -> compile("#"));
+ assertEquals(0, e.getPosition(), "Error position");
+ }
+ // test parser errors
+ {
+ ExpressionException e = assertThrows(ExpressionException.class,
+ () -> compile("x"));
+ assertEquals(0, e.getPosition(), "Error position");
+ }
+ {
+ ExpressionException e = assertThrows(ExpressionException.class,
+ () -> compile("x()"));
+ assertEquals(0, e.getPosition(), "Error position");
+ }
+ assertThrows(ExpressionException.class,
+ () -> compile("("));
+ assertThrows(ExpressionException.class,
+ () -> compile("x("));
+ // test overloader errors
+ {
+ ExpressionException e = assertThrows(ExpressionException.class,
+ () -> compile("atan2(1)"));
+ assertEquals(0, e.getPosition(), "Error position");
+ }
+ {
+ ExpressionException e = assertThrows(ExpressionException.class,
+ () -> compile("atan2(1, 2, 3)"));
+ assertEquals(0, e.getPosition(), "Error position");
+ }
+ {
+ ExpressionException e = assertThrows(ExpressionException.class,
+ () -> compile("rotate(1, 2, 3)"));
+ e.printStackTrace();
+ assertEquals(7, e.getPosition(), "Error position");
+ }
+
}
@Test
public void testAssign() throws ExpressionException {
Expression foo = compile("{a=x} b=y; c=z", "x", "y", "z", "a", "b", "c");
foo.evaluate(2D, 3D, 5D);
- assertEquals(2, foo.getVariable("a", false).getValue(), 0);
- assertEquals(3, foo.getVariable("b", false).getValue(), 0);
- assertEquals(5, foo.getVariable("c", false).getValue(), 0);
+ assertEquals(2, foo.getSlots().getSlotValue("a").orElse(-1), 0);
+ assertEquals(3, foo.getSlots().getSlotValue("b").orElse(-1), 0);
+ assertEquals(5, foo.getSlots().getSlotValue("c").orElse(-1), 0);
}
@Test
public void testIf() throws ExpressionException {
- assertEquals(40, simpleEval("if (1) x=4; else y=5; x*10+y;"), 0);
- assertEquals(5, simpleEval("if (0) x=4; else y=5; x*10+y;"), 0);
+ assertEquals(40, simpleEval("y=0; if (1) x=4; else y=5; x*10+y;"), 0);
+ assertEquals(5, simpleEval("x=0; if (0) x=4; else y=5; x*10+y;"), 0);
// test 'dangling else'
final Expression expression1 = compile("if (1) if (0) x=4; else y=5;", "x", "y");
expression1.evaluate(1D, 2D);
- assertEquals(1, expression1.getVariable("x", false).getValue(), 0);
- assertEquals(5, expression1.getVariable("y", false).getValue(), 0);
+ assertEquals(1, expression1.getSlots().getSlotValue("x").orElse(-1), 0);
+ assertEquals(5, expression1.getSlots().getSlotValue("y").orElse(-1), 0);
// test if the if construct is correctly recognized as a statement
final Expression expression2 = compile("if (0) if (1) x=5; y=4;", "x", "y");
expression2.evaluate(1D, 2D);
- assertEquals(4, expression2.getVariable("y", false).getValue(), 0);
+ assertEquals(4, expression2.getSlots().getSlotValue("y").orElse(-1), 0);
}
@Test
@@ -182,53 +173,12 @@ public class ExpressionTest {
@Test
public void testTimeout() {
- ExpressionTimeoutException e = assertThrows(ExpressionTimeoutException.class,
- () -> simpleEval("for(i=0;i<256;i++){for(j=0;j<256;j++){for(k=0;k<256;k++){for(l=0;l<256;l++){ln(pi)}}}}"),
- "Loop was not stopped.");
+ ExpressionTimeoutException e = assertTimeoutPreemptively(Duration.ofSeconds(10), () ->
+ assertThrows(ExpressionTimeoutException.class,
+ () -> simpleEval("for(i=0;i<256;i++){for(j=0;j<256;j++){for(k=0;k<256;k++){for(l=0;l<256;l++){ln(pi)}}}}"),
+ "Loop was not stopped.")
+ );
assertTrue(e.getMessage().contains("Calculations exceeded time limit"));
}
- private double simpleEval(String expressionString) throws ExpressionException {
- final Expression expression = compile(expressionString);
-
- expression.setEnvironment(new ExpressionEnvironment() {
- @Override
- public int getBlockType(double x, double y, double z) {
- return (int) x;
- }
-
- @Override
- public int getBlockData(double x, double y, double z) {
- return (int) y;
- }
-
- @Override
- public int getBlockTypeAbs(double x, double y, double z) {
- return (int) x*10;
- }
-
- @Override
- public int getBlockDataAbs(double x, double y, double z) {
- return (int) y*10;
- }
-
- @Override
- public int getBlockTypeRel(double x, double y, double z) {
- return (int) x*100;
- }
-
- @Override
- public int getBlockDataRel(double x, double y, double z) {
- return (int) y*100;
- }
- });
-
- return expression.evaluate();
- }
-
- private Expression compile(String expressionString, String... variableNames) throws ExpressionException, EvaluationException {
- final Expression expression = Expression.compile(expressionString, variableNames);
- expression.optimize();
- return expression;
- }
}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/RealExpressionTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/RealExpressionTest.java
new file mode 100644
index 000000000..625e023a1
--- /dev/null
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/RealExpressionTest.java
@@ -0,0 +1,171 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.internal.expression;
+
+import com.sk89q.worldedit.math.Vector3;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test class for various real-world expressions.
+ */
+class RealExpressionTest extends BaseExpressionTest {
+
+ private static final class TestCase {
+
+ final Vector3 loc;
+ final double result;
+ final Consumer postChecks;
+
+ private TestCase(Vector3 loc, double result, Consumer postChecks) {
+ this.loc = loc;
+ this.result = result;
+ this.postChecks = postChecks;
+ }
+
+ TestCase withData(int expectedData) {
+ return new TestCase(loc, result, expr -> {
+ postChecks.accept(expr);
+ double data = readSlot(expr, "data");
+ assertEquals(expectedData, (int) data,
+ "Test case " + this + " failed (data)");
+ });
+ }
+
+ @Override
+ public String toString() {
+ return loc + " -> " + result;
+ }
+ }
+
+ private static TestCase testCase(Vector3 loc, double result) {
+ return testCase(loc, result, e -> {
+ });
+ }
+
+ private static TestCase testCase(Vector3 loc, double result, Consumer postChecks) {
+ return new TestCase(loc, result, postChecks);
+ }
+
+ private void checkExpression(String expr, TestCase... cases) {
+ Expression compiled = compile(expr, "x", "y", "z");
+ for (TestCase aCase : cases) {
+ Vector3 loc = aCase.loc;
+ assertEquals(aCase.result, compiled.evaluate(loc.getX(), loc.getY(), loc.getZ()), 0,
+ "Test case " + aCase + " failed (result)");
+ aCase.postChecks.accept(compiled);
+ }
+ }
+
+ @Test
+ void torus() {
+ checkExpression("(0.75-sqrt(x^2+y^2))^2+z^2 < 0.25^2",
+ testCase(Vector3.at(0, 0, 0), 0),
+ testCase(Vector3.at(0.5, 0.5, 0.5), 0),
+ testCase(Vector3.at(1, 0, 0), 0),
+ testCase(Vector3.at(0.5, 0.5, 0), 1),
+ testCase(Vector3.at(0.75, 0.5, 0), 1),
+ testCase(Vector3.at(0.75, 0, 0), 1));
+ }
+
+ @Test
+ void gnarledOakTree() {
+ checkExpression("(0.5+sin(atan2(x,z)*8)*0.2)*(sqrt(x*x+z*z)/0.5)^(-2)-1.2 < y",
+ testCase(Vector3.at(-1, -1, -1), 1),
+ testCase(Vector3.at(-1, 0, 1), 1),
+ testCase(Vector3.at(1, 1, 1), 1),
+ testCase(Vector3.at(0, 0, -1), 1),
+ testCase(Vector3.at(0, 0, 0), 0),
+ testCase(Vector3.at(0, 1, 0), 0),
+ testCase(Vector3.at(0, 0, 0.32274), 0),
+ testCase(Vector3.at(0, 0, 0.32275), 1));
+ }
+
+ @Test
+ void rainbowTorus() {
+ checkExpression("data=(32+15/2/pi*atan2(x,y))%16; (0.75-sqrt(x^2+y^2))^2+z^2 < 0.25^2",
+ testCase(Vector3.at(0, 0, 0), 0),
+ testCase(Vector3.at(0.5, 0.5, 0.5), 0),
+ testCase(Vector3.at(1, 0, 0), 0),
+ testCase(Vector3.at(0.5, 0.5, 0), 1).withData(1),
+ testCase(Vector3.at(0.75, 0.5, 0), 1).withData(2),
+ testCase(Vector3.at(0.75, 0, 0), 1).withData(3));
+ }
+
+ @Test
+ void rainbowEgg() {
+ TestCase[] testCases = new TestCase[15];
+ for (int i = 0; i < testCases.length; i++) {
+ testCases[i] = testCase(Vector3.at(0, i / 16.0 - 0.5, 0), 1)
+ .withData((i + 9) % 16);
+ }
+ testCases = Stream.concat(Stream.of(testCases), Stream.of(
+ testCase(Vector3.at(0, 1, 0), 0)
+ )).toArray(TestCase[]::new);
+ checkExpression("data=(32+y*16+1)%16; y^2/9+x^2/6*(1/(1-0.4*y))+z^2/6*(1/(1-0.4*y))<0.08",
+ testCases);
+ }
+
+ @Test
+ void heart() {
+ checkExpression("(z/2)^2+x^2+(5*y/4-sqrt(abs(x)))^2<0.6",
+ testCase(Vector3.at(0, 0, -1), 1),
+ testCase(Vector3.at(0, 1, -1), 0),
+ testCase(Vector3.at(-0.5, 1, 0), 1));
+ }
+
+ @Test
+ void sineWave() {
+ checkExpression("sin(x*5)/2-0.03",
+ testCase(Vector3.at(0, 0, 0), 1),
+ testCase(Vector3.at(0, 1, 0), 1),
+ testCase(Vector3.at(0, 1, 1), 1),
+ testCase(Vector3.at(1, 1, 1), 1),
+ testCase(Vector3.at(0, 0, 1), 0),
+ testCase(Vector3.at(1, 0, 1), 0));
+ }
+}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/LocationTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/LocationTest.java
index 9330bf513..a68069f85 100644
--- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/LocationTest.java
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/LocationTest.java
@@ -20,11 +20,11 @@
package com.sk89q.worldedit.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.mock;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.world.World;
import org.junit.jupiter.api.Test;
+import static org.mockito.Mockito.mock;
/**
* Tests {@link Location}.
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java
new file mode 100644
index 000000000..64b18c84e
--- /dev/null
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java
@@ -0,0 +1,588 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.util.collection;
+
+import com.google.common.collect.ImmutableMap;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.extension.platform.Capability;
+import com.sk89q.worldedit.extension.platform.Platform;
+import com.sk89q.worldedit.extension.platform.PlatformManager;
+import com.sk89q.worldedit.extension.platform.Preference;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.registry.Registry;
+import com.sk89q.worldedit.util.VariedVectorsProvider;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import com.sk89q.worldedit.world.registry.BundledRegistries;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+@DisplayName("An ordered block map")
+class BlockMapTest {
+
+ private static Platform mockedPlatform = mock(Platform.class);
+
+ @BeforeAll
+ static void setupFakePlatform() {
+ when(mockedPlatform.getRegistries()).thenReturn(new BundledRegistries() {
+ });
+ when(mockedPlatform.getCapabilities()).thenReturn(ImmutableMap.of(
+ Capability.WORLD_EDITING, Preference.PREFERRED,
+ Capability.GAME_HOOKS, Preference.PREFERRED
+ ));
+ PlatformManager platformManager = WorldEdit.getInstance().getPlatformManager();
+ platformManager.register(mockedPlatform);
+
+ registerBlock("minecraft:air");
+ registerBlock("minecraft:oak_wood");
+ }
+
+ @AfterAll
+ static void tearDownFakePlatform() throws Exception {
+ WorldEdit.getInstance().getPlatformManager().unregister(mockedPlatform);
+ Field map = Registry.class.getDeclaredField("map");
+ map.setAccessible(true);
+ ((Map, ?>) map.get(BlockType.REGISTRY)).clear();
+ }
+
+ private static void registerBlock(String id) {
+ BlockType.REGISTRY.register(id, new BlockType(id));
+ }
+
+ @Mock
+ private Function super BlockVector3, ? extends BaseBlock> function;
+ @Mock
+ private BiFunction super BlockVector3, ? super BaseBlock, ? extends BaseBlock> biFunction;
+ @Mock
+ private BiFunction super BaseBlock, ? super BaseBlock, ? extends BaseBlock> mergeFunction;
+ @Mock
+ private BiConsumer super BlockVector3, ? super BaseBlock> biConsumer;
+
+ private final BaseBlock air = checkNotNull(BlockTypes.AIR).getDefaultState().toBaseBlock();
+ private final BaseBlock oakWood = checkNotNull(BlockTypes.OAK_WOOD).getDefaultState().toBaseBlock();
+
+ private BlockMap map = BlockMap.create();
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @AfterEach
+ void tearDown() {
+ map.clear();
+ }
+
+ @Test
+ @DisplayName("throws ClassCastException if invalid argument to get")
+ void throwsFromGetOnInvalidArgument() {
+ assertThrows(ClassCastException.class, () -> map.get(""));
+ }
+
+ @Nested
+ @DisplayName("when created")
+ class WhenCreated {
+
+ @Test
+ @DisplayName("is empty")
+ void isEmpty() {
+ assertEquals(0, map.size());
+ }
+
+ @Test
+ @DisplayName("is equal to another empty map")
+ void isEqualToEmptyMap() {
+ assertEquals(ImmutableMap.of(), map);
+ }
+
+ @Test
+ @DisplayName("has the same hashCode as another empty map")
+ void isHashCodeEqualToEmptyMap() {
+ assertEquals(ImmutableMap.of().hashCode(), map.hashCode());
+ }
+
+ @Test
+ @DisplayName("returns `null` from get")
+ void returnsNullFromGet() {
+ assertNull(map.get(BlockVector3.ZERO));
+ }
+
+ @Test
+ @DisplayName("contains no keys")
+ void containsNoKeys() {
+ assertEquals(0, map.keySet().size());
+ assertFalse(map.containsKey(BlockVector3.ZERO));
+ }
+
+ @Test
+ @DisplayName("contains no values")
+ void containsNoValues() {
+ assertEquals(0, map.values().size());
+ assertFalse(map.containsValue(air));
+ }
+
+ @Test
+ @DisplayName("contains no entries")
+ void containsNoEntries() {
+ assertEquals(0, map.entrySet().size());
+ }
+
+ @Test
+ @DisplayName("returns the default value from getOrDefault")
+ void returnsDefaultFromGetOrDefault() {
+ assertEquals(air, map.getOrDefault(BlockVector3.ZERO, air));
+ }
+
+ @Test
+ @DisplayName("never calls the forEach action")
+ void neverCallsForEachAction() {
+ map.forEach(biConsumer);
+ verifyZeroInteractions(biConsumer);
+ }
+
+ @Test
+ @DisplayName("never calls the replaceAll function")
+ void neverCallsReplaceAllFunction() {
+ map.replaceAll(biFunction);
+ verifyZeroInteractions(biFunction);
+ }
+
+ @Test
+ @DisplayName("inserts on putIfAbsent")
+ void insertOnPutIfAbsent() {
+ assertNull(map.putIfAbsent(BlockVector3.ZERO, air));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(BlockVector3.ZERO));
+ }
+
+ @Test
+ @DisplayName("remove(key) returns null")
+ void removeKeyReturnsNull() {
+ assertNull(map.remove(BlockVector3.ZERO));
+ }
+
+ @Test
+ @DisplayName("remove(key, value) returns false")
+ void removeKeyValueReturnsFalse() {
+ assertFalse(map.remove(BlockVector3.ZERO, air));
+ }
+
+ @Test
+ @DisplayName("does nothing on replace")
+ void doesNothingOnReplace() {
+ assertNull(map.replace(BlockVector3.ZERO, air));
+ assertEquals(0, map.size());
+ assertFalse(map.replace(BlockVector3.ZERO, null, air));
+ assertEquals(0, map.size());
+ }
+
+ @Test
+ @DisplayName("inserts on computeIfAbsent")
+ void insertOnComputeIfAbsent() {
+ assertEquals(air, map.computeIfAbsent(BlockVector3.ZERO, k -> air));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(BlockVector3.ZERO));
+ }
+
+ @Test
+ @DisplayName("inserts on compute")
+ void insertOnCompute() {
+ assertEquals(air, map.compute(BlockVector3.ZERO, (k, v) -> air));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(BlockVector3.ZERO));
+ }
+
+ @Test
+ @DisplayName("does nothing on computeIfPresent")
+ void doesNothingOnComputeIfPresent() {
+ assertNull(map.computeIfPresent(BlockVector3.ZERO, (k, v) -> air));
+ assertEquals(0, map.size());
+ }
+
+ @Test
+ @DisplayName("inserts on merge, without calling merge function")
+ void insertsOnMerge() {
+ assertEquals(air, map.merge(BlockVector3.ZERO, air, mergeFunction));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(BlockVector3.ZERO));
+ verifyZeroInteractions(mergeFunction);
+ }
+
+ }
+
+ @Nested
+ @DisplayName("after having an entry added")
+ @EnabledIfSystemProperty(named = "blockmap.fulltesting", matches = "true")
+ class AfterEntryAdded {
+
+ // Note: This section of tests would really benefit from
+ // being able to parameterize classes. It's not part of JUnit
+ // yet though: https://github.com/junit-team/junit5/issues/878
+
+ @VariedVectorsProvider.Test
+ @DisplayName("has a size of one")
+ void hasSizeOne(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(1, map.size());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("is equal to another map with the same entry")
+ void isEqualToSimilarMap(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(ImmutableMap.of(vec, air), map);
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("is not equal to another map with a different key")
+ void isNotEqualToDifferentKeyMap(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+ assertNotEquals(ImmutableMap.of(nonMatch, air), map);
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("is not equal to another map with a different value")
+ void isNotEqualToDifferentValueMap(BlockVector3 vec) {
+ map.put(vec, air);
+ assertNotEquals(ImmutableMap.of(vec, oakWood), map);
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("is not equal to an empty map")
+ void isNotEqualToEmptyMap(BlockVector3 vec) {
+ map.put(vec, air);
+ assertNotEquals(ImmutableMap.of(), map);
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("has the same hashCode as another map with the same entry")
+ void isHashCodeEqualToSimilarMap(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(ImmutableMap.of(vec, air).hashCode(), map.hashCode());
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("has a different hashCode from another map with a different key")
+ void isHashCodeNotEqualToDifferentKeyMap(BlockVector3 vec, BlockVector3 nonMatch) {
+ assumeFalse(vec.hashCode() == nonMatch.hashCode(),
+ "Vectors have equivalent hashCodes, maps will too.");
+ map.put(vec, air);
+ assertNotEquals(ImmutableMap.of(nonMatch, air).hashCode(), map.hashCode());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("has a different hashCode from another map with a different value")
+ void isHashCodeNotEqualToDifferentValueMap(BlockVector3 vec) {
+ map.put(vec, air);
+ assertNotEquals(ImmutableMap.of(vec, oakWood).hashCode(), map.hashCode());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("has a different hashCode from an empty map")
+ void isHashCodeNotEqualToEmptyMap(BlockVector3 vec) {
+ map.put(vec, air);
+ assertNotEquals(ImmutableMap.of().hashCode(), map.hashCode());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("returns value from get")
+ void returnsValueFromGet(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(air, map.get(vec));
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("returns `null` from get with different key")
+ void returnsValueFromGet(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+ assertNotEquals(air, map.get(nonMatch));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("contains the key")
+ void containsTheKey(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(1, map.keySet().size());
+ assertTrue(map.keySet().contains(vec));
+ assertTrue(map.containsKey(vec));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("contains the value")
+ void containsTheValue(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(1, map.values().size());
+ assertTrue(map.values().contains(air));
+ assertTrue(map.containsValue(air));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("contains the entry")
+ void containsTheEntry(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(1, map.entrySet().size());
+ assertEquals(new AbstractMap.SimpleImmutableEntry<>(vec, air), map.entrySet().iterator().next());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("returns the provided value from getOrDefault")
+ void returnsProvidedFromGetOrDefault(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(air, map.getOrDefault(vec, oakWood));
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("returns the default value from getOrDefault with a different key")
+ void returnsDefaultFromGetOrDefaultWrongKey(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+ assertEquals(oakWood, map.getOrDefault(nonMatch, oakWood));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("calls the forEach action once")
+ void neverCallsForEachAction(BlockVector3 vec) {
+ map.put(vec, air);
+ map.forEach(biConsumer);
+ verify(biConsumer).accept(vec, air);
+ verifyNoMoreInteractions(biConsumer);
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("replaces value using replaceAll")
+ void neverCallsReplaceAllFunction(BlockVector3 vec) {
+ map.put(vec, air);
+ map.replaceAll((v, b) -> oakWood);
+ assertEquals(oakWood, map.get(vec));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("does not insert on `putIfAbsent`")
+ void noInsertOnPutIfAbsent(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(air, map.putIfAbsent(vec, oakWood));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(vec));
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("inserts on `putIfAbsent` to a different key")
+ void insertOnPutIfAbsentDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+ assertNull(map.putIfAbsent(nonMatch, oakWood));
+ assertEquals(2, map.size());
+ assertEquals(air, map.get(vec));
+ assertEquals(oakWood, map.get(nonMatch));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("remove(key) returns the old value")
+ void removeKeyReturnsOldValue(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(air, map.remove(vec));
+ assertEquals(0, map.size());
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("remove(nonMatch) returns null")
+ void removeNonMatchingKeyReturnsNull(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+ assertNull(map.remove(nonMatch));
+ assertEquals(1, map.size());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("remove(key, value) returns true")
+ void removeKeyValueReturnsTrue(BlockVector3 vec) {
+ map.put(vec, air);
+ assertTrue(map.remove(vec, air));
+ assertEquals(0, map.size());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("remove(key, value) returns false for wrong value")
+ void removeKeyValueReturnsFalseWrongValue(BlockVector3 vec) {
+ map.put(vec, air);
+ assertFalse(map.remove(vec, oakWood));
+ assertEquals(1, map.size());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("replaces value at key")
+ void replacesValueAtKey(BlockVector3 vec) {
+ map.put(vec, air);
+
+ assertEquals(air, map.replace(vec, oakWood));
+ assertEquals(1, map.size());
+ assertEquals(oakWood, map.get(vec));
+
+ assertTrue(map.replace(vec, oakWood, air));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(vec));
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("does not replace value at different key")
+ void doesNotReplaceAtDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+
+ assertNull(map.replace(nonMatch, oakWood));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(vec));
+
+ assertFalse(map.replace(nonMatch, air, oakWood));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(vec));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("does not insert on computeIfAbsent")
+ void doesNotInsertComputeIfAbsent(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(air, map.computeIfAbsent(vec, k -> {
+ assertEquals(vec, k);
+ return oakWood;
+ }));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(vec));
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("inserts on computeIfAbsent with different key")
+ void insertsOnComputeIfAbsentDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+ assertEquals(oakWood, map.computeIfAbsent(nonMatch, k -> {
+ assertEquals(nonMatch, k);
+ return oakWood;
+ }));
+ assertEquals(2, map.size());
+ assertEquals(air, map.get(vec));
+ assertEquals(oakWood, map.get(nonMatch));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("replaces on compute")
+ void replaceOnCompute(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(oakWood, map.compute(vec, (k, v) -> {
+ assertEquals(vec, k);
+ assertEquals(air, v);
+ return oakWood;
+ }));
+ assertEquals(1, map.size());
+ assertEquals(oakWood, map.get(vec));
+ assertNull(map.compute(vec, (k, v) -> null));
+ assertEquals(0, map.size());
+ }
+
+ @VariedVectorsProvider.Test(provideNonMatching = true)
+ @DisplayName("inserts on compute with different key")
+ void insertOnComputeDifferentKey(BlockVector3 vec, BlockVector3 nonMatch) {
+ map.put(vec, air);
+ assertEquals(oakWood, map.compute(nonMatch, (k, v) -> {
+ assertEquals(nonMatch, k);
+ assertNull(v);
+ return oakWood;
+ }));
+ assertEquals(2, map.size());
+ assertEquals(air, map.get(vec));
+ assertEquals(oakWood, map.get(nonMatch));
+ assertNull(map.compute(nonMatch, (k, v) -> null));
+ assertEquals(1, map.size());
+ assertEquals(air, map.get(vec));
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("replaces on computeIfPresent")
+ void replacesOnComputeIfPresent(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(oakWood, map.computeIfPresent(vec, (k, v) -> {
+ assertEquals(vec, k);
+ assertEquals(air, v);
+ return oakWood;
+ }));
+ assertEquals(1, map.size());
+ assertEquals(oakWood, map.get(vec));
+ assertNull(map.computeIfPresent(vec, (k, v) -> null));
+ assertEquals(0, map.size());
+ }
+
+ @VariedVectorsProvider.Test
+ @DisplayName("inserts on merge, with call to merge function")
+ void insertsOnMerge(BlockVector3 vec) {
+ map.put(vec, air);
+ assertEquals(oakWood, map.merge(vec, oakWood, (o, n) -> {
+ assertEquals(air, o);
+ assertEquals(oakWood, n);
+ return n;
+ }));
+ assertEquals(1, map.size());
+ assertEquals(oakWood, map.get(vec));
+ }
+
+ }
+
+ @Test
+ @DisplayName("contains all inserted vectors")
+ void containsAllInsertedVectors() {
+ Set allVectors = VariedVectorsProvider.makeVectorsStream().collect(Collectors.toSet());
+ for (BlockVector3 vec : allVectors) {
+ map.put(vec, air);
+ }
+ assertEquals(allVectors.size(), map.size());
+ assertEquals(allVectors, map.keySet());
+ for (Map.Entry entry : map.entrySet()) {
+ assertTrue(allVectors.contains(entry.getKey()));
+ assertEquals(air, entry.getValue());
+ }
+ }
+
+}
diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/eventbus/EventBusTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/eventbus/EventBusTest.java
index 3e85c6fee..82fdb11c0 100644
--- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/eventbus/EventBusTest.java
+++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/eventbus/EventBusTest.java
@@ -19,13 +19,14 @@
package com.sk89q.worldedit.util.eventbus;
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
-import org.junit.jupiter.api.Test;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
public class EventBusTest {
diff --git a/worldedit-core/src/test/resources/junit-platform.properties b/worldedit-core/src/test/resources/junit-platform.properties
index ee7c4fad3..31bfedd9e 100644
--- a/worldedit-core/src/test/resources/junit-platform.properties
+++ b/worldedit-core/src/test/resources/junit-platform.properties
@@ -2,4 +2,4 @@ junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=same_thread
junit.jupiter.execution.parallel.config.strategy=dynamic
-junit.jupiter.execution.parallel.config.dynamic.factor=4
+junit.jupiter.execution.parallel.config.dynamic.factor=1
diff --git a/worldedit-fabric/build.gradle.kts b/worldedit-fabric/build.gradle.kts
new file mode 100644
index 000000000..fa38ce619
--- /dev/null
+++ b/worldedit-fabric/build.gradle.kts
@@ -0,0 +1,103 @@
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+import net.fabricmc.loom.task.RemapJarTask
+
+applyPlatformAndCoreConfiguration()
+applyShadowConfiguration()
+
+apply(plugin = "fabric-loom")
+
+val minecraftVersion = "1.14.4"
+val yarnMappings = "1.14.4+build.12"
+val loaderVersion = "0.6.2+build.166"
+
+configurations.all {
+ resolutionStrategy {
+ force("com.google.guava:guava:21.0")
+ }
+}
+
+dependencies {
+ "compile"(project(":worldedit-core"))
+ "compile"("org.apache.logging.log4j:log4j-slf4j-impl:2.8.1")
+
+ "minecraft"("com.mojang:minecraft:$minecraftVersion")
+ "mappings"("net.fabricmc:yarn:$yarnMappings")
+ "modCompile"("net.fabricmc:fabric-loader:$loaderVersion")
+
+ listOf(
+ "net.fabricmc.fabric-api:fabric-api-base:0.1.0+2983bc0442",
+ "net.fabricmc.fabric-api:fabric-events-interaction-v0:0.1.1+591e97ae42",
+ "net.fabricmc.fabric-api:fabric-events-lifecycle-v0:0.1.1+591e97ae42",
+ "net.fabricmc.fabric-api:fabric-networking-v0:0.1.3+591e97ae42"
+ ).forEach {
+ "include"(it)
+ "modImplementation"(it)
+ }
+
+ // Hook these up manually, because Fabric doesn't seem to quite do it properly.
+ "compileClasspath"("net.fabricmc:sponge-mixin:${project.versions.mixin}")
+ "annotationProcessor"("net.fabricmc:sponge-mixin:${project.versions.mixin}")
+ "annotationProcessor"("net.fabricmc:fabric-loom:${project.versions.loom}")
+
+ "testCompile"("org.mockito:mockito-core:1.9.0-rc1")
+}
+
+configure {
+ archivesBaseName = "$archivesBaseName-mc$minecraftVersion"
+}
+
+tasks.named("processResources") {
+ // this will ensure that this task is redone when the versions change.
+ inputs.property("version", project.ext["internalVersion"])
+
+ from(sourceSets["main"].resources.srcDirs) {
+ include("fabric.mod.json")
+ expand("version" to project.ext["internalVersion"])
+ }
+
+ // copy everything else except the mod json
+ from(sourceSets["main"].resources.srcDirs) {
+ exclude("fabric.mod.json")
+ }
+}
+
+tasks.named("jar") {
+ manifest {
+ attributes("Class-Path" to "truezip.jar WorldEdit/truezip.jar js.jar WorldEdit/js.jar",
+ "WorldEdit-Version" to project.version)
+ }
+}
+
+tasks.named("shadowJar") {
+ archiveClassifier.set("dist-dev")
+ dependencies {
+ relocate("org.slf4j", "com.sk89q.worldedit.slf4j")
+ relocate("org.apache.logging.slf4j", "com.sk89q.worldedit.log4jbridge")
+ relocate("org.antlr.v4", "com.sk89q.worldedit.antlr4")
+
+ include(dependency("org.slf4j:slf4j-api"))
+ include(dependency("org.apache.logging.log4j:log4j-slf4j-impl"))
+ include(dependency("org.antlr:antlr4-runtime"))
+ }
+}
+
+tasks.register("deobfJar") {
+ from(sourceSets["main"].output)
+ archiveClassifier.set("dev")
+}
+
+artifacts {
+ add("archives", tasks.named("deobfJar"))
+}
+
+tasks.register("remapShadowJar") {
+ val shadowJar = tasks.getByName("shadowJar")
+ dependsOn(shadowJar)
+ input.set(shadowJar.archiveFile)
+ archiveFileName.set(shadowJar.archiveFileName.get().replace(Regex("-dev\\.jar$"), ".jar"))
+ addNestedDependencies.set(true)
+}
+
+tasks.named("assemble").configure {
+ dependsOn("remapShadowJar")
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java
new file mode 100644
index 000000000..462975acb
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java
@@ -0,0 +1,129 @@
+/*
+ * 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.fabric;
+
+import static com.sk89q.worldedit.fabric.FabricAdapter.adaptPlayer;
+import static net.minecraft.server.command.CommandManager.argument;
+import static net.minecraft.server.command.CommandManager.literal;
+
+import com.google.common.collect.ImmutableList;
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.arguments.StringArgumentType;
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.context.StringRange;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.suggestion.Suggestion;
+import com.mojang.brigadier.suggestion.Suggestions;
+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.internal.util.Substring;
+import net.minecraft.entity.Entity;
+import net.minecraft.server.command.ServerCommandSource;
+import net.minecraft.server.network.ServerPlayerEntity;
+import org.enginehub.piston.inject.InjectedValueStore;
+import org.enginehub.piston.inject.Key;
+import org.enginehub.piston.inject.MapBackedValueStore;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Predicate;
+
+
+public final class CommandWrapper {
+
+ private CommandWrapper() {
+ }
+
+ public static void register(CommandDispatcher dispatcher, org.enginehub.piston.Command command) {
+ ImmutableList.Builder aliases = ImmutableList.builder();
+ aliases.add(command.getName()).addAll(command.getAliases());
+
+ Command commandRunner =
+ ctx -> {
+ WorldEdit.getInstance().getEventBus().post(new com.sk89q.worldedit.event.platform.CommandEvent(
+ adaptPlayer(ctx.getSource().getPlayer()),
+ ctx.getInput()
+ ));
+ return 0;
+ };
+
+ for (String alias : aliases.build()) {
+ LiteralArgumentBuilder base = literal(alias).executes(commandRunner)
+ .then(argument("args", StringArgumentType.greedyString())
+ .suggests(CommandWrapper::suggest)
+ .executes(commandRunner));
+ if (command.getCondition() != org.enginehub.piston.Command.Condition.TRUE) {
+ base.requires(requirementsFor(command));
+ }
+ dispatcher.register(base);
+ }
+ }
+
+ private static Predicate requirementsFor(org.enginehub.piston.Command mapping) {
+ return ctx -> {
+ final Entity entity = ctx.getEntity();
+ if (!(entity instanceof ServerPlayerEntity)) {
+ return true;
+ }
+ final Actor actor = FabricAdapter.adaptPlayer(((ServerPlayerEntity) entity));
+ InjectedValueStore store = MapBackedValueStore.create();
+ store.injectValue(Key.of(Actor.class), context -> Optional.of(actor));
+ return mapping.getCondition().satisfied(store);
+ };
+ }
+
+ private static CompletableFuture suggest(CommandContext context,
+ SuggestionsBuilder builder) throws CommandSyntaxException {
+ CommandSuggestionEvent event = new CommandSuggestionEvent(
+ FabricAdapter.adaptPlayer(context.getSource().getPlayer()),
+ builder.getInput()
+ );
+ WorldEdit.getInstance().getEventBus().post(event);
+ List suggestions = event.getSuggestions();
+
+ ImmutableList.Builder result = ImmutableList.builder();
+
+ for (Substring suggestion : suggestions) {
+ String suggestionText = suggestion.getSubstring();
+ // If at end, we are actually suggesting the next argument
+ // Ensure there is a space!
+ if (suggestion.getStart() == suggestion.getEnd()
+ && suggestion.getEnd() == builder.getInput().length()
+ && !builder.getInput().endsWith(" ")
+ && !builder.getInput().endsWith("\"")) {
+ suggestionText = " " + suggestionText;
+ }
+ result.add(new Suggestion(
+ StringRange.between(suggestion.getStart(), suggestion.getEnd()),
+ suggestionText
+ ));
+ }
+
+ return CompletableFuture.completedFuture(
+ Suggestions.create(builder.getInput(), result.build())
+ );
+ }
+
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java
new file mode 100644
index 000000000..317274331
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java
@@ -0,0 +1,241 @@
+/*
+ * 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.fabric;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.jnbt.Tag;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.registry.state.BooleanProperty;
+import com.sk89q.worldedit.registry.state.DirectionalProperty;
+import com.sk89q.worldedit.registry.state.EnumProperty;
+import com.sk89q.worldedit.registry.state.IntegerProperty;
+import com.sk89q.worldedit.registry.state.Property;
+import com.sk89q.worldedit.util.Direction;
+import com.sk89q.worldedit.world.World;
+import com.sk89q.worldedit.world.biome.BiomeType;
+import com.sk89q.worldedit.world.biome.BiomeTypes;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import com.sk89q.worldedit.world.item.ItemType;
+import com.sk89q.worldedit.world.item.ItemTypes;
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.state.StateFactory;
+import net.minecraft.state.property.DirectionProperty;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.StringIdentifiable;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.world.biome.Biome;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+public final class FabricAdapter {
+
+ private FabricAdapter() {
+ }
+
+ public static World adapt(net.minecraft.world.World world) {
+ return new FabricWorld(world);
+ }
+
+ public static Biome adapt(BiomeType biomeType) {
+ return Registry.BIOME.get(new Identifier(biomeType.getId()));
+ }
+
+ public static BiomeType adapt(Biome biome) {
+ return BiomeTypes.get(Registry.BIOME.getId(biome).toString());
+ }
+
+ public static Vector3 adapt(Vec3d vector) {
+ return Vector3.at(vector.x, vector.y, vector.z);
+ }
+
+ public static BlockVector3 adapt(BlockPos pos) {
+ return BlockVector3.at(pos.getX(), pos.getY(), pos.getZ());
+ }
+
+ public static Vec3d toVec3(BlockVector3 vector) {
+ return new Vec3d(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
+ }
+
+ public static net.minecraft.util.math.Direction adapt(Direction face) {
+ switch (face) {
+ case NORTH: return net.minecraft.util.math.Direction.NORTH;
+ case SOUTH: return net.minecraft.util.math.Direction.SOUTH;
+ case WEST: return net.minecraft.util.math.Direction.WEST;
+ case EAST: return net.minecraft.util.math.Direction.EAST;
+ case DOWN: return net.minecraft.util.math.Direction.DOWN;
+ case UP:
+ default:
+ return net.minecraft.util.math.Direction.UP;
+ }
+ }
+
+ public static Direction adaptEnumFacing(net.minecraft.util.math.Direction face) {
+ switch (face) {
+ case NORTH: return Direction.NORTH;
+ case SOUTH: return Direction.SOUTH;
+ case WEST: return Direction.WEST;
+ case EAST: return Direction.EAST;
+ case DOWN: return Direction.DOWN;
+ case UP:
+ default:
+ return Direction.UP;
+ }
+ }
+
+ public static BlockPos toBlockPos(BlockVector3 vector) {
+ return new BlockPos(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
+ }
+
+ public static Property> adaptProperty(net.minecraft.state.property.Property> property) {
+ if (property instanceof net.minecraft.state.property.BooleanProperty) {
+ return new BooleanProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.state.property.BooleanProperty) property).getValues()));
+ }
+ if (property instanceof net.minecraft.state.property.IntProperty) {
+ return new IntegerProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.state.property.IntProperty) property).getValues()));
+ }
+ if (property instanceof DirectionProperty) {
+ return new DirectionalProperty(property.getName(), ((DirectionProperty) property).getValues().stream()
+ .map(FabricAdapter::adaptEnumFacing)
+ .collect(Collectors.toList()));
+ }
+ if (property instanceof net.minecraft.state.property.EnumProperty) {
+ // Note: do not make x.asString a method reference.
+ // It will cause runtime bootstrap exceptions.
+ return new EnumProperty(property.getName(), ((net.minecraft.state.property.EnumProperty>) property).getValues().stream()
+ .map(x -> x.asString())
+ .collect(Collectors.toList()));
+ }
+ return new PropertyAdapter<>(property);
+ }
+
+ public static Map, Object> adaptProperties(BlockType block, Map, Comparable>> mcProps) {
+ Map, Object> props = new TreeMap<>(Comparator.comparing(Property::getName));
+ for (Map.Entry, Comparable>> prop : mcProps.entrySet()) {
+ Object value = prop.getValue();
+ if (prop.getKey() instanceof DirectionProperty) {
+ value = adaptEnumFacing((net.minecraft.util.math.Direction) value);
+ } else if (prop.getKey() instanceof net.minecraft.state.property.EnumProperty) {
+ value = ((StringIdentifiable) value).asString();
+ }
+ props.put(block.getProperty(prop.getKey().getName()), value);
+ }
+ return props;
+ }
+
+ private static net.minecraft.block.BlockState applyProperties(StateFactory stateContainer,
+ net.minecraft.block.BlockState newState, Map, Object> states) {
+ for (Map.Entry, Object> state : states.entrySet()) {
+ net.minecraft.state.property.Property property = stateContainer.getProperty(state.getKey().getName());
+ Comparable value = (Comparable) state.getValue();
+ // we may need to adapt this value, depending on the source prop
+ if (property instanceof DirectionProperty) {
+ Direction dir = (Direction) value;
+ value = adapt(dir);
+ } else if (property instanceof net.minecraft.state.property.EnumProperty) {
+ String enumName = (String) value;
+ value = ((net.minecraft.state.property.EnumProperty>) property).getValue((String) value).orElseGet(() -> {
+ throw new IllegalStateException("Enum property " + property.getName() + " does not contain " + enumName);
+ });
+ }
+
+ newState = newState.with(property, value);
+ }
+ return newState;
+ }
+
+ public static net.minecraft.block.BlockState adapt(BlockState blockState) {
+ Block mcBlock = adapt(blockState.getBlockType());
+ net.minecraft.block.BlockState newState = mcBlock.getDefaultState();
+ Map, Object> states = blockState.getStates();
+ return applyProperties(mcBlock.getStateFactory(), newState, states);
+ }
+
+ public static BlockState adapt(net.minecraft.block.BlockState blockState) {
+ BlockType blockType = adapt(blockState.getBlock());
+ return blockType.getState(adaptProperties(blockType, blockState.getEntries()));
+ }
+
+ public static Block adapt(BlockType blockType) {
+ return Registry.BLOCK.get(new Identifier(blockType.getId()));
+ }
+
+ public static BlockType adapt(Block block) {
+ return BlockTypes.get(Registry.BLOCK.getId(block).toString());
+ }
+
+ public static Item adapt(ItemType itemType) {
+ return Registry.ITEM.get(new Identifier(itemType.getId()));
+ }
+
+ public static ItemType adapt(Item item) {
+ return ItemTypes.get(Registry.ITEM.getId(item).toString());
+ }
+
+ public static ItemStack adapt(BaseItemStack baseItemStack) {
+ net.minecraft.nbt.CompoundTag fabricCompound = null;
+ if (baseItemStack.getNbtData() != null) {
+ fabricCompound = NBTConverter.toNative(baseItemStack.getNbtData());
+ }
+ final ItemStack itemStack = new ItemStack(adapt(baseItemStack.getType()), baseItemStack.getAmount());
+ itemStack.setTag(fabricCompound);
+ return itemStack;
+ }
+
+ public static BaseItemStack adapt(ItemStack itemStack) {
+ CompoundTag tag = NBTConverter.fromNative(itemStack.toTag(new net.minecraft.nbt.CompoundTag()));
+ if (tag.getValue().isEmpty()) {
+ tag = null;
+ } else {
+ final Tag tagTag = tag.getValue().get("tag");
+ if (tagTag instanceof CompoundTag) {
+ tag = ((CompoundTag) tagTag);
+ } else {
+ tag = null;
+ }
+ }
+ return new BaseItemStack(adapt(itemStack.getItem()), tag, itemStack.getCount());
+ }
+
+ /**
+ * Get the WorldEdit proxy for the given player.
+ *
+ * @param player the player
+ * @return the WorldEdit player
+ */
+ public static FabricPlayer adaptPlayer(ServerPlayerEntity player) {
+ checkNotNull(player);
+ return new FabricPlayer(player);
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBiomeRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBiomeRegistry.java
new file mode 100644
index 000000000..692d350e1
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBiomeRegistry.java
@@ -0,0 +1,58 @@
+/*
+ * 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.fabric;
+
+import com.sk89q.worldedit.world.biome.BiomeData;
+import com.sk89q.worldedit.world.biome.BiomeType;
+import com.sk89q.worldedit.world.registry.BiomeRegistry;
+import net.minecraft.world.biome.Biome;
+
+/**
+ * Provides access to biome data in Fabric.
+ */
+class FabricBiomeRegistry implements BiomeRegistry {
+
+ @Override
+ public BiomeData getData(BiomeType biome) {
+ return new FabricBiomeData(FabricAdapter.adapt(biome));
+ }
+
+ /**
+ * Cached biome data information.
+ */
+ private static class FabricBiomeData implements BiomeData {
+ private final Biome biome;
+
+ /**
+ * Create a new instance.
+ *
+ * @param biome the base biome
+ */
+ private FabricBiomeData(Biome biome) {
+ this.biome = biome;
+ }
+
+ @Override
+ public String getName() {
+ return biome.getName().asFormattedString();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java
new file mode 100644
index 000000000..87a72c9f6
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java
@@ -0,0 +1,40 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.fabric;
+
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.registry.BlockCategoryRegistry;
+import net.minecraft.tag.BlockTags;
+import net.minecraft.tag.Tag;
+import net.minecraft.util.Identifier;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class FabricBlockCategoryRegistry implements BlockCategoryRegistry {
+ @Override
+ public Set getCategorisedByName(String category) {
+ return Optional.ofNullable(BlockTags.getContainer().get(new Identifier(category)))
+ .map(Tag::values).orElse(Collections.emptySet())
+ .stream().map(FabricAdapter::adapt).collect(Collectors.toSet());
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockMaterial.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockMaterial.java
new file mode 100644
index 000000000..c9fc86e02
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockMaterial.java
@@ -0,0 +1,93 @@
+/*
+ * 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.fabric;
+
+import com.sk89q.worldedit.world.registry.BlockMaterial;
+import com.sk89q.worldedit.world.registry.PassthroughBlockMaterial;
+import net.minecraft.block.Material;
+import net.minecraft.block.piston.PistonBehavior;
+
+import javax.annotation.Nullable;
+
+/**
+ * Fabric block material that pulls as much info as possible from the Minecraft
+ * Material, and passes the rest to another implementation, typically the
+ * bundled block info.
+ */
+public class FabricBlockMaterial extends PassthroughBlockMaterial {
+
+ private final Material delegate;
+
+ public FabricBlockMaterial(Material delegate, @Nullable BlockMaterial secondary) {
+ super(secondary);
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean isAir() {
+ return delegate == Material.AIR || super.isAir();
+ }
+
+ @Override
+ public boolean isOpaque() {
+ return delegate.blocksLight();
+ }
+
+ @Override
+ public boolean isLiquid() {
+ return delegate.isLiquid();
+ }
+
+ @Override
+ public boolean isSolid() {
+ return delegate.isSolid();
+ }
+
+ @Override
+ public boolean isFragileWhenPushed() {
+ return delegate.getPistonBehavior() == PistonBehavior.DESTROY;
+ }
+
+ @Override
+ public boolean isUnpushable() {
+ return delegate.getPistonBehavior() == PistonBehavior.BLOCK;
+ }
+
+ @Override
+ public boolean isMovementBlocker() {
+ return delegate.blocksMovement();
+ }
+
+ @Override
+ public boolean isBurnable() {
+ return delegate.isBurnable();
+ }
+
+ @Override
+ public boolean isToolRequired() {
+ return !delegate.canBreakByHand();
+ }
+
+ @Override
+ public boolean isReplacedDuringPlacement() {
+ return delegate.isReplaceable();
+ }
+
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockRegistry.java
new file mode 100644
index 000000000..593e95817
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockRegistry.java
@@ -0,0 +1,80 @@
+/*
+ * 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.fabric;
+
+import com.sk89q.worldedit.registry.state.Property;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.registry.BlockMaterial;
+import com.sk89q.worldedit.world.registry.BundledBlockRegistry;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.block.Block;
+import net.minecraft.block.Material;
+
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.TreeMap;
+
+public class FabricBlockRegistry extends BundledBlockRegistry {
+
+ private Map materialMap = new HashMap<>();
+
+ @Nullable
+ @Override
+ public String getName(BlockType blockType) {
+ Block block = FabricAdapter.adapt(blockType);
+ if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
+ return block.getName().asFormattedString();
+ } else {
+ return super.getName(blockType);
+ }
+ }
+
+ @Override
+ public BlockMaterial getMaterial(BlockType blockType) {
+ Block block = FabricAdapter.adapt(blockType);
+ return materialMap.computeIfAbsent(block.getDefaultState().getMaterial(),
+ m -> new FabricBlockMaterial(m, super.getMaterial(blockType)));
+ }
+
+ @Override
+ public Map> getProperties(BlockType blockType) {
+ Block block = FabricAdapter.adapt(blockType);
+ Map> map = new TreeMap<>();
+ Collection> propertyKeys = block
+ .getDefaultState()
+ .getProperties();
+ for (net.minecraft.state.property.Property> key : propertyKeys) {
+ map.put(key.getName(), FabricAdapter.adaptProperty(key));
+ }
+ return map;
+ }
+
+ @Override
+ public OptionalInt getInternalBlockStateId(BlockState state) {
+ net.minecraft.block.BlockState equivalent = FabricAdapter.adapt(state);
+ return OptionalInt.of(Block.getRawIdFromState(equivalent));
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricConfiguration.java
similarity index 59%
rename from worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java
rename to worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricConfiguration.java
index b0f25c685..ab00d6f89 100644
--- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricConfiguration.java
@@ -19,21 +19,27 @@
package com.sk89q.worldedit.fabric;
-import com.sk89q.worldedit.extension.platform.Watchdog;
-import com.sk89q.worldedit.fabric.mixin.MixinMinecraftServer;
-import net.minecraft.server.dedicated.MinecraftDedicatedServer;
-import net.minecraft.util.SystemUtil;
+import com.sk89q.worldedit.util.PropertiesConfiguration;
-class FabricWatchdog implements Watchdog {
+import java.io.File;
- private final MinecraftDedicatedServer server;
+public class FabricConfiguration extends PropertiesConfiguration {
- FabricWatchdog(MinecraftDedicatedServer server) {
- this.server = server;
+ public boolean creativeEnable = false;
+ public boolean cheatMode = false;
+
+ public FabricConfiguration(FabricWorldEdit mod) {
+ super(new File(mod.getWorkingDir(), "worldedit.properties"));
}
@Override
- public void tick() {
- ((MixinMinecraftServer) (Object) server).timeReference = SystemUtil.getMeasuringTimeMs();
+ protected void loadExtra() {
+ creativeEnable = getBool("use-in-creative", false);
+ cheatMode = getBool("cheat-mode", false);
}
-}
+
+ @Override
+ public File getWorkingDirectory() {
+ return FabricWorldEdit.inst.getWorkingDir();
+ }
+}
\ No newline at end of file
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java
new file mode 100644
index 000000000..f307b789c
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java
@@ -0,0 +1,2728 @@
+/*
+ * 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.fabric;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.mojang.datafixers.DSL.TypeReference;
+import com.mojang.datafixers.DataFixer;
+import com.mojang.datafixers.DataFixerBuilder;
+import com.mojang.datafixers.Dynamic;
+import com.mojang.datafixers.schemas.Schema;
+import com.sk89q.jnbt.CompoundTag;
+import net.minecraft.datafixers.NbtOps;
+import net.minecraft.datafixers.Schemas;
+import net.minecraft.datafixers.TypeReferences;
+import net.minecraft.nbt.FloatTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
+import net.minecraft.text.LiteralText;
+import net.minecraft.text.Text;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.JsonHelper;
+import net.minecraft.util.math.Direction;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+/**
+ * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2)
+ *
+ * We register a DFU Fixer per Legacy Data Version and apply the fixes using legacy strategy
+ * which is safer, faster and cleaner code.
+ *
+ * The pre DFU code did not fail when the Source version was unknown.
+ *
+ * This class also provides util methods for converting compounds to wrap the update call to
+ * receive the source version in the compound
+ *
+ */
+@SuppressWarnings("UnnecessarilyQualifiedStaticUsage")
+class FabricDataFixer extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T fixUp(FixType type, T original, int srcVer) {
+ if (type == FixTypes.CHUNK) {
+ return (T) fixChunk((CompoundTag) original, srcVer);
+ } else if (type == FixTypes.BLOCK_ENTITY) {
+ return (T) fixBlockEntity((CompoundTag) original, srcVer);
+ } else if (type == FixTypes.ENTITY) {
+ return (T) fixEntity((CompoundTag) original, srcVer);
+ } else if (type == FixTypes.BLOCK_STATE) {
+ return (T) fixBlockState((String) original, srcVer);
+ } else if (type == FixTypes.ITEM_TYPE) {
+ return (T) fixItemType((String) original, srcVer);
+ } else if (type == FixTypes.BIOME) {
+ return (T) fixBiome((String) original, srcVer);
+ }
+ return original;
+ }
+
+ private CompoundTag fixChunk(CompoundTag originalChunk, int srcVer) {
+ net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(originalChunk);
+ net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer);
+ return NBTConverter.fromNative(fixed);
+ }
+
+ private CompoundTag fixBlockEntity(CompoundTag origTileEnt, int srcVer) {
+ net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(origTileEnt);
+ net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer);
+ return NBTConverter.fromNative(fixed);
+ }
+
+ private CompoundTag fixEntity(CompoundTag origEnt, int srcVer) {
+ net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(origEnt);
+ net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer);
+ return NBTConverter.fromNative(fixed);
+ }
+
+ private String fixBlockState(String blockState, int srcVer) {
+ net.minecraft.nbt.CompoundTag stateNBT = stateToNBT(blockState);
+ Dynamic dynamic = new Dynamic<>(OPS_NBT, stateNBT);
+ net.minecraft.nbt.CompoundTag fixed = (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(TypeReferences.BLOCK_STATE, dynamic, srcVer, DATA_VERSION).getValue();
+ return nbtToState(fixed);
+ }
+
+ private String nbtToState(net.minecraft.nbt.CompoundTag tagCompound) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(tagCompound.getString("Name"));
+ if (tagCompound.containsKey("Properties", 10)) {
+ sb.append('[');
+ net.minecraft.nbt.CompoundTag props = tagCompound.getCompound("Properties");
+ sb.append(props.getKeys().stream().map(k -> k + "=" + props.getString(k).replace("\"", "")).collect(Collectors.joining(",")));
+ sb.append(']');
+ }
+ return sb.toString();
+ }
+
+ private static net.minecraft.nbt.CompoundTag stateToNBT(String blockState) {
+ int propIdx = blockState.indexOf('[');
+ net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
+ if (propIdx < 0) {
+ tag.putString("Name", blockState);
+ } else {
+ tag.putString("Name", blockState.substring(0, propIdx));
+ net.minecraft.nbt.CompoundTag propTag = new net.minecraft.nbt.CompoundTag();
+ String props = blockState.substring(propIdx + 1, blockState.length() - 1);
+ String[] propArr = props.split(",");
+ for (String pair : propArr) {
+ final String[] split = pair.split("=");
+ propTag.putString(split[0], split[1]);
+ }
+ tag.put("Properties", propTag);
+ }
+ return tag;
+ }
+
+ private String fixBiome(String key, int srcVer) {
+ return fixName(key, srcVer, TypeReferences.BIOME);
+ }
+
+ private String fixItemType(String key, int srcVer) {
+ return fixName(key, srcVer, TypeReferences.ITEM_NAME);
+ }
+
+ private static String fixName(String key, int srcVer, TypeReference type) {
+ return INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, new StringTag(key)), srcVer, DATA_VERSION)
+ .asString().orElse(key);
+ }
+
+ private static final NbtOps OPS_NBT = NbtOps.INSTANCE;
+ private static final int LEGACY_VERSION = 1343;
+ private static int DATA_VERSION;
+ private static FabricDataFixer INSTANCE;
+
+ private final Map> converters = new EnumMap<>(LegacyType.class);
+ private final Map> inspectors = new EnumMap<>(LegacyType.class);
+
+ // Set on build
+ private DataFixer fixer;
+ private static final Map DFU_TO_LEGACY = new HashMap<>();
+
+ public enum LegacyType {
+ LEVEL(TypeReferences.LEVEL),
+ PLAYER(TypeReferences.PLAYER),
+ CHUNK(TypeReferences.CHUNK),
+ BLOCK_ENTITY(TypeReferences.BLOCK_ENTITY),
+ ENTITY(TypeReferences.ENTITY),
+ ITEM_INSTANCE(TypeReferences.ITEM_STACK),
+ OPTIONS(TypeReferences.OPTIONS),
+ STRUCTURE(TypeReferences.STRUCTURE);
+
+ private final TypeReference type;
+
+ LegacyType(TypeReference type) {
+ this.type = type;
+ DFU_TO_LEGACY.put(type.typeName(), this);
+ }
+
+ public TypeReference getDFUType() {
+ return type;
+ }
+ }
+
+ FabricDataFixer(int dataVersion) {
+ super(dataVersion);
+ DATA_VERSION = dataVersion;
+ INSTANCE = this;
+ registerConverters();
+ registerInspectors();
+ this.fixer = new WrappedDataFixer(Schemas.getFixer());
+ }
+
+ @Override
+ public DataFixer build(final Executor executor) {
+ return fixer;
+ }
+
+ private class WrappedDataFixer implements DataFixer {
+ private final DataFixer realFixer;
+
+ WrappedDataFixer(DataFixer realFixer) {
+ this.realFixer = realFixer;
+ }
+
+ @Override
+ public Dynamic update(TypeReference type, Dynamic dynamic, int sourceVer, int targetVer) {
+ LegacyType legacyType = DFU_TO_LEGACY.get(type.typeName());
+ if (sourceVer < LEGACY_VERSION && legacyType != null) {
+ net.minecraft.nbt.CompoundTag cmp = (net.minecraft.nbt.CompoundTag) dynamic.getValue();
+ int desiredVersion = Math.min(targetVer, LEGACY_VERSION);
+
+ cmp = convert(legacyType, cmp, sourceVer, desiredVersion);
+ sourceVer = desiredVersion;
+ dynamic = new Dynamic(OPS_NBT, cmp);
+ }
+ return realFixer.update(type, dynamic, sourceVer, targetVer);
+ }
+
+ private net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int desiredVersion) {
+ List converters = FabricDataFixer.this.converters.get(type);
+ if (converters != null && !converters.isEmpty()) {
+ for (DataConverter converter : converters) {
+ int dataVersion = converter.getDataVersion();
+ if (dataVersion > sourceVer && dataVersion <= desiredVersion) {
+ cmp = converter.convert(cmp);
+ }
+ }
+ }
+
+ List inspectors = FabricDataFixer.this.inspectors.get(type);
+ if (inspectors != null && !inspectors.isEmpty()) {
+ for (DataInspector inspector : inspectors) {
+ cmp = inspector.inspect(cmp, sourceVer, desiredVersion);
+ }
+ }
+
+ return cmp;
+ }
+
+ @Override
+ public Schema getSchema(int i) {
+ return realFixer.getSchema(i);
+ }
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp) {
+ return convert(type.getDFUType(), cmp);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) {
+ return convert(type.getDFUType(), cmp, sourceVer);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ return convert(type.getDFUType(), cmp, sourceVer, targetVer);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp) {
+ int i = cmp.containsKey("DataVersion", 99) ? cmp.getInt("DataVersion") : -1;
+ return convert(type, cmp, i);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) {
+ return convert(type, cmp, sourceVer, DATA_VERSION);
+ }
+
+ public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (sourceVer >= targetVer) {
+ return cmp;
+ }
+ return (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, cmp), sourceVer, targetVer).getValue();
+ }
+
+
+ public interface DataInspector {
+ net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer);
+ }
+
+ public interface DataConverter {
+
+ int getDataVersion();
+
+ net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp);
+ }
+
+
+ private void registerInspector(LegacyType type, DataInspector inspector) {
+ this.inspectors.computeIfAbsent(type, k -> new ArrayList<>()).add(inspector);
+ }
+
+ private void registerConverter(LegacyType type, DataConverter converter) {
+ int version = converter.getDataVersion();
+
+ List list = this.converters.computeIfAbsent(type, k -> new ArrayList<>());
+ if (!list.isEmpty() && list.get(list.size() - 1).getDataVersion() > version) {
+ for (int j = 0; j < list.size(); ++j) {
+ if (list.get(j).getDataVersion() > version) {
+ list.add(j, converter);
+ break;
+ }
+ }
+ } else {
+ list.add(converter);
+ }
+ }
+
+ private void registerInspectors() {
+ registerEntityItemList("EntityHorseDonkey", "SaddleItem", "Items");
+ registerEntityItemList("EntityHorseMule", "Items");
+ registerEntityItemList("EntityMinecartChest", "Items");
+ registerEntityItemList("EntityMinecartHopper", "Items");
+ registerEntityItemList("EntityVillager", "Inventory");
+ registerEntityItemListEquipment("EntityArmorStand");
+ registerEntityItemListEquipment("EntityBat");
+ registerEntityItemListEquipment("EntityBlaze");
+ registerEntityItemListEquipment("EntityCaveSpider");
+ registerEntityItemListEquipment("EntityChicken");
+ registerEntityItemListEquipment("EntityCow");
+ registerEntityItemListEquipment("EntityCreeper");
+ registerEntityItemListEquipment("EntityEnderDragon");
+ registerEntityItemListEquipment("EntityEnderman");
+ registerEntityItemListEquipment("EntityEndermite");
+ registerEntityItemListEquipment("EntityEvoker");
+ registerEntityItemListEquipment("EntityGhast");
+ registerEntityItemListEquipment("EntityGiantZombie");
+ registerEntityItemListEquipment("EntityGuardian");
+ registerEntityItemListEquipment("EntityGuardianElder");
+ registerEntityItemListEquipment("EntityHorse");
+ registerEntityItemListEquipment("EntityHorseDonkey");
+ registerEntityItemListEquipment("EntityHorseMule");
+ registerEntityItemListEquipment("EntityHorseSkeleton");
+ registerEntityItemListEquipment("EntityHorseZombie");
+ registerEntityItemListEquipment("EntityIronGolem");
+ registerEntityItemListEquipment("EntityMagmaCube");
+ registerEntityItemListEquipment("EntityMushroomCow");
+ registerEntityItemListEquipment("EntityOcelot");
+ registerEntityItemListEquipment("EntityPig");
+ registerEntityItemListEquipment("EntityPigZombie");
+ registerEntityItemListEquipment("EntityRabbit");
+ registerEntityItemListEquipment("EntitySheep");
+ registerEntityItemListEquipment("EntityShulker");
+ registerEntityItemListEquipment("EntitySilverfish");
+ registerEntityItemListEquipment("EntitySkeleton");
+ registerEntityItemListEquipment("EntitySkeletonStray");
+ registerEntityItemListEquipment("EntitySkeletonWither");
+ registerEntityItemListEquipment("EntitySlime");
+ registerEntityItemListEquipment("EntitySnowman");
+ registerEntityItemListEquipment("EntitySpider");
+ registerEntityItemListEquipment("EntitySquid");
+ registerEntityItemListEquipment("EntityVex");
+ registerEntityItemListEquipment("EntityVillager");
+ registerEntityItemListEquipment("EntityVindicator");
+ registerEntityItemListEquipment("EntityWitch");
+ registerEntityItemListEquipment("EntityWither");
+ registerEntityItemListEquipment("EntityWolf");
+ registerEntityItemListEquipment("EntityZombie");
+ registerEntityItemListEquipment("EntityZombieHusk");
+ registerEntityItemListEquipment("EntityZombieVillager");
+ registerEntityItemSingle("EntityFireworks", "FireworksItem");
+ registerEntityItemSingle("EntityHorse", "ArmorItem");
+ registerEntityItemSingle("EntityHorse", "SaddleItem");
+ registerEntityItemSingle("EntityHorseMule", "SaddleItem");
+ registerEntityItemSingle("EntityHorseSkeleton", "SaddleItem");
+ registerEntityItemSingle("EntityHorseZombie", "SaddleItem");
+ registerEntityItemSingle("EntityItem", "Item");
+ registerEntityItemSingle("EntityItemFrame", "Item");
+ registerEntityItemSingle("EntityPotion", "Potion");
+
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItem("TileEntityRecordPlayer", "RecordItem"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityBrewingStand", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityChest", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDispenser", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDropper", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityFurnace", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityHopper", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityShulkerBox", "Items"));
+ registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorMobSpawnerMobs());
+ registerInspector(LegacyType.CHUNK, new DataInspectorChunks());
+ registerInspector(LegacyType.ENTITY, new DataInspectorCommandBlock());
+ registerInspector(LegacyType.ENTITY, new DataInspectorEntityPassengers());
+ registerInspector(LegacyType.ENTITY, new DataInspectorMobSpawnerMinecart());
+ registerInspector(LegacyType.ENTITY, new DataInspectorVillagers());
+ registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorBlockEntity());
+ registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorEntity());
+ registerInspector(LegacyType.LEVEL, new DataInspectorLevelPlayer());
+ registerInspector(LegacyType.PLAYER, new DataInspectorPlayer());
+ registerInspector(LegacyType.PLAYER, new DataInspectorPlayerVehicle());
+ registerInspector(LegacyType.STRUCTURE, new DataInspectorStructure());
+ }
+
+ private void registerConverters() {
+ registerConverter(LegacyType.ENTITY, new DataConverterEquipment());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterSignText());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterMaterialId());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionId());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterSpawnEgg());
+ registerConverter(LegacyType.ENTITY, new DataConverterMinecart());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterMobSpawner());
+ registerConverter(LegacyType.ENTITY, new DataConverterUUID());
+ registerConverter(LegacyType.ENTITY, new DataConverterHealth());
+ registerConverter(LegacyType.ENTITY, new DataConverterSaddle());
+ registerConverter(LegacyType.ENTITY, new DataConverterHanging());
+ registerConverter(LegacyType.ENTITY, new DataConverterDropChances());
+ registerConverter(LegacyType.ENTITY, new DataConverterRiding());
+ registerConverter(LegacyType.ENTITY, new DataConverterArmorStand());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBook());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterCookedFish());
+ registerConverter(LegacyType.ENTITY, new DataConverterZombie());
+ registerConverter(LegacyType.OPTIONS, new DataConverterVBO());
+ registerConverter(LegacyType.ENTITY, new DataConverterGuardian());
+ registerConverter(LegacyType.ENTITY, new DataConverterSkeleton());
+ registerConverter(LegacyType.ENTITY, new DataConverterZombieType());
+ registerConverter(LegacyType.ENTITY, new DataConverterHorse());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterTileEntity());
+ registerConverter(LegacyType.ENTITY, new DataConverterEntity());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBanner());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionWater());
+ registerConverter(LegacyType.ENTITY, new DataConverterShulker());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterShulkerBoxItem());
+ registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterShulkerBoxBlock());
+ registerConverter(LegacyType.OPTIONS, new DataConverterLang());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterTotem());
+ registerConverter(LegacyType.CHUNK, new DataConverterBedBlock());
+ registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBedItem());
+ }
+
+ private void registerEntityItemList(String type, String... keys) {
+ registerInspector(LegacyType.ENTITY, new DataInspectorItemList(type, keys));
+ }
+
+ private void registerEntityItemSingle(String type, String key) {
+ registerInspector(LegacyType.ENTITY, new DataInspectorItem(type, key));
+ }
+
+ private void registerEntityItemListEquipment(String type) {
+ registerEntityItemList(type, "ArmorItems", "HandItems");
+ }
+ private static final Map OLD_ID_TO_KEY_MAP = new HashMap<>();
+
+ static {
+ final Map map = OLD_ID_TO_KEY_MAP;
+ map.put("EntityItem", new Identifier("item"));
+ map.put("EntityExperienceOrb", new Identifier("xp_orb"));
+ map.put("EntityAreaEffectCloud", new Identifier("area_effect_cloud"));
+ map.put("EntityGuardianElder", new Identifier("elder_guardian"));
+ map.put("EntitySkeletonWither", new Identifier("wither_skeleton"));
+ map.put("EntitySkeletonStray", new Identifier("stray"));
+ map.put("EntityEgg", new Identifier("egg"));
+ map.put("EntityLeash", new Identifier("leash_knot"));
+ map.put("EntityPainting", new Identifier("painting"));
+ map.put("EntityTippedArrow", new Identifier("arrow"));
+ map.put("EntitySnowball", new Identifier("snowball"));
+ map.put("EntityLargeFireball", new Identifier("fireball"));
+ map.put("EntitySmallFireball", new Identifier("small_fireball"));
+ map.put("EntityEnderPearl", new Identifier("ender_pearl"));
+ map.put("EntityEnderSignal", new Identifier("eye_of_ender_signal"));
+ map.put("EntityPotion", new Identifier("potion"));
+ map.put("EntityThrownExpBottle", new Identifier("xp_bottle"));
+ map.put("EntityItemFrame", new Identifier("item_frame"));
+ map.put("EntityWitherSkull", new Identifier("wither_skull"));
+ map.put("EntityTNTPrimed", new Identifier("tnt"));
+ map.put("EntityFallingBlock", new Identifier("falling_block"));
+ map.put("EntityFireworks", new Identifier("fireworks_rocket"));
+ map.put("EntityZombieHusk", new Identifier("husk"));
+ map.put("EntitySpectralArrow", new Identifier("spectral_arrow"));
+ map.put("EntityShulkerBullet", new Identifier("shulker_bullet"));
+ map.put("EntityDragonFireball", new Identifier("dragon_fireball"));
+ map.put("EntityZombieVillager", new Identifier("zombie_villager"));
+ map.put("EntityHorseSkeleton", new Identifier("skeleton_horse"));
+ map.put("EntityHorseZombie", new Identifier("zombie_horse"));
+ map.put("EntityArmorStand", new Identifier("armor_stand"));
+ map.put("EntityHorseDonkey", new Identifier("donkey"));
+ map.put("EntityHorseMule", new Identifier("mule"));
+ map.put("EntityEvokerFangs", new Identifier("evocation_fangs"));
+ map.put("EntityEvoker", new Identifier("evocation_illager"));
+ map.put("EntityVex", new Identifier("vex"));
+ map.put("EntityVindicator", new Identifier("vindication_illager"));
+ map.put("EntityIllagerIllusioner", new Identifier("illusion_illager"));
+ map.put("EntityMinecartCommandBlock", new Identifier("commandblock_minecart"));
+ map.put("EntityBoat", new Identifier("boat"));
+ map.put("EntityMinecartRideable", new Identifier("minecart"));
+ map.put("EntityMinecartChest", new Identifier("chest_minecart"));
+ map.put("EntityMinecartFurnace", new Identifier("furnace_minecart"));
+ map.put("EntityMinecartTNT", new Identifier("tnt_minecart"));
+ map.put("EntityMinecartHopper", new Identifier("hopper_minecart"));
+ map.put("EntityMinecartMobSpawner", new Identifier("spawner_minecart"));
+ map.put("EntityCreeper", new Identifier("creeper"));
+ map.put("EntitySkeleton", new Identifier("skeleton"));
+ map.put("EntitySpider", new Identifier("spider"));
+ map.put("EntityGiantZombie", new Identifier("giant"));
+ map.put("EntityZombie", new Identifier("zombie"));
+ map.put("EntitySlime", new Identifier("slime"));
+ map.put("EntityGhast", new Identifier("ghast"));
+ map.put("EntityPigZombie", new Identifier("zombie_pigman"));
+ map.put("EntityEnderman", new Identifier("enderman"));
+ map.put("EntityCaveSpider", new Identifier("cave_spider"));
+ map.put("EntitySilverfish", new Identifier("silverfish"));
+ map.put("EntityBlaze", new Identifier("blaze"));
+ map.put("EntityMagmaCube", new Identifier("magma_cube"));
+ map.put("EntityEnderDragon", new Identifier("ender_dragon"));
+ map.put("EntityWither", new Identifier("wither"));
+ map.put("EntityBat", new Identifier("bat"));
+ map.put("EntityWitch", new Identifier("witch"));
+ map.put("EntityEndermite", new Identifier("endermite"));
+ map.put("EntityGuardian", new Identifier("guardian"));
+ map.put("EntityShulker", new Identifier("shulker"));
+ map.put("EntityPig", new Identifier("pig"));
+ map.put("EntitySheep", new Identifier("sheep"));
+ map.put("EntityCow", new Identifier("cow"));
+ map.put("EntityChicken", new Identifier("chicken"));
+ map.put("EntitySquid", new Identifier("squid"));
+ map.put("EntityWolf", new Identifier("wolf"));
+ map.put("EntityMushroomCow", new Identifier("mooshroom"));
+ map.put("EntitySnowman", new Identifier("snowman"));
+ map.put("EntityOcelot", new Identifier("ocelot"));
+ map.put("EntityIronGolem", new Identifier("villager_golem"));
+ map.put("EntityHorse", new Identifier("horse"));
+ map.put("EntityRabbit", new Identifier("rabbit"));
+ map.put("EntityPolarBear", new Identifier("polar_bear"));
+ map.put("EntityLlama", new Identifier("llama"));
+ map.put("EntityLlamaSpit", new Identifier("llama_spit"));
+ map.put("EntityParrot", new Identifier("parrot"));
+ map.put("EntityVillager", new Identifier("villager"));
+ map.put("EntityEnderCrystal", new Identifier("ender_crystal"));
+ map.put("TileEntityFurnace", new Identifier("furnace"));
+ map.put("TileEntityChest", new Identifier("chest"));
+ map.put("TileEntityEnderChest", new Identifier("ender_chest"));
+ map.put("TileEntityRecordPlayer", new Identifier("jukebox"));
+ map.put("TileEntityDispenser", new Identifier("dispenser"));
+ map.put("TileEntityDropper", new Identifier("dropper"));
+ map.put("TileEntitySign", new Identifier("sign"));
+ map.put("TileEntityMobSpawner", new Identifier("mob_spawner"));
+ map.put("TileEntityNote", new Identifier("noteblock"));
+ map.put("TileEntityPiston", new Identifier("piston"));
+ map.put("TileEntityBrewingStand", new Identifier("brewing_stand"));
+ map.put("TileEntityEnchantTable", new Identifier("enchanting_table"));
+ map.put("TileEntityEnderPortal", new Identifier("end_portal"));
+ map.put("TileEntityBeacon", new Identifier("beacon"));
+ map.put("TileEntitySkull", new Identifier("skull"));
+ map.put("TileEntityLightDetector", new Identifier("daylight_detector"));
+ map.put("TileEntityHopper", new Identifier("hopper"));
+ map.put("TileEntityComparator", new Identifier("comparator"));
+ map.put("TileEntityFlowerPot", new Identifier("flower_pot"));
+ map.put("TileEntityBanner", new Identifier("banner"));
+ map.put("TileEntityStructure", new Identifier("structure_block"));
+ map.put("TileEntityEndGateway", new Identifier("end_gateway"));
+ map.put("TileEntityCommand", new Identifier("command_block"));
+ map.put("TileEntityShulkerBox", new Identifier("shulker_box"));
+ map.put("TileEntityBed", new Identifier("bed"));
+ }
+
+ private static Identifier getKey(String type) {
+ final Identifier key = OLD_ID_TO_KEY_MAP.get(type);
+ if (key == null) {
+ throw new IllegalArgumentException("Unknown mapping for " + type);
+ }
+ return key;
+ }
+
+ private static void convertCompound(LegacyType type, net.minecraft.nbt.CompoundTag cmp, String key, int sourceVer, int targetVer) {
+ cmp.put(key, convert(type, cmp.getCompound(key), sourceVer, targetVer));
+ }
+
+ private static void convertItem(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) {
+ if (nbttagcompound.containsKey(key, 10)) {
+ convertCompound(LegacyType.ITEM_INSTANCE, nbttagcompound, key, sourceVer, targetVer);
+ }
+ }
+
+ private static void convertItems(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) {
+ if (nbttagcompound.containsKey(key, 9)) {
+ ListTag nbttaglist = nbttagcompound.getList(key, 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.add(j, convert(LegacyType.ITEM_INSTANCE, nbttaglist.getCompoundTag(j), sourceVer, targetVer));
+ }
+ }
+
+ }
+
+ private static class DataConverterEquipment implements DataConverter {
+
+ DataConverterEquipment() {}
+
+ @Override
+ public int getDataVersion() {
+ return 100;
+ }
+
+ @Override
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ ListTag nbttaglist = cmp.getList("Equipment", 10);
+ ListTag nbttaglist1;
+
+ if (!nbttaglist.isEmpty() && !cmp.containsKey("HandItems", 10)) {
+ nbttaglist1 = new ListTag();
+ nbttaglist1.add(nbttaglist.get(0));
+ nbttaglist1.add(new net.minecraft.nbt.CompoundTag());
+ cmp.put("HandItems", nbttaglist1);
+ }
+
+ if (nbttaglist.size() > 1 && !cmp.containsKey("ArmorItem", 10)) {
+ nbttaglist1 = new ListTag();
+ nbttaglist1.add(nbttaglist.get(1));
+ nbttaglist1.add(nbttaglist.get(2));
+ nbttaglist1.add(nbttaglist.get(3));
+ nbttaglist1.add(nbttaglist.get(4));
+ cmp.put("ArmorItems", nbttaglist1);
+ }
+
+ cmp.remove("Equipment");
+ if (cmp.containsKey("DropChances", 9)) {
+ nbttaglist1 = cmp.getList("DropChances", 5);
+ ListTag nbttaglist2;
+
+ if (!cmp.containsKey("HandDropChances", 10)) {
+ nbttaglist2 = new ListTag();
+ nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(0)));
+ nbttaglist2.add(new FloatTag(0.0F));
+ cmp.put("HandDropChances", nbttaglist2);
+ }
+
+ if (!cmp.containsKey("ArmorDropChances", 10)) {
+ nbttaglist2 = new ListTag();
+ nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(1)));
+ nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(2)));
+ nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(3)));
+ nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(4)));
+ cmp.put("ArmorDropChances", nbttaglist2);
+ }
+
+ cmp.remove("DropChances");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorBlockEntity implements DataInspector {
+
+ private static final Map b = Maps.newHashMap();
+ private static final Map c = Maps.newHashMap();
+
+ DataInspectorBlockEntity() {}
+
+ @Nullable
+ private static String convertEntityId(int i, String s) {
+ String key = new Identifier(s).toString();
+ if (i < 515 && DataInspectorBlockEntity.b.containsKey(key)) {
+ return DataInspectorBlockEntity.b.get(key);
+ } else {
+ return DataInspectorBlockEntity.c.get(key);
+ }
+ }
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (!cmp.containsKey("tag", 10)) {
+ return cmp;
+ } else {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.containsKey("BlockEntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag");
+ String s = cmp.getString("id");
+ String s1 = convertEntityId(sourceVer, s);
+ boolean flag;
+
+ if (s1 == null) {
+ // CraftBukkit - Remove unnecessary warning (occurs when deserializing a Shulker Box item)
+ // DataInspectorBlockEntity.a.warn("Unable to resolve BlockEntity for ItemInstance: {}", s);
+ flag = false;
+ } else {
+ flag = !nbttagcompound2.containsKey("id");
+ nbttagcompound2.putString("id", s1);
+ }
+
+ convert(LegacyType.BLOCK_ENTITY, nbttagcompound2, sourceVer, targetVer);
+ if (flag) {
+ nbttagcompound2.remove("id");
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ static {
+ Map map = DataInspectorBlockEntity.b;
+
+ map.put("minecraft:furnace", "Furnace");
+ map.put("minecraft:lit_furnace", "Furnace");
+ map.put("minecraft:chest", "Chest");
+ map.put("minecraft:trapped_chest", "Chest");
+ map.put("minecraft:ender_chest", "EnderChest");
+ map.put("minecraft:jukebox", "RecordPlayer");
+ map.put("minecraft:dispenser", "Trap");
+ map.put("minecraft:dropper", "Dropper");
+ map.put("minecraft:sign", "Sign");
+ map.put("minecraft:mob_spawner", "MobSpawner");
+ map.put("minecraft:noteblock", "Music");
+ map.put("minecraft:brewing_stand", "Cauldron");
+ map.put("minecraft:enhanting_table", "EnchantTable");
+ map.put("minecraft:command_block", "CommandBlock");
+ map.put("minecraft:beacon", "Beacon");
+ map.put("minecraft:skull", "Skull");
+ map.put("minecraft:daylight_detector", "DLDetector");
+ map.put("minecraft:hopper", "Hopper");
+ map.put("minecraft:banner", "Banner");
+ map.put("minecraft:flower_pot", "FlowerPot");
+ map.put("minecraft:repeating_command_block", "CommandBlock");
+ map.put("minecraft:chain_command_block", "CommandBlock");
+ map.put("minecraft:standing_sign", "Sign");
+ map.put("minecraft:wall_sign", "Sign");
+ map.put("minecraft:piston_head", "Piston");
+ map.put("minecraft:daylight_detector_inverted", "DLDetector");
+ map.put("minecraft:unpowered_comparator", "Comparator");
+ map.put("minecraft:powered_comparator", "Comparator");
+ map.put("minecraft:wall_banner", "Banner");
+ map.put("minecraft:standing_banner", "Banner");
+ map.put("minecraft:structure_block", "Structure");
+ map.put("minecraft:end_portal", "Airportal");
+ map.put("minecraft:end_gateway", "EndGateway");
+ map.put("minecraft:shield", "Shield");
+ map = DataInspectorBlockEntity.c;
+ map.put("minecraft:furnace", "minecraft:furnace");
+ map.put("minecraft:lit_furnace", "minecraft:furnace");
+ map.put("minecraft:chest", "minecraft:chest");
+ map.put("minecraft:trapped_chest", "minecraft:chest");
+ map.put("minecraft:ender_chest", "minecraft:enderchest");
+ map.put("minecraft:jukebox", "minecraft:jukebox");
+ map.put("minecraft:dispenser", "minecraft:dispenser");
+ map.put("minecraft:dropper", "minecraft:dropper");
+ map.put("minecraft:sign", "minecraft:sign");
+ map.put("minecraft:mob_spawner", "minecraft:mob_spawner");
+ map.put("minecraft:noteblock", "minecraft:noteblock");
+ map.put("minecraft:brewing_stand", "minecraft:brewing_stand");
+ map.put("minecraft:enhanting_table", "minecraft:enchanting_table");
+ map.put("minecraft:command_block", "minecraft:command_block");
+ map.put("minecraft:beacon", "minecraft:beacon");
+ map.put("minecraft:skull", "minecraft:skull");
+ map.put("minecraft:daylight_detector", "minecraft:daylight_detector");
+ map.put("minecraft:hopper", "minecraft:hopper");
+ map.put("minecraft:banner", "minecraft:banner");
+ map.put("minecraft:flower_pot", "minecraft:flower_pot");
+ map.put("minecraft:repeating_command_block", "minecraft:command_block");
+ map.put("minecraft:chain_command_block", "minecraft:command_block");
+ map.put("minecraft:shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:white_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:orange_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:magenta_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:light_blue_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:yellow_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:lime_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:pink_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:gray_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:silver_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:cyan_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:purple_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:blue_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:brown_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:green_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:red_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:black_shulker_box", "minecraft:shulker_box");
+ map.put("minecraft:bed", "minecraft:bed");
+ map.put("minecraft:standing_sign", "minecraft:sign");
+ map.put("minecraft:wall_sign", "minecraft:sign");
+ map.put("minecraft:piston_head", "minecraft:piston");
+ map.put("minecraft:daylight_detector_inverted", "minecraft:daylight_detector");
+ map.put("minecraft:unpowered_comparator", "minecraft:comparator");
+ map.put("minecraft:powered_comparator", "minecraft:comparator");
+ map.put("minecraft:wall_banner", "minecraft:banner");
+ map.put("minecraft:standing_banner", "minecraft:banner");
+ map.put("minecraft:structure_block", "minecraft:structure_block");
+ map.put("minecraft:end_portal", "minecraft:end_portal");
+ map.put("minecraft:end_gateway", "minecraft:end_gateway");
+ map.put("minecraft:shield", "minecraft:shield");
+ }
+ }
+
+ private static class DataInspectorEntity implements DataInspector {
+
+ private static final Logger a = LogManager.getLogger(FabricDataFixer.class);
+
+ DataInspectorEntity() {}
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.containsKey("EntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag");
+ String s = cmp.getString("id");
+ String s1;
+
+ if ("minecraft:armor_stand".equals(s)) {
+ s1 = sourceVer < 515 ? "ArmorStand" : "minecraft:armor_stand";
+ } else {
+ if (!"minecraft:spawn_egg".equals(s)) {
+ return cmp;
+ }
+
+ s1 = nbttagcompound2.getString("id");
+ }
+
+ boolean flag;
+
+ if (s1 == null) {
+ DataInspectorEntity.a.warn("Unable to resolve Entity for ItemInstance: {}", s);
+ flag = false;
+ } else {
+ flag = !nbttagcompound2.containsKey("id", 8);
+ nbttagcompound2.putString("id", s1);
+ }
+
+ convert(LegacyType.ENTITY, nbttagcompound2, sourceVer, targetVer);
+ if (flag) {
+ nbttagcompound2.remove("id");
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+
+ private abstract static class DataInspectorTagged implements DataInspector {
+
+ private final Identifier key;
+
+ DataInspectorTagged(String type) {
+ this.key = getKey(type);
+ }
+
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (this.key.equals(new Identifier(cmp.getString("id")))) {
+ cmp = this.inspectChecked(cmp, sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+
+ abstract net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer);
+ }
+
+ private static class DataInspectorItemList extends DataInspectorTagged {
+
+ private final String[] keys;
+
+ DataInspectorItemList(String oclass, String... astring) {
+ super(oclass);
+ this.keys = astring;
+ }
+
+ net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) {
+ for (String s : this.keys) {
+ FabricDataFixer.convertItems(nbttagcompound, s, sourceVer, targetVer);
+ }
+
+ return nbttagcompound;
+ }
+ }
+ private static class DataInspectorItem extends DataInspectorTagged {
+
+ private final String[] keys;
+
+ DataInspectorItem(String oclass, String... astring) {
+ super(oclass);
+ this.keys = astring;
+ }
+
+ net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) {
+ for (String key : this.keys) {
+ FabricDataFixer.convertItem(nbttagcompound, key, sourceVer, targetVer);
+ }
+
+ return nbttagcompound;
+ }
+ }
+
+ private static class DataConverterMaterialId implements DataConverter {
+
+ private static final String[] materials = new String[2268];
+
+ DataConverterMaterialId() {}
+
+ public int getDataVersion() {
+ return 102;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.containsKey("id", 99)) {
+ short short0 = cmp.getShort("id");
+
+ if (short0 > 0 && short0 < materials.length && materials[short0] != null) {
+ cmp.putString("id", materials[short0]);
+ }
+ }
+
+ return cmp;
+ }
+
+ static {
+ materials[1] = "minecraft:stone";
+ materials[2] = "minecraft:grass";
+ materials[3] = "minecraft:dirt";
+ materials[4] = "minecraft:cobblestone";
+ materials[5] = "minecraft:planks";
+ materials[6] = "minecraft:sapling";
+ materials[7] = "minecraft:bedrock";
+ materials[8] = "minecraft:flowing_water";
+ materials[9] = "minecraft:water";
+ materials[10] = "minecraft:flowing_lava";
+ materials[11] = "minecraft:lava";
+ materials[12] = "minecraft:sand";
+ materials[13] = "minecraft:gravel";
+ materials[14] = "minecraft:gold_ore";
+ materials[15] = "minecraft:iron_ore";
+ materials[16] = "minecraft:coal_ore";
+ materials[17] = "minecraft:log";
+ materials[18] = "minecraft:leaves";
+ materials[19] = "minecraft:sponge";
+ materials[20] = "minecraft:glass";
+ materials[21] = "minecraft:lapis_ore";
+ materials[22] = "minecraft:lapis_block";
+ materials[23] = "minecraft:dispenser";
+ materials[24] = "minecraft:sandstone";
+ materials[25] = "minecraft:noteblock";
+ materials[27] = "minecraft:golden_rail";
+ materials[28] = "minecraft:detector_rail";
+ materials[29] = "minecraft:sticky_piston";
+ materials[30] = "minecraft:web";
+ materials[31] = "minecraft:tallgrass";
+ materials[32] = "minecraft:deadbush";
+ materials[33] = "minecraft:piston";
+ materials[35] = "minecraft:wool";
+ materials[37] = "minecraft:yellow_flower";
+ materials[38] = "minecraft:red_flower";
+ materials[39] = "minecraft:brown_mushroom";
+ materials[40] = "minecraft:red_mushroom";
+ materials[41] = "minecraft:gold_block";
+ materials[42] = "minecraft:iron_block";
+ materials[43] = "minecraft:double_stone_slab";
+ materials[44] = "minecraft:stone_slab";
+ materials[45] = "minecraft:brick_block";
+ materials[46] = "minecraft:tnt";
+ materials[47] = "minecraft:bookshelf";
+ materials[48] = "minecraft:mossy_cobblestone";
+ materials[49] = "minecraft:obsidian";
+ materials[50] = "minecraft:torch";
+ materials[51] = "minecraft:fire";
+ materials[52] = "minecraft:mob_spawner";
+ materials[53] = "minecraft:oak_stairs";
+ materials[54] = "minecraft:chest";
+ materials[56] = "minecraft:diamond_ore";
+ materials[57] = "minecraft:diamond_block";
+ materials[58] = "minecraft:crafting_table";
+ materials[60] = "minecraft:farmland";
+ materials[61] = "minecraft:furnace";
+ materials[62] = "minecraft:lit_furnace";
+ materials[65] = "minecraft:ladder";
+ materials[66] = "minecraft:rail";
+ materials[67] = "minecraft:stone_stairs";
+ materials[69] = "minecraft:lever";
+ materials[70] = "minecraft:stone_pressure_plate";
+ materials[72] = "minecraft:wooden_pressure_plate";
+ materials[73] = "minecraft:redstone_ore";
+ materials[76] = "minecraft:redstone_torch";
+ materials[77] = "minecraft:stone_button";
+ materials[78] = "minecraft:snow_layer";
+ materials[79] = "minecraft:ice";
+ materials[80] = "minecraft:snow";
+ materials[81] = "minecraft:cactus";
+ materials[82] = "minecraft:clay";
+ materials[84] = "minecraft:jukebox";
+ materials[85] = "minecraft:fence";
+ materials[86] = "minecraft:pumpkin";
+ materials[87] = "minecraft:netherrack";
+ materials[88] = "minecraft:soul_sand";
+ materials[89] = "minecraft:glowstone";
+ materials[90] = "minecraft:portal";
+ materials[91] = "minecraft:lit_pumpkin";
+ materials[95] = "minecraft:stained_glass";
+ materials[96] = "minecraft:trapdoor";
+ materials[97] = "minecraft:monster_egg";
+ materials[98] = "minecraft:stonebrick";
+ materials[99] = "minecraft:brown_mushroom_block";
+ materials[100] = "minecraft:red_mushroom_block";
+ materials[101] = "minecraft:iron_bars";
+ materials[102] = "minecraft:glass_pane";
+ materials[103] = "minecraft:melon_block";
+ materials[106] = "minecraft:vine";
+ materials[107] = "minecraft:fence_gate";
+ materials[108] = "minecraft:brick_stairs";
+ materials[109] = "minecraft:stone_brick_stairs";
+ materials[110] = "minecraft:mycelium";
+ materials[111] = "minecraft:waterlily";
+ materials[112] = "minecraft:nether_brick";
+ materials[113] = "minecraft:nether_brick_fence";
+ materials[114] = "minecraft:nether_brick_stairs";
+ materials[116] = "minecraft:enchanting_table";
+ materials[119] = "minecraft:end_portal";
+ materials[120] = "minecraft:end_portal_frame";
+ materials[121] = "minecraft:end_stone";
+ materials[122] = "minecraft:dragon_egg";
+ materials[123] = "minecraft:redstone_lamp";
+ materials[125] = "minecraft:double_wooden_slab";
+ materials[126] = "minecraft:wooden_slab";
+ materials[127] = "minecraft:cocoa";
+ materials[128] = "minecraft:sandstone_stairs";
+ materials[129] = "minecraft:emerald_ore";
+ materials[130] = "minecraft:ender_chest";
+ materials[131] = "minecraft:tripwire_hook";
+ materials[133] = "minecraft:emerald_block";
+ materials[134] = "minecraft:spruce_stairs";
+ materials[135] = "minecraft:birch_stairs";
+ materials[136] = "minecraft:jungle_stairs";
+ materials[137] = "minecraft:command_block";
+ materials[138] = "minecraft:beacon";
+ materials[139] = "minecraft:cobblestone_wall";
+ materials[141] = "minecraft:carrots";
+ materials[142] = "minecraft:potatoes";
+ materials[143] = "minecraft:wooden_button";
+ materials[145] = "minecraft:anvil";
+ materials[146] = "minecraft:trapped_chest";
+ materials[147] = "minecraft:light_weighted_pressure_plate";
+ materials[148] = "minecraft:heavy_weighted_pressure_plate";
+ materials[151] = "minecraft:daylight_detector";
+ materials[152] = "minecraft:redstone_block";
+ materials[153] = "minecraft:quartz_ore";
+ materials[154] = "minecraft:hopper";
+ materials[155] = "minecraft:quartz_block";
+ materials[156] = "minecraft:quartz_stairs";
+ materials[157] = "minecraft:activator_rail";
+ materials[158] = "minecraft:dropper";
+ materials[159] = "minecraft:stained_hardened_clay";
+ materials[160] = "minecraft:stained_glass_pane";
+ materials[161] = "minecraft:leaves2";
+ materials[162] = "minecraft:log2";
+ materials[163] = "minecraft:acacia_stairs";
+ materials[164] = "minecraft:dark_oak_stairs";
+ materials[170] = "minecraft:hay_block";
+ materials[171] = "minecraft:carpet";
+ materials[172] = "minecraft:hardened_clay";
+ materials[173] = "minecraft:coal_block";
+ materials[174] = "minecraft:packed_ice";
+ materials[175] = "minecraft:double_plant";
+ materials[256] = "minecraft:iron_shovel";
+ materials[257] = "minecraft:iron_pickaxe";
+ materials[258] = "minecraft:iron_axe";
+ materials[259] = "minecraft:flint_and_steel";
+ materials[260] = "minecraft:apple";
+ materials[261] = "minecraft:bow";
+ materials[262] = "minecraft:arrow";
+ materials[263] = "minecraft:coal";
+ materials[264] = "minecraft:diamond";
+ materials[265] = "minecraft:iron_ingot";
+ materials[266] = "minecraft:gold_ingot";
+ materials[267] = "minecraft:iron_sword";
+ materials[268] = "minecraft:wooden_sword";
+ materials[269] = "minecraft:wooden_shovel";
+ materials[270] = "minecraft:wooden_pickaxe";
+ materials[271] = "minecraft:wooden_axe";
+ materials[272] = "minecraft:stone_sword";
+ materials[273] = "minecraft:stone_shovel";
+ materials[274] = "minecraft:stone_pickaxe";
+ materials[275] = "minecraft:stone_axe";
+ materials[276] = "minecraft:diamond_sword";
+ materials[277] = "minecraft:diamond_shovel";
+ materials[278] = "minecraft:diamond_pickaxe";
+ materials[279] = "minecraft:diamond_axe";
+ materials[280] = "minecraft:stick";
+ materials[281] = "minecraft:bowl";
+ materials[282] = "minecraft:mushroom_stew";
+ materials[283] = "minecraft:golden_sword";
+ materials[284] = "minecraft:golden_shovel";
+ materials[285] = "minecraft:golden_pickaxe";
+ materials[286] = "minecraft:golden_axe";
+ materials[287] = "minecraft:string";
+ materials[288] = "minecraft:feather";
+ materials[289] = "minecraft:gunpowder";
+ materials[290] = "minecraft:wooden_hoe";
+ materials[291] = "minecraft:stone_hoe";
+ materials[292] = "minecraft:iron_hoe";
+ materials[293] = "minecraft:diamond_hoe";
+ materials[294] = "minecraft:golden_hoe";
+ materials[295] = "minecraft:wheat_seeds";
+ materials[296] = "minecraft:wheat";
+ materials[297] = "minecraft:bread";
+ materials[298] = "minecraft:leather_helmet";
+ materials[299] = "minecraft:leather_chestplate";
+ materials[300] = "minecraft:leather_leggings";
+ materials[301] = "minecraft:leather_boots";
+ materials[302] = "minecraft:chainmail_helmet";
+ materials[303] = "minecraft:chainmail_chestplate";
+ materials[304] = "minecraft:chainmail_leggings";
+ materials[305] = "minecraft:chainmail_boots";
+ materials[306] = "minecraft:iron_helmet";
+ materials[307] = "minecraft:iron_chestplate";
+ materials[308] = "minecraft:iron_leggings";
+ materials[309] = "minecraft:iron_boots";
+ materials[310] = "minecraft:diamond_helmet";
+ materials[311] = "minecraft:diamond_chestplate";
+ materials[312] = "minecraft:diamond_leggings";
+ materials[313] = "minecraft:diamond_boots";
+ materials[314] = "minecraft:golden_helmet";
+ materials[315] = "minecraft:golden_chestplate";
+ materials[316] = "minecraft:golden_leggings";
+ materials[317] = "minecraft:golden_boots";
+ materials[318] = "minecraft:flint";
+ materials[319] = "minecraft:porkchop";
+ materials[320] = "minecraft:cooked_porkchop";
+ materials[321] = "minecraft:painting";
+ materials[322] = "minecraft:golden_apple";
+ materials[323] = "minecraft:sign";
+ materials[324] = "minecraft:wooden_door";
+ materials[325] = "minecraft:bucket";
+ materials[326] = "minecraft:water_bucket";
+ materials[327] = "minecraft:lava_bucket";
+ materials[328] = "minecraft:minecart";
+ materials[329] = "minecraft:saddle";
+ materials[330] = "minecraft:iron_door";
+ materials[331] = "minecraft:redstone";
+ materials[332] = "minecraft:snowball";
+ materials[333] = "minecraft:boat";
+ materials[334] = "minecraft:leather";
+ materials[335] = "minecraft:milk_bucket";
+ materials[336] = "minecraft:brick";
+ materials[337] = "minecraft:clay_ball";
+ materials[338] = "minecraft:reeds";
+ materials[339] = "minecraft:paper";
+ materials[340] = "minecraft:book";
+ materials[341] = "minecraft:slime_ball";
+ materials[342] = "minecraft:chest_minecart";
+ materials[343] = "minecraft:furnace_minecart";
+ materials[344] = "minecraft:egg";
+ materials[345] = "minecraft:compass";
+ materials[346] = "minecraft:fishing_rod";
+ materials[347] = "minecraft:clock";
+ materials[348] = "minecraft:glowstone_dust";
+ materials[349] = "minecraft:fish";
+ materials[350] = "minecraft:cooked_fish"; // Paper - cooked_fished -> cooked_fish
+ materials[351] = "minecraft:dye";
+ materials[352] = "minecraft:bone";
+ materials[353] = "minecraft:sugar";
+ materials[354] = "minecraft:cake";
+ materials[355] = "minecraft:bed";
+ materials[356] = "minecraft:repeater";
+ materials[357] = "minecraft:cookie";
+ materials[358] = "minecraft:filled_map";
+ materials[359] = "minecraft:shears";
+ materials[360] = "minecraft:melon";
+ materials[361] = "minecraft:pumpkin_seeds";
+ materials[362] = "minecraft:melon_seeds";
+ materials[363] = "minecraft:beef";
+ materials[364] = "minecraft:cooked_beef";
+ materials[365] = "minecraft:chicken";
+ materials[366] = "minecraft:cooked_chicken";
+ materials[367] = "minecraft:rotten_flesh";
+ materials[368] = "minecraft:ender_pearl";
+ materials[369] = "minecraft:blaze_rod";
+ materials[370] = "minecraft:ghast_tear";
+ materials[371] = "minecraft:gold_nugget";
+ materials[372] = "minecraft:nether_wart";
+ materials[373] = "minecraft:potion";
+ materials[374] = "minecraft:glass_bottle";
+ materials[375] = "minecraft:spider_eye";
+ materials[376] = "minecraft:fermented_spider_eye";
+ materials[377] = "minecraft:blaze_powder";
+ materials[378] = "minecraft:magma_cream";
+ materials[379] = "minecraft:brewing_stand";
+ materials[380] = "minecraft:cauldron";
+ materials[381] = "minecraft:ender_eye";
+ materials[382] = "minecraft:speckled_melon";
+ materials[383] = "minecraft:spawn_egg";
+ materials[384] = "minecraft:experience_bottle";
+ materials[385] = "minecraft:fire_charge";
+ materials[386] = "minecraft:writable_book";
+ materials[387] = "minecraft:written_book";
+ materials[388] = "minecraft:emerald";
+ materials[389] = "minecraft:item_frame";
+ materials[390] = "minecraft:flower_pot";
+ materials[391] = "minecraft:carrot";
+ materials[392] = "minecraft:potato";
+ materials[393] = "minecraft:baked_potato";
+ materials[394] = "minecraft:poisonous_potato";
+ materials[395] = "minecraft:map";
+ materials[396] = "minecraft:golden_carrot";
+ materials[397] = "minecraft:skull";
+ materials[398] = "minecraft:carrot_on_a_stick";
+ materials[399] = "minecraft:nether_star";
+ materials[400] = "minecraft:pumpkin_pie";
+ materials[401] = "minecraft:fireworks";
+ materials[402] = "minecraft:firework_charge";
+ materials[403] = "minecraft:enchanted_book";
+ materials[404] = "minecraft:comparator";
+ materials[405] = "minecraft:netherbrick";
+ materials[406] = "minecraft:quartz";
+ materials[407] = "minecraft:tnt_minecart";
+ materials[408] = "minecraft:hopper_minecart";
+ materials[417] = "minecraft:iron_horse_armor";
+ materials[418] = "minecraft:golden_horse_armor";
+ materials[419] = "minecraft:diamond_horse_armor";
+ materials[420] = "minecraft:lead";
+ materials[421] = "minecraft:name_tag";
+ materials[422] = "minecraft:command_block_minecart";
+ materials[2256] = "minecraft:record_13";
+ materials[2257] = "minecraft:record_cat";
+ materials[2258] = "minecraft:record_blocks";
+ materials[2259] = "minecraft:record_chirp";
+ materials[2260] = "minecraft:record_far";
+ materials[2261] = "minecraft:record_mall";
+ materials[2262] = "minecraft:record_mellohi";
+ materials[2263] = "minecraft:record_stal";
+ materials[2264] = "minecraft:record_strad";
+ materials[2265] = "minecraft:record_ward";
+ materials[2266] = "minecraft:record_11";
+ materials[2267] = "minecraft:record_wait";
+ }
+ }
+
+ private static class DataConverterArmorStand implements DataConverter {
+
+ DataConverterArmorStand() {}
+
+ public int getDataVersion() {
+ return 147;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("ArmorStand".equals(cmp.getString("id")) && cmp.getBoolean("Silent") && !cmp.getBoolean("Marker")) {
+ cmp.remove("Silent");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterBanner implements DataConverter {
+
+ DataConverterBanner() {}
+
+ public int getDataVersion() {
+ return 804;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:banner".equals(cmp.getString("id")) && cmp.containsKey("tag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.containsKey("BlockEntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag");
+
+ if (nbttagcompound2.containsKey("Base", 99)) {
+ cmp.putShort("Damage", (short) (nbttagcompound2.getShort("Base") & 15));
+ if (nbttagcompound1.containsKey("display", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound1.getCompound("display");
+
+ if (nbttagcompound3.containsKey("Lore", 9)) {
+ ListTag nbttaglist = nbttagcompound3.getList("Lore", 8);
+
+ if (nbttaglist.size() == 1 && "(+NBT)".equals(nbttaglist.getString(0))) {
+ return cmp;
+ }
+ }
+ }
+
+ nbttagcompound2.remove("Base");
+ if (nbttagcompound2.isEmpty()) {
+ nbttagcompound1.remove("BlockEntityTag");
+ }
+
+ if (nbttagcompound1.isEmpty()) {
+ cmp.remove("tag");
+ }
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterPotionId implements DataConverter {
+
+ private static final String[] potions = new String[128];
+
+ DataConverterPotionId() {}
+
+ public int getDataVersion() {
+ return 102;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:potion".equals(cmp.getString("id"))) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+ short short0 = cmp.getShort("Damage");
+
+ if (!nbttagcompound1.containsKey("Potion", 8)) {
+ String s = DataConverterPotionId.potions[short0 & 127];
+
+ nbttagcompound1.putString("Potion", s == null ? "minecraft:water" : s);
+ cmp.put("tag", nbttagcompound1);
+ if ((short0 & 16384) == 16384) {
+ cmp.putString("id", "minecraft:splash_potion");
+ }
+ }
+
+ if (short0 != 0) {
+ cmp.putShort("Damage", (short) 0);
+ }
+ }
+
+ return cmp;
+ }
+
+ static {
+ DataConverterPotionId.potions[0] = "minecraft:water";
+ DataConverterPotionId.potions[1] = "minecraft:regeneration";
+ DataConverterPotionId.potions[2] = "minecraft:swiftness";
+ DataConverterPotionId.potions[3] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[4] = "minecraft:poison";
+ DataConverterPotionId.potions[5] = "minecraft:healing";
+ DataConverterPotionId.potions[6] = "minecraft:night_vision";
+ DataConverterPotionId.potions[7] = null;
+ DataConverterPotionId.potions[8] = "minecraft:weakness";
+ DataConverterPotionId.potions[9] = "minecraft:strength";
+ DataConverterPotionId.potions[10] = "minecraft:slowness";
+ DataConverterPotionId.potions[11] = "minecraft:leaping";
+ DataConverterPotionId.potions[12] = "minecraft:harming";
+ DataConverterPotionId.potions[13] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[14] = "minecraft:invisibility";
+ DataConverterPotionId.potions[15] = null;
+ DataConverterPotionId.potions[16] = "minecraft:awkward";
+ DataConverterPotionId.potions[17] = "minecraft:regeneration";
+ DataConverterPotionId.potions[18] = "minecraft:swiftness";
+ DataConverterPotionId.potions[19] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[20] = "minecraft:poison";
+ DataConverterPotionId.potions[21] = "minecraft:healing";
+ DataConverterPotionId.potions[22] = "minecraft:night_vision";
+ DataConverterPotionId.potions[23] = null;
+ DataConverterPotionId.potions[24] = "minecraft:weakness";
+ DataConverterPotionId.potions[25] = "minecraft:strength";
+ DataConverterPotionId.potions[26] = "minecraft:slowness";
+ DataConverterPotionId.potions[27] = "minecraft:leaping";
+ DataConverterPotionId.potions[28] = "minecraft:harming";
+ DataConverterPotionId.potions[29] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[30] = "minecraft:invisibility";
+ DataConverterPotionId.potions[31] = null;
+ DataConverterPotionId.potions[32] = "minecraft:thick";
+ DataConverterPotionId.potions[33] = "minecraft:strong_regeneration";
+ DataConverterPotionId.potions[34] = "minecraft:strong_swiftness";
+ DataConverterPotionId.potions[35] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[36] = "minecraft:strong_poison";
+ DataConverterPotionId.potions[37] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[38] = "minecraft:night_vision";
+ DataConverterPotionId.potions[39] = null;
+ DataConverterPotionId.potions[40] = "minecraft:weakness";
+ DataConverterPotionId.potions[41] = "minecraft:strong_strength";
+ DataConverterPotionId.potions[42] = "minecraft:slowness";
+ DataConverterPotionId.potions[43] = "minecraft:strong_leaping";
+ DataConverterPotionId.potions[44] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[45] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[46] = "minecraft:invisibility";
+ DataConverterPotionId.potions[47] = null;
+ DataConverterPotionId.potions[48] = null;
+ DataConverterPotionId.potions[49] = "minecraft:strong_regeneration";
+ DataConverterPotionId.potions[50] = "minecraft:strong_swiftness";
+ DataConverterPotionId.potions[51] = "minecraft:fire_resistance";
+ DataConverterPotionId.potions[52] = "minecraft:strong_poison";
+ DataConverterPotionId.potions[53] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[54] = "minecraft:night_vision";
+ DataConverterPotionId.potions[55] = null;
+ DataConverterPotionId.potions[56] = "minecraft:weakness";
+ DataConverterPotionId.potions[57] = "minecraft:strong_strength";
+ DataConverterPotionId.potions[58] = "minecraft:slowness";
+ DataConverterPotionId.potions[59] = "minecraft:strong_leaping";
+ DataConverterPotionId.potions[60] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[61] = "minecraft:water_breathing";
+ DataConverterPotionId.potions[62] = "minecraft:invisibility";
+ DataConverterPotionId.potions[63] = null;
+ DataConverterPotionId.potions[64] = "minecraft:mundane";
+ DataConverterPotionId.potions[65] = "minecraft:long_regeneration";
+ DataConverterPotionId.potions[66] = "minecraft:long_swiftness";
+ DataConverterPotionId.potions[67] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[68] = "minecraft:long_poison";
+ DataConverterPotionId.potions[69] = "minecraft:healing";
+ DataConverterPotionId.potions[70] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[71] = null;
+ DataConverterPotionId.potions[72] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[73] = "minecraft:long_strength";
+ DataConverterPotionId.potions[74] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[75] = "minecraft:long_leaping";
+ DataConverterPotionId.potions[76] = "minecraft:harming";
+ DataConverterPotionId.potions[77] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[78] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[79] = null;
+ DataConverterPotionId.potions[80] = "minecraft:awkward";
+ DataConverterPotionId.potions[81] = "minecraft:long_regeneration";
+ DataConverterPotionId.potions[82] = "minecraft:long_swiftness";
+ DataConverterPotionId.potions[83] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[84] = "minecraft:long_poison";
+ DataConverterPotionId.potions[85] = "minecraft:healing";
+ DataConverterPotionId.potions[86] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[87] = null;
+ DataConverterPotionId.potions[88] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[89] = "minecraft:long_strength";
+ DataConverterPotionId.potions[90] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[91] = "minecraft:long_leaping";
+ DataConverterPotionId.potions[92] = "minecraft:harming";
+ DataConverterPotionId.potions[93] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[94] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[95] = null;
+ DataConverterPotionId.potions[96] = "minecraft:thick";
+ DataConverterPotionId.potions[97] = "minecraft:regeneration";
+ DataConverterPotionId.potions[98] = "minecraft:swiftness";
+ DataConverterPotionId.potions[99] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[100] = "minecraft:poison";
+ DataConverterPotionId.potions[101] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[102] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[103] = null;
+ DataConverterPotionId.potions[104] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[105] = "minecraft:strength";
+ DataConverterPotionId.potions[106] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[107] = "minecraft:leaping";
+ DataConverterPotionId.potions[108] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[109] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[110] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[111] = null;
+ DataConverterPotionId.potions[112] = null;
+ DataConverterPotionId.potions[113] = "minecraft:regeneration";
+ DataConverterPotionId.potions[114] = "minecraft:swiftness";
+ DataConverterPotionId.potions[115] = "minecraft:long_fire_resistance";
+ DataConverterPotionId.potions[116] = "minecraft:poison";
+ DataConverterPotionId.potions[117] = "minecraft:strong_healing";
+ DataConverterPotionId.potions[118] = "minecraft:long_night_vision";
+ DataConverterPotionId.potions[119] = null;
+ DataConverterPotionId.potions[120] = "minecraft:long_weakness";
+ DataConverterPotionId.potions[121] = "minecraft:strength";
+ DataConverterPotionId.potions[122] = "minecraft:long_slowness";
+ DataConverterPotionId.potions[123] = "minecraft:leaping";
+ DataConverterPotionId.potions[124] = "minecraft:strong_harming";
+ DataConverterPotionId.potions[125] = "minecraft:long_water_breathing";
+ DataConverterPotionId.potions[126] = "minecraft:long_invisibility";
+ DataConverterPotionId.potions[127] = null;
+ }
+ }
+
+ private static class DataConverterSpawnEgg implements DataConverter {
+
+ private static final String[] eggs = new String[256];
+
+ DataConverterSpawnEgg() {}
+
+ public int getDataVersion() {
+ return 105;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:spawn_egg".equals(cmp.getString("id"))) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag");
+ short short0 = cmp.getShort("Damage");
+
+ if (!nbttagcompound2.containsKey("id", 8)) {
+ String s = DataConverterSpawnEgg.eggs[short0 & 255];
+
+ if (s != null) {
+ nbttagcompound2.putString("id", s);
+ nbttagcompound1.put("EntityTag", nbttagcompound2);
+ cmp.put("tag", nbttagcompound1);
+ }
+ }
+
+ if (short0 != 0) {
+ cmp.putShort("Damage", (short) 0);
+ }
+ }
+
+ return cmp;
+ }
+
+ static {
+
+ DataConverterSpawnEgg.eggs[1] = "Item";
+ DataConverterSpawnEgg.eggs[2] = "XPOrb";
+ DataConverterSpawnEgg.eggs[7] = "ThrownEgg";
+ DataConverterSpawnEgg.eggs[8] = "LeashKnot";
+ DataConverterSpawnEgg.eggs[9] = "Painting";
+ DataConverterSpawnEgg.eggs[10] = "Arrow";
+ DataConverterSpawnEgg.eggs[11] = "Snowball";
+ DataConverterSpawnEgg.eggs[12] = "Fireball";
+ DataConverterSpawnEgg.eggs[13] = "SmallFireball";
+ DataConverterSpawnEgg.eggs[14] = "ThrownEnderpearl";
+ DataConverterSpawnEgg.eggs[15] = "EyeOfEnderSignal";
+ DataConverterSpawnEgg.eggs[16] = "ThrownPotion";
+ DataConverterSpawnEgg.eggs[17] = "ThrownExpBottle";
+ DataConverterSpawnEgg.eggs[18] = "ItemFrame";
+ DataConverterSpawnEgg.eggs[19] = "WitherSkull";
+ DataConverterSpawnEgg.eggs[20] = "PrimedTnt";
+ DataConverterSpawnEgg.eggs[21] = "FallingSand";
+ DataConverterSpawnEgg.eggs[22] = "FireworksRocketEntity";
+ DataConverterSpawnEgg.eggs[23] = "TippedArrow";
+ DataConverterSpawnEgg.eggs[24] = "SpectralArrow";
+ DataConverterSpawnEgg.eggs[25] = "ShulkerBullet";
+ DataConverterSpawnEgg.eggs[26] = "DragonFireball";
+ DataConverterSpawnEgg.eggs[30] = "ArmorStand";
+ DataConverterSpawnEgg.eggs[41] = "Boat";
+ DataConverterSpawnEgg.eggs[42] = "MinecartRideable";
+ DataConverterSpawnEgg.eggs[43] = "MinecartChest";
+ DataConverterSpawnEgg.eggs[44] = "MinecartFurnace";
+ DataConverterSpawnEgg.eggs[45] = "MinecartTNT";
+ DataConverterSpawnEgg.eggs[46] = "MinecartHopper";
+ DataConverterSpawnEgg.eggs[47] = "MinecartSpawner";
+ DataConverterSpawnEgg.eggs[40] = "MinecartCommandBlock";
+ DataConverterSpawnEgg.eggs[48] = "Mob";
+ DataConverterSpawnEgg.eggs[49] = "Monster";
+ DataConverterSpawnEgg.eggs[50] = "Creeper";
+ DataConverterSpawnEgg.eggs[51] = "Skeleton";
+ DataConverterSpawnEgg.eggs[52] = "Spider";
+ DataConverterSpawnEgg.eggs[53] = "Giant";
+ DataConverterSpawnEgg.eggs[54] = "Zombie";
+ DataConverterSpawnEgg.eggs[55] = "Slime";
+ DataConverterSpawnEgg.eggs[56] = "Ghast";
+ DataConverterSpawnEgg.eggs[57] = "PigZombie";
+ DataConverterSpawnEgg.eggs[58] = "Enderman";
+ DataConverterSpawnEgg.eggs[59] = "CaveSpider";
+ DataConverterSpawnEgg.eggs[60] = "Silverfish";
+ DataConverterSpawnEgg.eggs[61] = "Blaze";
+ DataConverterSpawnEgg.eggs[62] = "LavaSlime";
+ DataConverterSpawnEgg.eggs[63] = "EnderDragon";
+ DataConverterSpawnEgg.eggs[64] = "WitherBoss";
+ DataConverterSpawnEgg.eggs[65] = "Bat";
+ DataConverterSpawnEgg.eggs[66] = "Witch";
+ DataConverterSpawnEgg.eggs[67] = "Endermite";
+ DataConverterSpawnEgg.eggs[68] = "Guardian";
+ DataConverterSpawnEgg.eggs[69] = "Shulker";
+ DataConverterSpawnEgg.eggs[90] = "Pig";
+ DataConverterSpawnEgg.eggs[91] = "Sheep";
+ DataConverterSpawnEgg.eggs[92] = "Cow";
+ DataConverterSpawnEgg.eggs[93] = "Chicken";
+ DataConverterSpawnEgg.eggs[94] = "Squid";
+ DataConverterSpawnEgg.eggs[95] = "Wolf";
+ DataConverterSpawnEgg.eggs[96] = "MushroomCow";
+ DataConverterSpawnEgg.eggs[97] = "SnowMan";
+ DataConverterSpawnEgg.eggs[98] = "Ozelot";
+ DataConverterSpawnEgg.eggs[99] = "VillagerGolem";
+ DataConverterSpawnEgg.eggs[100] = "EntityHorse";
+ DataConverterSpawnEgg.eggs[101] = "Rabbit";
+ DataConverterSpawnEgg.eggs[120] = "Villager";
+ DataConverterSpawnEgg.eggs[200] = "EnderCrystal";
+ }
+ }
+
+ private static class DataConverterMinecart implements DataConverter {
+
+ private static final List a = Lists.newArrayList(new String[] { "MinecartRideable", "MinecartChest", "MinecartFurnace", "MinecartTNT", "MinecartSpawner", "MinecartHopper", "MinecartCommandBlock"});
+
+ DataConverterMinecart() {}
+
+ public int getDataVersion() {
+ return 106;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Minecart".equals(cmp.getString("id"))) {
+ String s = "MinecartRideable";
+ int i = cmp.getInt("Type");
+
+ if (i > 0 && i < DataConverterMinecart.a.size()) {
+ s = DataConverterMinecart.a.get(i);
+ }
+
+ cmp.putString("id", s);
+ cmp.remove("Type");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterMobSpawner implements DataConverter {
+
+ DataConverterMobSpawner() {}
+
+ public int getDataVersion() {
+ return 107;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (!"MobSpawner".equals(cmp.getString("id"))) {
+ return cmp;
+ } else {
+ if (cmp.containsKey("EntityId", 8)) {
+ String s = cmp.getString("EntityId");
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("SpawnData");
+
+ nbttagcompound1.putString("id", s.isEmpty() ? "Pig" : s);
+ cmp.put("SpawnData", nbttagcompound1);
+ cmp.remove("EntityId");
+ }
+
+ if (cmp.containsKey("SpawnPotentials", 9)) {
+ ListTag nbttaglist = cmp.getList("SpawnPotentials", 10);
+
+ for (int i = 0; i < nbttaglist.size(); ++i) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompoundTag(i);
+
+ if (nbttagcompound2.containsKey("Type", 8)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound2.getCompound("Properties");
+
+ nbttagcompound3.putString("id", nbttagcompound2.getString("Type"));
+ nbttagcompound2.put("Entity", nbttagcompound3);
+ nbttagcompound2.remove("Type");
+ nbttagcompound2.remove("Properties");
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+ }
+
+ private static class DataConverterUUID implements DataConverter {
+
+ DataConverterUUID() {}
+
+ public int getDataVersion() {
+ return 108;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.containsKey("UUID", 8)) {
+ cmp.putUuid("UUID", UUID.fromString(cmp.getString("UUID")));
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterHealth implements DataConverter {
+
+ private static final Set a = Sets.newHashSet("ArmorStand", "Bat", "Blaze", "CaveSpider", "Chicken", "Cow", "Creeper", "EnderDragon", "Enderman", "Endermite", "EntityHorse", "Ghast", "Giant", "Guardian", "LavaSlime", "MushroomCow", "Ozelot", "Pig", "PigZombie", "Rabbit", "Sheep", "Shulker", "Silverfish", "Skeleton", "Slime", "SnowMan", "Spider", "Squid", "Villager", "VillagerGolem", "Witch", "WitherBoss", "Wolf", "Zombie");
+
+ DataConverterHealth() {}
+
+ public int getDataVersion() {
+ return 109;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (DataConverterHealth.a.contains(cmp.getString("id"))) {
+ float f;
+
+ if (cmp.containsKey("HealF", 99)) {
+ f = cmp.getFloat("HealF");
+ cmp.remove("HealF");
+ } else {
+ if (!cmp.containsKey("Health", 99)) {
+ return cmp;
+ }
+
+ f = cmp.getFloat("Health");
+ }
+
+ cmp.putFloat("Health", f);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterSaddle implements DataConverter {
+
+ DataConverterSaddle() {}
+
+ public int getDataVersion() {
+ return 110;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("EntityHorse".equals(cmp.getString("id")) && !cmp.containsKey("SaddleItem", 10) && cmp.getBoolean("Saddle")) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = new net.minecraft.nbt.CompoundTag();
+
+ nbttagcompound1.putString("id", "minecraft:saddle");
+ nbttagcompound1.putByte("Count", (byte) 1);
+ nbttagcompound1.putShort("Damage", (short) 0);
+ cmp.put("SaddleItem", nbttagcompound1);
+ cmp.remove("Saddle");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterHanging implements DataConverter {
+
+ DataConverterHanging() {}
+
+ public int getDataVersion() {
+ return 111;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = cmp.getString("id");
+ boolean flag = "Painting".equals(s);
+ boolean flag1 = "ItemFrame".equals(s);
+
+ if ((flag || flag1) && !cmp.containsKey("Facing", 99)) {
+ Direction enumdirection;
+
+ if (cmp.containsKey("Direction", 99)) {
+ enumdirection = Direction.fromHorizontal(cmp.getByte("Direction"));
+ cmp.putInt("TileX", cmp.getInt("TileX") + enumdirection.getOffsetX());
+ cmp.putInt("TileY", cmp.getInt("TileY") + enumdirection.getOffsetY());
+ cmp.putInt("TileZ", cmp.getInt("TileZ") + enumdirection.getOffsetZ());
+ cmp.remove("Direction");
+ if (flag1 && cmp.containsKey("ItemRotation", 99)) {
+ cmp.putByte("ItemRotation", (byte) (cmp.getByte("ItemRotation") * 2));
+ }
+ } else {
+ enumdirection = Direction.fromHorizontal(cmp.getByte("Dir"));
+ cmp.remove("Dir");
+ }
+
+ cmp.putByte("Facing", (byte) enumdirection.getHorizontal());
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterDropChances implements DataConverter {
+
+ DataConverterDropChances() {}
+
+ public int getDataVersion() {
+ return 113;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ ListTag nbttaglist;
+
+ if (cmp.containsKey("HandDropChances", 9)) {
+ nbttaglist = cmp.getList("HandDropChances", 5);
+ if (nbttaglist.size() == 2 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F) {
+ cmp.remove("HandDropChances");
+ }
+ }
+
+ if (cmp.containsKey("ArmorDropChances", 9)) {
+ nbttaglist = cmp.getList("ArmorDropChances", 5);
+ if (nbttaglist.size() == 4 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F && nbttaglist.getFloat(2) == 0.0F && nbttaglist.getFloat(3) == 0.0F) {
+ cmp.remove("ArmorDropChances");
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterRiding implements DataConverter {
+
+ DataConverterRiding() {}
+
+ public int getDataVersion() {
+ return 135;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ while (cmp.containsKey("Riding", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = this.b(cmp);
+
+ this.convert(cmp, nbttagcompound1);
+ cmp = nbttagcompound1;
+ }
+
+ return cmp;
+ }
+
+ protected void convert(net.minecraft.nbt.CompoundTag nbttagcompound, net.minecraft.nbt.CompoundTag nbttagcompound1) {
+ ListTag nbttaglist = new ListTag();
+
+ nbttaglist.add(nbttagcompound);
+ nbttagcompound1.put("Passengers", nbttaglist);
+ }
+
+ protected net.minecraft.nbt.CompoundTag b(net.minecraft.nbt.CompoundTag nbttagcompound) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Riding");
+
+ nbttagcompound.remove("Riding");
+ return nbttagcompound1;
+ }
+ }
+
+ private static class DataConverterBook implements DataConverter {
+
+ DataConverterBook() {}
+
+ public int getDataVersion() {
+ return 165;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:written_book".equals(cmp.getString("id"))) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.containsKey("pages", 9)) {
+ ListTag nbttaglist = nbttagcompound1.getList("pages", 8);
+
+ for (int i = 0; i < nbttaglist.size(); ++i) {
+ String s = nbttaglist.getString(i);
+ Object object = null;
+
+ if (!"null".equals(s) && !Strings.isNullOrEmpty(s)) {
+ if ((s.charAt(0) != 34 || s.charAt(s.length() - 1) != 34) && (s.charAt(0) != 123 || s.charAt(s.length() - 1) != 125)) {
+ object = new LiteralText(s);
+ } else {
+ try {
+ object = JsonHelper.deserialize(DataConverterSignText.a, s, Text.class, true);
+ if (object == null) {
+ object = new LiteralText("");
+ }
+ } catch (JsonParseException jsonparseexception) {
+ ;
+ }
+
+ if (object == null) {
+ try {
+ object = Text.Serializer.fromJson(s);
+ } catch (JsonParseException jsonparseexception1) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ try {
+ object = Text.Serializer.fromLenientJson(s);
+ } catch (JsonParseException jsonparseexception2) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ object = new LiteralText(s);
+ }
+ }
+ } else {
+ object = new LiteralText("");
+ }
+
+ nbttaglist.set(i, new StringTag(Text.Serializer.toJson((Text) object)));
+ }
+
+ nbttagcompound1.put("pages", nbttaglist);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterCookedFish implements DataConverter {
+
+ private static final Identifier a = new Identifier("cooked_fished");
+
+ DataConverterCookedFish() {}
+
+ public int getDataVersion() {
+ return 502;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.containsKey("id", 8) && DataConverterCookedFish.a.equals(new Identifier(cmp.getString("id")))) {
+ cmp.putString("id", "minecraft:cooked_fish");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterZombie implements DataConverter {
+
+ private static final Random a = new Random();
+
+ DataConverterZombie() {}
+
+ public int getDataVersion() {
+ return 502;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Zombie".equals(cmp.getString("id")) && cmp.getBoolean("IsVillager")) {
+ if (!cmp.containsKey("ZombieType", 99)) {
+ int i = -1;
+
+ if (cmp.containsKey("VillagerProfession", 99)) {
+ try {
+ i = this.convert(cmp.getInt("VillagerProfession"));
+ } catch (RuntimeException runtimeexception) {
+ ;
+ }
+ }
+
+ if (i == -1) {
+ i = this.convert(DataConverterZombie.a.nextInt(6));
+ }
+
+ cmp.putInt("ZombieType", i);
+ }
+
+ cmp.remove("IsVillager");
+ }
+
+ return cmp;
+ }
+
+ private int convert(int i) {
+ return i >= 0 && i < 6 ? i : -1;
+ }
+ }
+
+ private static class DataConverterVBO implements DataConverter {
+
+ DataConverterVBO() {}
+
+ public int getDataVersion() {
+ return 505;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ cmp.putString("useVbo", "true");
+ return cmp;
+ }
+ }
+
+ private static class DataConverterGuardian implements DataConverter {
+
+ DataConverterGuardian() {}
+
+ public int getDataVersion() {
+ return 700;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Guardian".equals(cmp.getString("id"))) {
+ if (cmp.getBoolean("Elder")) {
+ cmp.putString("id", "ElderGuardian");
+ }
+
+ cmp.remove("Elder");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterSkeleton implements DataConverter {
+
+ DataConverterSkeleton() {}
+
+ public int getDataVersion() {
+ return 701;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = cmp.getString("id");
+
+ if ("Skeleton".equals(s)) {
+ int i = cmp.getInt("SkeletonType");
+
+ if (i == 1) {
+ cmp.putString("id", "WitherSkeleton");
+ } else if (i == 2) {
+ cmp.putString("id", "Stray");
+ }
+
+ cmp.remove("SkeletonType");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterZombieType implements DataConverter {
+
+ DataConverterZombieType() {}
+
+ public int getDataVersion() {
+ return 702;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Zombie".equals(cmp.getString("id"))) {
+ int i = cmp.getInt("ZombieType");
+
+ switch (i) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ cmp.putString("id", "ZombieVillager");
+ cmp.putInt("Profession", i - 1);
+ break;
+ case 6:
+ cmp.putString("id", "Husk");
+ case 0:
+ default:
+ break;
+ }
+
+ cmp.remove("ZombieType");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterHorse implements DataConverter {
+
+ DataConverterHorse() {}
+
+ public int getDataVersion() {
+ return 703;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("EntityHorse".equals(cmp.getString("id"))) {
+ int i = cmp.getInt("Type");
+
+ switch (i) {
+ case 1:
+ cmp.putString("id", "Donkey");
+ break;
+
+ case 2:
+ cmp.putString("id", "Mule");
+ break;
+
+ case 3:
+ cmp.putString("id", "ZombieHorse");
+ break;
+
+ case 4:
+ cmp.putString("id", "SkeletonHorse");
+ break;
+
+ case 0:
+ default:
+ cmp.putString("id", "Horse");
+ break;
+ }
+
+ cmp.remove("Type");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterTileEntity implements DataConverter {
+
+ private static final Map a = Maps.newHashMap();
+
+ DataConverterTileEntity() {}
+
+ public int getDataVersion() {
+ return 704;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = DataConverterTileEntity.a.get(cmp.getString("id"));
+
+ if (s != null) {
+ cmp.putString("id", s);
+ }
+
+ return cmp;
+ }
+
+ static {
+ DataConverterTileEntity.a.put("Airportal", "minecraft:end_portal");
+ DataConverterTileEntity.a.put("Banner", "minecraft:banner");
+ DataConverterTileEntity.a.put("Beacon", "minecraft:beacon");
+ DataConverterTileEntity.a.put("Cauldron", "minecraft:brewing_stand");
+ DataConverterTileEntity.a.put("Chest", "minecraft:chest");
+ DataConverterTileEntity.a.put("Comparator", "minecraft:comparator");
+ DataConverterTileEntity.a.put("Control", "minecraft:command_block");
+ DataConverterTileEntity.a.put("DLDetector", "minecraft:daylight_detector");
+ DataConverterTileEntity.a.put("Dropper", "minecraft:dropper");
+ DataConverterTileEntity.a.put("EnchantTable", "minecraft:enchanting_table");
+ DataConverterTileEntity.a.put("EndGateway", "minecraft:end_gateway");
+ DataConverterTileEntity.a.put("EnderChest", "minecraft:ender_chest");
+ DataConverterTileEntity.a.put("FlowerPot", "minecraft:flower_pot");
+ DataConverterTileEntity.a.put("Furnace", "minecraft:furnace");
+ DataConverterTileEntity.a.put("Hopper", "minecraft:hopper");
+ DataConverterTileEntity.a.put("MobSpawner", "minecraft:mob_spawner");
+ DataConverterTileEntity.a.put("Music", "minecraft:noteblock");
+ DataConverterTileEntity.a.put("Piston", "minecraft:piston");
+ DataConverterTileEntity.a.put("RecordPlayer", "minecraft:jukebox");
+ DataConverterTileEntity.a.put("Sign", "minecraft:sign");
+ DataConverterTileEntity.a.put("Skull", "minecraft:skull");
+ DataConverterTileEntity.a.put("Structure", "minecraft:structure_block");
+ DataConverterTileEntity.a.put("Trap", "minecraft:dispenser");
+ }
+ }
+
+ private static class DataConverterEntity implements DataConverter {
+
+ private static final Map a = Maps.newHashMap();
+
+ DataConverterEntity() {}
+
+ public int getDataVersion() {
+ return 704;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = DataConverterEntity.a.get(cmp.getString("id"));
+
+ if (s != null) {
+ cmp.putString("id", s);
+ }
+
+ return cmp;
+ }
+
+ static {
+ DataConverterEntity.a.put("AreaEffectCloud", "minecraft:area_effect_cloud");
+ DataConverterEntity.a.put("ArmorStand", "minecraft:armor_stand");
+ DataConverterEntity.a.put("Arrow", "minecraft:arrow");
+ DataConverterEntity.a.put("Bat", "minecraft:bat");
+ DataConverterEntity.a.put("Blaze", "minecraft:blaze");
+ DataConverterEntity.a.put("Boat", "minecraft:boat");
+ DataConverterEntity.a.put("CaveSpider", "minecraft:cave_spider");
+ DataConverterEntity.a.put("Chicken", "minecraft:chicken");
+ DataConverterEntity.a.put("Cow", "minecraft:cow");
+ DataConverterEntity.a.put("Creeper", "minecraft:creeper");
+ DataConverterEntity.a.put("Donkey", "minecraft:donkey");
+ DataConverterEntity.a.put("DragonFireball", "minecraft:dragon_fireball");
+ DataConverterEntity.a.put("ElderGuardian", "minecraft:elder_guardian");
+ DataConverterEntity.a.put("EnderCrystal", "minecraft:ender_crystal");
+ DataConverterEntity.a.put("EnderDragon", "minecraft:ender_dragon");
+ DataConverterEntity.a.put("Enderman", "minecraft:enderman");
+ DataConverterEntity.a.put("Endermite", "minecraft:endermite");
+ DataConverterEntity.a.put("EyeOfEnderSignal", "minecraft:eye_of_ender_signal");
+ DataConverterEntity.a.put("FallingSand", "minecraft:falling_block");
+ DataConverterEntity.a.put("Fireball", "minecraft:fireball");
+ DataConverterEntity.a.put("FireworksRocketEntity", "minecraft:fireworks_rocket");
+ DataConverterEntity.a.put("Ghast", "minecraft:ghast");
+ DataConverterEntity.a.put("Giant", "minecraft:giant");
+ DataConverterEntity.a.put("Guardian", "minecraft:guardian");
+ DataConverterEntity.a.put("Horse", "minecraft:horse");
+ DataConverterEntity.a.put("Husk", "minecraft:husk");
+ DataConverterEntity.a.put("Item", "minecraft:item");
+ DataConverterEntity.a.put("ItemFrame", "minecraft:item_frame");
+ DataConverterEntity.a.put("LavaSlime", "minecraft:magma_cube");
+ DataConverterEntity.a.put("LeashKnot", "minecraft:leash_knot");
+ DataConverterEntity.a.put("MinecartChest", "minecraft:chest_minecart");
+ DataConverterEntity.a.put("MinecartCommandBlock", "minecraft:commandblock_minecart");
+ DataConverterEntity.a.put("MinecartFurnace", "minecraft:furnace_minecart");
+ DataConverterEntity.a.put("MinecartHopper", "minecraft:hopper_minecart");
+ DataConverterEntity.a.put("MinecartRideable", "minecraft:minecart");
+ DataConverterEntity.a.put("MinecartSpawner", "minecraft:spawner_minecart");
+ DataConverterEntity.a.put("MinecartTNT", "minecraft:tnt_minecart");
+ DataConverterEntity.a.put("Mule", "minecraft:mule");
+ DataConverterEntity.a.put("MushroomCow", "minecraft:mooshroom");
+ DataConverterEntity.a.put("Ozelot", "minecraft:ocelot");
+ DataConverterEntity.a.put("Painting", "minecraft:painting");
+ DataConverterEntity.a.put("Pig", "minecraft:pig");
+ DataConverterEntity.a.put("PigZombie", "minecraft:zombie_pigman");
+ DataConverterEntity.a.put("PolarBear", "minecraft:polar_bear");
+ DataConverterEntity.a.put("PrimedTnt", "minecraft:tnt");
+ DataConverterEntity.a.put("Rabbit", "minecraft:rabbit");
+ DataConverterEntity.a.put("Sheep", "minecraft:sheep");
+ DataConverterEntity.a.put("Shulker", "minecraft:shulker");
+ DataConverterEntity.a.put("ShulkerBullet", "minecraft:shulker_bullet");
+ DataConverterEntity.a.put("Silverfish", "minecraft:silverfish");
+ DataConverterEntity.a.put("Skeleton", "minecraft:skeleton");
+ DataConverterEntity.a.put("SkeletonHorse", "minecraft:skeleton_horse");
+ DataConverterEntity.a.put("Slime", "minecraft:slime");
+ DataConverterEntity.a.put("SmallFireball", "minecraft:small_fireball");
+ DataConverterEntity.a.put("SnowMan", "minecraft:snowman");
+ DataConverterEntity.a.put("Snowball", "minecraft:snowball");
+ DataConverterEntity.a.put("SpectralArrow", "minecraft:spectral_arrow");
+ DataConverterEntity.a.put("Spider", "minecraft:spider");
+ DataConverterEntity.a.put("Squid", "minecraft:squid");
+ DataConverterEntity.a.put("Stray", "minecraft:stray");
+ DataConverterEntity.a.put("ThrownEgg", "minecraft:egg");
+ DataConverterEntity.a.put("ThrownEnderpearl", "minecraft:ender_pearl");
+ DataConverterEntity.a.put("ThrownExpBottle", "minecraft:xp_bottle");
+ DataConverterEntity.a.put("ThrownPotion", "minecraft:potion");
+ DataConverterEntity.a.put("Villager", "minecraft:villager");
+ DataConverterEntity.a.put("VillagerGolem", "minecraft:villager_golem");
+ DataConverterEntity.a.put("Witch", "minecraft:witch");
+ DataConverterEntity.a.put("WitherBoss", "minecraft:wither");
+ DataConverterEntity.a.put("WitherSkeleton", "minecraft:wither_skeleton");
+ DataConverterEntity.a.put("WitherSkull", "minecraft:wither_skull");
+ DataConverterEntity.a.put("Wolf", "minecraft:wolf");
+ DataConverterEntity.a.put("XPOrb", "minecraft:xp_orb");
+ DataConverterEntity.a.put("Zombie", "minecraft:zombie");
+ DataConverterEntity.a.put("ZombieHorse", "minecraft:zombie_horse");
+ DataConverterEntity.a.put("ZombieVillager", "minecraft:zombie_villager");
+ }
+ }
+
+ private static class DataConverterPotionWater implements DataConverter {
+
+ DataConverterPotionWater() {}
+
+ public int getDataVersion() {
+ return 806;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ String s = cmp.getString("id");
+
+ if ("minecraft:potion".equals(s) || "minecraft:splash_potion".equals(s) || "minecraft:lingering_potion".equals(s) || "minecraft:tipped_arrow".equals(s)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (!nbttagcompound1.containsKey("Potion", 8)) {
+ nbttagcompound1.putString("Potion", "minecraft:water");
+ }
+
+ if (!cmp.containsKey("tag", 10)) {
+ cmp.put("tag", nbttagcompound1);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterShulker implements DataConverter {
+
+ DataConverterShulker() {}
+
+ public int getDataVersion() {
+ return 808;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:shulker".equals(cmp.getString("id")) && !cmp.containsKey("Color", 99)) {
+ cmp.putByte("Color", (byte) 10);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterShulkerBoxItem implements DataConverter {
+
+ public static final String[] a = new String[] { "minecraft:white_shulker_box", "minecraft:orange_shulker_box", "minecraft:magenta_shulker_box", "minecraft:light_blue_shulker_box", "minecraft:yellow_shulker_box", "minecraft:lime_shulker_box", "minecraft:pink_shulker_box", "minecraft:gray_shulker_box", "minecraft:silver_shulker_box", "minecraft:cyan_shulker_box", "minecraft:purple_shulker_box", "minecraft:blue_shulker_box", "minecraft:brown_shulker_box", "minecraft:green_shulker_box", "minecraft:red_shulker_box", "minecraft:black_shulker_box"};
+
+ DataConverterShulkerBoxItem() {}
+
+ public int getDataVersion() {
+ return 813;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:shulker_box".equals(cmp.getString("id")) && cmp.containsKey("tag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag");
+
+ if (nbttagcompound1.containsKey("BlockEntityTag", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag");
+
+ if (nbttagcompound2.getList("Items", 10).isEmpty()) {
+ nbttagcompound2.remove("Items");
+ }
+
+ int i = nbttagcompound2.getInt("Color");
+
+ nbttagcompound2.remove("Color");
+ if (nbttagcompound2.isEmpty()) {
+ nbttagcompound1.remove("BlockEntityTag");
+ }
+
+ if (nbttagcompound1.isEmpty()) {
+ cmp.remove("tag");
+ }
+
+ cmp.putString("id", DataConverterShulkerBoxItem.a[i % 16]);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterShulkerBoxBlock implements DataConverter {
+
+ DataConverterShulkerBoxBlock() {}
+
+ public int getDataVersion() {
+ return 813;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:shulker".equals(cmp.getString("id"))) {
+ cmp.remove("Color");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterLang implements DataConverter {
+
+ DataConverterLang() {}
+
+ public int getDataVersion() {
+ return 816;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if (cmp.containsKey("lang", 8)) {
+ cmp.putString("lang", cmp.getString("lang").toLowerCase(Locale.ROOT));
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterTotem implements DataConverter {
+
+ DataConverterTotem() {}
+
+ public int getDataVersion() {
+ return 820;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:totem".equals(cmp.getString("id"))) {
+ cmp.putString("id", "minecraft:totem_of_undying");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterBedBlock implements DataConverter {
+
+ private static final Logger a = LogManager.getLogger(FabricDataFixer.class);
+
+ DataConverterBedBlock() {}
+
+ public int getDataVersion() {
+ return 1125;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ boolean flag = true;
+
+ try {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level");
+ int i = nbttagcompound1.getInt("xPos");
+ int j = nbttagcompound1.getInt("zPos");
+ ListTag nbttaglist = nbttagcompound1.getList("TileEntities", 10);
+ ListTag nbttaglist1 = nbttagcompound1.getList("Sections", 10);
+
+ for (int k = 0; k < nbttaglist1.size(); ++k) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist1.getCompoundTag(k);
+ byte b0 = nbttagcompound2.getByte("Y");
+ byte[] abyte = nbttagcompound2.getByteArray("Blocks");
+
+ for (int l = 0; l < abyte.length; ++l) {
+ if (416 == (abyte[l] & 255) << 4) {
+ int i1 = l & 15;
+ int j1 = l >> 8 & 15;
+ int k1 = l >> 4 & 15;
+ net.minecraft.nbt.CompoundTag nbttagcompound3 = new net.minecraft.nbt.CompoundTag();
+
+ nbttagcompound3.putString("id", "bed");
+ nbttagcompound3.putInt("x", i1 + (i << 4));
+ nbttagcompound3.putInt("y", j1 + (b0 << 4));
+ nbttagcompound3.putInt("z", k1 + (j << 4));
+ nbttaglist.add(nbttagcompound3);
+ }
+ }
+ }
+ } catch (Exception exception) {
+ DataConverterBedBlock.a.warn("Unable to datafix Bed blocks, level format may be missing tags.");
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterBedItem implements DataConverter {
+
+ DataConverterBedItem() {}
+
+ public int getDataVersion() {
+ return 1125;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("minecraft:bed".equals(cmp.getString("id")) && cmp.getShort("Damage") == 0) {
+ cmp.putShort("Damage", (short) DyeColor.RED.getId());
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataConverterSignText implements DataConverter {
+
+ public static final Gson a = new GsonBuilder().registerTypeAdapter(Text.class, new JsonDeserializer() {
+ Text a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException {
+ if (jsonelement.isJsonPrimitive()) {
+ return new LiteralText(jsonelement.getAsString());
+ } else if (jsonelement.isJsonArray()) {
+ JsonArray jsonarray = jsonelement.getAsJsonArray();
+ Text iTextComponent = null;
+ Iterator iterator = jsonarray.iterator();
+
+ while (iterator.hasNext()) {
+ JsonElement jsonelement1 = (JsonElement) iterator.next();
+ Text iTextComponent1 = this.a(jsonelement1, jsonelement1.getClass(), jsondeserializationcontext);
+
+ if (iTextComponent == null) {
+ iTextComponent = iTextComponent1;
+ } else {
+ iTextComponent.append(iTextComponent1);
+ }
+ }
+
+ return iTextComponent;
+ } else {
+ throw new JsonParseException("Don\'t know how to turn " + jsonelement + " into a Component");
+ }
+ }
+
+ public Object deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException {
+ return this.a(jsonelement, type, jsondeserializationcontext);
+ }
+ }).create();
+
+ DataConverterSignText() {}
+
+ public int getDataVersion() {
+ return 101;
+ }
+
+ public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) {
+ if ("Sign".equals(cmp.getString("id"))) {
+ this.convert(cmp, "Text1");
+ this.convert(cmp, "Text2");
+ this.convert(cmp, "Text3");
+ this.convert(cmp, "Text4");
+ }
+
+ return cmp;
+ }
+
+ private void convert(net.minecraft.nbt.CompoundTag nbttagcompound, String s) {
+ String s1 = nbttagcompound.getString(s);
+ Object object = null;
+
+ if (!"null".equals(s1) && !Strings.isNullOrEmpty(s1)) {
+ if ((s1.charAt(0) != 34 || s1.charAt(s1.length() - 1) != 34) && (s1.charAt(0) != 123 || s1.charAt(s1.length() - 1) != 125)) {
+ object = new LiteralText(s1);
+ } else {
+ try {
+ object = JsonHelper.deserialize(DataConverterSignText.a, s1, Text.class, true);
+ if (object == null) {
+ object = new LiteralText("");
+ }
+ } catch (JsonParseException jsonparseexception) {
+ ;
+ }
+
+ if (object == null) {
+ try {
+ object = Text.Serializer.fromJson(s1);
+ } catch (JsonParseException jsonparseexception1) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ try {
+ object = Text.Serializer.fromLenientJson(s1);
+ } catch (JsonParseException jsonparseexception2) {
+ ;
+ }
+ }
+
+ if (object == null) {
+ object = new LiteralText(s1);
+ }
+ }
+ } else {
+ object = new LiteralText("");
+ }
+
+ nbttagcompound.putString(s, Text.Serializer.toJson((Text) object));
+ }
+ }
+
+ private static class DataInspectorPlayerVehicle implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.containsKey("RootVehicle", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("RootVehicle");
+
+ if (nbttagcompound1.containsKey("Entity", 10)) {
+ convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer);
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorLevelPlayer implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.containsKey("Player", 10)) {
+ convertCompound(LegacyType.PLAYER, cmp, "Player", sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorStructure implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ ListTag nbttaglist;
+ int j;
+ net.minecraft.nbt.CompoundTag nbttagcompound1;
+
+ if (cmp.containsKey("entities", 9)) {
+ nbttaglist = cmp.getList("entities", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j);
+ if (nbttagcompound1.containsKey("nbt", 10)) {
+ convertCompound(LegacyType.ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer);
+ }
+ }
+ }
+
+ if (cmp.containsKey("blocks", 9)) {
+ nbttaglist = cmp.getList("blocks", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j);
+ if (nbttagcompound1.containsKey("nbt", 10)) {
+ convertCompound(LegacyType.BLOCK_ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer);
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorChunks implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.containsKey("Level", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level");
+ ListTag nbttaglist;
+ int j;
+
+ if (nbttagcompound1.containsKey("Entities", 9)) {
+ nbttaglist = nbttagcompound1.getList("Entities", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.set(j, convert(LegacyType.ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer));
+ }
+ }
+
+ if (nbttagcompound1.containsKey("TileEntities", 9)) {
+ nbttaglist = nbttagcompound1.getList("TileEntities", 10);
+
+ for (j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.set(j, convert(LegacyType.BLOCK_ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer));
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorEntityPassengers implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (cmp.containsKey("Passengers", 9)) {
+ ListTag nbttaglist = cmp.getList("Passengers", 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ nbttaglist.set(j, convert(LegacyType.ENTITY, nbttaglist.getCompoundTag(j), sourceVer, targetVer));
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorPlayer implements DataInspector {
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ convertItems(cmp, "Inventory", sourceVer, targetVer);
+ convertItems(cmp, "EnderItems", sourceVer, targetVer);
+ if (cmp.containsKey("ShoulderEntityLeft", 10)) {
+ convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityLeft", sourceVer, targetVer);
+ }
+
+ if (cmp.containsKey("ShoulderEntityRight", 10)) {
+ convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityRight", sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorVillagers implements DataInspector {
+ Identifier entityVillager = getKey("EntityVillager");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (entityVillager.equals(new Identifier(cmp.getString("id"))) && cmp.containsKey("Offers", 10)) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Offers");
+
+ if (nbttagcompound1.containsKey("Recipes", 9)) {
+ ListTag nbttaglist = nbttagcompound1.getList("Recipes", 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompoundTag(j);
+
+ convertItem(nbttagcompound2, "buy", sourceVer, targetVer);
+ convertItem(nbttagcompound2, "buyB", sourceVer, targetVer);
+ convertItem(nbttagcompound2, "sell", sourceVer, targetVer);
+ nbttaglist.set(j, nbttagcompound2);
+ }
+ }
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorMobSpawnerMinecart implements DataInspector {
+ Identifier entityMinecartMobSpawner = getKey("EntityMinecartMobSpawner");
+ Identifier tileEntityMobSpawner = getKey("TileEntityMobSpawner");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ String s = cmp.getString("id");
+ if (entityMinecartMobSpawner.equals(new Identifier(s))) {
+ cmp.putString("id", tileEntityMobSpawner.toString());
+ convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer);
+ cmp.putString("id", s);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorMobSpawnerMobs implements DataInspector {
+ Identifier tileEntityMobSpawner = getKey("TileEntityMobSpawner");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (tileEntityMobSpawner.equals(new Identifier(cmp.getString("id")))) {
+ if (cmp.containsKey("SpawnPotentials", 9)) {
+ ListTag nbttaglist = cmp.getList("SpawnPotentials", 10);
+
+ for (int j = 0; j < nbttaglist.size(); ++j) {
+ net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttaglist.getCompoundTag(j);
+
+ convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer);
+ }
+ }
+
+ convertCompound(LegacyType.ENTITY, cmp, "SpawnData", sourceVer, targetVer);
+ }
+
+ return cmp;
+ }
+ }
+
+ private static class DataInspectorCommandBlock implements DataInspector {
+ Identifier tileEntityCommand = getKey("TileEntityCommand");
+
+ @Override
+ public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) {
+ if (tileEntityCommand.equals(new Identifier(cmp.getString("id")))) {
+ cmp.putString("id", "Control");
+ convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer);
+ cmp.putString("id", "MinecartCommandBlock");
+ }
+
+ return cmp;
+ }
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java
new file mode 100644
index 000000000..d1fb75a04
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java
@@ -0,0 +1,116 @@
+/*
+ * 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.fabric;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.entity.Entity;
+import com.sk89q.worldedit.entity.metadata.EntityProperties;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.util.Location;
+import com.sk89q.worldedit.world.NullWorld;
+import com.sk89q.worldedit.world.entity.EntityTypes;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import java.lang.ref.WeakReference;
+
+import javax.annotation.Nullable;
+
+class FabricEntity implements Entity {
+
+ private final WeakReference entityRef;
+
+ FabricEntity(net.minecraft.entity.Entity entity) {
+ checkNotNull(entity);
+ this.entityRef = new WeakReference<>(entity);
+ }
+
+ @Override
+ public BaseEntity getState() {
+ net.minecraft.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ Identifier id = Registry.ENTITY_TYPE.getId(entity.getType());
+ CompoundTag tag = new CompoundTag();
+ entity.toTag(tag);
+ return new BaseEntity(EntityTypes.get(id.toString()), NBTConverter.fromNative(tag));
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Location getLocation() {
+ net.minecraft.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ Vector3 position = Vector3.at(entity.x, entity.y, entity.z);
+ float yaw = entity.yaw;
+ float pitch = entity.pitch;
+
+ return new Location(FabricAdapter.adapt(entity.world), position, yaw, pitch);
+ } else {
+ return new Location(NullWorld.getInstance());
+ }
+ }
+
+ @Override
+ public boolean setLocation(Location location) {
+ // TODO unused atm
+ return false;
+ }
+
+ @Override
+ public Extent getExtent() {
+ net.minecraft.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ return FabricAdapter.adapt(entity.world);
+ } else {
+ return NullWorld.getInstance();
+ }
+ }
+
+ @Override
+ public boolean remove() {
+ net.minecraft.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ entity.remove();
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Nullable
+ @Override
+ public T getFacet(Class extends T> cls) {
+ net.minecraft.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ if (EntityProperties.class.isAssignableFrom(cls)) {
+ return (T) new FabricEntityProperties(entity);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntityProperties.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntityProperties.java
new file mode 100644
index 000000000..eafbe5c94
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntityProperties.java
@@ -0,0 +1,151 @@
+/*
+ * 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.fabric;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.sk89q.worldedit.entity.metadata.EntityProperties;
+import net.minecraft.entity.EnderEyeEntity;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.ExperienceOrbEntity;
+import net.minecraft.entity.FallingBlockEntity;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.entity.Npc;
+import net.minecraft.entity.TntEntity;
+import net.minecraft.entity.boss.dragon.EnderDragonEntity;
+import net.minecraft.entity.decoration.ArmorStandEntity;
+import net.minecraft.entity.decoration.ItemFrameEntity;
+import net.minecraft.entity.decoration.painting.PaintingEntity;
+import net.minecraft.entity.mob.AmbientEntity;
+import net.minecraft.entity.mob.MobEntity;
+import net.minecraft.entity.passive.AnimalEntity;
+import net.minecraft.entity.passive.TameableEntity;
+import net.minecraft.entity.passive.GolemEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.projectile.Projectile;
+import net.minecraft.entity.vehicle.AbstractMinecartEntity;
+import net.minecraft.entity.vehicle.BoatEntity;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.village.Trader;
+
+public class FabricEntityProperties implements EntityProperties {
+
+ private final Entity entity;
+
+ public FabricEntityProperties(Entity entity) {
+ checkNotNull(entity);
+ this.entity = entity;
+ }
+
+ @Override
+ public boolean isPlayerDerived() {
+ return entity instanceof PlayerEntity;
+ }
+
+ @Override
+ public boolean isProjectile() {
+ return entity instanceof EnderEyeEntity || entity instanceof Projectile;
+ }
+
+ @Override
+ public boolean isItem() {
+ return entity instanceof ItemEntity;
+ }
+
+ @Override
+ public boolean isFallingBlock() {
+ return entity instanceof FallingBlockEntity;
+ }
+
+ @Override
+ public boolean isPainting() {
+ return entity instanceof PaintingEntity;
+ }
+
+ @Override
+ public boolean isItemFrame() {
+ return entity instanceof ItemFrameEntity;
+ }
+
+ @Override
+ public boolean isBoat() {
+ return entity instanceof BoatEntity;
+ }
+
+ @Override
+ public boolean isMinecart() {
+ return entity instanceof AbstractMinecartEntity;
+ }
+
+ @Override
+ public boolean isTNT() {
+ return entity instanceof TntEntity;
+ }
+
+ @Override
+ public boolean isExperienceOrb() {
+ return entity instanceof ExperienceOrbEntity;
+ }
+
+ @Override
+ public boolean isLiving() {
+ return entity instanceof MobEntity;
+ }
+
+ @Override
+ public boolean isAnimal() {
+ return entity instanceof AnimalEntity;
+ }
+
+ @Override
+ public boolean isAmbient() {
+ return entity instanceof AmbientEntity;
+ }
+
+ @Override
+ public boolean isNPC() {
+ return entity instanceof Npc || entity instanceof Trader;
+ }
+
+ @Override
+ public boolean isGolem() {
+ return entity instanceof GolemEntity;
+ }
+
+ @Override
+ public boolean isTamed() {
+ return entity instanceof TameableEntity && ((TameableEntity) entity).isTamed();
+ }
+
+ @Override
+ public boolean isTagged() {
+ return entity.hasCustomName();
+ }
+
+ @Override
+ public boolean isArmorStand() {
+ return entity instanceof ArmorStandEntity;
+ }
+
+ @Override
+ public boolean isPasteable() {
+ return !(entity instanceof ServerPlayerEntity || entity instanceof EnderDragonEntity);
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java
new file mode 100644
index 000000000..bcdc7a0c2
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java
@@ -0,0 +1,40 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.fabric;
+
+import com.sk89q.worldedit.world.item.ItemType;
+import com.sk89q.worldedit.world.registry.ItemCategoryRegistry;
+import net.minecraft.tag.ItemTags;
+import net.minecraft.tag.Tag;
+import net.minecraft.util.Identifier;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class FabricItemCategoryRegistry implements ItemCategoryRegistry {
+ @Override
+ public Set getCategorisedByName(String category) {
+ return Optional.ofNullable(ItemTags.getContainer().get(new Identifier(category)))
+ .map(Tag::values).orElse(Collections.emptySet())
+ .stream().map(FabricAdapter::adapt).collect(Collectors.toSet());
+ }
+}
diff --git a/worldedit-bukkit/src/test/java/com/sk89q/worldedit/bukkit/ItemRegistryTest.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemRegistry.java
similarity index 56%
rename from worldedit-bukkit/src/test/java/com/sk89q/worldedit/bukkit/ItemRegistryTest.java
rename to worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemRegistry.java
index d8e6a8f91..e3497234f 100644
--- a/worldedit-bukkit/src/test/java/com/sk89q/worldedit/bukkit/ItemRegistryTest.java
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemRegistry.java
@@ -17,23 +17,26 @@
* along with this program. If not, see .
*/
-package com.sk89q.worldedit.bukkit;
+package com.sk89q.worldedit.fabric;
import com.sk89q.worldedit.world.item.ItemType;
-import org.bukkit.Material;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
+import com.sk89q.worldedit.world.registry.BundledItemRegistry;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.item.Item;
-public class ItemRegistryTest {
+import javax.annotation.Nullable;
- @Test
- public void testItemRegistry() {
- for (Material material : Material.values()) {
- if (material.isItem() && !material.isLegacy()) {
- ItemType.REGISTRY.register(material.getKey().toString(), new ItemType(material.getKey().toString()));
- }
+public class FabricItemRegistry extends BundledItemRegistry {
+
+ @Nullable
+ @Override
+ public String getName(ItemType itemType) {
+ if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
+ final Item item = FabricAdapter.adapt(itemType);
+ return I18n.translate(item.getTranslationKey());
}
- Assertions.assertNotNull(ItemType.REGISTRY.get("minecraft:wooden_axe"));
+ return super.getName(itemType);
}
-
}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPermissionsProvider.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPermissionsProvider.java
new file mode 100644
index 000000000..68ff54bef
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPermissionsProvider.java
@@ -0,0 +1,50 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * Copyright (C) WorldEdit team and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.sk89q.worldedit.fabric;
+
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.world.GameMode;
+
+public interface FabricPermissionsProvider {
+
+ boolean hasPermission(ServerPlayerEntity player, String permission);
+
+ void registerPermission(String permission);
+
+ class VanillaPermissionsProvider implements FabricPermissionsProvider {
+
+ private FabricPlatform platform;
+
+ public VanillaPermissionsProvider(FabricPlatform platform) {
+ this.platform = platform;
+ }
+
+ @Override
+ public boolean hasPermission(ServerPlayerEntity player, String permission) {
+ FabricConfiguration configuration = platform.getConfiguration();
+ return configuration.cheatMode ||
+ player.server.getPlayerManager().isOperator(player.getGameProfile()) ||
+ (configuration.creativeEnable && player.interactionManager.getGameMode() == GameMode.CREATIVE);
+ }
+
+ @Override
+ public void registerPermission(String permission) {}
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java
index f67fdcc59..beb6d5208 100644
--- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java
@@ -26,7 +26,7 @@ import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.MultiUserPlatform;
import com.sk89q.worldedit.extension.platform.Preference;
-import com.sk89q.worldedit.fabric.mixin.MixinMinecraftServer;
+import com.sk89q.worldedit.extension.platform.Watchdog;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.registry.Registries;
@@ -58,7 +58,7 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform {
private final FabricWorldEdit mod;
private final MinecraftServer server;
private final FabricDataFixer dataFixer;
- private final @Nullable FabricWatchdog watchdog;
+ private final @Nullable Watchdog watchdog;
private boolean hookingEvents = false;
FabricPlatform(FabricWorldEdit mod, MinecraftServer server) {
@@ -66,7 +66,7 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform {
this.server = server;
this.dataFixer = new FabricDataFixer(getDataVersion());
this.watchdog = server instanceof MinecraftDedicatedServer
- ? new FabricWatchdog((MinecraftDedicatedServer) server) : null;
+ ? (Watchdog) server : null;
}
boolean isHookingEvents() {
@@ -105,7 +105,7 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform {
@Override
@Nullable
- public FabricWatchdog getWatchdog() {
+ public Watchdog getWatchdog() {
return watchdog;
}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java
new file mode 100644
index 000000000..60003a259
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java
@@ -0,0 +1,281 @@
+/*
+ * 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.fabric;
+
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.util.StringUtil;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.extension.platform.AbstractPlayerActor;
+import com.sk89q.worldedit.extent.inventory.BlockBag;
+import com.sk89q.worldedit.fabric.net.handler.WECUIPacketHandler;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.session.SessionKey;
+import com.sk89q.worldedit.util.HandSide;
+import com.sk89q.worldedit.util.Location;
+import com.sk89q.worldedit.util.formatting.WorldEditText;
+import com.sk89q.worldedit.util.formatting.text.Component;
+import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer;
+import com.sk89q.worldedit.world.World;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import com.sk89q.worldedit.world.block.BlockStateHolder;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import io.netty.buffer.Unpooled;
+import net.minecraft.block.Block;
+import net.minecraft.client.network.packet.BlockEntityUpdateS2CPacket;
+import net.minecraft.client.network.packet.BlockUpdateS2CPacket;
+import net.minecraft.client.network.packet.CustomPayloadS2CPacket;
+import net.minecraft.item.ItemStack;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.text.LiteralText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Hand;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.PacketByteBuf;
+import net.minecraft.util.math.BlockPos;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.util.UUID;
+
+public class FabricPlayer extends AbstractPlayerActor {
+
+ // see ClientPlayNetHandler: search for "invalid update packet", lots of hardcoded consts
+ private static final int STRUCTURE_BLOCK_PACKET_ID = 7;
+ private final ServerPlayerEntity player;
+
+ protected FabricPlayer(ServerPlayerEntity player) {
+ this.player = player;
+ ThreadSafeCache.getInstance().getOnlineIds().add(getUniqueId());
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return player.getUuid();
+ }
+
+ @Override
+ public BaseItemStack getItemInHand(HandSide handSide) {
+ ItemStack is = this.player.getStackInHand(handSide == HandSide.MAIN_HAND ? Hand.MAIN_HAND : Hand.OFF_HAND);
+ return FabricAdapter.adapt(is);
+ }
+
+ @Override
+ public String getName() {
+ return this.player.getName().asFormattedString();
+ }
+
+ @Override
+ public BaseEntity getState() {
+ throw new UnsupportedOperationException("Cannot create a state from this object");
+ }
+
+ @Override
+ public Location getLocation() {
+ Vector3 position = Vector3.at(this.player.x, this.player.y, this.player.z);
+ return new Location(
+ FabricWorldEdit.inst.getWorld(this.player.world),
+ position,
+ this.player.yaw,
+ this.player.pitch);
+ }
+
+ @Override
+ public boolean setLocation(Location location) {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public World getWorld() {
+ return FabricWorldEdit.inst.getWorld(this.player.world);
+ }
+
+ @Override
+ public void giveItem(BaseItemStack itemStack) {
+ this.player.inventory.insertStack(FabricAdapter.adapt(itemStack));
+ }
+
+ @Override
+ public void dispatchCUIEvent(CUIEvent event) {
+ String[] params = event.getParameters();
+ String send = event.getTypeId();
+ if (params.length > 0) {
+ send = send + "|" + StringUtil.joinString(params, "|");
+ }
+ PacketByteBuf buffer = new PacketByteBuf(Unpooled.copiedBuffer(send.getBytes(WECUIPacketHandler.UTF_8_CHARSET)));
+ CustomPayloadS2CPacket packet = new CustomPayloadS2CPacket(new Identifier(FabricWorldEdit.MOD_ID, FabricWorldEdit.CUI_PLUGIN_CHANNEL), buffer);
+ this.player.networkHandler.sendPacket(packet);
+ }
+
+ @Override
+ public void printRaw(String msg) {
+ for (String part : msg.split("\n")) {
+ this.player.sendMessage(new LiteralText(part));
+ }
+ }
+
+ @Override
+ public void printDebug(String msg) {
+ sendColorized(msg, Formatting.GRAY);
+ }
+
+ @Override
+ public void print(String msg) {
+ sendColorized(msg, Formatting.LIGHT_PURPLE);
+ }
+
+ @Override
+ public void printError(String msg) {
+ sendColorized(msg, Formatting.RED);
+ }
+
+ @Override
+ public void print(Component component) {
+ this.player.sendMessage(Text.Serializer.fromJson(GsonComponentSerializer.INSTANCE.serialize(WorldEditText.format(component))));
+ }
+
+ private void sendColorized(String msg, Formatting formatting) {
+ for (String part : msg.split("\n")) {
+ LiteralText component = new LiteralText(part);
+ component.getStyle().setColor(formatting);
+ this.player.sendMessage(component);
+ }
+ }
+
+ @Override
+ public void setPosition(Vector3 pos, float pitch, float yaw) {
+ this.player.networkHandler.requestTeleport(pos.getX(), pos.getY(), pos.getZ(), yaw, pitch);
+ }
+
+ @Override
+ public String[] getGroups() {
+ return new String[]{}; // WorldEditMod.inst.getPermissionsResolver().getGroups(this.player.username);
+ }
+
+ @Override
+ public BlockBag getInventoryBlockBag() {
+ return null;
+ }
+
+ @Override
+ public boolean hasPermission(String perm) {
+ return FabricWorldEdit.inst.getPermissionsProvider().hasPermission(player, perm);
+ }
+
+ @Nullable
+ @Override
+ public T getFacet(Class extends T> cls) {
+ return null;
+ }
+
+ @Override
+ public boolean isAllowedToFly() {
+ return player.abilities.allowFlying;
+ }
+
+ @Override
+ public void setFlying(boolean flying) {
+ if (player.abilities.flying != flying) {
+ player.abilities.flying = flying;
+ player.sendAbilitiesUpdate();
+ }
+ }
+
+ @Override
+ public > void sendFakeBlock(BlockVector3 pos, B block) {
+ World world = getWorld();
+ if (!(world instanceof FabricWorld)) {
+ return;
+ }
+ BlockPos loc = FabricAdapter.toBlockPos(pos);
+ if (block == null) {
+ final BlockUpdateS2CPacket packetOut = new BlockUpdateS2CPacket(((FabricWorld) world).getWorld(), loc);
+ player.networkHandler.sendPacket(packetOut);
+ } else {
+ final BlockUpdateS2CPacket packetOut = new BlockUpdateS2CPacket();
+ PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
+ buf.writeBlockPos(loc);
+ buf.writeVarInt(Block.getRawIdFromState(FabricAdapter.adapt(block.toImmutableState())));
+ try {
+ packetOut.read(buf);
+ } catch (IOException e) {
+ return;
+ }
+ player.networkHandler.sendPacket(packetOut);
+ if (block instanceof BaseBlock && block.getBlockType().equals(BlockTypes.STRUCTURE_BLOCK)) {
+ final BaseBlock baseBlock = (BaseBlock) block;
+ final CompoundTag nbtData = baseBlock.getNbtData();
+ if (nbtData != null) {
+ player.networkHandler.sendPacket(new BlockEntityUpdateS2CPacket(
+ new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
+ STRUCTURE_BLOCK_PACKET_ID,
+ NBTConverter.toNative(nbtData))
+ );
+ }
+ }
+ }
+ }
+
+ @Override
+ public SessionKey getSessionKey() {
+ return new SessionKeyImpl(player.getUuid(), player.getName().getString());
+ }
+
+ private static class SessionKeyImpl implements SessionKey {
+ // If not static, this will leak a reference
+
+ private final UUID uuid;
+ private final String name;
+
+ private SessionKeyImpl(UUID uuid, String name) {
+ this.uuid = uuid;
+ this.name = name;
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return uuid;
+ }
+
+ @Nullable
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isActive() {
+ // We can't directly check if the player is online because
+ // the list of players is not thread safe
+ return ThreadSafeCache.getInstance().getOnlineIds().contains(uuid);
+ }
+
+ @Override
+ public boolean isPersistent() {
+ return true;
+ }
+
+ }
+
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricRegistries.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricRegistries.java
new file mode 100644
index 000000000..7c703b723
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricRegistries.java
@@ -0,0 +1,75 @@
+/*
+ * 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.fabric;
+
+import com.sk89q.worldedit.world.registry.BiomeRegistry;
+import com.sk89q.worldedit.world.registry.BlockCategoryRegistry;
+import com.sk89q.worldedit.world.registry.BlockRegistry;
+import com.sk89q.worldedit.world.registry.BundledRegistries;
+import com.sk89q.worldedit.world.registry.ItemCategoryRegistry;
+import com.sk89q.worldedit.world.registry.ItemRegistry;
+
+/**
+ * World data for the Fabric platform.
+ */
+class FabricRegistries extends BundledRegistries {
+
+ private static final FabricRegistries INSTANCE = new FabricRegistries();
+ private final BlockRegistry blockRegistry = new FabricBlockRegistry();
+ private final BiomeRegistry biomeRegistry = new FabricBiomeRegistry();
+ private final ItemRegistry itemRegistry = new FabricItemRegistry();
+ private final BlockCategoryRegistry blockCategoryRegistry = new FabricBlockCategoryRegistry();
+ private final ItemCategoryRegistry itemCategoryRegistry = new FabricItemCategoryRegistry();
+
+ @Override
+ public BlockRegistry getBlockRegistry() {
+ return blockRegistry;
+ }
+
+ @Override
+ public BiomeRegistry getBiomeRegistry() {
+ return biomeRegistry;
+ }
+
+ @Override
+ public ItemRegistry getItemRegistry() {
+ return itemRegistry;
+ }
+
+ @Override
+ public BlockCategoryRegistry getBlockCategoryRegistry() {
+ return blockCategoryRegistry;
+ }
+
+ @Override
+ public ItemCategoryRegistry getItemCategoryRegistry() {
+ return itemCategoryRegistry;
+ }
+
+ /**
+ * Get a static instance.
+ *
+ * @return an instance
+ */
+ public static FabricRegistries getInstance() {
+ return INSTANCE;
+ }
+
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java
new file mode 100644
index 000000000..886523df5
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java
@@ -0,0 +1,618 @@
+/*
+ * 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.fabric;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.io.Files;
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.MaxChangedBlocksException;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.blocks.BaseItem;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.entity.Entity;
+import com.sk89q.worldedit.internal.Constants;
+import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
+import com.sk89q.worldedit.math.BlockVector2;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.regions.CuboidRegion;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.util.Direction;
+import com.sk89q.worldedit.util.Location;
+import com.sk89q.worldedit.util.TreeGenerator.TreeType;
+import com.sk89q.worldedit.world.AbstractWorld;
+import com.sk89q.worldedit.world.biome.BiomeType;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockStateHolder;
+import com.sk89q.worldedit.world.item.ItemTypes;
+import com.sk89q.worldedit.world.weather.WeatherType;
+import com.sk89q.worldedit.world.weather.WeatherTypes;
+import net.minecraft.block.Block;
+import net.minecraft.block.Blocks;
+import net.minecraft.block.LeavesBlock;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.ItemUsageContext;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.WorldGenerationProgressListener;
+import net.minecraft.server.world.ServerChunkManager;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Clearable;
+import net.minecraft.util.Hand;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldSaveHandler;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.ChunkManager;
+import net.minecraft.world.chunk.ChunkStatus;
+import net.minecraft.world.chunk.WorldChunk;
+import net.minecraft.world.gen.feature.BirchTreeFeature;
+import net.minecraft.world.gen.feature.DarkOakTreeFeature;
+import net.minecraft.world.gen.feature.DefaultFeatureConfig;
+import net.minecraft.world.gen.feature.Feature;
+import net.minecraft.world.gen.feature.FeatureConfig;
+import net.minecraft.world.gen.feature.HugeBrownMushroomFeature;
+import net.minecraft.world.gen.feature.HugeRedMushroomFeature;
+import net.minecraft.world.gen.feature.JungleGroundBushFeature;
+import net.minecraft.world.gen.feature.JungleTreeFeature;
+import net.minecraft.world.gen.feature.LargeOakTreeFeature;
+import net.minecraft.world.gen.feature.MegaJungleTreeFeature;
+import net.minecraft.world.gen.feature.MegaPineTreeFeature;
+import net.minecraft.world.gen.feature.OakTreeFeature;
+import net.minecraft.world.gen.feature.PineTreeFeature;
+import net.minecraft.world.gen.feature.PlantedFeatureConfig;
+import net.minecraft.world.gen.feature.SavannaTreeFeature;
+import net.minecraft.world.gen.feature.SpruceTreeFeature;
+import net.minecraft.world.gen.feature.SwampTreeFeature;
+import net.minecraft.world.level.LevelProperties;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.Random;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+/**
+ * An adapter to Minecraft worlds for WorldEdit.
+ */
+public class FabricWorld extends AbstractWorld {
+
+ private static final Random random = new Random();
+ private static final int UPDATE = 1, NOTIFY = 2;
+
+ private static final net.minecraft.block.BlockState JUNGLE_LOG = Blocks.JUNGLE_LOG.getDefaultState();
+ private static final net.minecraft.block.BlockState JUNGLE_LEAF = Blocks.JUNGLE_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE);
+ private static final net.minecraft.block.BlockState JUNGLE_SHRUB = Blocks.OAK_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE);
+
+ private final WeakReference worldRef;
+
+ /**
+ * Construct a new world.
+ *
+ * @param world the world
+ */
+ FabricWorld(World world) {
+ checkNotNull(world);
+ this.worldRef = new WeakReference<>(world);
+ }
+
+ /**
+ * Get the underlying handle to the world.
+ *
+ * @return the world
+ * @throws WorldEditException thrown if a reference to the world was lost (i.e. world was unloaded)
+ */
+ public World getWorldChecked() throws WorldEditException {
+ World world = worldRef.get();
+ if (world != null) {
+ return world;
+ } else {
+ throw new WorldReferenceLostException("The reference to the world was lost (i.e. the world may have been unloaded)");
+ }
+ }
+
+ /**
+ * Get the underlying handle to the world.
+ *
+ * @return the world
+ * @throws RuntimeException thrown if a reference to the world was lost (i.e. world was unloaded)
+ */
+ public World getWorld() {
+ World world = worldRef.get();
+ if (world != null) {
+ return world;
+ } else {
+ throw new RuntimeException("The reference to the world was lost (i.e. the world may have been unloaded)");
+ }
+ }
+
+ @Override
+ public String getName() {
+ return getWorld().getLevelProperties().getLevelName();
+ }
+
+ @Override
+ public String getId() {
+ return getWorld().getLevelProperties().getLevelName()
+ .replace(" ", "_").toLowerCase(Locale.ROOT)
+ + getWorld().dimension.getType().getSuffix();
+ }
+
+ @Override
+ public Path getStoragePath() {
+ final World world = getWorld();
+ if (world instanceof ServerWorld) {
+ return ((ServerWorld) world).getSaveHandler().getWorldDir().toPath();
+ }
+ return null;
+ }
+
+ @Override
+ public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
+ checkNotNull(position);
+ checkNotNull(block);
+
+ World world = getWorldChecked();
+ int x = position.getBlockX();
+ int y = position.getBlockY();
+ int z = position.getBlockZ();
+
+ // First set the block
+ Chunk chunk = world.getChunk(x >> 4, z >> 4);
+ BlockPos pos = new BlockPos(x, y, z);
+ net.minecraft.block.BlockState old = chunk.getBlockState(pos);
+ OptionalInt stateId = BlockStateIdAccess.getBlockStateId(block.toImmutableState());
+ net.minecraft.block.BlockState newState = stateId.isPresent() ? Block.getStateFromRawId(stateId.getAsInt()) : FabricAdapter.adapt(block.toImmutableState());
+ net.minecraft.block.BlockState successState = chunk.setBlockState(pos, newState, false);
+ boolean successful = successState != null;
+
+ // Create the TileEntity
+ if (successful || old == newState) {
+ if (block instanceof BaseBlock) {
+ CompoundTag tag = ((BaseBlock) block).getNbtData();
+ if (tag != null) {
+ net.minecraft.nbt.CompoundTag nativeTag = NBTConverter.toNative(tag);
+ BlockEntity tileEntity = getWorld().getWorldChunk(pos).getBlockEntity(pos);
+ if (tileEntity != null) {
+ tileEntity.fromTag(nativeTag);
+ tileEntity.setPos(pos);
+ tileEntity.setWorld(world);
+ successful = true; // update if TE changed as well
+ }
+ }
+ }
+ }
+
+ if (successful && notifyAndLight) {
+ world.getChunkManager().getLightingProvider().enqueueLightUpdate(pos);
+ world.scheduleBlockRender(pos, old, newState);
+ world.updateListeners(pos, old, newState, UPDATE | NOTIFY);
+ world.updateNeighbors(pos, newState.getBlock());
+ if (old.hasComparatorOutput()) {
+ world.updateHorizontalAdjacent(pos, newState.getBlock());
+ }
+ }
+
+ return successful;
+ }
+
+ @Override
+ public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException {
+ BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ());
+ net.minecraft.block.BlockState state = getWorld().getBlockState(pos);
+ getWorld().updateListeners(pos, FabricAdapter.adapt(previousType), state, 1 | 2);
+ getWorld().updateNeighbors(pos, state.getBlock());
+ return true;
+ }
+
+ @Override
+ public int getBlockLightLevel(BlockVector3 position) {
+ checkNotNull(position);
+ return getWorld().getLightLevel(FabricAdapter.toBlockPos(position));
+ }
+
+ @Override
+ public boolean clearContainerBlockContents(BlockVector3 position) {
+ checkNotNull(position);
+ BlockEntity tile = getWorld().getBlockEntity(FabricAdapter.toBlockPos(position));
+ if ((tile instanceof Clearable)) {
+ ((Clearable) tile).clear();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public BiomeType getBiome(BlockVector2 position) {
+ checkNotNull(position);
+ return FabricAdapter.adapt(getWorld().getBiome(new BlockPos(position.getBlockX(), 0, position.getBlockZ())));
+ }
+
+ @Override
+ public boolean setBiome(BlockVector2 position, BiomeType biome) {
+ checkNotNull(position);
+ checkNotNull(biome);
+
+ Chunk chunk = getWorld().getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4, ChunkStatus.FULL, false);
+ if (chunk == null) {
+ return false;
+ }
+ chunk.getBiomeArray()[((position.getBlockZ() & 0xF) << 4 | position.getBlockX() & 0xF)] = FabricAdapter.adapt(biome);
+ return true;
+ }
+
+ private static final LoadingCache fakePlayers
+ = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(WorldEditFakePlayer::new));
+
+ @Override
+ public boolean useItem(BlockVector3 position, BaseItem item, Direction face) {
+ ItemStack stack = FabricAdapter.adapt(new BaseItemStack(item.getType(), item.getNbtData(), 1));
+ ServerWorld world = (ServerWorld) getWorld();
+ final WorldEditFakePlayer fakePlayer;
+ try {
+ fakePlayer = fakePlayers.get(world);
+ } catch (ExecutionException ignored) {
+ return false;
+ }
+ fakePlayer.setStackInHand(Hand.MAIN_HAND, stack);
+ fakePlayer.setPositionAndAngles(position.getBlockX(), position.getBlockY(), position.getBlockZ(),
+ (float) face.toVector().toYaw(), (float) face.toVector().toPitch());
+ final BlockPos blockPos = FabricAdapter.toBlockPos(position);
+ final BlockHitResult rayTraceResult = new BlockHitResult(FabricAdapter.toVec3(position),
+ FabricAdapter.adapt(face), blockPos, false);
+ ItemUsageContext itemUseContext = new ItemUsageContext(fakePlayer, Hand.MAIN_HAND, rayTraceResult);
+ ActionResult used = stack.useOnBlock(itemUseContext);
+ if (used != ActionResult.SUCCESS) {
+ // try activating the block
+ if (getWorld().getBlockState(blockPos).activate(world, fakePlayer, Hand.MAIN_HAND, rayTraceResult)) {
+ used = ActionResult.SUCCESS;
+ } else {
+ used = stack.getItem().use(world, fakePlayer, Hand.MAIN_HAND).getResult();
+ }
+ }
+ return used == ActionResult.SUCCESS;
+ }
+
+ @Override
+ public void dropItem(Vector3 position, BaseItemStack item) {
+ checkNotNull(position);
+ checkNotNull(item);
+
+ if (item.getType() == ItemTypes.AIR) {
+ return;
+ }
+
+ ItemEntity entity = new ItemEntity(getWorld(), position.getX(), position.getY(), position.getZ(), FabricAdapter.adapt(item));
+ entity.setPickupDelay(10);
+ getWorld().spawnEntity(entity);
+ }
+
+ @Override
+ public void simulateBlockMine(BlockVector3 position) {
+ BlockPos pos = FabricAdapter.toBlockPos(position);
+ getWorld().breakBlock(pos, true);
+ }
+
+ @Override
+ public boolean regenerate(Region region, EditSession editSession) {
+ // Don't even try to regen if it's going to fail.
+ ChunkManager provider = getWorld().getChunkManager();
+ if (!(provider instanceof ServerChunkManager)) {
+ return false;
+ }
+
+ File saveFolder = Files.createTempDir();
+ // register this just in case something goes wrong
+ // normally it should be deleted at the end of this method
+ saveFolder.deleteOnExit();
+ try {
+ ServerWorld originalWorld = (ServerWorld) getWorld();
+
+ MinecraftServer server = originalWorld.getServer();
+ WorldSaveHandler saveHandler = new WorldSaveHandler(saveFolder, originalWorld.getSaveHandler().getWorldDir().getName(), server, server.getDataFixer());
+ World freshWorld = new ServerWorld(server, server.getWorkerExecutor(), saveHandler, originalWorld.getLevelProperties(),
+ originalWorld.dimension.getType(), originalWorld.getProfiler(), new NoOpChunkStatusListener());
+
+ // Pre-gen all the chunks
+ // We need to also pull one more chunk in every direction
+ CuboidRegion expandedPreGen = new CuboidRegion(region.getMinimumPoint().subtract(16, 0, 16), region.getMaximumPoint().add(16, 0, 16));
+ for (BlockVector2 chunk : expandedPreGen.getChunks()) {
+ freshWorld.getChunk(chunk.getBlockX(), chunk.getBlockZ());
+ }
+
+ FabricWorld from = new FabricWorld(freshWorld);
+ for (BlockVector3 vec : region) {
+ editSession.setBlock(vec, from.getFullBlock(vec));
+ }
+ } catch (MaxChangedBlocksException e) {
+ throw new RuntimeException(e);
+ } finally {
+ saveFolder.delete();
+ }
+
+ return true;
+ }
+
+ @Nullable
+ private static Feature extends FeatureConfig> createTreeFeatureGenerator(TreeType type) {
+ switch (type) {
+ case TREE: return new OakTreeFeature(DefaultFeatureConfig::deserialize, true);
+ case BIG_TREE: return new LargeOakTreeFeature(DefaultFeatureConfig::deserialize, true);
+ case REDWOOD: return new PineTreeFeature(DefaultFeatureConfig::deserialize);
+ case TALL_REDWOOD: return new SpruceTreeFeature(DefaultFeatureConfig::deserialize, true);
+ case BIRCH: return new BirchTreeFeature(DefaultFeatureConfig::deserialize, true, false);
+ case JUNGLE: return new MegaJungleTreeFeature(DefaultFeatureConfig::deserialize, true, 10, 20, JUNGLE_LOG, JUNGLE_LEAF);
+ case SMALL_JUNGLE: return new JungleTreeFeature(DefaultFeatureConfig::deserialize, true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, false);
+ case SHORT_JUNGLE: return new JungleTreeFeature(DefaultFeatureConfig::deserialize, true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, true);
+ case JUNGLE_BUSH: return new JungleGroundBushFeature(DefaultFeatureConfig::deserialize, JUNGLE_LOG, JUNGLE_SHRUB);
+ case SWAMP: return new SwampTreeFeature(DefaultFeatureConfig::deserialize);
+ case ACACIA: return new SavannaTreeFeature(DefaultFeatureConfig::deserialize, true);
+ case DARK_OAK: return new DarkOakTreeFeature(DefaultFeatureConfig::deserialize, true);
+ case MEGA_REDWOOD: return new MegaPineTreeFeature(DefaultFeatureConfig::deserialize, true, random.nextBoolean());
+ case TALL_BIRCH: return new BirchTreeFeature(DefaultFeatureConfig::deserialize, true, true);
+ case RED_MUSHROOM: return new HugeRedMushroomFeature(PlantedFeatureConfig::deserialize);
+ case BROWN_MUSHROOM: return new HugeBrownMushroomFeature(PlantedFeatureConfig::deserialize);
+ case RANDOM: return createTreeFeatureGenerator(TreeType.values()[ThreadLocalRandom.current().nextInt(TreeType.values().length)]);
+ default:
+ return null;
+ }
+ }
+
+ private FeatureConfig createFeatureConfig(TreeType type) {
+ if (type == TreeType.RED_MUSHROOM || type == TreeType.BROWN_MUSHROOM) {
+ return new PlantedFeatureConfig(true);
+ } else {
+ return new DefaultFeatureConfig();
+ }
+ }
+
+ @Override
+ public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException {
+ @SuppressWarnings("unchecked")
+ Feature generator = (Feature) createTreeFeatureGenerator(type);
+ return generator != null
+ && generator.generate(getWorld(), getWorld().getChunkManager().getChunkGenerator(), random,
+ FabricAdapter.toBlockPos(position), createFeatureConfig(type));
+ }
+
+ @Override
+ public void checkLoadedChunk(BlockVector3 pt) {
+ getWorld().getChunk(FabricAdapter.toBlockPos(pt));
+ }
+
+ @Override
+ public void fixAfterFastMode(Iterable chunks) {
+ fixLighting(chunks);
+ }
+
+ @Override
+ public void fixLighting(Iterable chunks) {
+ World world = getWorld();
+ for (BlockVector2 chunk : chunks) {
+ world.getChunkManager().getLightingProvider().suppressLight(new ChunkPos(chunk.getBlockX(), chunk.getBlockZ()), true);
+ }
+ }
+
+ @Override
+ public boolean playEffect(Vector3 position, int type, int data) {
+ getWorld().playLevelEvent(type, FabricAdapter.toBlockPos(position.toBlockPoint()), data);
+ return true;
+ }
+
+ @Override
+ public WeatherType getWeather() {
+ LevelProperties info = getWorld().getLevelProperties();
+ if (info.isThundering()) {
+ return WeatherTypes.THUNDER_STORM;
+ }
+ if (info.isRaining()) {
+ return WeatherTypes.RAIN;
+ }
+ return WeatherTypes.CLEAR;
+ }
+
+ @Override
+ public long getRemainingWeatherDuration() {
+ LevelProperties info = getWorld().getLevelProperties();
+ if (info.isThundering()) {
+ return info.getThunderTime();
+ }
+ if (info.isRaining()) {
+ return info.getRainTime();
+ }
+ return info.getClearWeatherTime();
+ }
+
+ @Override
+ public void setWeather(WeatherType weatherType) {
+ setWeather(weatherType, 0);
+ }
+
+ @Override
+ public void setWeather(WeatherType weatherType, long duration) {
+ LevelProperties info = getWorld().getLevelProperties();
+ if (weatherType == WeatherTypes.THUNDER_STORM) {
+ info.setClearWeatherTime(0);
+ info.setThundering(true);
+ info.setThunderTime((int) duration);
+ } else if (weatherType == WeatherTypes.RAIN) {
+ info.setClearWeatherTime(0);
+ info.setRaining(true);
+ info.setRainTime((int) duration);
+ } else if (weatherType == WeatherTypes.CLEAR) {
+ info.setRaining(false);
+ info.setThundering(false);
+ info.setClearWeatherTime((int) duration);
+ }
+ }
+
+ @Override
+ public int getMaxY() {
+ return getWorld().getHeight() - 1;
+ }
+
+ @Override
+ public BlockVector3 getSpawnPosition() {
+ return FabricAdapter.adapt(getWorld().getSpawnPos());
+ }
+
+ @Override
+ public BlockState getBlock(BlockVector3 position) {
+ net.minecraft.block.BlockState mcState = getWorld()
+ .getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4)
+ .getBlockState(FabricAdapter.toBlockPos(position));
+
+ BlockState matchingBlock = BlockStateIdAccess.getBlockStateById(Block.getRawIdFromState(mcState));
+ if (matchingBlock != null) {
+ return matchingBlock;
+ }
+
+ return FabricAdapter.adapt(mcState);
+ }
+
+ @Override
+ public BaseBlock getFullBlock(BlockVector3 position) {
+ BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
+ // Avoid creation by using the CHECK mode -- if it's needed, it'll be re-created anyways
+ BlockEntity tile = ((WorldChunk) getWorld().getChunk(pos)).getBlockEntity(pos, WorldChunk.CreationType.CHECK);
+
+ if (tile != null) {
+ net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag();
+ tile.toTag(tag);
+ return getBlock(position).toBaseBlock(NBTConverter.fromNative(tag));
+ } else {
+ return getBlock(position).toBaseBlock();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return getWorld().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ((o instanceof FabricWorld)) {
+ FabricWorld other = ((FabricWorld) o);
+ World otherWorld = other.worldRef.get();
+ World thisWorld = worldRef.get();
+ return otherWorld != null && otherWorld.equals(thisWorld);
+ } else if (o instanceof com.sk89q.worldedit.world.World) {
+ return ((com.sk89q.worldedit.world.World) o).getName().equals(getName());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public List extends Entity> getEntities(Region region) {
+ final World world = getWorld();
+ if (!(world instanceof ServerWorld)) {
+ return Collections.emptyList();
+ }
+ return ((ServerWorld) world).getEntities(null, entity -> true)
+ .stream()
+ .filter(e -> region.contains(FabricAdapter.adapt(e.getBlockPos())))
+ .map(FabricEntity::new).collect(Collectors.toList());
+ }
+
+ @Override
+ public List extends Entity> getEntities() {
+ final World world = getWorld();
+ if (!(world instanceof ServerWorld)) {
+ return Collections.emptyList();
+ }
+ return ((ServerWorld) world).getEntities(null, entity -> true)
+ .stream()
+ .map(FabricEntity::new)
+ .collect(Collectors.toList());
+ }
+
+ @Nullable
+ @Override
+ public Entity createEntity(Location location, BaseEntity entity) {
+ World world = getWorld();
+ final Optional> entityType = EntityType.get(entity.getType().getId());
+ if (!entityType.isPresent()) return null;
+ net.minecraft.entity.Entity createdEntity = entityType.get().create(world);
+ if (createdEntity != null) {
+ CompoundTag nativeTag = entity.getNbtData();
+ if (nativeTag != null) {
+ net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(entity.getNbtData());
+ for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
+ tag.remove(name);
+ }
+ createdEntity.fromTag(tag);
+ }
+
+ createdEntity.setPositionAndAngles(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
+
+ world.spawnEntity(createdEntity);
+ return new FabricEntity(createdEntity);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Thrown when the reference to the world is lost.
+ */
+ @SuppressWarnings("serial")
+ private static final class WorldReferenceLostException extends WorldEditException {
+ private WorldReferenceLostException(String message) {
+ super(message);
+ }
+ }
+
+ private static class NoOpChunkStatusListener implements WorldGenerationProgressListener {
+ @Override
+ public void start(ChunkPos chunkPos) {
+ }
+
+ @Override
+ public void setChunkStatus(ChunkPos chunkPos, @Nullable ChunkStatus chunkStatus) {
+ }
+
+ @Override
+ public void stop() {
+ }
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java
new file mode 100644
index 000000000..0175a618a
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java
@@ -0,0 +1,326 @@
+/*
+ * 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.fabric;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.sk89q.worldedit.fabric.FabricAdapter.adaptPlayer;
+
+import com.sk89q.worldedit.LocalSession;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
+import com.sk89q.worldedit.extension.platform.Platform;
+import com.sk89q.worldedit.fabric.net.handler.WECUIPacketHandler;
+import com.sk89q.worldedit.util.Location;
+import com.sk89q.worldedit.world.biome.BiomeType;
+import com.sk89q.worldedit.world.block.BlockCategory;
+import com.sk89q.worldedit.world.block.BlockType;
+import com.sk89q.worldedit.world.entity.EntityType;
+import com.sk89q.worldedit.world.item.ItemCategory;
+import com.sk89q.worldedit.world.item.ItemType;
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
+import net.fabricmc.fabric.api.event.player.UseBlockCallback;
+import net.fabricmc.fabric.api.event.player.UseItemCallback;
+import net.fabricmc.fabric.api.event.server.ServerStartCallback;
+import net.fabricmc.fabric.api.event.server.ServerStopCallback;
+import net.fabricmc.fabric.api.event.server.ServerTickCallback;
+import net.fabricmc.loader.api.FabricLoader;
+import net.fabricmc.loader.api.ModContainer;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.tag.BlockTags;
+import net.minecraft.tag.ItemTags;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Hand;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.world.World;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * The Fabric implementation of WorldEdit.
+ */
+public class FabricWorldEdit implements ModInitializer {
+
+ private static final Logger LOGGER = LogManager.getLogger();
+ public static final String MOD_ID = "worldedit";
+ public static final String CUI_PLUGIN_CHANNEL = "cui";
+
+ private FabricPermissionsProvider provider;
+
+ public static FabricWorldEdit inst;
+
+ private FabricPlatform platform;
+ private FabricConfiguration config;
+ private Path workingDir;
+
+ private ModContainer container;
+
+ public FabricWorldEdit() {
+ inst = this;
+ }
+
+ @Override
+ public void onInitialize() {
+ this.container = FabricLoader.getInstance().getModContainer("worldedit").orElseThrow(
+ () -> new IllegalStateException("WorldEdit mod missing in Fabric")
+ );
+
+ // Setup working directory
+ workingDir = new File(FabricLoader.getInstance().getConfigDirectory(), "worldedit").toPath();
+ if (!Files.exists(workingDir)) {
+ try {
+ Files.createDirectory(workingDir);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ WECUIPacketHandler.init();
+
+ ServerTickCallback.EVENT.register(ThreadSafeCache.getInstance());
+ ServerStartCallback.EVENT.register(this::onStartServer);
+ ServerStopCallback.EVENT.register(this::onStopServer);
+ AttackBlockCallback.EVENT.register(this::onLeftClickBlock);
+ UseBlockCallback.EVENT.register(this::onRightClickBlock);
+ UseItemCallback.EVENT.register(this::onRightClickAir);
+ LOGGER.info("WorldEdit for Fabric (version " + getInternalVersion() + ") is loaded");
+ }
+
+ private void setupPlatform(MinecraftServer server) {
+ this.platform = new FabricPlatform(this, server);
+
+ WorldEdit.getInstance().getPlatformManager().register(platform);
+
+ this.provider = new FabricPermissionsProvider.VanillaPermissionsProvider(platform);
+ }
+
+ private void setupRegistries() {
+ // Blocks
+ for (Identifier name : Registry.BLOCK.getIds()) {
+ if (BlockType.REGISTRY.get(name.toString()) == null) {
+ BlockType.REGISTRY.register(name.toString(), new BlockType(name.toString(),
+ input -> FabricAdapter.adapt(FabricAdapter.adapt(input.getBlockType()).getDefaultState())));
+ }
+ }
+ // Items
+ for (Identifier name : Registry.ITEM.getIds()) {
+ if (ItemType.REGISTRY.get(name.toString()) == null) {
+ ItemType.REGISTRY.register(name.toString(), new ItemType(name.toString()));
+ }
+ }
+ // Entities
+ for (Identifier name : Registry.ENTITY_TYPE.getIds()) {
+ if (EntityType.REGISTRY.get(name.toString()) == null) {
+ EntityType.REGISTRY.register(name.toString(), new EntityType(name.toString()));
+ }
+ }
+ // Biomes
+ for (Identifier name : Registry.BIOME.getIds()) {
+ if (BiomeType.REGISTRY.get(name.toString()) == null) {
+ BiomeType.REGISTRY.register(name.toString(), new BiomeType(name.toString()));
+ }
+ }
+ // Tags
+ for (Identifier name : BlockTags.getContainer().getKeys()) {
+ if (BlockCategory.REGISTRY.get(name.toString()) == null) {
+ BlockCategory.REGISTRY.register(name.toString(), new BlockCategory(name.toString()));
+ }
+ }
+ for (Identifier name : ItemTags.getContainer().getKeys()) {
+ if (ItemCategory.REGISTRY.get(name.toString()) == null) {
+ ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(name.toString()));
+ }
+ }
+ }
+
+ private void onStartServer(MinecraftServer minecraftServer) {
+ setupPlatform(minecraftServer);
+ setupRegistries();
+
+ config = new FabricConfiguration(this);
+ config.load();
+ WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent());
+ }
+
+ private void onStopServer(MinecraftServer minecraftServer) {
+ WorldEdit worldEdit = WorldEdit.getInstance();
+ worldEdit.getSessionManager().unload();
+ worldEdit.getPlatformManager().unregister(platform);
+ }
+
+ private boolean shouldSkip() {
+ if (platform == null) {
+ return true;
+ }
+
+ return !platform.isHookingEvents(); // We have to be told to catch these events
+ }
+
+ private ActionResult onLeftClickBlock(PlayerEntity playerEntity, World world, Hand hand, BlockPos blockPos, Direction direction) {
+ if (shouldSkip() || hand == Hand.OFF_HAND || world.isClient) {
+ return ActionResult.PASS;
+ }
+
+ WorldEdit we = WorldEdit.getInstance();
+ FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity);
+ FabricWorld localWorld = getWorld(world);
+ Location pos = new Location(localWorld,
+ blockPos.getX(),
+ blockPos.getY(),
+ blockPos.getZ()
+ );
+
+ if (we.handleBlockLeftClick(player, pos)) {
+ return ActionResult.SUCCESS;
+ }
+
+ if (we.handleArmSwing(player)) {
+ return ActionResult.SUCCESS;
+ }
+
+ return ActionResult.PASS;
+ }
+
+ public void onLeftClickAir(PlayerEntity playerEntity, World world, Hand hand) {
+ WorldEdit we = WorldEdit.getInstance();
+ FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity);
+ we.handleArmSwing(player);
+ }
+
+ private ActionResult onRightClickBlock(PlayerEntity playerEntity, World world, Hand hand, BlockHitResult blockHitResult) {
+ if (shouldSkip() || hand == Hand.OFF_HAND || world.isClient) {
+ return ActionResult.PASS;
+ }
+
+ WorldEdit we = WorldEdit.getInstance();
+ FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity);
+ FabricWorld localWorld = getWorld(world);
+ Location pos = new Location(localWorld,
+ blockHitResult.getBlockPos().getX(),
+ blockHitResult.getBlockPos().getY(),
+ blockHitResult.getBlockPos().getZ()
+ );
+
+ if (we.handleBlockRightClick(player, pos)) {
+ return ActionResult.SUCCESS;
+ }
+
+ if (we.handleRightClick(player)) {
+ return ActionResult.SUCCESS;
+ }
+
+ return ActionResult.PASS;
+ }
+
+ private ActionResult onRightClickAir(PlayerEntity playerEntity, World world, Hand hand) {
+ if (shouldSkip() || hand == Hand.OFF_HAND || world.isClient) {
+ return ActionResult.PASS;
+ }
+
+ WorldEdit we = WorldEdit.getInstance();
+ FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity);
+
+ if (we.handleRightClick(player)) {
+ return ActionResult.SUCCESS;
+ }
+
+ return ActionResult.PASS;
+ }
+
+ // TODO Pass empty left click to server
+
+ /**
+ * Get the configuration.
+ *
+ * @return the Fabric configuration
+ */
+ FabricConfiguration getConfig() {
+ return this.config;
+ }
+
+ /**
+ * Get the session for a player.
+ *
+ * @param player the player
+ * @return the session
+ */
+ public LocalSession getSession(ServerPlayerEntity player) {
+ checkNotNull(player);
+ return WorldEdit.getInstance().getSessionManager().get(adaptPlayer(player));
+ }
+
+ /**
+ * Get the WorldEdit proxy for the given world.
+ *
+ * @param world the world
+ * @return the WorldEdit world
+ */
+ public FabricWorld getWorld(World world) {
+ checkNotNull(world);
+ return new FabricWorld(world);
+ }
+
+ /**
+ * Get the WorldEdit proxy for the platform.
+ *
+ * @return the WorldEdit platform
+ */
+ public Platform getPlatform() {
+ return this.platform;
+ }
+
+ /**
+ * Get the working directory where WorldEdit's files are stored.
+ *
+ * @return the working directory
+ */
+ public File getWorkingDir() {
+ return this.workingDir.toFile();
+ }
+
+ /**
+ * Get the version of the WorldEdit-Fabric implementation.
+ *
+ * @return a version string
+ */
+ String getInternalVersion() {
+ return container.getMetadata().getVersion().getFriendlyString();
+ }
+
+ public void setPermissionsProvider(FabricPermissionsProvider provider) {
+ this.provider = provider;
+ }
+
+ public FabricPermissionsProvider getPermissionsProvider() {
+ return provider;
+ }
+}
diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java
new file mode 100644
index 000000000..450dfcb65
--- /dev/null
+++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java
@@ -0,0 +1,252 @@
+/*
+ * 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.fabric;
+
+import com.sk89q.jnbt.ByteArrayTag;
+import com.sk89q.jnbt.ByteTag;
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.jnbt.DoubleTag;
+import com.sk89q.jnbt.EndTag;
+import com.sk89q.jnbt.FloatTag;
+import com.sk89q.jnbt.IntArrayTag;
+import com.sk89q.jnbt.IntTag;
+import com.sk89q.jnbt.ListTag;
+import com.sk89q.jnbt.LongTag;
+import com.sk89q.jnbt.ShortTag;
+import com.sk89q.jnbt.StringTag;
+import com.sk89q.jnbt.Tag;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Converts between JNBT and Minecraft NBT classes.
+ */
+final class NBTConverter {
+
+ private NBTConverter() {
+ }
+
+ public static net.minecraft.nbt.Tag toNative(Tag tag) {
+ if (tag instanceof IntArrayTag) {
+ return toNative((IntArrayTag) tag);
+
+ } else if (tag instanceof ListTag) {
+ return toNative((ListTag) tag);
+
+ } else if (tag instanceof LongTag) {
+ return toNative((LongTag) tag);
+
+ } else if (tag instanceof StringTag) {
+ return toNative((StringTag) tag);
+
+ } else if (tag instanceof IntTag) {
+ return toNative((IntTag) tag);
+
+ } else if (tag instanceof ByteTag) {
+ return toNative((ByteTag) tag);
+
+ } else if (tag instanceof ByteArrayTag) {
+ return toNative((ByteArrayTag) tag);
+
+ } else if (tag instanceof CompoundTag) {
+ return toNative((CompoundTag) tag);
+
+ } else if (tag instanceof FloatTag) {
+ return toNative((FloatTag) tag);
+
+ } else if (tag instanceof ShortTag) {
+ return toNative((ShortTag) tag);
+
+ } else if (tag instanceof DoubleTag) {
+ return toNative((DoubleTag) tag);
+ } else {
+ throw new IllegalArgumentException("Can't convert tag of type " + tag.getClass().getCanonicalName());
+ }
+ }
+
+ public static net.minecraft.nbt.IntArrayTag toNative(IntArrayTag tag) {
+ int[] value = tag.getValue();
+ return new net.minecraft.nbt.IntArrayTag(Arrays.copyOf(value, value.length));
+ }
+
+ public static net.minecraft.nbt.ListTag toNative(ListTag tag) {
+ net.minecraft.nbt.ListTag list = new net.minecraft.nbt.ListTag();
+ for (Tag child : tag.getValue()) {
+ if (child instanceof EndTag) {
+ continue;
+ }
+ list.add(toNative(child));
+ }
+ return list;
+ }
+
+ public static net.minecraft.nbt.LongTag toNative(LongTag tag) {
+ return new net.minecraft.nbt.LongTag(tag.getValue());
+ }
+
+ public static net.minecraft.nbt.StringTag toNative(StringTag tag) {
+ return new net.minecraft.nbt.StringTag(tag.getValue());
+ }
+
+ public static net.minecraft.nbt.IntTag toNative(IntTag tag) {
+ return new net.minecraft.nbt.IntTag(tag.getValue());
+ }
+
+ public static net.minecraft.nbt.ByteTag toNative(ByteTag tag) {
+ return new net.minecraft.nbt.ByteTag(tag.getValue());
+ }
+
+ public static net.minecraft.nbt.ByteArrayTag toNative(ByteArrayTag tag) {
+ byte[] value = tag.getValue();
+ return new net.minecraft.nbt.ByteArrayTag(Arrays.copyOf(value, value.length));
+ }
+
+ public static net.minecraft.nbt.CompoundTag toNative(CompoundTag tag) {
+ net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag();
+ for (Entry child : tag.getValue().entrySet()) {
+ compound.put(child.getKey(), toNative(child.getValue()));
+ }
+ return compound;
+ }
+
+ public static net.minecraft.nbt.FloatTag toNative(FloatTag tag) {
+ return new net.minecraft.nbt.FloatTag(tag.getValue());
+ }
+
+ public static net.minecraft.nbt.ShortTag toNative(ShortTag tag) {
+ return new net.minecraft.nbt.ShortTag(tag.getValue());
+ }
+
+ public static net.minecraft.nbt.DoubleTag toNative(DoubleTag tag) {
+ return new net.minecraft.nbt.DoubleTag(tag.getValue());
+ }
+
+ public static Tag fromNative(net.minecraft.nbt.Tag other) {
+ if (other instanceof net.minecraft.nbt.IntArrayTag) {
+ return fromNative((net.minecraft.nbt.IntArrayTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.ListTag) {
+ return fromNative((net.minecraft.nbt.ListTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.EndTag) {
+ return fromNative((net.minecraft.nbt.EndTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.LongTag) {
+ return fromNative((net.minecraft.nbt.LongTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.StringTag) {
+ return fromNative((net.minecraft.nbt.StringTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.IntTag) {
+ return fromNative((net.minecraft.nbt.IntTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.ByteTag) {
+ return fromNative((net.minecraft.nbt.ByteTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.ByteArrayTag) {
+ return fromNative((net.minecraft.nbt.ByteArrayTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.CompoundTag) {
+ return fromNative((net.minecraft.nbt.CompoundTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.FloatTag) {
+ return fromNative((net.minecraft.nbt.FloatTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.ShortTag) {
+ return fromNative((net.minecraft.nbt.ShortTag) other);
+
+ } else if (other instanceof net.minecraft.nbt.DoubleTag) {
+ return fromNative((net.minecraft.nbt.DoubleTag) other);
+ } else {
+ throw new IllegalArgumentException("Can't convert other of type " + other.getClass().getCanonicalName());
+ }
+ }
+
+ public static IntArrayTag fromNative(net.minecraft.nbt.IntArrayTag other) {
+ int[] value = other.getIntArray();
+ return new IntArrayTag(Arrays.copyOf(value, value.length));
+ }
+
+ public static ListTag fromNative(net.minecraft.nbt.ListTag other) {
+ other = (net.minecraft.nbt.ListTag) other.copy();
+ List