diff --git a/README.html b/README.html deleted file mode 100644 index 54e4fb40d8..0000000000 --- a/README.html +++ /dev/null @@ -1,197 +0,0 @@ - - - -${project.name} ${version} - - - - - -
-

${project.name} ${version}

-
-

Getting Started

-

- Thanks for choosing ${project.name}! When you first install ${project.name}, - no one will be able to do anything until you give permissions (unless everyone is an op). - Because ${project.name} is largely command-driven, it has no effect until - someone uses its commands, you can install WorldEdit and deal with - configuration whenever you are ready. -

- -

- Need help? - See the ways that you can get assistance. -

-
-
-

Common Issues

- -

I or others don't have permission to build.

-

- This is not a problem caused by ${project.name}. - ${project.name} doesn't deny build permissions as it is merely - a world editing program. -

- -

${project.name} doesn't seem to work.

-

- A common mistake is making - a syntax error in one of ${project.name}'s configuration files (this is very - fatal unfortunately, and can be caused by a single character typed in the wrong place). Try - asking in IRC - to see whether anyone can help you. -

- -

Can I use this with mod blocks?

-

- Yes, depending on what you are using WorldEdit with - (Bukkit, Single Player Commands, MinecraftEdu, etc.). We don't guarantee - 100% compatibility with all known custom blocks, - but we have pretty - decent support. Please be aware that support for these mods is - considered 'experimental,' and will likely be for the far forseeable - future. -

- -

How do I protect my spawn?

-

- You'll have to install our accompanying WorldGuard plugin, which works - in conjunction with ${project.name} in order to protect areas. With it, - you can make a selection with WorldEdit and then 'define' a region - that prevents other from building in it. -

- -

Can I use ${project.name}'s selections in my Bukkit plugin?

-

- Absolutely! We haven't documented how to yet, but you can try - looking at the Javadocs - for the plugin's main class. You'll have to get a reference to WorldEdit - from Bukkit's plugin manager (don't try to create WorldEditPlugin), and then - call one of its selection methods. -

- -

- Still need help? - See the ways that you can get assistance. -

-
-
-

Contributing

-

- Did you know that ${project.name} is open source? That means that you can - read the code and learn from it, as well as modify it and submit back - changes to help the community! -

- -
-
- - \ No newline at end of file diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml index 36ccf3d0ba..c783552a9d 100644 --- a/config/checkstyle/import-control.xml +++ b/config/checkstyle/import-control.xml @@ -57,6 +57,7 @@ + diff --git a/worldedit-bukkit/build.gradle b/worldedit-bukkit/build.gradle index b771a1aa70..21b5bdebbc 100644 --- a/worldedit-bukkit/build.gradle +++ b/worldedit-bukkit/build.gradle @@ -26,14 +26,13 @@ dependencies { } processResources { - from (sourceSets.main.resources.srcDirs) { + filesMatching('plugin.yml') { expand 'internalVersion': project.internalVersion - include 'plugin.yml' - } - - from (sourceSets.main.resources.srcDirs) { - exclude 'plugin.yml' } + from (zipTree('src/main/resources/worldedit-adapters.jar').matching { + exclude 'META-INF/' + }) + exclude '**/worldedit-adapters.jar' } jar { 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 34ad4c2576..ccc025d8c9 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 @@ -28,6 +28,7 @@ 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.world.DataFixer; import com.sk89q.worldedit.world.registry.Registries; import org.bukkit.Bukkit; import org.bukkit.Server; @@ -75,9 +76,20 @@ public int getDataVersion() { return -1; } + @Override + public DataFixer getDataFixer() { + if (plugin.getBukkitImplAdapter() != null) { + return plugin.getBukkitImplAdapter().getDataFixer(); + } + return null; + } + @Override public boolean isValidMobType(String type) { - final EntityType entityType = EntityType.fromName(type); + if (!type.startsWith("minecraft:")) { + return false; + } + final EntityType entityType = EntityType.fromName(type.substring(10)); return entityType != null && entityType.isAlive(); } 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 15361fc104..69c803880f 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.bukkit; -import com.sk89q.util.StringUtil; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; @@ -33,7 +32,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandSendEvent; import org.bukkit.event.player.PlayerGameModeChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -55,12 +53,6 @@ public class WorldEditListener implements Listener { private WorldEditPlugin plugin; - /** - * Called when a player plays an animation, such as an arm swing - * - * @param event Relevant event details - */ - /** * Construct the object; * @@ -112,12 +104,8 @@ public void onPlayerInteract(PlayerInteractEvent event) { return; } - try { - if (event.getHand() == EquipmentSlot.OFF_HAND) { - return; // TODO api needs to be able to get either hand depending on event - // for now just ignore all off hand interacts - } - } catch (NoSuchMethodError | NoSuchFieldError ignored) { + if (event.getHand() == EquipmentSlot.OFF_HAND) { + return; } final Player player = plugin.wrapPlayer(event.getPlayer()); @@ -143,7 +131,6 @@ public void onPlayerInteract(PlayerInteractEvent event) { event.setCancelled(true); } - } else if (action == Action.RIGHT_CLICK_BLOCK) { final Block clickedBlock = event.getClickedBlock(); final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 11fc7de09d..3c147456c0 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 @@ -58,6 +58,10 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldInitEvent; import org.bukkit.plugin.java.JavaPlugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -102,11 +106,6 @@ public void onLoad() { // Setup platform server = new BukkitServerInterface(this, getServer()); worldEdit.getPlatformManager().register(server); - loadAdapter(); // Need an adapter to work with special blocks with NBT data - setupRegistries(); - worldEdit.loadMappings(); - - loadConfig(); // Load configuration } /** @@ -114,8 +113,6 @@ public void onLoad() { */ @Override public void onEnable() { - setupTags(); // these have to be done post-world since they rely on MC registries. the other ones just use Bukkit enums - PermissionsResolverManager.initialize(this); // Setup permission resolver // Register CUI @@ -125,10 +122,8 @@ public void onEnable() { // Now we can register events getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); - // If we are on MCPC+/Cauldron, then Forge will have already loaded - // Forge WorldEdit and there's (probably) not going to be any other - // platforms to be worried about... at the current time of writing - WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); + // register this so we can load world-dependent data right as the first world is loading + getServer().getPluginManager().registerEvents(new WorldInitListener(), this); // Enable metrics new Metrics(this); @@ -433,4 +428,20 @@ BukkitImplAdapter getBukkitImplAdapter() { return bukkitAdapter; } + private class WorldInitListener implements Listener { + private boolean loaded = false; + @EventHandler(priority = EventPriority.LOWEST) + public void onWorldInit(@SuppressWarnings("unused") WorldInitEvent event) { + if (loaded) return; + loaded = true; + + loadAdapter(); // Need an adapter to work with special blocks with NBT data + setupRegistries(); + WorldEdit.getInstance().loadMappings(); + loadConfig(); // Load configuration + setupTags(); + + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); + } + } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index 68782f2265..4e96ad45c0 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -48,6 +49,14 @@ public interface BukkitImplAdapter { */ int getDataVersion(); + /** + * Get a data fixer, or null if not supported + * + * @return the data fixer + */ + @Nullable + DataFixer getDataFixer(); + /** * Get the block at the given location. * diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java index 612290995f..950e4f862e 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java @@ -157,7 +157,7 @@ public BukkitImplAdapter loadAdapter() throws AdapterLoadException { if (BukkitImplAdapter.class.isAssignableFrom(cls)) { return (BukkitImplAdapter) cls.newInstance(); } else { - log.warn("Failed to load the Bukkit adapter class '" + className + + log.debug("Failed to load the Bukkit adapter class '" + className + "' because it does not implement " + BukkitImplAdapter.class.getCanonicalName()); } } catch (ClassNotFoundException e) { diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1$1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1$1.class deleted file mode 100644 index 68556cab91..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1$1.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1.class deleted file mode 100644 index de8e81b01a..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2$1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2$1.class deleted file mode 100644 index e8e38065a2..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2$1.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2.class deleted file mode 100644 index 22e2b2ba3d..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2_2$1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2_2$1.class deleted file mode 100644 index 86e5c9d332..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2_2$1.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2_2.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2_2.class deleted file mode 100644 index d2291fb600..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2_2.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1$1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1$1.class deleted file mode 100644 index 66d9acaf19..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1$1.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1.class deleted file mode 100644 index da36d279ff..0000000000 Binary files a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1.class and /dev/null differ diff --git a/worldedit-bukkit/src/main/resources/plugin.yml b/worldedit-bukkit/src/main/resources/plugin.yml index e64d26bdce..27808257fe 100644 --- a/worldedit-bukkit/src/main/resources/plugin.yml +++ b/worldedit-bukkit/src/main/resources/plugin.yml @@ -1,6 +1,7 @@ name: WorldEdit main: com.sk89q.worldedit.bukkit.WorldEditPlugin version: "${internalVersion}" +load: STARTUP api-version: 1.13 # Permissions aren't here. Read http://wiki.sk89q.com/wiki/WEPIF/DinnerPerms 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 0000000000..74ff20abd7 Binary files /dev/null and b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar differ diff --git a/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java b/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java index eb0c2e3826..cde191e64b 100644 --- a/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java +++ b/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java @@ -19,7 +19,10 @@ package com.sk89q.worldedit.blocks; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTUtils; import com.sk89q.jnbt.ShortTag; @@ -38,17 +41,17 @@ public class MobSpawnerBlock extends BaseBlock { private String mobType; - private short delay; + private short delay = -1; // advanced mob spawner features - private short spawnCount; - private short spawnRange; + private short spawnCount = 4; + private short spawnRange = 4; private CompoundTag spawnData; private ListTag spawnPotentials; - private short minSpawnDelay; - private short maxSpawnDelay; - private short maxNearbyEntities; - private short requiredPlayerRange; + private short minSpawnDelay = 200; + private short maxSpawnDelay = 800; + private short maxNearbyEntities = 6; + private short requiredPlayerRange = 16; /** * Construct the mob spawner block with a specified data value. @@ -119,7 +122,6 @@ public String getNbtId() { @Override public CompoundTag getNbtData() { Map values = new HashMap<>(); - values.put("EntityId", new StringTag(mobType)); values.put("Delay", new ShortTag(delay)); values.put("SpawnCount", new ShortTag(spawnCount)); values.put("SpawnRange", new ShortTag(spawnRange)); @@ -127,10 +129,16 @@ public CompoundTag getNbtData() { values.put("MaxSpawnDelay", new ShortTag(maxSpawnDelay)); values.put("MaxNearbyEntities", new ShortTag(maxNearbyEntities)); values.put("RequiredPlayerRange", new ShortTag(requiredPlayerRange)); - if (spawnData != null) { + if (spawnData == null) { + values.put("SpawnData", new CompoundTag(ImmutableMap.of("id", new StringTag(mobType)))); + } else { values.put("SpawnData", new CompoundTag(spawnData.getValue())); } - if (spawnPotentials != null) { + if (spawnPotentials == null) { + values.put("SpawnPotentials", new ListTag(CompoundTag.class, ImmutableList.of( + new CompoundTag(ImmutableMap.of("Weight", new IntTag(1), "Entity", + new CompoundTag(ImmutableMap.of("id", new StringTag(mobType)))))))); + } else { values.put("SpawnPotentials", new ListTag(CompoundTag.class, spawnPotentials.getValue())); } @@ -150,19 +158,27 @@ public void setNbtData(CompoundTag rootTag) { throw new RuntimeException("'MobSpawner' tile entity expected"); } - StringTag mobTypeTag; + CompoundTag spawnDataTag; + String mobType; ShortTag delayTag; try { - mobTypeTag = NBTUtils.getChildTag(values, "EntityId", StringTag.class); + spawnDataTag = NBTUtils.getChildTag(values, "SpawnData", CompoundTag.class); + mobType = spawnDataTag.getString("id"); + if (mobType.equals("")) { + throw new InvalidFormatException("No spawn id."); + } + this.mobType = mobType; + } catch (InvalidFormatException ignored) { + throw new RuntimeException("Invalid mob spawner data: no SpawnData and/or no Delay"); + } + try { delayTag = NBTUtils.getChildTag(values, "Delay", ShortTag.class); + this.delay = delayTag.getValue(); } catch (InvalidFormatException ignored) { - throw new RuntimeException("Invalid mob spawner data: no EntityId and/or no Delay"); + this.delay = -1; } - this.mobType = mobTypeTag.getValue(); - this.delay = delayTag.getValue(); - ShortTag spawnCountTag = null; ShortTag spawnRangeTag = null; ShortTag minSpawnDelayTag = null; @@ -170,7 +186,6 @@ public void setNbtData(CompoundTag rootTag) { ShortTag maxNearbyEntitiesTag = null; ShortTag requiredPlayerRangeTag = null; ListTag spawnPotentialsTag = null; - CompoundTag spawnDataTag = null; try { spawnCountTag = NBTUtils.getChildTag(values, "SpawnCount", ShortTag.class); } catch (InvalidFormatException ignored) { @@ -199,10 +214,6 @@ public void setNbtData(CompoundTag rootTag) { spawnPotentialsTag = NBTUtils.getChildTag(values, "SpawnPotentials", ListTag.class); } catch (InvalidFormatException ignored) { } - try { - spawnDataTag = NBTUtils.getChildTag(values, "SpawnData", CompoundTag.class); - } catch (InvalidFormatException ignored) { - } if (spawnCountTag != null) { this.spawnCount = spawnCountTag.getValue(); @@ -225,9 +236,6 @@ public void setNbtData(CompoundTag rootTag) { if (spawnPotentialsTag != null) { this.spawnPotentials = new ListTag(CompoundTag.class, spawnPotentialsTag.getValue()); } - if (spawnDataTag != null) { - this.spawnData = new CompoundTag(spawnDataTag.getValue()); - } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java index 49c323d173..a5b7b6c562 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/UnknownDirectionException.java @@ -32,6 +32,7 @@ public class UnknownDirectionException extends WorldEditException { * @param dir the input that was tried */ public UnknownDirectionException(String dir) { + super("Unknown direction: " + dir); this.dir = dir; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java b/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java index 38c7cbd26b..c9e9262229 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.BlockInteractEvent; @@ -44,6 +46,7 @@ import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.concurrency.EvenMoreExecutors; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.util.io.file.FileSelectionAbortedException; import com.sk89q.worldedit.util.io.file.FilenameException; @@ -102,6 +105,7 @@ public final class WorldEdit { private final PlatformManager platformManager = new PlatformManager(this); private final EditSessionFactory editSessionFactory = new EditSessionFactory.EditSessionFactoryImpl(eventBus); private final SessionManager sessions = new SessionManager(this); + private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20));; private final Supervisor supervisor = new SimpleSupervisor(); private final BlockFactory blockFactory = new BlockFactory(this); @@ -152,7 +156,7 @@ public EventBus getEventBus() { } /** - * Get the supervisor. + * Get the supervisor. Internal, not for API use. * * @return the supervisor */ @@ -160,6 +164,15 @@ public Supervisor getSupervisor() { return supervisor; } + /** + * Get the executor service. Internal, not for API use. + * + * @return the executor service + */ + public ListeningExecutorService getExecutorService() { + return executorService; + } + /** * Get the block factory from which new {@link BlockStateHolder}s can be * constructed. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ClothColor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ClothColor.java deleted file mode 100644 index 64719c1138..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/ClothColor.java +++ /dev/null @@ -1,100 +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.blocks; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import javax.annotation.Nullable; - -/** - * The colors for wool. - * - *

This class may be removed in the future.

- */ -public enum ClothColor { - - WHITE("White", "white"), - ORANGE("Orange", "orange"), - MAGENTA("Magenta", "magenta"), - LIGHT_BLUE("Light blue", "lightblue"), - YELLOW("Yellow", "yellow"), - LIGHT_GREEN("Light green", "lightgreen"), - PINK("Pink", "pink", "lightred"), - GRAY("Gray", "grey", "gray"), - LIGHT_GRAY("Light gray", "lightgrey", "lightgray"), - CYAN("Cyan", "cyan", "turquoise"), - PURPLE("Purple", "purple", "violet"), - BLUE("Blue", "blue"), - BROWN("Brown", "brown", "cocoa", "coffee"), - DARK_GREEN("Dark green", "green", "darkgreen", "cactusgreen", "cactigreen"), - RED("Red", "red"), - BLACK("Black", "black"); - /** - * Stores a map of the names for fast access. - */ - private static final Map lookup = new HashMap<>(); - - private final String name; - private final String[] lookupKeys; - - static { - for (ClothColor type : EnumSet.allOf(ClothColor.class)) { - for (String key : type.lookupKeys) { - lookup.put(key, type); - } - } - } - - - /** - * Construct the type. - * - * @param name the name of the color - * @param lookupKeys a name to refer to the color by - */ - ClothColor(String name, String ... lookupKeys) { - this.name = name; - this.lookupKeys = lookupKeys; - } - - /** - * Return type from name. May return null. - * - * @param name the name of the color - * @return a color or null - */ - @Nullable - public static ClothColor lookup(String name) { - return lookup.get(name.toLowerCase(Locale.ROOT)); - } - - /** - * Get user-friendly item name. - * - * @return the name - */ - public String getName() { - return name; - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/metadata/MobType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/metadata/MobType.java deleted file mode 100644 index 762f39715b..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/metadata/MobType.java +++ /dev/null @@ -1,66 +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.blocks.metadata; - -/** - * Represents the possible types of mobs. - */ -public enum MobType { - BAT("Bat"), - BLAZE("Blaze"), - CAVE_SPIDER("CaveSpider"), - CHICKEN("Chicken"), - COW("Cow"), - CREEPER("Creeper"), - ENDERDRAGON("EnderDragon"), - ENDERMAN("Enderman"), - GHAST("Ghast"), - GIANT("Giant"), - VILLAGER_GOLEM("VillagerGolem"), - HORSE("EntityHorse"), - MAGMA_CUBE("LavaSlime"), - MOOSHROOM("MushroomCow"), - OCELOT("Ozelot"), - PIG("Pig"), - PIG_ZOMBIE("PigZombie"), - SHEEP("Sheep"), - SILVERFISH("Silverfish"), - SKELETON("Skeleton"), - SLIME("Slime"), - SNOWMAN("SnowMan"), - SPIDER("Spider"), - SQUID("Squid"), - VILLAGER("Villager"), - WITCH("Witch"), - WITHER("WitherBoss"), - WOLF("Wolf"), - ZOMBIE("Zombie"); - - private final String name; - - MobType(String name) { - this.name = name; - } - - public String getName() { - return name; - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index c63397fb0e..8b0e20a1fb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -143,7 +143,7 @@ public void paste(Player player, LocalSession session, EditSession editSession, boolean pasteEntities, @Switch(name = 'b', desc = "Paste biomes if available") boolean pasteBiomes, - @ArgFlag(name = 'm', desc = "Skip blocks matching this mask", def = "") + @ArgFlag(name = 'm', desc = "Only paste blocks matching this mask", def = "") Mask sourceMask) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ExpandCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ExpandCommands.java new file mode 100644 index 0000000000..e931f5225c --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ExpandCommands.java @@ -0,0 +1,152 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command; + +import com.google.common.collect.ImmutableSet; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.command.util.Logging; +import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.MultiDirection; +import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import org.enginehub.piston.Command; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.CommandManagerService; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.part.SubCommandPart; + +import java.util.List; + +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; +import static com.sk89q.worldedit.internal.command.CommandUtil.requireIV; + +/** + * Extracted from {@link SelectionCommands} to allow importing of {@link Command}. + */ +@CommandContainer +public class ExpandCommands { + + public static void register(CommandRegistrationHandler registration, + CommandManager commandManager, + CommandManagerService commandManagerService) { + // Collect the general expand command + CommandManager collect = commandManagerService.newCommandManager(); + + registration.register( + collect, + ExpandCommandsRegistration.builder(), + new ExpandCommands() + ); + + Command expandBaseCommand = collect.getCommand("/expand") + .orElseThrow(() -> new IllegalStateException("No /expand command")); + + commandManager.register("/expand", command -> { + command.condition(new PermissionCondition(ImmutableSet.of("worldedit.selection.expand"))); + + command.addPart(SubCommandPart.builder( + TranslatableComponent.of("vert"), + TextComponent.of("Vertical expansion sub-command") + ) + .withCommands(ImmutableSet.of(createVertCommand(commandManager))) + .optional() + .build()); + + command.addParts(expandBaseCommand.getParts()); + command.action(expandBaseCommand.getAction()); + command.description(expandBaseCommand.getDescription()); + }); + } + + private static Command createVertCommand(CommandManager commandManager) { + return commandManager.newCommand("vert") + .description(TextComponent.of("Vertically expand the selection to world limits.")) + .action(parameters -> { + expandVert( + requireIV(Key.of(LocalSession.class), "localSession", parameters), + requireIV(Key.of(Player.class), "localSession", parameters) + ); + return 1; + }) + .build(); + } + + private static void expandVert(LocalSession session, Player player) throws IncompleteRegionException { + Region region = session.getSelection(player.getWorld()); + try { + int oldSize = region.getArea(); + region.expand( + BlockVector3.at(0, (player.getWorld().getMaxY() + 1), 0), + BlockVector3.at(0, -(player.getWorld().getMaxY() + 1), 0)); + session.getRegionSelector(player.getWorld()).learnChanges(); + int newSize = region.getArea(); + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + player.print("Region expanded " + (newSize - oldSize) + + " blocks [top-to-bottom]."); + } catch (RegionOperationException e) { + player.printError(e.getMessage()); + } + } + + @org.enginehub.piston.annotation.Command( + name = "/expand", + desc = "Expand the selection area" + ) + @Logging(REGION) + public void expand(Player player, LocalSession session, + @Arg(desc = "Amount to expand the selection by, can be `vert` to expand to the whole vertical column") + int amount, + @Arg(desc = "Amount to expand the selection by in the other direction", def = "0") + int reverseAmount, + @Arg(desc = "Direction to expand", def = Direction.AIM) + @MultiDirection + List direction) throws WorldEditException { + Region region = session.getSelection(player.getWorld()); + int oldSize = region.getArea(); + + if (reverseAmount == 0) { + for (BlockVector3 dir : direction) { + region.expand(dir.multiply(amount)); + } + } else { + for (BlockVector3 dir : direction) { + region.expand(dir.multiply(amount), dir.multiply(-reverseAmount)); + } + } + + session.getRegionSelector(player.getWorld()).learnChanges(); + int newSize = region.getArea(); + + session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); + + player.print("Region expanded " + (newSize - oldSize) + " block(s)."); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 5f5229aeb7..6c370a1a1c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -47,6 +47,7 @@ import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; import static com.sk89q.worldedit.command.util.Logging.LogMode.POSITION; +import static com.sk89q.worldedit.internal.command.CommandUtil.checkCommandArgument; /** * Commands for the generation of shapes and other objects. @@ -203,9 +204,7 @@ public int forestGen(Player player, LocalSession session, EditSession editSessio TreeType type, @Arg(desc = "The density of the forest, between 0 and 100", def = "5") double density) throws WorldEditException { - if (density < 0 || density > 100) { - throw new IllegalArgumentException("Density must be between 0 and 100"); - } + checkCommandArgument(0 <= density && density <= 100, "Density must be between 0 and 100"); density = density / 100; int affected = editSession.makeForest(session.getPlacementPosition(player), size, density, type); player.print(affected + " trees created."); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index a8591e1def..ec3c09e4a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -61,10 +61,10 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; import static com.sk89q.worldedit.command.util.Logging.LogMode.ORIENTATION_REGION; import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; +import static com.sk89q.worldedit.internal.command.CommandUtil.checkCommandArgument; import static com.sk89q.worldedit.regions.Regions.asFlatRegion; import static com.sk89q.worldedit.regions.Regions.maximumBlockY; import static com.sk89q.worldedit.regions.Regions.minimumBlockY; @@ -125,7 +125,7 @@ public int line(Player player, EditSession editSession, player.printError("//line only works with cuboid selections"); return 0; } - checkArgument(thickness >= 0, "Thickness must be >= 0"); + checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); CuboidRegion cuboidregion = (CuboidRegion) region; BlockVector3 pos1 = cuboidregion.getPos1(); @@ -155,7 +155,7 @@ public int curve(Player player, EditSession editSession, player.printError("//curve only works with convex polyhedral selections"); return 0; } - checkArgument(thickness >= 0, "Thickness must be >= 0"); + checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region; List vectors = new ArrayList<>(cpregion.getVertices()); @@ -294,9 +294,7 @@ public int move(Player player, EditSession editSession, LocalSession session, boolean moveSelection, @Switch(name = 'a', desc = "Ignore air blocks") boolean ignoreAirBlocks) throws WorldEditException { - if (count < 1) { - throw new IllegalArgumentException("Count must be >= 1"); - } + checkCommandArgument(count >= 1, "Count must be >= 1"); int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, replace); @@ -433,7 +431,7 @@ public int hollow(Player player, EditSession editSession, int thickness, @Arg(desc = "The pattern of blocks to replace the hollowed area with", def = "air") Pattern pattern) throws WorldEditException { - checkArgument(thickness >= 0, "Thickness must be >= 0"); + checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); int affected = editSession.hollowOutRegion(region, thickness, pattern); player.print(affected + " block(s) have been changed."); @@ -451,7 +449,7 @@ public int forest(Player player, EditSession editSession, @Selection Region regi TreeType type, @Arg(desc = "The density of the forest", def = "5") double density) throws WorldEditException { - checkArgument(0 <= density && density <= 100, "Density must be in [0, 100]"); + checkCommandArgument(0 <= density && density <= 100, "Density must be in [0, 100]"); int affected = editSession.makeForest(region, density / 100, type); player.print(affected + " trees created."); return affected; @@ -466,7 +464,7 @@ public int forest(Player player, EditSession editSession, @Selection Region regi public int flora(Player player, EditSession editSession, @Selection Region region, @Arg(desc = "The density of the forest", def = "5") double density) throws WorldEditException { - checkArgument(0 <= density && density <= 100, "Density must be in [0, 100]"); + checkCommandArgument(0 <= density && density <= 100, "Density must be in [0, 100]"); density = density / 100; FloraGenerator generator = new FloraGenerator(editSession); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 829af8a9ca..fee1717168 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.command.util.AsyncCommandBuilder; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.entity.Player; @@ -60,6 +61,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.Callable; import static com.google.common.base.Preconditions.checkNotNull; @@ -113,20 +115,13 @@ public void load(Player player, LocalSession session, return; } - try (Closer closer = Closer.create()) { - FileInputStream fis = closer.register(new FileInputStream(f)); - BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); - ClipboardReader reader = closer.register(format.getReader(bis)); - - Clipboard clipboard = reader.read(); - session.setClipboard(new ClipboardHolder(clipboard)); - - log.info(player.getName() + " loaded " + f.getCanonicalPath()); - player.print(filename + " loaded. Paste it with //paste"); - } catch (IOException e) { - player.printError("Schematic could not read or it does not exist: " + e.getMessage()); - log.warn("Failed to load schematic: " + e.getMessage()); - } + SchematicLoadTask task = new SchematicLoadTask(player, f, format); + AsyncCommandBuilder.wrap(task, player) + .registerWithSupervisor(worldEdit.getSupervisor(), "Loading schematic " + filename) + .sendMessageAfterDelay("(Please wait... loading schematic.)") + .onSuccess(filename + " loaded. Paste it with //paste", session::setClipboard) + .onFailure("Failed to load schematic", worldEdit.getPlatformManager().getPlatformCommandManager().getExceptionConverter()) + .buildAndExec(worldEdit.getExecutorService()); } @Command( @@ -165,42 +160,24 @@ public void save(Player player, LocalSession session, } } - ClipboardHolder holder = session.getClipboard(); - Clipboard clipboard = holder.getClipboard(); - Transform transform = holder.getTransform(); - Clipboard target; - - // If we have a transform, bake it into the copy - if (!transform.isIdentity()) { - FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform); - target = new BlockArrayClipboard(result.getTransformedRegion()); - target.setOrigin(clipboard.getOrigin()); - Operations.completeLegacy(result.copyTo(target)); - } else { - target = clipboard; - } - // Create parent directories File parent = f.getParentFile(); if (parent != null && !parent.exists()) { if (!parent.mkdirs()) { throw new StopExecutionException(TextComponent.of( - "Could not create folder for schematics!")); + "Could not create folder for schematics!")); } } - try (Closer closer = Closer.create()) { - FileOutputStream fos = closer.register(new FileOutputStream(f)); - BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos)); - ClipboardWriter writer = closer.register(format.getWriter(bos)); - writer.write(target); + ClipboardHolder holder = session.getClipboard(); - log.info(player.getName() + " saved " + f.getCanonicalPath() + (overwrite ? " (overwriting previous file)" : "")); - player.print(filename + " saved" + (overwrite ? " (overwriting previous file)." : ".")); - } catch (IOException e) { - player.printError("Schematic could not written: " + e.getMessage()); - log.warn("Failed to write a saved clipboard", e); - } + SchematicSaveTask task = new SchematicSaveTask(player, f, format, holder, overwrite); + AsyncCommandBuilder.wrap(task, player) + .registerWithSupervisor(worldEdit.getSupervisor(), "Saving schematic " + filename) + .sendMessageAfterDelay("(Please wait... saving schematic.)") + .onSuccess(filename + " saved" + (overwrite ? " (overwriting previous file)." : "."), null) + .onFailure("Failed to load schematic", worldEdit.getPlatformManager().getPlatformCommandManager().getExceptionConverter()) + .buildAndExec(worldEdit.getExecutorService()); } @Command( @@ -329,4 +306,71 @@ private List allFiles(File root) { return fileList; } + private static class SchematicLoadTask implements Callable { + private final Player player; + private final File file; + private final ClipboardFormat format; + + SchematicLoadTask(Player player, File file, ClipboardFormat format) { + this.player = player; + this.file = file; + this.format = format; + } + + @Override + public ClipboardHolder call() throws Exception { + try (Closer closer = Closer.create()) { + FileInputStream fis = closer.register(new FileInputStream(file)); + BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); + ClipboardReader reader = closer.register(format.getReader(bis)); + + Clipboard clipboard = reader.read(); + log.info(player.getName() + " loaded " + file.getCanonicalPath()); + return new ClipboardHolder(clipboard); + } + } + } + + private static class SchematicSaveTask implements Callable { + private final Player player; + private final File file; + private final ClipboardFormat format; + private final ClipboardHolder holder; + private final boolean overwrite; + + SchematicSaveTask(Player player, File file, ClipboardFormat format, ClipboardHolder holder, boolean overwrite) { + this.player = player; + this.file = file; + this.format = format; + this.holder = holder; + this.overwrite = overwrite; + } + + @Override + public Void call() throws Exception { + Clipboard clipboard = holder.getClipboard(); + Transform transform = holder.getTransform(); + Clipboard target; + + // If we have a transform, bake it into the copy + if (transform.isIdentity()) { + target = clipboard; + } else { + FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform); + target = new BlockArrayClipboard(result.getTransformedRegion()); + target.setOrigin(clipboard.getOrigin()); + Operations.completeLegacy(result.copyTo(target)); + } + + try (Closer closer = Closer.create()) { + FileOutputStream fos = closer.register(new FileOutputStream(file)); + BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos)); + ClipboardWriter writer = closer.register(format.getWriter(bos)); + writer.write(target); + + log.info(player.getName() + " saved " + file.getCanonicalPath() + (overwrite ? " (overwriting previous file)" : "")); + } + return null; + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index bac02819ad..155385c52f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -24,7 +24,6 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; -import com.sk89q.worldedit.command.argument.ExpandAmount; import com.sk89q.worldedit.command.argument.SelectorChoice; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; @@ -267,63 +266,6 @@ public void toggleWand(Player player, LocalSession session) throws WorldEditExce } } - @Command( - name = "/expand", - desc = "Expand the selection area" - ) - @Logging(REGION) - @CommandPermissions("worldedit.selection.expand") - public void expand(Player player, LocalSession session, - @Arg(desc = "Amount to expand the selection by, can be `vert` to expand to the whole vertical column") - ExpandAmount amount, - @Arg(desc = "Amount to expand the selection by in the other direction", def = "0") - int reverseAmount, - @Arg(desc = "Direction to expand", def = Direction.AIM) - @MultiDirection - List direction) throws WorldEditException { - - // Special syntax (//expand vert) to expand the selection between - // sky and bedrock. - if (amount.isVert()) { - Region region = session.getSelection(player.getWorld()); - try { - int oldSize = region.getArea(); - region.expand( - BlockVector3.at(0, (player.getWorld().getMaxY() + 1), 0), - BlockVector3.at(0, -(player.getWorld().getMaxY() + 1), 0)); - session.getRegionSelector(player.getWorld()).learnChanges(); - int newSize = region.getArea(); - session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); - player.print("Region expanded " + (newSize - oldSize) - + " blocks [top-to-bottom]."); - } catch (RegionOperationException e) { - player.printError(e.getMessage()); - } - - return; - } - - Region region = session.getSelection(player.getWorld()); - int oldSize = region.getArea(); - - if (reverseAmount == 0) { - for (BlockVector3 dir : direction) { - region.expand(dir.multiply(amount.getAmount())); - } - } else { - for (BlockVector3 dir : direction) { - region.expand(dir.multiply(amount.getAmount()), dir.multiply(-reverseAmount)); - } - } - - session.getRegionSelector(player.getWorld()).learnChanges(); - int newSize = region.getArea(); - - session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); - - player.print("Region expanded " + (newSize - oldSize) + " block(s)."); - } - @Command( name = "/contract", desc = "Contract the selection area" diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index e053eccb8f..2597372d55 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -51,7 +51,7 @@ public ToolUtilCommands(WorldEdit we) { @CommandPermissions("worldedit.superpickaxe") public void togglePickaxe(Player player, LocalSession session, @Arg(desc = "The new super pickaxe state", def = "") - Boolean superPickaxe) throws WorldEditException { + Boolean superPickaxe) { boolean hasSuperPickAxe = session.hasSuperPickAxe(); if (superPickaxe != null && superPickaxe == hasSuperPickAxe) { player.printError("Super pickaxe already " + (superPickaxe ? "enabled" : "disabled") + "."); @@ -75,11 +75,10 @@ public void togglePickaxe(Player player, LocalSession session, public void mask(Player player, LocalSession session, @Arg(desc = "The mask to set", def = "") Mask mask) throws WorldEditException { + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(mask); if (mask == null) { - session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(null); player.print("Brush mask disabled."); } else { - session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(mask); player.print("Brush mask set."); } } @@ -122,4 +121,20 @@ public void size(Player player, LocalSession session, session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setSize(size); player.print("Brush size set."); } + + @Command( + name = "tracemask", + desc = "Set the mask used to stop tool traces" + ) + @CommandPermissions("worldedit.brush.options.tracemask") + public void traceMask(Player player, LocalSession session, + @Arg(desc = "The trace mask to set", def = "") + Mask mask) throws WorldEditException { + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setTraceMask(mask); + if (mask == null) { + player.print("Trace mask disabled."); + } else { + player.print("Trace mask set."); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index af59e5adc5..3b6217154b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -515,11 +515,13 @@ public void calc(Actor actor, ) @CommandPermissions("worldedit.help") public void help(Actor actor, + @Switch(name = 's', desc = "List sub-commands of the given command, if applicable") + boolean listSubCommands, @Arg(desc = "The page to retrieve", def = "1") int page, @Arg(desc = "The command to retrieve help for", def = "", variable = true) List command) throws WorldEditException { - PrintCommandHelp.help(command, page, we, actor); + PrintCommandHelp.help(command, page, listSubCommands, we, actor); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index 0d19d1538c..1a383a7670 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -67,7 +67,7 @@ public WorldEditCommands(WorldEdit we) { aliases = { "ver" }, desc = "Get WorldEdit version" ) - public void version(Actor actor) throws WorldEditException { + public void version(Actor actor) { actor.print("WorldEdit version " + WorldEdit.getVersion()); actor.print("https://github.com/EngineHub/worldedit/"); @@ -90,7 +90,7 @@ public void version(Actor actor) throws WorldEditException { desc = "Reload configuration" ) @CommandPermissions("worldedit.reload") - public void reload(Actor actor) throws WorldEditException { + public void reload(Actor actor) { we.getPlatformManager().queryCapability(Capability.CONFIGURATION).reload(); we.getEventBus().post(new ConfigurationLoadEvent(we.getPlatformManager().queryCapability(Capability.CONFIGURATION).getConfiguration())); actor.print("Configuration reloaded!"); @@ -100,7 +100,7 @@ public void reload(Actor actor) throws WorldEditException { name = "report", desc = "Writes a report on WorldEdit" ) - @CommandPermissions({"worldedit.report"}) + @CommandPermissions("worldedit.report") public void report(Actor actor, @Switch(name = 'p', desc = "Pastebins the report") boolean pastebin) throws WorldEditException { @@ -110,7 +110,7 @@ public void report(Actor actor, String result = report.toString(); try { - File dest = new File(we.getWorkingDirectoryFile(we.getConfiguration().saveDir), "report.txt"); + File dest = new File(we.getConfiguration().getWorkingDirectory(), "report.txt"); Files.write(result, dest, Charset.forName("UTF-8")); actor.print("WorldEdit report written to " + dest.getAbsolutePath()); } catch (IOException e) { @@ -119,10 +119,7 @@ public void report(Actor actor, if (pastebin) { actor.checkPermission("worldedit.report.pastebin"); - ActorCallbackPaste.pastebin( - we.getSupervisor(), actor, result, "WorldEdit report: %s.report", - WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getExceptionConverter() - ); + ActorCallbackPaste.pastebin(we.getSupervisor(), actor, result, "WorldEdit report: %s.report"); } } @@ -130,7 +127,7 @@ public void report(Actor actor, name = "cui", desc = "Complete CUI handshake (internal usage)" ) - public void cui(Player player, LocalSession session) throws WorldEditException { + public void cui(Player player, LocalSession session) { session.setCUISupport(true); session.dispatchCUISetup(player); } @@ -141,7 +138,7 @@ public void cui(Player player, LocalSession session) throws WorldEditException { ) public void tz(Player player, LocalSession session, @Arg(desc = "The timezone to set") - String timezone) throws WorldEditException { + String timezone) { try { ZoneId tz = ZoneId.of(timezone); session.setTimezone(tz); @@ -160,10 +157,12 @@ public void tz(Player player, LocalSession session, ) @CommandPermissions("worldedit.help") public void help(Actor actor, + @Switch(name = 's', desc = "List sub-commands of the given command, if applicable") + boolean listSubCommands, @Arg(desc = "The page to retrieve", def = "1") int page, @Arg(desc = "The command to retrieve help for", def = "", variable = true) List command) throws WorldEditException { - PrintCommandHelp.help(command, page, we, actor); + PrintCommandHelp.help(command, page, listSubCommands, we, actor); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmount.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmount.java deleted file mode 100644 index 062a330c9c..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmount.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.command.argument; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkNotNull; - -public final class ExpandAmount { - - public static ExpandAmount vert() { - return new ExpandAmount(null); - } - - public static ExpandAmount from(int amount) { - return new ExpandAmount(amount); - } - - @Nullable - private final Integer amount; - - private ExpandAmount(@Nullable Integer amount) { - this.amount = amount; - } - - public boolean isVert() { - return amount == null; - } - - public int getAmount() { - return checkNotNull(amount, "This amount is vertical, i.e. undefined"); - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmountConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmountConverter.java deleted file mode 100644 index b54311bee4..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/ExpandAmountConverter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.command.argument; - -import com.google.common.reflect.TypeToken; -import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.formatting.text.TextComponent; -import org.enginehub.piston.CommandManager; -import org.enginehub.piston.converter.ArgumentConverter; -import org.enginehub.piston.converter.ArgumentConverters; -import org.enginehub.piston.converter.ConversionResult; -import org.enginehub.piston.converter.SuccessfulConversion; -import org.enginehub.piston.inject.InjectedValueAccess; -import org.enginehub.piston.inject.Key; - -import java.util.List; -import java.util.stream.Stream; - -import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; - -public class ExpandAmountConverter implements ArgumentConverter { - - public static void register(CommandManager commandManager) { - commandManager.registerConverter(Key.of(ExpandAmount.class), new ExpandAmountConverter()); - } - - private final ArgumentConverter integerConverter = - ArgumentConverters.get(TypeToken.of(int.class)); - - private ExpandAmountConverter() { - } - - @Override - public Component describeAcceptableArguments() { - return TextComponent.of("`vert` or ").append(integerConverter.describeAcceptableArguments()); - } - - @Override - public List getSuggestions(String input) { - return limitByPrefix(Stream.concat( - Stream.of("vert"), integerConverter.getSuggestions(input).stream() - ), input); - } - - @Override - public ConversionResult convert(String argument, InjectedValueAccess context) { - if (argument.equalsIgnoreCase("vert") - || argument.equalsIgnoreCase("vertical")) { - return SuccessfulConversion.fromSingle(ExpandAmount.vert()); - } - return integerConverter.convert(argument, context).mapSingle(ExpandAmount::from); - } -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java index 0de24e5a7c..b3b2b81081 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java @@ -81,6 +81,7 @@ public ConversionResult convert(String argument, InjectedValueAccess context) } } parserContext.setSession(session); + parserContext.setRestricted(true); try { return SuccessfulConversion.fromSingle( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index cb98b9a3a5..2e324f9ee4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -34,7 +34,6 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.MaskIntersection; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.Location; import javax.annotation.Nullable; @@ -47,6 +46,7 @@ public class BrushTool implements TraceTool { protected static int MAX_RANGE = 500; protected int range = -1; private Mask mask = null; + private Mask traceMask = null; private Brush brush = new SphereBrush(); @Nullable private Pattern material; @@ -86,6 +86,24 @@ public void setMask(Mask filter) { this.mask = filter; } + /** + * Get the mask used for identifying where to stop traces. + * + * @return the mask used to stop block traces + */ + public @Nullable Mask getTraceMask() { + return mask; + } + + /** + * Set the block mask used for identifying where to stop traces. + * + * @param traceMask the mask used to stop block traces + */ + public void setTraceMask(@Nullable Mask traceMask) { + this.traceMask = traceMask; + } + /** * Set the brush. * @@ -162,8 +180,7 @@ public void setRange(int range) { @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { - Location target = null; - target = player.getBlockTrace(getRange(), true); + Location target = player.getBlockTrace(getRange(), true, traceMask); if (target == null) { player.printError("No block in sight!"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java index 5fa95d04b5..48d3dbf7ae 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.util.Location; @@ -58,7 +59,6 @@ public boolean actSecondary(Platform server, LocalConfiguration config, Player p } return false; - } @Override @@ -78,12 +78,13 @@ public boolean actPrimary(Platform server, LocalConfiguration config, Player pla return false; } - public Location getTarget(Player player) { + private Location getTarget(Player player) { Location target; + Mask mask = getTraceMask(); if (this.range > -1) { - target = player.getBlockTrace(getRange(), true); + target = player.getBlockTrace(getRange(), true, mask); } else { - target = player.getBlockTrace(MAX_RANGE); + target = player.getBlockTrace(MAX_RANGE, false, mask); } if (target == null) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java index 155beb4e8e..27011c5ea5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java @@ -26,6 +26,7 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Location; @@ -91,8 +92,14 @@ public boolean actPrimary(Platform server, LocalConfiguration config, Player pla return false; } - public Location getTargetFace(Player player) { - Location target = player.getBlockTraceFace(getRange(), true); + private Location getTargetFace(Player player) { + Location target; + Mask mask = getTraceMask(); + if (this.range > -1) { + target = player.getBlockTrace(getRange(), true, mask); + } else { + target = player.getBlockTrace(MAX_RANGE, false, mask); + } if (target == null) { player.printError("No block in sight!"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandBuilder.java new file mode 100644 index 0000000000..d2f70a006c --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandBuilder.java @@ -0,0 +1,193 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.google.common.base.Strings; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.internal.command.exception.ExceptionConverter; +import com.sk89q.worldedit.util.formatting.component.ErrorFormat; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; +import com.sk89q.worldedit.util.task.FutureForwardingTask; +import com.sk89q.worldedit.util.task.Supervisor; +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.exception.CommandExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.concurrent.Callable; +import java.util.function.Consumer; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +public final class AsyncCommandBuilder { + + private static final Logger logger = LoggerFactory.getLogger(AsyncCommandBuilder.class); + + private final Callable callable; + private final Actor sender; + + @Nullable + private Supervisor supervisor; + @Nullable + private String description; + @Nullable + private String delayMessage; + + @Nullable + private Component successMessage; + @Nullable + private Consumer consumer; + + @Nullable + private Component failureMessage; + @Nullable + private ExceptionConverter exceptionConverter; + + private AsyncCommandBuilder(Callable callable, Actor sender) { + checkNotNull(callable); + checkNotNull(sender); + this.callable = callable; + this.sender = sender; + } + + public static AsyncCommandBuilder wrap(Callable callable, Actor sender) { + return new AsyncCommandBuilder<>(callable, sender); + } + + public AsyncCommandBuilder registerWithSupervisor(Supervisor supervisor, String description) { + this.supervisor = checkNotNull(supervisor); + this.description = checkNotNull(description); + return this; + } + + public AsyncCommandBuilder sendMessageAfterDelay(String message) { + this.delayMessage = checkNotNull(message); + return this; + } + + public AsyncCommandBuilder onSuccess(@Nullable Component message, @Nullable Consumer consumer) { + checkArgument(message != null || consumer != null, "Can't have null message AND consumer"); + this.successMessage = message; + this.consumer = consumer; + return this; + } + + public AsyncCommandBuilder onSuccess(@Nullable String message, @Nullable Consumer consumer) { + checkArgument(message != null || consumer != null, "Can't have null message AND consumer"); + this.successMessage = message == null ? null : TextComponent.of(message, TextColor.LIGHT_PURPLE); + this.consumer = consumer; + return this; + } + + public AsyncCommandBuilder onFailure(@Nullable Component message, @Nullable ExceptionConverter exceptionConverter) { + checkArgument(message != null || exceptionConverter != null, "Can't have null message AND exceptionConverter"); + this.failureMessage = message; + this.exceptionConverter = exceptionConverter; + return this; + } + + public AsyncCommandBuilder onFailure(@Nullable String message, @Nullable ExceptionConverter exceptionConverter) { + checkArgument(message != null || exceptionConverter != null, "Can't have null message AND exceptionConverter"); + this.failureMessage = message == null ? null : ErrorFormat.wrap(message); + this.exceptionConverter = exceptionConverter; + return this; + } + + public ListenableFuture buildAndExec(ListeningExecutorService executor) { + final ListenableFuture future = checkNotNull(executor).submit(this::runTask); + if (delayMessage != null) { + FutureProgressListener.addProgressListener(future, sender, delayMessage); + } + if (supervisor != null && description != null) { + supervisor.monitor(FutureForwardingTask.create(future, description, sender)); + } + return future; + } + + private T runTask() { + T result = null; + try { + result = callable.call(); + if (consumer != null) { + consumer.accept(result); + } + if (successMessage != null) { + sender.print(successMessage); + } + } catch (Exception orig) { + Component failure = failureMessage != null ? failureMessage : TextComponent.of("An error occurred"); + try { + if (exceptionConverter != null) { + try { + exceptionConverter.convert(orig); + throw orig; + } catch (CommandException converted) { + Component message; + + // TODO remove this once WG migrates to piston and can use piston exceptions everywhere + message = tryExtractOldCommandException(converted); + + if (message == null) { + if (Strings.isNullOrEmpty(converted.getMessage())) { + message = TextComponent.of("Unknown error."); + } else { + message = converted.getRichMessage(); + } + } + sender.print(failure.append(TextComponent.of(": ")).append(message)); + } + } else { + throw orig; + } + } catch (Throwable unknown) { + sender.print(failure.append(TextComponent.of(": Unknown error. Please see console."))); + logger.error("Uncaught exception occurred in task: " + description, orig); + } + } + return result; + } + + // this is needed right now since worldguard is still on the 2011 command framework which throws and converts + // com.sk89q.minecraft.util.commands.CommandException. the ExceptionConverter currently expects converted + // exceptions to be org.enginehub.piston.CommandException, throw it wraps the resulting InvocationTargetException in + // a CommandExecutionException. here, we unwrap those layers to retrieve the original WG error message + private Component tryExtractOldCommandException(CommandException converted) { + Component message = null; + if (converted instanceof CommandExecutionException) { + Throwable parentCause = converted; + while ((parentCause = parentCause.getCause()) != null) { + if (parentCause instanceof com.sk89q.minecraft.util.commands.CommandException) { + final String msg = parentCause.getMessage(); + if (!Strings.isNullOrEmpty(msg)) { + message = TextComponent.of(msg); + } + break; + } + } + } + return message; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandHelper.java deleted file mode 100644 index 842cd57529..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/AsyncCommandHelper.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.command.util; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.internal.command.exception.ExceptionConverter; -import com.sk89q.worldedit.util.task.FutureForwardingTask; -import com.sk89q.worldedit.util.task.Supervisor; -import com.sk89q.worldedit.world.World; - -import javax.annotation.Nullable; -import java.util.concurrent.ForkJoinPool; - -public class AsyncCommandHelper { - - private final ListenableFuture future; - private final Supervisor supervisor; - private final Actor sender; - private final ExceptionConverter exceptionConverter; - @Nullable - private Object[] formatArgs; - - private AsyncCommandHelper(ListenableFuture future, Supervisor supervisor, Actor sender, ExceptionConverter exceptionConverter) { - checkNotNull(future); - checkNotNull(supervisor); - checkNotNull(sender); - checkNotNull(exceptionConverter); - - this.future = future; - this.supervisor = supervisor; - this.sender = sender; - this.exceptionConverter = exceptionConverter; - } - - public AsyncCommandHelper formatUsing(Object... args) { - this.formatArgs = args; - return this; - } - - private String format(String message) { - if (formatArgs != null) { - return String.format(message, formatArgs); - } else { - return message; - } - } - - public AsyncCommandHelper registerWithSupervisor(String description) { - supervisor.monitor( - FutureForwardingTask.create( - future, format(description), sender)); - return this; - } - - public AsyncCommandHelper sendMessageAfterDelay(String message) { - FutureProgressListener.addProgressListener(future, sender, format(message)); - return this; - } - - public AsyncCommandHelper thenRespondWith(String success, String failure) { - // Send a response message - Futures.addCallback( - future, - new MessageFutureCallback.Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess(format(success)) - .onFailure(format(failure)) - .build(), - ForkJoinPool.commonPool()); - return this; - } - - public AsyncCommandHelper thenTellErrorsOnly(String failure) { - // Send a response message - Futures.addCallback( - future, - new MessageFutureCallback.Builder(sender) - .exceptionConverter(exceptionConverter) - .onFailure(format(failure)) - .build(), - ForkJoinPool.commonPool()); - return this; - } - - public AsyncCommandHelper forRegionDataLoad(World world, boolean silent) { - checkNotNull(world); - - formatUsing(world.getName()); - registerWithSupervisor("Loading region data for '%s'"); - if (silent) { - thenTellErrorsOnly("Failed to load regions '%s'"); - } else { - sendMessageAfterDelay("(Please wait... loading the region data for '%s')"); - thenRespondWith( - "Loaded region data for '%s'", - "Failed to load regions '%s'"); - } - - return this; - } - - public AsyncCommandHelper forRegionDataSave(World world, boolean silent) { - checkNotNull(world); - - formatUsing(world.getName()); - registerWithSupervisor("Saving region data for '%s'"); - if (silent) { - thenTellErrorsOnly("Failed to save regions '%s'"); - } else { - sendMessageAfterDelay("(Please wait... saving the region data for '%s')"); - thenRespondWith( - "Saved region data for '%s'", - "Failed to load regions '%s'"); - } - - return this; - } - - public static AsyncCommandHelper wrap(ListenableFuture future, Supervisor supervisor, Actor sender, ExceptionConverter exceptionConverter) { - return new AsyncCommandHelper(future, supervisor, sender, exceptionConverter); - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java deleted file mode 100644 index 2a330259e6..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.command.util; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.util.concurrent.FutureCallback; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.internal.command.exception.ExceptionConverter; -import org.enginehub.piston.exception.CommandException; - -import javax.annotation.Nullable; - -public class MessageFutureCallback implements FutureCallback { - - private final ExceptionConverter exceptionConverter; - private final Actor sender; - @Nullable - private final String success; - @Nullable - private final String failure; - - private MessageFutureCallback(ExceptionConverter exceptionConverter, Actor sender, @Nullable String success, @Nullable String failure) { - this.exceptionConverter = exceptionConverter; - this.sender = sender; - this.success = success; - this.failure = failure; - } - - @Override - public void onSuccess(@Nullable V v) { - if (success != null) { - sender.print(success); - } - } - - @Override - public void onFailure(@Nullable Throwable throwable) { - try { - exceptionConverter.convert(throwable); - } catch (CommandException e) { - String failure = this.failure != null ? this.failure : "An error occurred"; - String message = e.getMessage() != null ? e.getMessage() : "An unknown error occurred. Please see the console!"; - sender.printError(failure + ": " + message); - } - } - - public static class Builder { - private final Actor sender; - @Nullable - private String success; - @Nullable - private String failure; - private ExceptionConverter exceptionConverter; - - public Builder(Actor sender) { - checkNotNull(sender); - - this.sender = sender; - } - - public Builder exceptionConverter(ExceptionConverter exceptionConverter) { - this.exceptionConverter = exceptionConverter; - return this; - } - - public Builder onSuccess(@Nullable String message) { - this.success = message; - return this; - } - - public Builder onFailure(@Nullable String message) { - this.failure = message; - return this; - } - - public MessageFutureCallback build() { - checkNotNull(exceptionConverter); - return new MessageFutureCallback<>(exceptionConverter, sender, success, failure); - } - } - - public static MessageFutureCallback createRegionLoadCallback(ExceptionConverter exceptionConverter, Actor sender) { - return new Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess("Successfully load the region data.") - .build(); - } - - public static MessageFutureCallback createRegionSaveCallback(ExceptionConverter exceptionConverter, Actor sender) { - return new Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess("Successfully saved the region data.") - .build(); - } - -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java index aa911b7058..ab21a39b95 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java @@ -21,6 +21,7 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.util.formatting.component.CommandListBox; @@ -66,7 +67,7 @@ private static Command detectCommand(CommandManager manager, String command) { return mapping.orElse(null); } - public static void help(List commandPath, int page, WorldEdit we, Actor actor) throws InvalidComponentException { + public static void help(List commandPath, int page, boolean listSubCommands, WorldEdit we, Actor actor) throws InvalidComponentException { CommandManager manager = we.getPlatformManager().getPlatformCommandManager().getCommandManager(); if (commandPath.isEmpty()) { @@ -89,7 +90,7 @@ public static void help(List commandPath, int page, WorldEdit we, Actor if (subCommands.isEmpty()) { actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)", - Joiner.on(" ").join(visited.stream().map(Command::getName).iterator()), subCommand)); + toCommandString(visited), subCommand)); // full help for single command CommandUsageBox box = new CommandUsageBox(visited, visited.stream() .map(Command::getName).collect(Collectors.joining(" "))); @@ -102,27 +103,28 @@ public static void help(List commandPath, int page, WorldEdit we, Actor visited.add(currentCommand); } else { actor.printError(String.format("The sub-command '%s' under '%s' could not be found.", - subCommand, Joiner.on(" ").join(visited.stream().map(Command::getName).iterator()))); + subCommand, toCommandString(visited))); // list subcommands for currentCommand - CommandUsageBox box = new CommandUsageBox(visited, visited.stream() - .map(Command::getName).collect(Collectors.joining(" "))); - actor.print(box.create()); + printCommands(page, getSubCommands(Iterables.getLast(visited)).values().stream(), actor, visited); return; } } Map subCommands = getSubCommands(currentCommand); - if (subCommands.isEmpty()) { + if (subCommands.isEmpty() || !listSubCommands) { // Create the message - CommandUsageBox box = new CommandUsageBox(visited, visited.stream() - .map(Command::getName).collect(Collectors.joining(" "))); + CommandUsageBox box = new CommandUsageBox(visited, toCommandString(visited)); actor.print(box.create()); } else { printCommands(page, subCommands.values().stream(), actor, visited); } } + private static String toCommandString(List visited) { + return "/" + Joiner.on(" ").join(visited.stream().map(Command::getName).iterator()); + } + private static void printCommands(int page, Stream commandStream, Actor actor, List commandList) throws InvalidComponentException { // Get a list of aliases @@ -130,11 +132,10 @@ private static void printCommands(int page, Stream commandStream, Actor .sorted(byCleanName()) .collect(toList()); - String used = commandList.isEmpty() ? null - : Joiner.on(" ").join(commandList.stream().map(Command::getName).iterator()); + String used = commandList.isEmpty() ? null : toCommandString(commandList); CommandListBox box = new CommandListBox( (used == null ? "Help" : "Subcommands: " + used), - "//help %page%" + (used == null ? "" : " " + used)); + "//help -s %page%" + (used == null ? "" : " " + used)); if (!actor.isPlayer()) { box.formatForConsole(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java index d5be2e40d7..3673d8842b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Direction; @@ -210,6 +211,17 @@ public interface Player extends Entity, Actor { */ Location getBlockTrace(int range, boolean useLastBlock); + /** + * Get the point of the block being looked at. May return null. + * Will return the farthest away block before matching the stop mask if useLastBlock is true and no other block is found. + * + * @param range how far to checks for blocks + * @param useLastBlock try to return the last valid block not matching the stop mask found + * @param stopMask the mask used to determine when to stop tracing + * @return point + */ + Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask); + /** * Get the face that the player is looking at. * @@ -219,6 +231,16 @@ public interface Player extends Entity, Actor { */ Location getBlockTraceFace(int range, boolean useLastBlock); + /** + * Get the face that the player is looking at. + * + * @param range the range + * @param useLastBlock try to return the last valid block not matching the stop mask found + * @param stopMask the mask used to determine when to stop tracing + * @return a face + */ + Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask); + /** * Get the point of the block being looked at. May return null. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index a103318efe..d2212a40e6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -27,7 +27,6 @@ import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.blocks.SignBlock; import com.sk89q.worldedit.blocks.SkullBlock; -import com.sk89q.worldedit.blocks.metadata.MobType; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.DisallowedUsageException; import com.sk89q.worldedit.extension.input.InputParseException; @@ -46,6 +45,8 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.FuzzyBlockState; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; import java.util.HashMap; @@ -323,18 +324,17 @@ private BaseBlock parseLogic(String input, ParserContext context) throws InputPa // Allow setting mob spawn type if (blockAndExtraData.length > 1) { String mobName = blockAndExtraData[1]; - for (MobType mobType : MobType.values()) { - if (mobType.getName().toLowerCase(Locale.ROOT).equals(mobName.toLowerCase(Locale.ROOT))) { - mobName = mobType.getName(); - break; - } + EntityType ent = EntityTypes.get(mobName.toLowerCase(Locale.ROOT)); + if (ent == null) { + throw new NoMatchException("Unknown entity type '" + mobName + "'"); } + mobName = ent.getId(); if (!worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS).isValidMobType(mobName)) { throw new NoMatchException("Unknown mob type '" + mobName + "'"); } return new MobSpawnerBlock(state, mobName); } else { - return new MobSpawnerBlock(state, MobType.PIG.getName()); + return new MobSpawnerBlock(state, EntityTypes.PIG.getId()); } } else if (blockType == BlockTypes.PLAYER_HEAD || blockType == BlockTypes.PLAYER_WALL_HEAD) { // allow setting type/player/rotation diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java index 7cc6e10091..762884ab80 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extension.platform; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import java.util.Collections; @@ -39,4 +40,8 @@ public List getWorlds() { return Collections.emptyList(); } + @Override + public DataFixer getDataFixer() { + return null; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index e7aeac9979..f51f992aca 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -41,6 +42,7 @@ import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; +import javax.annotation.Nullable; import java.io.File; /** @@ -323,13 +325,29 @@ public Location getBlockOn() { @Override public Location getBlockTrace(int range, boolean useLastBlock) { + return getBlockTrace(range, useLastBlock, null); + } + + @Override + public Location getBlockTraceFace(int range, boolean useLastBlock) { + return getBlockTraceFace(range, useLastBlock, null); + } + + @Override + public Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask) { TargetBlock tb = new TargetBlock(this, range, 0.2); + if (stopMask != null) { + tb.setStopMask(stopMask); + } return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock()); } @Override - public Location getBlockTraceFace(int range, boolean useLastBlock) { + public Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask) { TargetBlock tb = new TargetBlock(this, range, 0.2); + if (stopMask != null) { + tb.setStopMask(stopMask); + } return (useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index 1a25497f50..fcbd6ff294 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -21,6 +21,7 @@ import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import org.enginehub.piston.CommandManager; @@ -51,11 +52,18 @@ public interface Platform { */ int getDataVersion(); + /** + * Get a DataFixer capable of upgrading old data. + * + * @return a data fixer, or null if not supported by this platform + */ + DataFixer getDataFixer(); + /** * Checks if a mob type is valid. * * @param type The mob type name to check - * @return Whether the name is a valid mod bype + * @return Whether the name is a valid mod type */ boolean isValidMobType(String type); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 09f34dbd0d..8708de6781 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -36,6 +36,7 @@ import com.sk89q.worldedit.command.ChunkCommandsRegistration; import com.sk89q.worldedit.command.ClipboardCommands; import com.sk89q.worldedit.command.ClipboardCommandsRegistration; +import com.sk89q.worldedit.command.ExpandCommands; import com.sk89q.worldedit.command.GeneralCommands; import com.sk89q.worldedit.command.GeneralCommandsRegistration; import com.sk89q.worldedit.command.GenerationCommands; @@ -73,7 +74,6 @@ import com.sk89q.worldedit.command.argument.DirectionConverter; import com.sk89q.worldedit.command.argument.EntityRemoverConverter; import com.sk89q.worldedit.command.argument.EnumConverter; -import com.sk89q.worldedit.command.argument.ExpandAmountConverter; import com.sk89q.worldedit.command.argument.FactoryConverter; import com.sk89q.worldedit.command.argument.RegionFactoryConverter; import com.sk89q.worldedit.command.argument.RegistryConverter; @@ -104,6 +104,7 @@ import org.enginehub.piston.ColorConfig; import org.enginehub.piston.Command; import org.enginehub.piston.CommandManager; +import org.enginehub.piston.TextConfig; import org.enginehub.piston.converter.ArgumentConverters; import org.enginehub.piston.exception.CommandException; import org.enginehub.piston.exception.CommandExecutionException; @@ -148,6 +149,10 @@ public final class PlatformCommandManager { private static final java.util.logging.Logger COMMAND_LOG = java.util.logging.Logger.getLogger("com.sk89q.worldedit.CommandLog"); + static { + TextConfig.setCommandPrefix("/"); + } + private final WorldEdit worldEdit; private final PlatformManager platformManager; private final CommandManagerServiceImpl commandManagerService; @@ -206,7 +211,6 @@ private void registerArgumentConverters() { VectorConverter.register(commandManager); EnumConverter.register(commandManager); RegistryConverter.register(commandManager); - ExpandAmountConverter.register(commandManager); ZonedDateTimeConverter.register(commandManager); BooleanConverter.register(commandManager); EntityRemoverConverter.register(commandManager); @@ -281,7 +285,7 @@ private void registerAllCommands() { registerSubCommands( "snapshot", ImmutableList.of("snap"), - "Snapshot commands for saving/loading snapshots", + "Snapshot commands for restoring backups", SnapshotCommandsRegistration.builder(), new SnapshotCommands(worldEdit) ); @@ -294,7 +298,7 @@ private void registerAllCommands() { ); registerSubCommands( "brush", - ImmutableList.of("br"), + ImmutableList.of("br", "/brush", "/br"), "Brushing commands", BrushCommandsRegistration.builder(), new BrushCommands(worldEdit), @@ -360,6 +364,7 @@ private void registerAllCommands() { SelectionCommandsRegistration.builder(), new SelectionCommands(worldEdit) ); + ExpandCommands.register(registration, commandManager, commandManagerService); this.registration.register( commandManager, SnapshotUtilCommandsRegistration.builder(), diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java index dc99d66dc4..bcba783a43 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java @@ -44,6 +44,7 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; @@ -67,22 +68,21 @@ */ public class MCEditSchematicReader extends NBTSchematicReader { + private static final Logger log = LoggerFactory.getLogger(MCEditSchematicReader.class); + private final NBTInputStream inputStream; + private final DataFixer fixer; private static final ImmutableList COMPATIBILITY_HANDLERS - = ImmutableList.of( - new SignCompatibilityHandler(), - new FlowerPotCompatibilityHandler(), - new NoteBlockCompatibilityHandler(), - new SkullBlockCompatibilityHandler() - // TODO - item tags for inventories...? DFUs :> + = ImmutableList.of( + new SignCompatibilityHandler(), + new FlowerPotCompatibilityHandler(), + new NoteBlockCompatibilityHandler(), + new SkullBlockCompatibilityHandler() ); private static final ImmutableList ENTITY_COMPATIBILITY_HANDLERS - = ImmutableList.of( - new Pre13HangingCompatibilityHandler() + = ImmutableList.of( + new Pre13HangingCompatibilityHandler() ); - private static final Logger log = LoggerFactory.getLogger(MCEditSchematicReader.class); - private final NBTInputStream inputStream; - /** * Create a new instance. * @@ -91,6 +91,9 @@ public class MCEditSchematicReader extends NBTSchematicReader { public MCEditSchematicReader(NBTInputStream inputStream) { checkNotNull(inputStream); this.inputStream = inputStream; + this.fixer = null; + //com.sk89q.worldedit.WorldEdit.getInstance().getPlatformManager().queryCapability( + //com.sk89q.worldedit.extension.platform.Capability.WORLD_EDITING).getDataFixer(); } @Override @@ -176,39 +179,44 @@ public Clipboard read() throws IOException { // Need to pull out tile entities List tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue(); Map> tileEntitiesMap = new HashMap<>(); - Map blockOverrides = new HashMap<>(); + Map blockStates = new HashMap<>(); for (Tag tag : tileEntities) { if (!(tag instanceof CompoundTag)) continue; CompoundTag t = (CompoundTag) tag; - + Map values = new HashMap<>(t.getValue()); + String id = t.getString("id"); + values.put("id", new StringTag(convertBlockEntityId(id))); int x = t.getInt("x"); int y = t.getInt("y"); int z = t.getInt("z"); - String id = t.getString("id"); - - Map values = new HashMap<>(t.getValue()); - values.put("id", new StringTag(convertBlockEntityId(id))); - int index = y * width * length + z * width + x; - BlockState block = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); + + BlockState block = getBlockState(blocks[index], blockData[index]); BlockState newBlock = block; if (newBlock != null) { for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) { if (handler.isAffectedBlock(newBlock)) { newBlock = handler.updateNBT(block, values); - if (newBlock == null) { + if (newBlock == null || values.isEmpty()) { break; } } } } + if (values.isEmpty()) { + t = null; + } + + if (fixer != null && t != null) { + t = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, t, -1); + } BlockVector3 vec = BlockVector3.at(x, y, z); - tileEntitiesMap.put(vec, values); - if (newBlock != block) { - blockOverrides.put(vec, newBlock); + if (t != null) { + tileEntitiesMap.put(vec, t.getValue()); } + blockStates.put(vec, newBlock); } BlockArrayClipboard clipboard = new BlockArrayClipboard(region); @@ -221,10 +229,7 @@ public Clipboard read() throws IOException { for (int z = 0; z < length; ++z) { int index = y * width * length + z * width + x; BlockVector3 pt = BlockVector3.at(x, y, z); - boolean useOverride = blockOverrides.containsKey(pt); - BlockState state = useOverride - ? blockOverrides.get(pt) - : LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); + BlockState state = blockStates.computeIfAbsent(pt, p -> getBlockState(blocks[index], blockData[index])); try { if (state != null) { @@ -233,7 +238,7 @@ public Clipboard read() throws IOException { } else { clipboard.setBlock(region.getMinimumPoint().add(pt), state); } - } else if (!useOverride) { + } else { short block = blocks[index]; byte data = blockData[index]; int combined = block << 8 | data; @@ -258,9 +263,11 @@ public Clipboard read() throws IOException { for (Tag tag : entityTags) { if (tag instanceof CompoundTag) { CompoundTag compound = (CompoundTag) tag; + if (fixer != null) { + compound = fixer.fixUp(DataFixer.FixTypes.ENTITY, compound, -1); + } String id = convertEntityId(compound.getString("id")); Location location = NBTConversions.toLocation(clipboard, compound.getListTag("Pos"), compound.getListTag("Rotation")); - if (!id.isEmpty()) { EntityType entityType = EntityTypes.get(id.toLowerCase(Locale.ROOT)); if (entityType != null) { @@ -343,26 +350,43 @@ private String convertEntityId(String id) { } private String convertBlockEntityId(String id) { - switch(id) { - case "Cauldron": return "brewing_stand"; - case "Control": return "command_block"; - case "DLDetector": return "daylight_detector"; - case "Trap": return "dispenser"; - case "EnchantTable": return "enchanting_table"; - case "EndGateway": return "end_gateway"; - case "AirPortal": return "end_portal"; - case "EnderChest": return "ender_chest"; - case "FlowerPot": return "flower_pot"; - case "RecordPlayer": return "jukebox"; - case "MobSpawner": return "mob_spawner"; + switch (id) { + case "Cauldron": + return "brewing_stand"; + case "Control": + return "command_block"; + case "DLDetector": + return "daylight_detector"; + case "Trap": + return "dispenser"; + case "EnchantTable": + return "enchanting_table"; + case "EndGateway": + return "end_gateway"; + case "AirPortal": + return "end_portal"; + case "EnderChest": + return "ender_chest"; + case "FlowerPot": + return "flower_pot"; + case "RecordPlayer": + return "jukebox"; + case "MobSpawner": + return "mob_spawner"; case "Music": case "noteblock": return "note_block"; - case "Structure": return "structure_block"; - default: return id; + case "Structure": + return "structure_block"; + default: + return id; } } + private BlockState getBlockState(int id, int data) { + return LegacyMapper.getInstance().getBlockFromLegacy(id, data); + } + @Override public void close() throws IOException { inputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index d42d76afde..4a9362b6e6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -36,14 +36,15 @@ import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -54,7 +55,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -68,14 +68,10 @@ */ public class SpongeSchematicReader extends NBTSchematicReader { - private static final List COMPATIBILITY_HANDLERS = new ArrayList<>(); - - static { - // If NBT Compat handlers are needed - add them here. - } - private static final Logger log = LoggerFactory.getLogger(SpongeSchematicReader.class); private final NBTInputStream inputStream; + private DataFixer fixer = null; + private int dataVersion = -1; /** * Create a new instance. @@ -97,17 +93,32 @@ public Clipboard read() throws IOException { // Check Map schematic = schematicTag.getValue(); + int version = requireTag(schematic, "Version", IntTag.class).getValue(); + final Platform platform = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.WORLD_EDITING); + int liveDataVersion = platform.getDataVersion(); + if (version == 1) { + dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1- + fixer = platform.getDataFixer(); return readVersion1(schematicTag); } else if (version == 2) { - int dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); - int liveDataVersion = WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.WORLD_EDITING).getDataVersion(); + dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); if (dataVersion > liveDataVersion) { log.warn("Schematic was made in a newer Minecraft version ({} > {}). Data may be incompatible.", dataVersion, liveDataVersion); + } else if (dataVersion < liveDataVersion) { + fixer = platform.getDataFixer(); + if (fixer != null) { + log.info("Schematic was made in an older Minecraft version ({} < {}), will attempt DFU.", + dataVersion, liveDataVersion); + } else { + log.info("Schematic was made in an older Minecraft version ({} < {}), but DFU is not available. Data may be incompatible.", + dataVersion, liveDataVersion); + } } + BlockArrayClipboard clip = readVersion1(schematicTag); return readVersion2(clip, schematicTag); } @@ -159,6 +170,9 @@ private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOExce for (String palettePart : paletteObject.keySet()) { int id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); + if (fixer != null) { + palettePart = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); + } BlockState state; try { state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState(); @@ -184,7 +198,18 @@ private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOExce for (Map tileEntity : tileEntityTags) { int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); - tileEntitiesMap.put(BlockVector3.at(pos[0], pos[1], pos[2]), tileEntity); + final BlockVector3 pt = BlockVector3.at(pos[0], pos[1], pos[2]); + if (fixer != null) { + Map values = Maps.newHashMap(tileEntity); + values.put("x", new IntTag(pt.getBlockX())); + values.put("y", new IntTag(pt.getBlockY())); + values.put("z", new IntTag(pt.getBlockZ())); + values.put("id", values.get("Id")); + values.remove("Id"); + values.remove("Pos"); + tileEntity = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, new CompoundTag(values), dataVersion).getValue(); + } + tileEntitiesMap.put(pt, tileEntity); } } @@ -218,19 +243,7 @@ private BlockArrayClipboard readVersion1(CompoundTag schematicTag) throws IOExce BlockVector3 pt = BlockVector3.at(x, y, z); try { if (tileEntitiesMap.containsKey(pt)) { - Map values = Maps.newHashMap(tileEntitiesMap.get(pt)); - for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) { - if (handler.isAffectedBlock(state)) { - handler.updateNBT(state, values); - } - } - values.put("x", new IntTag(pt.getBlockX())); - values.put("y", new IntTag(pt.getBlockY())); - values.put("z", new IntTag(pt.getBlockZ())); - values.put("id", values.get("Id")); - values.remove("Id"); - values.remove("Pos"); - clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(values))); + clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state.toBaseBlock(new CompoundTag(tileEntitiesMap.get(pt)))); } else { clipboard.setBlock(clipboard.getMinimumPoint().add(pt), state); } @@ -267,9 +280,13 @@ private void readBiomes(BlockArrayClipboard clipboard, Map schemati Map paletteEntries = paletteTag.getValue(); for (Entry palettePart : paletteEntries.entrySet()) { - BiomeType biome = BiomeTypes.get(palettePart.getKey()); + String key = palettePart.getKey(); + if (fixer != null) { + key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion); + } + BiomeType biome = BiomeTypes.get(key); if (biome == null) { - log.warn("Unknown biome type :" + palettePart.getKey() + + log.warn("Unknown biome type :" + key + " in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); } Tag idTag = palettePart.getValue(); @@ -322,14 +339,18 @@ private void readEntities(BlockArrayClipboard clipboard, Map schema CompoundTag entityTag = (CompoundTag) et; Map tags = entityTag.getValue(); String id = requireTag(tags, "Id", StringTag.class).getValue(); + entityTag = entityTag.createBuilder().putString("id", id).remove("Id").build(); + + if (fixer != null) { + entityTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, entityTag, dataVersion); + } EntityType entityType = EntityTypes.get(id); if (entityType != null) { Location location = NBTConversions.toLocation(clipboard, requireTag(tags, "Pos", ListTag.class), requireTag(tags, "Rotation", ListTag.class)); - BaseEntity state = new BaseEntity(entityType, - entityTag.createBuilder().putString("id", id).remove("Id").build()); + BaseEntity state = new BaseEntity(entityType, entityTag); clipboard.createEntity(location, state); } else { log.warn("Unknown entity when pasting schematic: " + id); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java index 225a9555ea..df18a95da7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java @@ -48,6 +48,7 @@ public > B updateNBT(B block, Map val } BlockState newState = convertLegacyBlockType(id, data); if (newState != null) { + values.clear(); return (B) newState; // generics pls :\ } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java index 940086b8fb..a839efe291 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java @@ -54,7 +54,8 @@ public > B updateNBT(B block, Map val if (noteTag instanceof ByteTag) { Byte note = ((ByteTag) noteTag).getValue(); if (note != null) { - return block.with(NoteProperty, (int) note); + values.clear(); + return (B) block.with(NoteProperty, (int) note).toImmutableState(); } } return block; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java index 395153e7e0..b982360900 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java @@ -19,10 +19,16 @@ package com.sk89q.worldedit.internal.command; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.internal.util.Substring; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import org.enginehub.piston.Command; +import org.enginehub.piston.exception.CommandException; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; import org.enginehub.piston.part.SubCommandPart; import java.util.Comparator; @@ -92,6 +98,36 @@ private static Optional suggestLast(Substring last, Substring suggestion return Optional.of(builder.toString()); } + /** + * Require {@code condition} to be {@code true}, otherwise throw a {@link CommandException} + * with the given message. + * + * @param condition the condition to check + * @param message the message for failure + */ + public static void checkCommandArgument(boolean condition, String message) { + checkCommandArgument(condition, TextComponent.of(message)); + } + + /** + * Require {@code condition} to be {@code true}, otherwise throw a {@link CommandException} + * with the given message. + * + * @param condition the condition to check + * @param message the message for failure + */ + public static void checkCommandArgument(boolean condition, Component message) { + if (!condition) { + throw new CommandException(message, ImmutableList.of()); + } + } + + public static T requireIV(Key type, String name, InjectedValueAccess injectedValueAccess) { + return injectedValueAccess.injectedValue(type).orElseThrow(() -> + new IllegalStateException("No injected value for " + name + " (type " + type + ")") + ); + } + private CommandUtil() { } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java index b200e11b35..5270cab873 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java @@ -165,11 +165,6 @@ public void convert(WorldEditException e) throws CommandException { throw newCommandException(e.getMessage(), e); } - @ExceptionMatch - public void convert(IllegalArgumentException e) throws CommandException { - throw newCommandException(e.getMessage(), e); - } - // Prevent investigation into UsageExceptions @ExceptionMatch public void convert(UsageException e) throws CommandException { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Keyed.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Keyed.java index 5351ed36b3..c5161bd900 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Keyed.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Keyed.java @@ -17,7 +17,6 @@ * along with this program. If not, see . */ - package com.sk89q.worldedit.registry; /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java index dc5aac815f..ab47f7c51c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java @@ -45,7 +45,8 @@ protected Extent getExtent() { if (request == null || !request.isValid()) { request = Request.request(); } - return request.getEditSession(); + final EditSession editSession = request.getEditSession(); + return editSession == null ? request.getWorld() : editSession; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java index 9d0cf2baa9..b887168c59 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java @@ -20,10 +20,15 @@ package com.sk89q.worldedit.util; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; +import javax.annotation.Nullable; + /** * This class uses an inefficient method to figure out what block a player * is looking towards. @@ -33,7 +38,8 @@ */ public class TargetBlock { - private World world; + private final World world; + private int maxDistance; private double checkDistance, curDistance; private BlockVector3 targetPos = BlockVector3.ZERO; @@ -41,6 +47,11 @@ public class TargetBlock { private BlockVector3 prevPos = BlockVector3.ZERO; private Vector3 offset = Vector3.ZERO; + // the mask which dictates when to stop a trace - defaults to stopping at non-air blocks + private Mask stopMask; + // the mask which dictates when to stop a solid block trace - default to BlockMaterial#isMovementBlocker + private Mask solidMask; + /** * Constructor requiring a player, uses default values * @@ -50,6 +61,8 @@ public TargetBlock(Player player) { this.world = player.getWorld(); this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(), 300, 1.65, 0.2); + this.stopMask = new ExistingBlockMask(world); + this.solidMask = new SolidBlockMask(world); } /** @@ -62,6 +75,36 @@ public TargetBlock(Player player) { public TargetBlock(Player player, int maxDistance, double checkDistance) { this.world = player.getWorld(); this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(), maxDistance, 1.65, checkDistance); + this.stopMask = new ExistingBlockMask(world); + this.solidMask = new SolidBlockMask(world); + } + + /** + * Set the mask used for determine where to stop traces. + * Setting to null will restore the default. + * + * @param stopMask the mask used to stop traces + */ + public void setStopMask(@Nullable Mask stopMask) { + if (stopMask == null) { + this.stopMask = new ExistingBlockMask(world); + } else { + this.stopMask = stopMask; + } + } + + /** + * Set the mask used for determine where to stop solid block traces. + * Setting to null will restore the default. + * + * @param solidMask the mask used to stop solid block traces + */ + public void setSolidMask(@Nullable Mask solidMask) { + if (solidMask == null) { + this.solidMask = new SolidBlockMask(world); + } else { + this.solidMask = solidMask; + } } /** @@ -79,7 +122,7 @@ private void setValues(Vector3 loc, double xRotation, double yRotation, int maxD this.checkDistance = checkDistance; this.curDistance = 0; xRotation = (xRotation + 90) % 360; - yRotation = yRotation * -1; + yRotation *= -1; double h = (checkDistance * Math.cos(Math.toRadians(yRotation))); @@ -102,15 +145,15 @@ public Location getAnyTargetBlock() { boolean searchForLastBlock = true; Location lastBlock = null; while (getNextBlock() != null) { - if (world.getBlock(targetPos).getBlockType().getMaterial().isAir()) { + if (stopMask.test(targetPos)) { + break; + } else { if (searchForLastBlock) { lastBlock = getCurrentBlock(); if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) { searchForLastBlock = false; } } - } else { - break; } } Location currentBlock = getCurrentBlock(); @@ -124,7 +167,8 @@ public Location getAnyTargetBlock() { * @return Block */ public Location getTargetBlock() { - while (getNextBlock() != null && world.getBlock(targetPos).getBlockType().getMaterial().isAir()) ; + //noinspection StatementWithEmptyBody + while (getNextBlock() != null && !stopMask.test(targetPos)) ; return getCurrentBlock(); } @@ -135,7 +179,8 @@ public Location getTargetBlock() { * @return Block */ public Location getSolidTargetBlock() { - while (getNextBlock() != null && !world.getBlock(targetPos).getBlockType().getMaterial().isMovementBlocker()) ; + //noinspection StatementWithEmptyBody + while (getNextBlock() != null && !solidMask.test(targetPos)) ; return getCurrentBlock(); } 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 46347d1075..e8abe0e386 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 @@ -20,17 +20,19 @@ package com.sk89q.worldedit.util.formatting.component; import com.google.common.collect.Iterables; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextDecoration; +import org.enginehub.piston.ColorConfig; import org.enginehub.piston.Command; import org.enginehub.piston.CommandParameters; import org.enginehub.piston.util.HelpGenerator; import javax.annotation.Nullable; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldedit.internal.command.CommandUtil.byCleanName; import static com.sk89q.worldedit.internal.command.CommandUtil.getSubCommands; /** @@ -58,35 +60,26 @@ public CommandUsageBox(List commands, String commandString) throws Inva public CommandUsageBox(List commands, String commandString, @Nullable CommandParameters parameters) throws InvalidComponentException { checkNotNull(commands); checkNotNull(commandString); - Map subCommands = getSubCommands(Iterables.getLast(commands)); - if (subCommands.isEmpty()) { - attachCommandUsage(commands, commandString); - } else { - attachSubcommandUsage(subCommands, commandString, parameters); - } - } - - private void attachSubcommandUsage(Map dispatcher, String commandString, @Nullable CommandParameters parameters) throws InvalidComponentException { - CommandListBox box = new CommandListBox(commandString.isEmpty() ? "Help" : "Subcommands:" + commandString, - "//help %page%" + (commandString.isEmpty() ? "" : " " + commandString)); - String prefix = !commandString.isEmpty() ? commandString + " " : ""; - - List list = dispatcher.values().stream() - .sorted(byCleanName()) - .collect(Collectors.toList()); - - for (Command mapping : list) { - if (parameters == null || mapping.getCondition().satisfied(parameters)) { - box.appendCommand(prefix + mapping.getName(), mapping.getDescription()); - } - } - - append(box.create(1)); + attachCommandUsage(commands, commandString); } private void attachCommandUsage(List commands, String commandString) { + TextComponentProducer boxContent = new TextComponentProducer() + .append(HelpGenerator.create(commands).getFullHelp()); + if (getSubCommands(Iterables.getLast(commands)).size() > 0) { + boxContent.append(TextComponent.newline()) + .append(TextComponent.builder("> ") + .color(ColorConfig.getHelpText()) + .append(TextComponent.builder("List Subcommands") + .color(ColorConfig.getMainText()) + .decoration(TextDecoration.ITALIC, true) + .clickEvent(ClickEvent.runCommand("//help -s " + commandString)) + .hoverEvent(HoverEvent.showText(TextComponent.of("List all subcommands of this command"))) + .build()) + .build()); + } MessageBox box = new MessageBox("Help for " + commandString, - new TextComponentProducer().append(HelpGenerator.create(commands).getFullHelp())); + boxContent); append(box.create()); } 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 0509275f6d..1eb51a21f6 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,22 +19,16 @@ package com.sk89q.worldedit.util.paste; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.worldedit.command.util.AsyncCommandHelper; +import com.sk89q.worldedit.command.util.AsyncCommandBuilder; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.internal.command.exception.ExceptionConverter; import com.sk89q.worldedit.util.task.Supervisor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.net.URL; -import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Callable; -public class ActorCallbackPaste { +public final class ActorCallbackPaste { - private static final Logger LOGGER = LoggerFactory.getLogger(ActorCallbackPaste.class); + private static final Paster paster = new EngineHubPaste(); private ActorCallbackPaste() { } @@ -48,25 +42,15 @@ private ActorCallbackPaste() { * @param content The content * @param successMessage The message, formatted with {@link String#format(String, Object...)} on success */ - public static void pastebin(Supervisor supervisor, final Actor sender, String content, final String successMessage, final ExceptionConverter exceptionConverter) { - ListenableFuture future = new EngineHubPaste().paste(content); - - AsyncCommandHelper.wrap(future, supervisor, sender, exceptionConverter) - .registerWithSupervisor("Submitting content to a pastebin service...") - .sendMessageAfterDelay("(Please wait... sending output to pastebin...)"); - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(URL url) { - sender.print(String.format(successMessage, url)); - } - - @Override - public void onFailure(Throwable throwable) { - LOGGER.warn("Failed to submit pastebin", throwable); - sender.printError("Failed to submit to a pastebin. Please see console for the error."); - } - }, ForkJoinPool.commonPool()); + public static void pastebin(Supervisor supervisor, final Actor sender, String content, final String successMessage) { + Callable task = paster.paste(content); + + AsyncCommandBuilder.wrap(task, sender) + .registerWithSupervisor(supervisor, "Submitting content to a pastebin service.") + .sendMessageAfterDelay("(Please wait... sending output to pastebin...)") + .onSuccess((String) null, url -> sender.print(String.format(successMessage, url))) + .onFailure("Failed to submit paste", null) + .buildAndExec(Pasters.getExecutor()); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java index da6b701fdb..ac7484846d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/EngineHubPaste.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.util.paste; -import com.google.common.util.concurrent.ListenableFuture; import com.sk89q.worldedit.util.net.HttpRequest; import org.json.simple.JSONValue; @@ -35,11 +34,11 @@ public class EngineHubPaste implements Paster { private static final Pattern URL_PATTERN = Pattern.compile("https?://.+$"); @Override - public ListenableFuture paste(String content) { - return Pasters.getExecutor().submit(new PasteTask(content)); + public Callable paste(String content) { + return new PasteTask(content); } - private final class PasteTask implements Callable { + private static final class PasteTask implements Callable { private final String content; private PasteTask(String content) { @@ -50,7 +49,7 @@ private PasteTask(String content) { public URL call() throws IOException, InterruptedException { HttpRequest.Form form = HttpRequest.Form.create(); form.add("content", content); - form.add("from", "worldguard"); + form.add("from", "enginehub"); URL url = HttpRequest.url("http://paste.enginehub.org/paste"); String result = HttpRequest.post(url) 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 dcbef09b0b..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Pastebin.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.util.paste; - -import com.google.common.util.concurrent.ListenableFuture; -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 ListenableFuture paste(String content) { - if (mungingLinks) { - content = content.replaceAll("http://", "http_//"); - } - - return Pasters.getExecutor().submit(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/util/paste/Paster.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Paster.java index 7a7d74cac8..a65ccd6c17 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Paster.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/paste/Paster.java @@ -19,12 +19,11 @@ package com.sk89q.worldedit.util.paste; -import com.google.common.util.concurrent.ListenableFuture; - import java.net.URL; +import java.util.concurrent.Callable; public interface Paster { - ListenableFuture paste(String content); + Callable paste(String content); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/DataFixer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/DataFixer.java new file mode 100644 index 0000000000..70182550dc --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/DataFixer.java @@ -0,0 +1,53 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world; + +import com.google.common.annotations.Beta; +import com.sk89q.jnbt.CompoundTag; + +/** + * This entire class is subject to heavy changes. Do not use this as API. + */ +@Beta +public interface DataFixer { + + final class FixType { + private FixType() { + } + } + + final class FixTypes { + private FixTypes() { + } + + public static FixType CHUNK = new FixType<>(); + public static FixType BLOCK_ENTITY = new FixType<>(); + public static FixType ENTITY = new FixType<>(); + public static FixType BLOCK_STATE = new FixType<>(); + public static FixType BIOME = new FixType<>(); + public static FixType ITEM_TYPE = new FixType<>(); + } + + default T fixUp(FixType type, T original) { + return fixUp(type, original, -1); + } + + T fixUp(FixType type, T original, int srcVer); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java index 2c9cd41b68..832fbf020a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk13.java @@ -35,12 +35,11 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.storage.InvalidFormatException; +import javax.annotation.Nullable; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nullable; - /** * The chunk format for Minecraft 1.13 and newer */ @@ -160,11 +159,13 @@ private BlockState getBlockStateWith(BlockState source, Property property * @throws DataException */ private void populateTileEntities() throws DataException { + tileEntities = new HashMap<>(); + if (!rootTag.getValue().containsKey("TileEntities")) { + return; + } List tags = NBTUtils.getChildTag(rootTag.getValue(), "TileEntities", ListTag.class).getValue(); - tileEntities = new HashMap<>(); - for (Tag tag : tags) { if (!(tag instanceof CompoundTag)) { throw new InvalidFormatException("CompoundTag expected in TileEntities"); @@ -172,33 +173,10 @@ private void populateTileEntities() throws DataException { CompoundTag t = (CompoundTag) tag; - int x = 0; - int y = 0; - int z = 0; - - Map values = new HashMap<>(); - - for (Map.Entry entry : t.getValue().entrySet()) { - switch (entry.getKey()) { - case "x": - if (entry.getValue() instanceof IntTag) { - x = ((IntTag) entry.getValue()).getValue(); - } - break; - case "y": - if (entry.getValue() instanceof IntTag) { - y = ((IntTag) entry.getValue()).getValue(); - } - break; - case "z": - if (entry.getValue() instanceof IntTag) { - z = ((IntTag) entry.getValue()).getValue(); - } - break; - } - - values.put(entry.getKey(), entry.getValue()); - } + Map values = new HashMap<>(t.getValue()); + int x = ((IntTag) values.get("x")).getValue(); + int y = ((IntTag) values.get("y")).getValue(); + int z = ((IntTag) values.get("z")).getValue(); BlockVector3 vec = BlockVector3.at(x, y, z); tileEntities.put(vec, values); 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 05acbeb1d7..d12503f19c 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 @@ -26,10 +26,13 @@ import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.gson.VectorAdapter; import com.sk89q.worldedit.util.io.ResourceLoader; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; @@ -41,18 +44,18 @@ import java.net.URL; import java.nio.charset.Charset; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; -import static com.google.common.base.Preconditions.checkNotNull; - -public class LegacyMapper { +public final class LegacyMapper { private static final Logger log = LoggerFactory.getLogger(LegacyMapper.class); private static LegacyMapper INSTANCE; - private Multimap stringToBlockMap = HashMultimap.create(); + private Map blockEntries = new HashMap<>(); + private Map stringToBlockMap = new HashMap<>(); private Multimap blockToStringMap = HashMultimap.create(); - private Multimap stringToItemMap = HashMultimap.create(); + private Map stringToItemMap = new HashMap<>(); private Multimap itemToStringMap = HashMultimap.create(); /** @@ -82,31 +85,50 @@ private void loadFromResource() throws IOException { String data = Resources.toString(url, Charset.defaultCharset()); LegacyDataFile dataFile = gson.fromJson(data, new TypeToken() {}.getType()); + DataFixer fixer = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataFixer(); ParserContext parserContext = new ParserContext(); parserContext.setPreferringWildcard(false); parserContext.setRestricted(false); parserContext.setTryLegacy(false); // This is legacy. Don't match itself. for (Map.Entry blockEntry : dataFile.blocks.entrySet()) { + String id = blockEntry.getKey(); + blockEntries.put(id, blockEntry.getValue()); try { - String id = blockEntry.getKey(); BlockState state = WorldEdit.getInstance().getBlockFactory().parseFromInput(blockEntry.getValue(), parserContext).toImmutableState(); blockToStringMap.put(state, id); stringToBlockMap.put(id, state); - } catch (Exception e) { - log.warn("Unknown block: " + blockEntry.getValue()); + } catch (InputParseException e) { + boolean fixed = false; + if (fixer != null) { + String newEntry = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, blockEntry.getValue(), 1631); + try { + BlockState state = WorldEdit.getInstance().getBlockFactory().parseFromInput(newEntry, parserContext).toImmutableState(); + blockToStringMap.put(state, id); + stringToBlockMap.put(id, state); + fixed = true; + } catch (InputParseException ignored) { + } + } + if (!fixed) { + log.warn("Unknown block: " + blockEntry.getValue()); + } } } for (Map.Entry itemEntry : dataFile.items.entrySet()) { - try { - String id = itemEntry.getKey(); - ItemType type = ItemTypes.get(itemEntry.getValue()); - checkNotNull(type); + String id = itemEntry.getKey(); + String value = itemEntry.getValue(); + ItemType type = ItemTypes.get(value); + if (type == null && fixer != null) { + value = fixer.fixUp(DataFixer.FixTypes.ITEM_TYPE, value, 1631); + type = ItemTypes.get(value); + } + if (type == null) { + log.warn("Unknown item: " + value); + } else { itemToStringMap.put(type, id); stringToItemMap.put(id, type); - } catch (Exception e) { - log.warn("Unknown item: " + itemEntry.getValue()); } } } @@ -118,16 +140,16 @@ public ItemType getItemFromLegacy(int legacyId) { @Nullable public ItemType getItemFromLegacy(int legacyId, int data) { - return stringToItemMap.get(legacyId + ":" + data).stream().findFirst().orElse(null); + return stringToItemMap.get(legacyId + ":" + data); } @Nullable public int[] getLegacyFromItem(ItemType itemType) { - if (!itemToStringMap.containsKey(itemType)) { - return null; - } else { + if (itemToStringMap.containsKey(itemType)) { String value = itemToStringMap.get(itemType).stream().findFirst().get(); return Arrays.stream(value.split(":")).mapToInt(Integer::parseInt).toArray(); + } else { + return null; } } @@ -138,16 +160,16 @@ public BlockState getBlockFromLegacy(int legacyId) { @Nullable public BlockState getBlockFromLegacy(int legacyId, int data) { - return stringToBlockMap.get(legacyId + ":" + data).stream().findFirst().orElse(null); + return stringToBlockMap.get(legacyId + ":" + data); } @Nullable public int[] getLegacyFromBlock(BlockState blockState) { - if (!blockToStringMap.containsKey(blockState)) { - return null; - } else { + if (blockToStringMap.containsKey(blockState)) { String value = blockToStringMap.get(blockState).stream().findFirst().get(); return Arrays.stream(value.split(":")).mapToInt(Integer::parseInt).toArray(); + } else { + return null; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java index fef791531a..a965943cc4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/PassthroughBlockMaterial.java @@ -160,7 +160,7 @@ public boolean isBurnable() { if (blockMaterial == null) { return true; } else { - return blockMaterial.isOpaque(); + return blockMaterial.isBurnable(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java index 3195037401..edd0799b42 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStore.java @@ -21,9 +21,13 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.DataException; +import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.chunk.AnvilChunk; import com.sk89q.worldedit.world.chunk.AnvilChunk13; @@ -42,7 +46,7 @@ public abstract class ChunkStore implements Closeable { /** * The DataVersion for Minecraft 1.13 */ - public static final int DATA_VERSION_MC_1_13 = 1519; + private static final int DATA_VERSION_MC_1_13 = 1519; /** * {@code >>} - to chunk @@ -102,6 +106,15 @@ public Chunk getChunk(BlockVector2 position, World world) throws DataException, } int dataVersion = rootTag.getInt("DataVersion"); + if (dataVersion == 0) dataVersion = -1; + final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING); + final int currentDataVersion = platform.getDataVersion(); + if (dataVersion < currentDataVersion) { + final DataFixer dataFixer = platform.getDataFixer(); + if (dataFixer != null) { + return new AnvilChunk13((CompoundTag) dataFixer.fixUp(DataFixer.FixTypes.CHUNK, rootTag, dataVersion).getValue().get("Level")); + } + } if (dataVersion >= DATA_VERSION_MC_1_13) { return new AnvilChunk13(tag); } @@ -114,6 +127,7 @@ public Chunk getChunk(BlockVector2 position, World world) throws DataException, return new OldChunk(world, tag); } + @Override public void close() throws IOException { } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeDataFixer.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeDataFixer.java new file mode 100644 index 0000000000..30e5ae74fe --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeDataFixer.java @@ -0,0 +1,2730 @@ +/* + * 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.forge; + +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.DataFixTypes; +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.item.EnumDyeColor; +import net.minecraft.nbt.INBTBase; +import net.minecraft.nbt.NBTDynamicOps; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagString; +import net.minecraft.nbt.NBTTagFloat; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.JsonUtils; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StringUtils; +import net.minecraft.util.datafix.DataFixesManager; +import net.minecraft.util.datafix.TypeReferences; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.annotation.Nullable; +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; + +/** + * 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 ForgeDataFixer 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) { + NBTTagCompound tag = NBTConverter.toNative(originalChunk); + NBTTagCompound fixed = convert(LegacyType.CHUNK, tag, srcVer); + return NBTConverter.fromNative(fixed); + } + + private CompoundTag fixBlockEntity(CompoundTag origTileEnt, int srcVer) { + NBTTagCompound tag = NBTConverter.toNative(origTileEnt); + NBTTagCompound fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer); + return NBTConverter.fromNative(fixed); + } + + private CompoundTag fixEntity(CompoundTag origEnt, int srcVer) { + NBTTagCompound tag = NBTConverter.toNative(origEnt); + NBTTagCompound fixed = convert(LegacyType.ENTITY, tag, srcVer); + return NBTConverter.fromNative(fixed); + } + + private String fixBlockState(String blockState, int srcVer) { + NBTTagCompound stateNBT = stateToNBT(blockState); + Dynamic dynamic = new Dynamic<>(OPS_NBT, stateNBT); + NBTTagCompound fixed = (NBTTagCompound) INSTANCE.fixer.update(TypeReferences.BLOCK_STATE, dynamic, srcVer, DATA_VERSION).getValue(); + return nbtToState(fixed); + } + + private String nbtToState(NBTTagCompound tagCompound) { + StringBuilder sb = new StringBuilder(); + sb.append(tagCompound.getString("Name")); + if (tagCompound.contains("Properties", 10)) { + sb.append('['); + NBTTagCompound props = tagCompound.getCompound("Properties"); + sb.append(props.keySet().stream().map(k -> k + "=" + props.getString(k).replace("\"", "")).collect(Collectors.joining(","))); + sb.append(']'); + } + return sb.toString(); + } + + private static NBTTagCompound stateToNBT(String blockState) { + int propIdx = blockState.indexOf('['); + NBTTagCompound tag = new NBTTagCompound(); + if (propIdx < 0) { + tag.putString("Name", blockState); + } else { + tag.putString("Name", blockState.substring(0, propIdx)); + NBTTagCompound propTag = new NBTTagCompound(); + 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 NBTTagString(key)), srcVer, DATA_VERSION) + .getStringValue().orElse(key); + } + + private static final NBTDynamicOps OPS_NBT = NBTDynamicOps.INSTANCE; + private static final int LEGACY_VERSION = 1343; + private static int DATA_VERSION; + private static ForgeDataFixer 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(DataFixTypes.LEVEL), + PLAYER(DataFixTypes.PLAYER), + CHUNK(DataFixTypes.CHUNK), + BLOCK_ENTITY(TypeReferences.BLOCK_ENTITY), + ENTITY(TypeReferences.ENTITY), + ITEM_INSTANCE(TypeReferences.ITEM_STACK), + OPTIONS(DataFixTypes.OPTIONS), + STRUCTURE(DataFixTypes.STRUCTURE); + + private final TypeReference type; + + LegacyType(TypeReference type) { + this.type = type; + DFU_TO_LEGACY.put(type.typeName(), this); + } + + public TypeReference getDFUType() { + return type; + } + } + + public ForgeDataFixer(int dataVersion) { + super(dataVersion); + DATA_VERSION = dataVersion; + INSTANCE = this; + registerConverters(); + registerInspectors(); + } + + + // Called after fixers are built and ready for FIXING + @Override + public DataFixer build(final Executor executor) { + return this.fixer = new WrappedDataFixer(DataFixesManager.getDataFixer()); + } + + 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) { + NBTTagCompound cmp = (NBTTagCompound) 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 NBTTagCompound convert(LegacyType type, NBTTagCompound cmp, int sourceVer, int desiredVersion) { + List converters = ForgeDataFixer.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 = ForgeDataFixer.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 NBTTagCompound convert(LegacyType type, NBTTagCompound cmp) { + return convert(type.getDFUType(), cmp); + } + + public static NBTTagCompound convert(LegacyType type, NBTTagCompound cmp, int sourceVer) { + return convert(type.getDFUType(), cmp, sourceVer); + } + + public static NBTTagCompound convert(LegacyType type, NBTTagCompound cmp, int sourceVer, int targetVer) { + return convert(type.getDFUType(), cmp, sourceVer, targetVer); + } + + public static NBTTagCompound convert(TypeReference type, NBTTagCompound cmp) { + int i = cmp.contains("DataVersion", 99) ? cmp.getInt("DataVersion") : -1; + return convert(type, cmp, i); + } + + public static NBTTagCompound convert(TypeReference type, NBTTagCompound cmp, int sourceVer) { + return convert(type, cmp, sourceVer, DATA_VERSION); + } + + public static NBTTagCompound convert(TypeReference type, NBTTagCompound cmp, int sourceVer, int targetVer) { + if (sourceVer >= targetVer) { + return cmp; + } + return (NBTTagCompound) INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, cmp), sourceVer, targetVer).getValue(); + } + + + public interface DataInspector { + NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer); + } + + public interface DataConverter { + + int getDataVersion(); + + NBTTagCompound convert(NBTTagCompound 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 ResourceLocation("item")); + map.put("EntityExperienceOrb", new ResourceLocation("xp_orb")); + map.put("EntityAreaEffectCloud", new ResourceLocation("area_effect_cloud")); + map.put("EntityGuardianElder", new ResourceLocation("elder_guardian")); + map.put("EntitySkeletonWither", new ResourceLocation("wither_skeleton")); + map.put("EntitySkeletonStray", new ResourceLocation("stray")); + map.put("EntityEgg", new ResourceLocation("egg")); + map.put("EntityLeash", new ResourceLocation("leash_knot")); + map.put("EntityPainting", new ResourceLocation("painting")); + map.put("EntityTippedArrow", new ResourceLocation("arrow")); + map.put("EntitySnowball", new ResourceLocation("snowball")); + map.put("EntityLargeFireball", new ResourceLocation("fireball")); + map.put("EntitySmallFireball", new ResourceLocation("small_fireball")); + map.put("EntityEnderPearl", new ResourceLocation("ender_pearl")); + map.put("EntityEnderSignal", new ResourceLocation("eye_of_ender_signal")); + map.put("EntityPotion", new ResourceLocation("potion")); + map.put("EntityThrownExpBottle", new ResourceLocation("xp_bottle")); + map.put("EntityItemFrame", new ResourceLocation("item_frame")); + map.put("EntityWitherSkull", new ResourceLocation("wither_skull")); + map.put("EntityTNTPrimed", new ResourceLocation("tnt")); + map.put("EntityFallingBlock", new ResourceLocation("falling_block")); + map.put("EntityFireworks", new ResourceLocation("fireworks_rocket")); + map.put("EntityZombieHusk", new ResourceLocation("husk")); + map.put("EntitySpectralArrow", new ResourceLocation("spectral_arrow")); + map.put("EntityShulkerBullet", new ResourceLocation("shulker_bullet")); + map.put("EntityDragonFireball", new ResourceLocation("dragon_fireball")); + map.put("EntityZombieVillager", new ResourceLocation("zombie_villager")); + map.put("EntityHorseSkeleton", new ResourceLocation("skeleton_horse")); + map.put("EntityHorseZombie", new ResourceLocation("zombie_horse")); + map.put("EntityArmorStand", new ResourceLocation("armor_stand")); + map.put("EntityHorseDonkey", new ResourceLocation("donkey")); + map.put("EntityHorseMule", new ResourceLocation("mule")); + map.put("EntityEvokerFangs", new ResourceLocation("evocation_fangs")); + map.put("EntityEvoker", new ResourceLocation("evocation_illager")); + map.put("EntityVex", new ResourceLocation("vex")); + map.put("EntityVindicator", new ResourceLocation("vindication_illager")); + map.put("EntityIllagerIllusioner", new ResourceLocation("illusion_illager")); + map.put("EntityMinecartCommandBlock", new ResourceLocation("commandblock_minecart")); + map.put("EntityBoat", new ResourceLocation("boat")); + map.put("EntityMinecartRideable", new ResourceLocation("minecart")); + map.put("EntityMinecartChest", new ResourceLocation("chest_minecart")); + map.put("EntityMinecartFurnace", new ResourceLocation("furnace_minecart")); + map.put("EntityMinecartTNT", new ResourceLocation("tnt_minecart")); + map.put("EntityMinecartHopper", new ResourceLocation("hopper_minecart")); + map.put("EntityMinecartMobSpawner", new ResourceLocation("spawner_minecart")); + map.put("EntityCreeper", new ResourceLocation("creeper")); + map.put("EntitySkeleton", new ResourceLocation("skeleton")); + map.put("EntitySpider", new ResourceLocation("spider")); + map.put("EntityGiantZombie", new ResourceLocation("giant")); + map.put("EntityZombie", new ResourceLocation("zombie")); + map.put("EntitySlime", new ResourceLocation("slime")); + map.put("EntityGhast", new ResourceLocation("ghast")); + map.put("EntityPigZombie", new ResourceLocation("zombie_pigman")); + map.put("EntityEnderman", new ResourceLocation("enderman")); + map.put("EntityCaveSpider", new ResourceLocation("cave_spider")); + map.put("EntitySilverfish", new ResourceLocation("silverfish")); + map.put("EntityBlaze", new ResourceLocation("blaze")); + map.put("EntityMagmaCube", new ResourceLocation("magma_cube")); + map.put("EntityEnderDragon", new ResourceLocation("ender_dragon")); + map.put("EntityWither", new ResourceLocation("wither")); + map.put("EntityBat", new ResourceLocation("bat")); + map.put("EntityWitch", new ResourceLocation("witch")); + map.put("EntityEndermite", new ResourceLocation("endermite")); + map.put("EntityGuardian", new ResourceLocation("guardian")); + map.put("EntityShulker", new ResourceLocation("shulker")); + map.put("EntityPig", new ResourceLocation("pig")); + map.put("EntitySheep", new ResourceLocation("sheep")); + map.put("EntityCow", new ResourceLocation("cow")); + map.put("EntityChicken", new ResourceLocation("chicken")); + map.put("EntitySquid", new ResourceLocation("squid")); + map.put("EntityWolf", new ResourceLocation("wolf")); + map.put("EntityMushroomCow", new ResourceLocation("mooshroom")); + map.put("EntitySnowman", new ResourceLocation("snowman")); + map.put("EntityOcelot", new ResourceLocation("ocelot")); + map.put("EntityIronGolem", new ResourceLocation("villager_golem")); + map.put("EntityHorse", new ResourceLocation("horse")); + map.put("EntityRabbit", new ResourceLocation("rabbit")); + map.put("EntityPolarBear", new ResourceLocation("polar_bear")); + map.put("EntityLlama", new ResourceLocation("llama")); + map.put("EntityLlamaSpit", new ResourceLocation("llama_spit")); + map.put("EntityParrot", new ResourceLocation("parrot")); + map.put("EntityVillager", new ResourceLocation("villager")); + map.put("EntityEnderCrystal", new ResourceLocation("ender_crystal")); + map.put("TileEntityFurnace", new ResourceLocation("furnace")); + map.put("TileEntityChest", new ResourceLocation("chest")); + map.put("TileEntityEnderChest", new ResourceLocation("ender_chest")); + map.put("TileEntityRecordPlayer", new ResourceLocation("jukebox")); + map.put("TileEntityDispenser", new ResourceLocation("dispenser")); + map.put("TileEntityDropper", new ResourceLocation("dropper")); + map.put("TileEntitySign", new ResourceLocation("sign")); + map.put("TileEntityMobSpawner", new ResourceLocation("mob_spawner")); + map.put("TileEntityNote", new ResourceLocation("noteblock")); + map.put("TileEntityPiston", new ResourceLocation("piston")); + map.put("TileEntityBrewingStand", new ResourceLocation("brewing_stand")); + map.put("TileEntityEnchantTable", new ResourceLocation("enchanting_table")); + map.put("TileEntityEnderPortal", new ResourceLocation("end_portal")); + map.put("TileEntityBeacon", new ResourceLocation("beacon")); + map.put("TileEntitySkull", new ResourceLocation("skull")); + map.put("TileEntityLightDetector", new ResourceLocation("daylight_detector")); + map.put("TileEntityHopper", new ResourceLocation("hopper")); + map.put("TileEntityComparator", new ResourceLocation("comparator")); + map.put("TileEntityFlowerPot", new ResourceLocation("flower_pot")); + map.put("TileEntityBanner", new ResourceLocation("banner")); + map.put("TileEntityStructure", new ResourceLocation("structure_block")); + map.put("TileEntityEndGateway", new ResourceLocation("end_gateway")); + map.put("TileEntityCommand", new ResourceLocation("command_block")); + map.put("TileEntityShulkerBox", new ResourceLocation("shulker_box")); + map.put("TileEntityBed", new ResourceLocation("bed")); + } + + private static ResourceLocation getKey(String type) { + final ResourceLocation 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, NBTTagCompound cmp, String key, int sourceVer, int targetVer) { + cmp.put(key, convert(type, cmp.getCompound(key), sourceVer, targetVer)); + } + + private static void convertItem(NBTTagCompound nbttagcompound, String key, int sourceVer, int targetVer) { + if (nbttagcompound.contains(key, 10)) { + convertCompound(LegacyType.ITEM_INSTANCE, nbttagcompound, key, sourceVer, targetVer); + } + } + + private static void convertItems(NBTTagCompound nbttagcompound, String key, int sourceVer, int targetVer) { + if (nbttagcompound.contains(key, 9)) { + NBTTagList nbttaglist = nbttagcompound.getList(key, 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.add(j, convert(LegacyType.ITEM_INSTANCE, nbttaglist.getCompound(j), sourceVer, targetVer)); + } + } + + } + + private static class DataConverterEquipment implements DataConverter { + + DataConverterEquipment() {} + + @Override + public int getDataVersion() { + return 100; + } + + @Override + public NBTTagCompound convert(NBTTagCompound cmp) { + NBTTagList nbttaglist = cmp.getList("Equipment", 10); + NBTTagList nbttaglist1; + + if (!nbttaglist.isEmpty() && !cmp.contains("HandItems", 10)) { + nbttaglist1 = new NBTTagList(); + nbttaglist1.add(nbttaglist.get(0)); + nbttaglist1.add(new NBTTagCompound()); + cmp.put("HandItems", nbttaglist1); + } + + if (nbttaglist.size() > 1 && !cmp.contains("ArmorItem", 10)) { + nbttaglist1 = new NBTTagList(); + 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.contains("DropChances", 9)) { + nbttaglist1 = cmp.getList("DropChances", 5); + NBTTagList nbttaglist2; + + if (!cmp.contains("HandDropChances", 10)) { + nbttaglist2 = new NBTTagList(); + nbttaglist2.add(new NBTTagFloat(nbttaglist1.getFloat(0))); + nbttaglist2.add(new NBTTagFloat(0.0F)); + cmp.put("HandDropChances", nbttaglist2); + } + + if (!cmp.contains("ArmorDropChances", 10)) { + nbttaglist2 = new NBTTagList(); + nbttaglist2.add(new NBTTagFloat(nbttaglist1.getFloat(1))); + nbttaglist2.add(new NBTTagFloat(nbttaglist1.getFloat(2))); + nbttaglist2.add(new NBTTagFloat(nbttaglist1.getFloat(3))); + nbttaglist2.add(new NBTTagFloat(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 ResourceLocation(s).toString(); + if (i < 515 && DataInspectorBlockEntity.b.containsKey(key)) { + return DataInspectorBlockEntity.b.get(key); + } else { + return DataInspectorBlockEntity.c.get(key); + } + } + + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (!cmp.contains("tag", 10)) { + return cmp; + } else { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + NBTTagCompound 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.contains("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(ForgeDataFixer.class); + + DataInspectorEntity() {} + + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("EntityTag", 10)) { + NBTTagCompound 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.contains("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 ResourceLocation key; + + DataInspectorTagged(String type) { + this.key = getKey(type); + } + + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (this.key.equals(new ResourceLocation(cmp.getString("id")))) { + cmp = this.inspectChecked(cmp, sourceVer, targetVer); + } + + return cmp; + } + + abstract NBTTagCompound inspectChecked(NBTTagCompound 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; + } + + NBTTagCompound inspectChecked(NBTTagCompound nbttagcompound, int sourceVer, int targetVer) { + for (String s : this.keys) { + ForgeDataFixer.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; + } + + NBTTagCompound inspectChecked(NBTTagCompound nbttagcompound, int sourceVer, int targetVer) { + for (String key : this.keys) { + ForgeDataFixer.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 NBTTagCompound convert(NBTTagCompound cmp) { + if (cmp.contains("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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound cmp) { + if ("minecraft:banner".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + NBTTagCompound nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + + if (nbttagcompound2.contains("Base", 99)) { + cmp.putShort("Damage", (short) (nbttagcompound2.getShort("Base") & 15)); + if (nbttagcompound1.contains("display", 10)) { + NBTTagCompound nbttagcompound3 = nbttagcompound1.getCompound("display"); + + if (nbttagcompound3.contains("Lore", 9)) { + NBTTagList 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 NBTTagCompound convert(NBTTagCompound cmp) { + if ("minecraft:potion".equals(cmp.getString("id"))) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + short short0 = cmp.getShort("Damage"); + + if (!nbttagcompound1.contains("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 NBTTagCompound convert(NBTTagCompound cmp) { + if ("minecraft:spawn_egg".equals(cmp.getString("id"))) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + NBTTagCompound nbttagcompound2 = nbttagcompound1.getCompound("EntityTag"); + short short0 = cmp.getShort("Damage"); + + if (!nbttagcompound2.contains("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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound cmp) { + if (!"MobSpawner".equals(cmp.getString("id"))) { + return cmp; + } else { + if (cmp.contains("EntityId", 8)) { + String s = cmp.getString("EntityId"); + NBTTagCompound nbttagcompound1 = cmp.getCompound("SpawnData"); + + nbttagcompound1.putString("id", s.isEmpty() ? "Pig" : s); + cmp.put("SpawnData", nbttagcompound1); + cmp.remove("EntityId"); + } + + if (cmp.contains("SpawnPotentials", 9)) { + NBTTagList nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound2 = nbttaglist.getCompound(i); + + if (nbttagcompound2.contains("Type", 8)) { + NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound cmp) { + if (cmp.contains("UUID", 8)) { + cmp.putUniqueId("UUID", UUID.fromString(cmp.getString("UUID"))); + } + + return cmp; + } + } + + private static class DataConverterHealth implements DataConverter { + + private static final Set a = Sets.newHashSet(new String[] { "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 NBTTagCompound convert(NBTTagCompound cmp) { + if (DataConverterHealth.a.contains(cmp.getString("id"))) { + float f; + + if (cmp.contains("HealF", 99)) { + f = cmp.getFloat("HealF"); + cmp.remove("HealF"); + } else { + if (!cmp.contains("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 NBTTagCompound convert(NBTTagCompound cmp) { + if ("EntityHorse".equals(cmp.getString("id")) && !cmp.contains("SaddleItem", 10) && cmp.getBoolean("Saddle")) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + 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 NBTTagCompound convert(NBTTagCompound cmp) { + String s = cmp.getString("id"); + boolean flag = "Painting".equals(s); + boolean flag1 = "ItemFrame".equals(s); + + if ((flag || flag1) && !cmp.contains("Facing", 99)) { + EnumFacing enumdirection; + + if (cmp.contains("Direction", 99)) { + enumdirection = EnumFacing.byHorizontalIndex(cmp.getByte("Direction")); + cmp.putInt("TileX", cmp.getInt("TileX") + enumdirection.getXOffset()); + cmp.putInt("TileY", cmp.getInt("TileY") + enumdirection.getYOffset()); + cmp.putInt("TileZ", cmp.getInt("TileZ") + enumdirection.getZOffset()); + cmp.remove("Direction"); + if (flag1 && cmp.contains("ItemRotation", 99)) { + cmp.putByte("ItemRotation", (byte) (cmp.getByte("ItemRotation") * 2)); + } + } else { + enumdirection = EnumFacing.byHorizontalIndex(cmp.getByte("Dir")); + cmp.remove("Dir"); + } + + cmp.putByte("Facing", (byte) enumdirection.getHorizontalIndex()); + } + + return cmp; + } + } + + private static class DataConverterDropChances implements DataConverter { + + DataConverterDropChances() {} + + public int getDataVersion() { + return 113; + } + + public NBTTagCompound convert(NBTTagCompound cmp) { + NBTTagList nbttaglist; + + if (cmp.contains("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.contains("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 NBTTagCompound convert(NBTTagCompound cmp) { + while (cmp.contains("Riding", 10)) { + NBTTagCompound nbttagcompound1 = this.b(cmp); + + this.convert(cmp, nbttagcompound1); + cmp = nbttagcompound1; + } + + return cmp; + } + + protected void convert(NBTTagCompound nbttagcompound, NBTTagCompound nbttagcompound1) { + NBTTagList nbttaglist = new NBTTagList(); + + nbttaglist.add(nbttagcompound); + nbttagcompound1.put("Passengers", nbttaglist); + } + + protected NBTTagCompound b(NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Riding"); + + nbttagcompound.remove("Riding"); + return nbttagcompound1; + } + } + + private static class DataConverterBook implements DataConverter { + + DataConverterBook() {} + + public int getDataVersion() { + return 165; + } + + public NBTTagCompound convert(NBTTagCompound cmp) { + if ("minecraft:written_book".equals(cmp.getString("id"))) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("pages", 9)) { + NBTTagList 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) && !StringUtils.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 TextComponentString(s); + } else { + try { + object = JsonUtils.fromJson(DataConverterSignText.a, s, ITextComponent.class, true); + if (object == null) { + object = new TextComponentString(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = ITextComponent.Serializer.fromJson(s); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = ITextComponent.Serializer.fromJsonLenient(s); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = new TextComponentString(s); + } + } + } else { + object = new TextComponentString(""); + } + + nbttaglist.set(i, new NBTTagString(ITextComponent.Serializer.toJson((ITextComponent) object))); + } + + nbttagcompound1.put("pages", nbttaglist); + } + } + + return cmp; + } + } + + private static class DataConverterCookedFish implements DataConverter { + + private static final ResourceLocation a = new ResourceLocation("cooked_fished"); + + DataConverterCookedFish() {} + + public int getDataVersion() { + return 502; + } + + public NBTTagCompound convert(NBTTagCompound cmp) { + if (cmp.contains("id", 8) && DataConverterCookedFish.a.equals(new ResourceLocation(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 NBTTagCompound convert(NBTTagCompound cmp) { + if ("Zombie".equals(cmp.getString("id")) && cmp.getBoolean("IsVillager")) { + if (!cmp.contains("ZombieType", 99)) { + int i = -1; + + if (cmp.contains("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 NBTTagCompound convert(NBTTagCompound cmp) { + cmp.putString("useVbo", "true"); + return cmp; + } + } + + private static class DataConverterGuardian implements DataConverter { + + DataConverterGuardian() {} + + public int getDataVersion() { + return 700; + } + + public NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound 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)) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + + if (!nbttagcompound1.contains("Potion", 8)) { + nbttagcompound1.putString("Potion", "minecraft:water"); + } + + if (!cmp.contains("tag", 10)) { + cmp.put("tag", nbttagcompound1); + } + } + + return cmp; + } + } + + private static class DataConverterShulker implements DataConverter { + + DataConverterShulker() {} + + public int getDataVersion() { + return 808; + } + + public NBTTagCompound convert(NBTTagCompound cmp) { + if ("minecraft:shulker".equals(cmp.getString("id")) && !cmp.contains("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 NBTTagCompound convert(NBTTagCompound cmp) { + if ("minecraft:shulker_box".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound 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 NBTTagCompound convert(NBTTagCompound cmp) { + if (cmp.contains("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 NBTTagCompound convert(NBTTagCompound 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(ForgeDataFixer.class); + + DataConverterBedBlock() {} + + public int getDataVersion() { + return 1125; + } + + public NBTTagCompound convert(NBTTagCompound cmp) { + boolean flag = true; + + try { + NBTTagCompound nbttagcompound1 = cmp.getCompound("Level"); + int i = nbttagcompound1.getInt("xPos"); + int j = nbttagcompound1.getInt("zPos"); + NBTTagList nbttaglist = nbttagcompound1.getList("TileEntities", 10); + NBTTagList nbttaglist1 = nbttagcompound1.getList("Sections", 10); + + for (int k = 0; k < nbttaglist1.size(); ++k) { + NBTTagCompound nbttagcompound2 = nbttaglist1.getCompound(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; + NBTTagCompound nbttagcompound3 = new NBTTagCompound(); + + 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 NBTTagCompound convert(NBTTagCompound cmp) { + if ("minecraft:bed".equals(cmp.getString("id")) && cmp.getShort("Damage") == 0) { + cmp.putShort("Damage", (short) EnumDyeColor.RED.getId()); + } + + return cmp; + } + } + + private static class DataConverterSignText implements DataConverter { + + public static final Gson a = new GsonBuilder().registerTypeAdapter(ITextComponent.class, new JsonDeserializer() { + ITextComponent a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + if (jsonelement.isJsonPrimitive()) { + return new TextComponentString(jsonelement.getAsString()); + } else if (jsonelement.isJsonArray()) { + JsonArray jsonarray = jsonelement.getAsJsonArray(); + ITextComponent iTextComponent = null; + Iterator iterator = jsonarray.iterator(); + + while (iterator.hasNext()) { + JsonElement jsonelement1 = (JsonElement) iterator.next(); + ITextComponent iTextComponent1 = this.a(jsonelement1, jsonelement1.getClass(), jsondeserializationcontext); + + if (iTextComponent == null) { + iTextComponent = iTextComponent1; + } else { + iTextComponent.appendSibling(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 NBTTagCompound convert(NBTTagCompound 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(NBTTagCompound nbttagcompound, String s) { + String s1 = nbttagcompound.getString(s); + Object object = null; + + if (!"null".equals(s1) && !StringUtils.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 TextComponentString(s1); + } else { + try { + object = JsonUtils.fromJson(DataConverterSignText.a, s1, ITextComponent.class, true); + if (object == null) { + object = new TextComponentString(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = ITextComponent.Serializer.fromJson(s1); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = ITextComponent.Serializer.fromJsonLenient(s1); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = new TextComponentString(s1); + } + } + } else { + object = new TextComponentString(""); + } + + nbttagcompound.putString(s, ITextComponent.Serializer.toJson((ITextComponent) object)); + } + } + + private static class DataInspectorPlayerVehicle implements DataInspector { + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (cmp.contains("RootVehicle", 10)) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("RootVehicle"); + + if (nbttagcompound1.contains("Entity", 10)) { + convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); + } + } + + return cmp; + } + } + + private static class DataInspectorLevelPlayer implements DataInspector { + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (cmp.contains("Player", 10)) { + convertCompound(LegacyType.PLAYER, cmp, "Player", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorStructure implements DataInspector { + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + NBTTagList nbttaglist; + int j; + NBTTagCompound nbttagcompound1; + + if (cmp.contains("entities", 9)) { + nbttaglist = cmp.getList("entities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (NBTTagCompound) nbttaglist.get(j); + if (nbttagcompound1.contains("nbt", 10)) { + convertCompound(LegacyType.ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); + } + } + } + + if (cmp.contains("blocks", 9)) { + nbttaglist = cmp.getList("blocks", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (NBTTagCompound) nbttaglist.get(j); + if (nbttagcompound1.contains("nbt", 10)) { + convertCompound(LegacyType.BLOCK_ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); + } + } + } + + return cmp; + } + } + + private static class DataInspectorChunks implements DataInspector { + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (cmp.contains("Level", 10)) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("Level"); + NBTTagList nbttaglist; + int j; + + if (nbttagcompound1.contains("Entities", 9)) { + nbttaglist = nbttagcompound1.getList("Entities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ENTITY, (NBTTagCompound) nbttaglist.get(j), sourceVer, targetVer)); + } + } + + if (nbttagcompound1.contains("TileEntities", 9)) { + nbttaglist = nbttagcompound1.getList("TileEntities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.BLOCK_ENTITY, (NBTTagCompound) nbttaglist.get(j), sourceVer, targetVer)); + } + } + } + + return cmp; + } + } + + private static class DataInspectorEntityPassengers implements DataInspector { + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (cmp.contains("Passengers", 9)) { + NBTTagList nbttaglist = cmp.getList("Passengers", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ENTITY, nbttaglist.getCompound(j), sourceVer, targetVer)); + } + } + + return cmp; + } + } + + private static class DataInspectorPlayer implements DataInspector { + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + convertItems(cmp, "Inventory", sourceVer, targetVer); + convertItems(cmp, "EnderItems", sourceVer, targetVer); + if (cmp.contains("ShoulderEntityLeft", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityLeft", sourceVer, targetVer); + } + + if (cmp.contains("ShoulderEntityRight", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityRight", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorVillagers implements DataInspector { + ResourceLocation entityVillager = getKey("EntityVillager"); + + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (entityVillager.equals(new ResourceLocation(cmp.getString("id"))) && cmp.contains("Offers", 10)) { + NBTTagCompound nbttagcompound1 = cmp.getCompound("Offers"); + + if (nbttagcompound1.contains("Recipes", 9)) { + NBTTagList nbttaglist = nbttagcompound1.getList("Recipes", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + NBTTagCompound nbttagcompound2 = nbttaglist.getCompound(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 { + ResourceLocation entityMinecartMobSpawner = getKey("EntityMinecartMobSpawner"); + ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); + + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + String s = cmp.getString("id"); + if (entityMinecartMobSpawner.equals(new ResourceLocation(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 { + ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); + + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (tileEntityMobSpawner.equals(new ResourceLocation(cmp.getString("id")))) { + if (cmp.contains("SpawnPotentials", 9)) { + NBTTagList nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(j); + + convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); + } + } + + convertCompound(LegacyType.ENTITY, cmp, "SpawnData", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorCommandBlock implements DataInspector { + ResourceLocation tileEntityCommand = getKey("TileEntityCommand"); + + @Override + public NBTTagCompound inspect(NBTTagCompound cmp, int sourceVer, int targetVer) { + if (tileEntityCommand.equals(new ResourceLocation(cmp.getString("id")))) { + cmp.putString("id", "Control"); + convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); + cmp.putString("id", "MinecartCommandBlock"); + } + + return cmp; + } + } +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index 7f4fa238a4..1a3958d55e 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -26,6 +26,7 @@ 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.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import net.minecraft.command.Commands; @@ -53,11 +54,13 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { private final ForgeWorldEdit mod; private final MinecraftServer server; + private final ForgeDataFixer dataFixer; private boolean hookingEvents = false; ForgePlatform(ForgeWorldEdit mod) { this.mod = mod; this.server = ServerLifecycleHooks.getCurrentServer(); + this.dataFixer = new ForgeDataFixer(getDataVersion()); } boolean isHookingEvents() { @@ -75,6 +78,11 @@ public int getDataVersion() { return 1631; } + @Override + public DataFixer getDataFixer() { + return dataFixer; + } + @Override public boolean isValidMobType(String type) { return net.minecraftforge.registries.ForgeRegistries.ENTITIES.containsKey(new ResourceLocation(type)); diff --git a/worldedit-libs/build.gradle b/worldedit-libs/build.gradle index a889835d90..a1954e09d5 100644 --- a/worldedit-libs/build.gradle +++ b/worldedit-libs/build.gradle @@ -88,10 +88,10 @@ configure(subprojects + project("core:ap")) { build.dependsOn(jar, sourcesJar) } -def textExtrasVersion = "3.0.1" +def textExtrasVersion = "3.0.2" project("core") { def textVersion = "3.0.0" - def pistonVersion = '0.2.2' + def pistonVersion = '0.3.0' dependencies { shade "net.kyori:text-api:$textVersion"