diff --git a/build.gradle b/build.gradle index 118600546..b22524f26 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,8 @@ dependencies { compile group: 'com.sk89q', name: 'jchronic', version:'0.2.4a' compile group: 'com.google.code.findbugs', name: 'jsr305', version: '1.3.9' compile group: 'com.thoughtworks.paranamer', name: 'paranamer', version: '2.6' + compile group: 'com.google.code.gson', name: 'gson', version: '2.2.4' + compile group: 'net.sf.trove4j', name: 'trove4j', version: '3.0.3' testCompile group: 'org.mockito', name: 'mockito-core', version:'1.9.0-rc1' } diff --git a/pom.xml b/pom.xml index 6b09a8665..5d33c64b6 100644 --- a/pom.xml +++ b/pom.xml @@ -142,6 +142,22 @@ jar + + + com.google.code.gson + gson + 2.2.4 + true + + + + + net.sf.trove4j + trove4j + 3.0.3 + true + + com.google.code.findbugs @@ -187,6 +203,18 @@ install ${basedir}/src/main/java/ + + + + . + false + ${basedir}/src/main/resources/ + + **/*.json + + + + @@ -367,12 +395,25 @@ shade + true com.sk89q:jchronic com.thoughtworks.paranamer:paranamer + com.google.code.gson:gson + net.sf.trove4j:trove4j + + + gnu.trove + com.sk89q.worldedit.internal.trove + + + com.google.gson + com.sk89q.worldedit.internal.gson + + @@ -513,16 +554,6 @@ jar true - - - - org.bukkit - craftbukkit - 1.7.9-R0.1-SNAPSHOT - compile - jar - true - @@ -549,11 +580,11 @@ - nmsblocks/ + . false - ${basedir}/src/bukkit/resources/nmsblocks/ + ${basedir}/src/bukkit/resources/ - *.class + **/*.class diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java new file mode 100644 index 000000000..c9de48b73 --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java @@ -0,0 +1,144 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; +import org.bukkit.Bukkit; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Adapts between Bukkit and WorldEdit equivalent objects. + */ +final class BukkitAdapter { + + private BukkitAdapter() { + } + + /** + * Create a WorldEdit world from a Bukkit world. + * + * @param world the Bukkit world + * @return a WorldEdit world + */ + public static World adapt(org.bukkit.World world) { + checkNotNull(world); + return new BukkitWorld(world); + } + + /** + * Create a Bukkit world from a WorldEdit world. + * + * @param world the WorldEdit world + * @return a Bukkit world + */ + public static org.bukkit.World adapt(World world) { + checkNotNull(world); + if (world instanceof BukkitWorld) { + return ((BukkitWorld) world).getWorld(); + } else { + org.bukkit.World match = Bukkit.getServer().getWorld(world.getName()); + if (match != null) { + return match; + } else { + throw new IllegalArgumentException("Can't find a Bukkit world for " + world); + } + } + } + + /** + * Create a WorldEdit location from a Bukkit location. + * + * @param location the Bukkit location + * @return a WorldEdit location + */ + public static Location adapt(org.bukkit.Location location) { + checkNotNull(location); + Vector position = BukkitUtil.toVector(location); + return new com.sk89q.worldedit.util.Location( + adapt(location.getWorld()), + position, + location.getYaw(), + location.getPitch()); + } + + /** + * Create a Bukkit location from a WorldEdit location. + * + * @param location the WorldEdit location + * @return a Bukkit location + */ + public static org.bukkit.Location adapt(Location location) { + checkNotNull(location); + Vector position = location.toVector(); + return new org.bukkit.Location( + adapt((World) location.getExtent()), + position.getX(), position.getY(), position.getZ(), + location.getYaw(), + location.getPitch()); + } + + /** + * Create a Bukkit location from a WorldEdit position with a Bukkit world. + * + * @param world the Bukkit world + * @param position the WorldEdit position + * @return a Bukkit location + */ + public static org.bukkit.Location adapt(org.bukkit.World world, Vector position) { + checkNotNull(world); + checkNotNull(position); + return new org.bukkit.Location( + world, + position.getX(), position.getY(), position.getZ()); + } + + /** + * Create a Bukkit location from a WorldEdit location with a Bukkit world. + * + * @param world the Bukkit world + * @param location the WorldEdit location + * @return a Bukkit location + */ + public static org.bukkit.Location adapt(org.bukkit.World world, Location location) { + checkNotNull(world); + checkNotNull(location); + return new org.bukkit.Location( + world, + location.getX(), location.getY(), location.getZ(), + location.getYaw(), + location.getPitch()); + } + + /** + * Create a WorldEdit entity from a Bukkit entity. + * + * @param entity the Bukkit entity + * @return a WorldEdit entity + */ + public static Entity adapt(org.bukkit.entity.Entity entity) { + checkNotNull(entity); + return new BukkitEntity(entity); + } + +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeRegistry.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeRegistry.java new file mode 100644 index 000000000..72e3379fc --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeRegistry.java @@ -0,0 +1,79 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit; + +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.BiomeData; +import com.sk89q.worldedit.world.registry.BiomeRegistry; +import org.bukkit.block.Biome; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A biome registry for Bukkit. + */ +class BukkitBiomeRegistry implements BiomeRegistry { + + BukkitBiomeRegistry() { + } + + @Nullable + @Override + public BaseBiome createFromId(int id) { + return new BaseBiome(id); + } + + @Override + public List getBiomes() { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + List biomes = new ArrayList(); + for (Biome biome : Biome.values()) { + int biomeId = adapter.getBiomeId(biome); + biomes.add(new BaseBiome(biomeId)); + } + return biomes; + } else { + return Collections.emptyList(); + } + } + + @Nullable + @Override + public BiomeData getData(BaseBiome biome) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + final Biome bukkitBiome = adapter.getBiome(biome.getId()); + return new BiomeData() { + @Override + public String getName() { + return bukkitBiome.name(); + } + }; + } else { + return null; + } + } + +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeType.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeType.java deleted file mode 100644 index 5962d54f8..000000000 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeType.java +++ /dev/null @@ -1,106 +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.bukkit; - -import java.util.Locale; - -import org.bukkit.block.Biome; - -import com.sk89q.worldedit.BiomeType; - -public enum BukkitBiomeType implements BiomeType { - - SWAMPLAND(Biome.SWAMPLAND), - FOREST(Biome.FOREST), - TAIGA(Biome.TAIGA), - DESERT(Biome.DESERT), - PLAINS(Biome.PLAINS), - HELL(Biome.HELL), - SKY(Biome.SKY), - RIVER(Biome.RIVER), - EXTREME_HILLS(Biome.EXTREME_HILLS), - OCEAN(Biome.OCEAN), - FROZEN_OCEAN(Biome.FROZEN_OCEAN), - FROZEN_RIVER(Biome.FROZEN_RIVER), - ICE_PLAINS(Biome.ICE_PLAINS), - ICE_MOUNTAINS(Biome.ICE_MOUNTAINS), - MUSHROOM_ISLAND(Biome.MUSHROOM_ISLAND), - MUSHROOM_SHORE(Biome.MUSHROOM_SHORE), - BEACH(Biome.BEACH), - DESERT_HILLS(Biome.DESERT_HILLS), - FOREST_HILLS(Biome.FOREST_HILLS), - TAIGA_HILLS(Biome.TAIGA_HILLS), - SMALL_MOUNTAINS(Biome.SMALL_MOUNTAINS), - JUNGLE(Biome.JUNGLE), - JUNGLE_HILLS(Biome.JUNGLE_HILLS), - JUNGLE_EDGE(Biome.JUNGLE_EDGE), - DEEP_OCEAN(Biome.DEEP_OCEAN), - STONE_BEACH(Biome.STONE_BEACH), - COLD_BEACH(Biome.COLD_BEACH), - BIRCH_FOREST(Biome.BIRCH_FOREST), - BIRCH_FOREST_HILLS(Biome.BIRCH_FOREST_HILLS), - ROOFED_FOREST(Biome.ROOFED_FOREST), - COLD_TAIGA(Biome.COLD_TAIGA), - COLD_TAIGA_HILLS(Biome.COLD_TAIGA_HILLS), - MEGA_TAIGA(Biome.MEGA_TAIGA), - MEGA_TAIGA_HILLS(Biome.MEGA_TAIGA_HILLS), - EXTREME_HILLS_PLUS(Biome.EXTREME_HILLS_PLUS), - SAVANNA(Biome.SAVANNA), - SAVANNA_PLATEAU(Biome.SAVANNA_PLATEAU), - MESA(Biome.MESA), - MESA_PLATEAU_FOREST(Biome.MESA_PLATEAU_FOREST), - MESA_PLATEAU(Biome.MESA_PLATEAU), - SUNFLOWER_PLAINS(Biome.SUNFLOWER_PLAINS), - DESERT_MOUNTAINS(Biome.DESERT_MOUNTAINS), - FLOWER_FOREST(Biome.FLOWER_FOREST), - TAIGA_MOUNTAINS(Biome.TAIGA_MOUNTAINS), - SWAMPLAND_MOUNTAINS(Biome.SWAMPLAND_MOUNTAINS), - ICE_PLAINS_SPIKES(Biome.ICE_PLAINS_SPIKES), - JUNGLE_MOUNTAINS(Biome.JUNGLE_MOUNTAINS), - JUNGLE_EDGE_MOUNTAINS(Biome.JUNGLE_EDGE_MOUNTAINS), - COLD_TAIGA_MOUNTAINS(Biome.COLD_TAIGA_MOUNTAINS), - SAVANNA_MOUNTAINS(Biome.SAVANNA_MOUNTAINS), - SAVANNA_PLATEAU_MOUNTAINS(Biome.SAVANNA_PLATEAU_MOUNTAINS), - MESA_BRYCE(Biome.MESA_BRYCE), - MESA_PLATEAU_FOREST_MOUNTAINS(Biome.MESA_PLATEAU_FOREST_MOUNTAINS), - MESA_PLATEAU_MOUNTAINS(Biome.MESA_PLATEAU_MOUNTAINS), - BIRCH_FOREST_MOUNTAINS(Biome.BIRCH_FOREST_MOUNTAINS), - BIRCH_FOREST_HILLS_MOUNTAINS(Biome.BIRCH_FOREST_HILLS_MOUNTAINS), - ROOFED_FOREST_MOUNTAINS(Biome.ROOFED_FOREST_MOUNTAINS), - MEGA_SPRUCE_TAIGA(Biome.MEGA_SPRUCE_TAIGA), - EXTREME_HILLS_MOUNTAINS(Biome.EXTREME_HILLS_MOUNTAINS), - EXTREME_HILLS_PLUS_MOUNTAINS(Biome.EXTREME_HILLS_PLUS_MOUNTAINS), - MEGA_SPRUCE_TAIGA_HILLS(Biome.MEGA_SPRUCE_TAIGA_HILLS); - - private Biome bukkitBiome; - - private BukkitBiomeType(Biome biome) { - this.bukkitBiome = biome; - } - - @Override - public String getName() { - return name().toLowerCase(Locale.ENGLISH); - } - - public Biome getBukkitBiome() { - return bukkitBiome; - } -} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeTypes.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeTypes.java deleted file mode 100644 index 8a74fa91b..000000000 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitBiomeTypes.java +++ /dev/null @@ -1,59 +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.bukkit; - -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -import com.sk89q.worldedit.BiomeType; -import com.sk89q.worldedit.BiomeTypes; -import com.sk89q.worldedit.UnknownBiomeTypeException; - -public class BukkitBiomeTypes implements BiomeTypes { - - public BukkitBiomeTypes() { - } - - @Override - public boolean has(String name) { - try { - BukkitBiomeType.valueOf(name.toUpperCase(Locale.ENGLISH)); - return true; - } catch (IllegalArgumentException exc) { - return false; - } - } - - @Override - public BiomeType get(String name) throws UnknownBiomeTypeException { - try { - return BukkitBiomeType.valueOf(name.toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException exc) { - throw new UnknownBiomeTypeException(name); - } - } - - @Override - public List all() { - return Arrays.asList(BukkitBiomeType.values()); - } - -} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitEntity.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitEntity.java new file mode 100644 index 000000000..483b740eb --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitEntity.java @@ -0,0 +1,110 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit; + +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.entity.metadata.EntityType; +import com.sk89q.worldedit.entity.metadata.Tameable; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An adapter to adapt a Bukkit entity into a WorldEdit one. + */ +class BukkitEntity implements Entity { + + private final org.bukkit.entity.Entity entity; + + /** + * Create a new instance. + * + * @param entity the entity + */ + BukkitEntity(org.bukkit.entity.Entity entity) { + checkNotNull(entity); + this.entity = entity; + } + + /** + * Get the underlying Bukkit entity. + * + * @return the Bukkit entity + */ + protected org.bukkit.entity.Entity getEntity() { + return entity; + } + + @SuppressWarnings("unchecked") + T getMetaData(Class metaDataClass) { + if (metaDataClass == Tameable.class && getEntity() instanceof org.bukkit.entity.Tameable) { + return (T) new TameableAdapter((org.bukkit.entity.Tameable) getEntity()); + } else { + return null; + } + } + + @Override + public Extent getExtent() { + return BukkitAdapter.adapt(getEntity().getWorld()); + } + + @Override + public Location getLocation() { + return BukkitAdapter.adapt(getEntity().getLocation()); + } + + @Override + public BaseEntity getState() { + if (entity instanceof Player) { + return null; + } + + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + return adapter.getEntity(entity); + } else { + return null; + } + } + + @Override + public boolean remove() { + entity.remove(); + return entity.isDead(); + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T getFacet(Class cls) { + if (EntityType.class.isAssignableFrom(cls)) { + return (T) new BukkitEntityType(entity); + } else { + return null; + } + } +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitEntityType.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitEntityType.java new file mode 100644 index 000000000..d80526c83 --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitEntityType.java @@ -0,0 +1,140 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit; + +import com.sk89q.worldedit.entity.metadata.EntityType; +import com.sk89q.worldedit.util.Enums; +import org.bukkit.entity.Ambient; +import org.bukkit.entity.Animals; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; +import org.bukkit.entity.ExperienceOrb; +import org.bukkit.entity.FallingBlock; +import org.bukkit.entity.Golem; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Item; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Minecart; +import org.bukkit.entity.Painting; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.entity.Tameable; +import org.bukkit.entity.Villager; + +import static com.google.common.base.Preconditions.checkNotNull; + +class BukkitEntityType implements EntityType { + + private static final org.bukkit.entity.EntityType tntMinecartType = + Enums.findByValue(org.bukkit.entity.EntityType.class, "MINECART_TNT"); + + private final Entity entity; + + BukkitEntityType(Entity entity) { + checkNotNull(entity); + this.entity = entity; + } + + @Override + public boolean isPlayerDerived() { + return entity instanceof HumanEntity; + } + + @Override + public boolean isProjectile() { + return entity instanceof Projectile; + } + + @Override + public boolean isItem() { + return entity instanceof Item; + } + + @Override + public boolean isFallingBlock() { + return entity instanceof FallingBlock; + } + + @Override + public boolean isPainting() { + return entity instanceof Painting; + } + + @Override + public boolean isItemFrame() { + return entity instanceof ItemFrame; + } + + @Override + public boolean isBoat() { + return entity instanceof Boat; + } + + @Override + public boolean isMinecart() { + return entity instanceof Minecart; + } + + @Override + public boolean isTNT() { + return entity instanceof TNTPrimed || entity.getType() == tntMinecartType; + } + + @Override + public boolean isExperienceOrb() { + return entity instanceof ExperienceOrb; + } + + @Override + public boolean isLiving() { + return entity instanceof LivingEntity; + } + + @Override + public boolean isAnimal() { + return entity instanceof Animals; + } + + @Override + public boolean isAmbient() { + return entity instanceof Ambient; + } + + @Override + public boolean isNPC() { + return entity instanceof Villager; + } + + @Override + public boolean isGolem() { + return entity instanceof Golem; + } + + @Override + public boolean isTamed() { + return entity instanceof Tameable && ((Tameable) entity).isTamed(); + } + + @Override + public boolean isTagged() { + return entity instanceof LivingEntity && ((LivingEntity) entity).getCustomName() != null; + } +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 637c7c7e8..519aee885 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -19,22 +19,23 @@ package com.sk89q.worldedit.bukkit; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.util.Vectors; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - import com.sk89q.util.StringUtil; import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.ServerInterface; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.internal.cui.CUIEvent; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; public class BukkitPlayer extends LocalPlayer { private Player player; @@ -170,11 +171,25 @@ public class BukkitPlayer extends LocalPlayer { player.setFlying(true); } + @Override + public BaseEntity getState() { + throw new UnsupportedOperationException("Cannot create a state from this object"); + } + @Override public com.sk89q.worldedit.util.Location getLocation() { Location nativeLocation = player.getLocation(); Vector position = BukkitUtil.toVector(nativeLocation); - Vector direction = Vectors.fromEulerDeg(nativeLocation.getYaw(), nativeLocation.getPitch()); - return new com.sk89q.worldedit.util.Location(getWorld(), position, direction); + return new com.sk89q.worldedit.util.Location( + getWorld(), + position, + nativeLocation.getYaw(), + nativeLocation.getPitch()); + } + + @Nullable + @Override + public T getFacet(Class cls) { + return null; } } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 1bf4c6a61..adb25c7de 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.bukkit; import com.sk89q.bukkit.util.CommandInfo; import com.sk89q.bukkit.util.CommandRegistration; -import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.ServerInterface; @@ -50,13 +49,13 @@ public class BukkitServerInterface extends ServerInterface implements MultiUserP public Server server; public WorldEditPlugin plugin; private CommandRegistration dynamicCommands; - private BukkitBiomeTypes biomes; + private BukkitBiomeRegistry biomes; private boolean hookingEvents; public BukkitServerInterface(WorldEditPlugin plugin, Server server) { this.plugin = plugin; this.server = server; - this.biomes = new BukkitBiomeTypes(); + this.biomes = new BukkitBiomeRegistry(); dynamicCommands = new CommandRegistration(plugin); } @@ -81,11 +80,6 @@ public class BukkitServerInterface extends ServerInterface implements MultiUserP plugin.loadConfiguration(); } - @Override - public BiomeTypes getBiomes() { - return biomes; - } - @Override public int schedule(long delay, long period, Runnable task) { return Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, task, delay, period); diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 4c73526d3..c95e4130d 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -19,32 +19,42 @@ package com.sk89q.worldedit.bukkit; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.EntityType; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.*; -import com.sk89q.worldedit.blocks.ContainerBlock; -import com.sk89q.worldedit.blocks.NoteBlock; -import com.sk89q.worldedit.bukkit.entity.BukkitEntity; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.blocks.LazyBlock; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator; -import org.bukkit.*; -import org.bukkit.Location; -import org.bukkit.block.*; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.*; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.WorldData; +import org.bukkit.Effect; +import org.bukkit.Material; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.Chest; +import org.bukkit.entity.Entity; import org.bukkit.inventory.DoubleChestInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; +import javax.annotation.Nullable; import java.lang.ref.WeakReference; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -53,153 +63,69 @@ import static com.google.common.base.Preconditions.checkNotNull; public class BukkitWorld extends LocalWorld { private static final Logger logger = WorldEdit.logger; - private final WeakReference worldRef; - private static boolean skipNmsAccess = false; - private static boolean skipNmsSafeSet = false; - private static boolean skipNmsValidBlockCheck = false; - /* - * holder for the nmsblock class that we should use - */ - private static Class nmsBlockType; - private static Method nmsSetMethod; - private static Method nmsValidBlockMethod; - private static Method nmsGetMethod; - private static Method nmsSetSafeMethod; - - // copied from WG - private static > T tryEnum(Class enumType, String ... values) { - for (String val : values) { - try { - return Enum.valueOf(enumType, val); - } catch (IllegalArgumentException e) {} + private static final Map effects = new HashMap(); + static { + for (Effect effect : Effect.values()) { + effects.put(effect.getId(), effect); } - return null; } - private static org.bukkit.entity.EntityType tntMinecartType; - private static boolean checkMinecartType = true; + + private final WeakReference worldRef; /** * Construct the object. - * @param world + * + * @param world the world */ @SuppressWarnings("unchecked") public BukkitWorld(World world) { this.worldRef = new WeakReference(world); - - if (checkMinecartType) { - tntMinecartType = tryEnum(org.bukkit.entity.EntityType.class, "MINECART_TNT"); - checkMinecartType = false; - } - // check if we have a class we can use for nms access - - // only run once per server startup - if (nmsBlockType != null || skipNmsAccess || skipNmsSafeSet || skipNmsValidBlockCheck) return; - Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit"); - if (!(plugin instanceof WorldEditPlugin)) return; // hopefully never happens - WorldEditPlugin wePlugin = ((WorldEditPlugin) plugin); - File nmsBlocksDir = new File(wePlugin.getDataFolder() + File.separator + "nmsblocks" + File.separator); - if (nmsBlocksDir.listFiles() == null) { // no files to use - skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true; - return; - } - try { - // make a classloader that can handle our blocks - NmsBlockClassLoader loader = new NmsBlockClassLoader(BukkitWorld.class.getClassLoader(), nmsBlocksDir); - String filename; - for (File f : nmsBlocksDir.listFiles()) { - if (!f.isFile()) continue; - filename = f.getName(); - // load class using magic keyword - Class testBlock = null; - try { - testBlock = loader.loadClass("CL-NMS" + filename); - } catch (Throwable e) { - // someone is putting things where they don't belong - continue; - } - filename = filename.replaceFirst(".class$", ""); // get rid of extension - if (NmsBlock.class.isAssignableFrom(testBlock)) { - // got a NmsBlock, test it now - Class nmsClass = (Class) testBlock; - boolean canUse = false; - try { - canUse = (Boolean) nmsClass.getMethod("verify").invoke(null); - } catch (Throwable e) { - continue; - } - if (!canUse) continue; // not for this server - nmsBlockType = nmsClass; - nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class); - nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class); - nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class); - nmsSetSafeMethod = nmsBlockType.getMethod("setSafely", - BukkitWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class); - // phew - break; - } - } - if (nmsBlockType != null) { - logger.info("[WorldEdit] Using external NmsBlock for this version: " + nmsBlockType.getName()); - } else { - // try our default - try { - nmsBlockType = (Class) Class.forName("com.sk89q.worldedit.bukkit.DefaultNmsBlock"); - boolean canUse = (Boolean) nmsBlockType.getMethod("verify").invoke(null); - if (canUse) { - nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class); - nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class); - nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class); - nmsSetSafeMethod = nmsBlockType.getMethod("setSafely", - BukkitWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class); - logger.info("[WorldEdit] Using inbuilt NmsBlock for this version."); - } - } catch (Throwable e) { - // OMG DEVS WAI U NO SUPPORT SERVER - skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true; - logger.warning("[WorldEdit] No compatible nms block class found."); - } - } - } catch (Throwable e) { - logger.warning("[WorldEdit] Unable to load NmsBlock classes, make sure they are installed correctly."); - e.printStackTrace(); - skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true; - } } - private class NmsBlockClassLoader extends ClassLoader { - public File searchDir; - public NmsBlockClassLoader(ClassLoader parent, File searchDir) { - super(parent); - this.searchDir = searchDir; - } + @Override + public List getEntities(Region region) { + World world = getWorld(); - @Override - public Class loadClass(String name) throws ClassNotFoundException { - if (!name.startsWith("CL-NMS")) { - return super.loadClass(name); - } else { - name = name.replace("CL-NMS", ""); // hacky lol + List entities = new ArrayList(); + for (Vector2D pt : region.getChunks()) { + if (!world.isChunkLoaded(pt.getBlockX(), pt.getBlockZ())) { + continue; } - try { - URL url = new File(searchDir, name).toURI().toURL(); - InputStream input = url.openConnection().getInputStream(); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int data = input.read(); - while (data != -1) { - buffer.write(data); - data = input.read(); + final Entity[] ents = world.getChunkAt(pt.getBlockX(), pt.getBlockZ()).getEntities(); + for (Entity ent : ents) { + if (region.contains(BukkitUtil.toVector(ent.getLocation()))) { + entities.add(BukkitAdapter.adapt(ent)); } - input.close(); - - byte[] classData = buffer.toByteArray(); - - return defineClass(name.replaceFirst(".class$", ""), classData, 0, classData.length); - } catch (Throwable e) { - throw new ClassNotFoundException(); } } + return entities; + } + + @Override + public List getEntities() { + List list = new ArrayList(); + for (Entity entity : getWorld().getEntities()) { + list.add(BukkitAdapter.adapt(entity)); + } + return list; + } + + @Nullable + @Override + public com.sk89q.worldedit.entity.Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + Entity createdEntity = adapter.createEntity(BukkitAdapter.adapt(getWorld(), location), entity); + if (createdEntity != null) { + return new BukkitEntity(createdEntity); + } else { + return null; + } + } else { + return null; + } } /** @@ -229,90 +155,11 @@ public class BukkitWorld extends LocalWorld { return getWorld().getName(); } - /** - * Set block type. - * - * @param pt - * @param type - * @return - */ - @Override - public boolean setBlockType(Vector pt, int type) { - return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type); - } - - /** - * Set block type. - * - * @param pt - * @param type - * @return - */ - @Override - public boolean setBlockTypeFast(Vector pt, int type) { - return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type, false); - } - - @Override - public boolean setTypeIdAndData(Vector pt, int type, int data) { - return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, true); - } - - @Override - public boolean setTypeIdAndDataFast(Vector pt, int type, int data) { - return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, false); - } - - /** - * Get block type. - * - * @param pt - * @return - */ - @Override - public int getBlockType(Vector pt) { - return getWorld().getBlockTypeIdAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - } - - @Override - public void setBlockData(Vector pt, int data) { - getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data); - } - - @Override - public void setBlockDataFast(Vector pt, int data) { - getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data, false); - } - - @Override - public int getBlockData(Vector pt) { - return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getData(); - } - @Override public int getBlockLightLevel(Vector pt) { return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel(); } - @Override - public BiomeType getBiome(Vector2D pt) { - Biome bukkitBiome = getWorld().getBiome(pt.getBlockX(), pt.getBlockZ()); - try { - return BukkitBiomeType.valueOf(bukkitBiome.name()); - } catch (IllegalArgumentException exc) { - return BiomeType.UNKNOWN; - } - } - - @Override - public void setBiome(Vector2D pt, BiomeType biome) { - if (biome instanceof BukkitBiomeType) { - Biome bukkitBiome; - bukkitBiome = ((BukkitBiomeType) biome).getBukkitBiome(); - getWorld().setBiome(pt.getBlockX(), pt.getBlockZ(), bukkitBiome); - } - } - @Override public boolean regenerate(Region region, EditSession editSession) { BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)]; @@ -334,7 +181,7 @@ public class BukkitWorld extends LocalWorld { try { getWorld().regenerateChunk(chunk.getBlockX(), chunk.getBlockZ()); } catch (Throwable t) { - t.printStackTrace(); + logger.log(Level.WARNING, "Chunk generation via Bukkit raised an error", t); } // Then restore @@ -359,303 +206,6 @@ public class BukkitWorld extends LocalWorld { return true; } - @Override - public boolean copyToWorld(Vector pt, BaseBlock block) { - World world = getWorld(); - - if (block instanceof SignBlock) { - // Signs - setSignText(pt, ((SignBlock) block).getText()); - return true; - } - - if (block instanceof FurnaceBlock) { - // Furnaces - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof Furnace)) return false; - Furnace bukkit = (Furnace) state; - FurnaceBlock we = (FurnaceBlock) block; - bukkit.setBurnTime(we.getBurnTime()); - bukkit.setCookTime(we.getCookTime()); - return setContainerBlockContents(pt, ((ContainerBlock) block).getItems()); - } - - if (block instanceof ContainerBlock) { - // Chests/dispenser - return setContainerBlockContents(pt, ((ContainerBlock) block).getItems()); - } - - if (block instanceof MobSpawnerBlock) { - // Mob spawners - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof CreatureSpawner)) return false; - CreatureSpawner bukkit = (CreatureSpawner) state; - MobSpawnerBlock we = (MobSpawnerBlock) block; - bukkit.setCreatureTypeByName(we.getMobType()); - bukkit.setDelay(we.getDelay()); - return true; - } - - if (block instanceof NoteBlock) { - // Note block - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof org.bukkit.block.NoteBlock)) return false; - org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state; - NoteBlock we = (NoteBlock) block; - bukkit.setRawNote(we.getNote()); - return true; - } - - if (block instanceof SkullBlock) { - // Skull block - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof org.bukkit.block.Skull)) return false; - Skull bukkit = (Skull) state; - SkullBlock we = (SkullBlock) block; - // this is dumb - SkullType skullType = SkullType.SKELETON; - switch (we.getSkullType()) { - case 0: - skullType = SkullType.SKELETON; - break; - case 1: - skullType = SkullType.WITHER; - break; - case 2: - skullType = SkullType.ZOMBIE; - break; - case 3: - skullType = SkullType.PLAYER; - break; - case 4: - skullType = SkullType.CREEPER; - break; - } - bukkit.setSkullType(skullType); - BlockFace rotation; - switch (we.getRot()) { - // soooo dumb - case 0: - rotation = BlockFace.NORTH; - break; - case 1: - rotation = BlockFace.NORTH_NORTH_EAST; - break; - case 2: - rotation = BlockFace.NORTH_EAST; - break; - case 3: - rotation = BlockFace.EAST_NORTH_EAST; - break; - case 4: - rotation = BlockFace.EAST; - break; - case 5: - rotation = BlockFace.EAST_SOUTH_EAST; - break; - case 6: - rotation = BlockFace.SOUTH_EAST; - break; - case 7: - rotation = BlockFace.SOUTH_SOUTH_EAST; - break; - case 8: - rotation = BlockFace.SOUTH; - break; - case 9: - rotation = BlockFace.SOUTH_SOUTH_WEST; - break; - case 10: - rotation = BlockFace.SOUTH_WEST; - break; - case 11: - rotation = BlockFace.WEST_SOUTH_WEST; - break; - case 12: - rotation = BlockFace.WEST; - break; - case 13: - rotation = BlockFace.WEST_NORTH_WEST; - break; - case 14: - rotation = BlockFace.NORTH_WEST; - break; - case 15: - rotation = BlockFace.NORTH_NORTH_WEST; - break; - default: - rotation = BlockFace.NORTH; - break; - } - bukkit.setRotation(rotation); - if (we.getOwner() != null && !we.getOwner().isEmpty()) bukkit.setOwner(we.getOwner()); - bukkit.update(true); - return true; - } - - if (!skipNmsAccess) { - try { - return (Boolean) nmsSetMethod.invoke(null, world, pt, block); - } catch (Throwable t) { - logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t); - skipNmsAccess = true; - } - } - - return false; - } - - @Override - public boolean copyFromWorld(Vector pt, BaseBlock block) { - World world = getWorld(); - - if (block instanceof SignBlock) { - // Signs - ((SignBlock) block).setText(getSignText(pt)); - return true; - } - - if (block instanceof FurnaceBlock) { - // Furnaces - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof Furnace)) return false; - Furnace bukkit = (Furnace) state; - FurnaceBlock we = (FurnaceBlock) block; - we.setBurnTime(bukkit.getBurnTime()); - we.setCookTime(bukkit.getCookTime()); - ((ContainerBlock) block).setItems(getContainerBlockContents(pt)); - return true; - } - - if (block instanceof ContainerBlock) { - // Chests/dispenser - ((ContainerBlock) block).setItems(getContainerBlockContents(pt)); - return true; - } - - if (block instanceof MobSpawnerBlock) { - // Mob spawners - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof CreatureSpawner)) return false; - CreatureSpawner bukkit = (CreatureSpawner) state; - MobSpawnerBlock we = (MobSpawnerBlock) block; - we.setMobType(bukkit.getCreatureTypeName()); - we.setDelay((short) bukkit.getDelay()); - return true; - } - - if (block instanceof NoteBlock) { - // Note block - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof org.bukkit.block.NoteBlock)) return false; - org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state; - NoteBlock we = (NoteBlock) block; - we.setNote(bukkit.getRawNote()); - return true; - } - - if (block instanceof SkullBlock) { - // Skull block - Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (bukkitBlock == null) return false; - BlockState state = bukkitBlock.getState(); - if (!(state instanceof org.bukkit.block.Skull)) return false; - Skull bukkit = (Skull) state; - SkullBlock we = (SkullBlock) block; - byte skullType = 0; - switch (bukkit.getSkullType()) { - // this is dumb but whoever wrote the class is stupid - case SKELETON: - skullType = 0; - break; - case WITHER: - skullType = 1; - break; - case ZOMBIE: - skullType = 2; - break; - case PLAYER: - skullType = 3; - break; - case CREEPER: - skullType = 4; - break; - } - we.setSkullType(skullType); - byte rot = 0; - switch (bukkit.getRotation()) { - // this is even more dumb, hurray for copy/paste - case NORTH: - rot = (byte) 0; - break; - case NORTH_NORTH_EAST: - rot = (byte) 1; - break; - case NORTH_EAST: - rot = (byte) 2; - break; - case EAST_NORTH_EAST: - rot = (byte) 3; - break; - case EAST: - rot = (byte) 4; - break; - case EAST_SOUTH_EAST: - rot = (byte) 5; - break; - case SOUTH_EAST: - rot = (byte) 6; - break; - case SOUTH_SOUTH_EAST: - rot = (byte) 7; - break; - case SOUTH: - rot = (byte) 8; - break; - case SOUTH_SOUTH_WEST: - rot = (byte) 9; - break; - case SOUTH_WEST: - rot = (byte) 10; - break; - case WEST_SOUTH_WEST: - rot = (byte) 11; - break; - case WEST: - rot = (byte) 12; - break; - case WEST_NORTH_WEST: - rot = (byte) 13; - break; - case NORTH_WEST: - rot = (byte) 14; - break; - case NORTH_NORTH_WEST: - rot = (byte) 15; - break; - } - we.setRot(rot); - we.setOwner(bukkit.hasOwner() ? bukkit.getOwner() : ""); - return true; - } - - return false; - } - /** * Gets the single block inventory for a potentially double chest. * Handles people who have an old version of Bukkit. @@ -781,321 +331,9 @@ public class BukkitWorld extends LocalWorld { world.dropItemNaturally(BukkitUtil.toLocation(world, pt), bukkitItem); } - @Override - public int killMobs(Vector origin, double radius, int flags) { - World world = getWorld(); - - boolean killPets = (flags & KillFlags.PETS) != 0; - boolean killNPCs = (flags & KillFlags.NPCS) != 0; - boolean killAnimals = (flags & KillFlags.ANIMALS) != 0; - boolean withLightning = (flags & KillFlags.WITH_LIGHTNING) != 0; - boolean killGolems = (flags & KillFlags.GOLEMS) != 0; - boolean killAmbient = (flags & KillFlags.AMBIENT) != 0; - boolean killTagged = (flags & KillFlags.TAGGED) != 0; - - int num = 0; - double radiusSq = radius * radius; - - Location bukkitOrigin = BukkitUtil.toLocation(world, origin); - - for (LivingEntity ent : world.getLivingEntities()) { - if (ent instanceof HumanEntity) { - continue; - } - - if (!killAnimals && ent instanceof Animals) { - continue; - } - - if (!killPets && ent instanceof Tameable && ((Tameable) ent).isTamed()) { - continue; // tamed pet - } - - if (!killGolems && ent instanceof Golem) { - continue; - } - - if (!killNPCs && ent instanceof Villager) { - continue; - } - - if (!killAmbient && ent instanceof Ambient) { - continue; - } - - if (!killTagged && isTagged(ent)) { - continue; - } - - if (radius < 0 || bukkitOrigin.distanceSquared(ent.getLocation()) <= radiusSq) { - if (withLightning) { - world.strikeLightningEffect(ent.getLocation()); - } - ent.remove(); - ++num; - } - } - - return num; - } - - private static boolean isTagged(LivingEntity ent) { - return ent.getCustomName() != null; - } - - /** - * Remove entities in an area. - * - * @param origin - * @param radius - * @return - */ - @Override - public int removeEntities(EntityType type, Vector origin, int radius) { - World world = getWorld(); - - int num = 0; - double radiusSq = Math.pow(radius, 2); - - for (Entity ent : world.getEntities()) { - if (radius != -1 - && origin.distanceSq(BukkitUtil.toVector(ent.getLocation())) > radiusSq) { - continue; - } - - switch (type) { - case ALL: - if (ent instanceof Projectile || ent instanceof Boat || ent instanceof Item - || ent instanceof FallingBlock || ent instanceof Minecart || ent instanceof Hanging - || ent instanceof TNTPrimed || ent instanceof ExperienceOrb) { - ent.remove(); - num++; - } - break; - - case PROJECTILES: - case ARROWS: - if (ent instanceof Projectile) { - // covers: arrow, egg, enderpearl, fireball, fish, snowball, throwpotion, thrownexpbottle - ent.remove(); - ++num; - } - break; - - case BOATS: - if (ent instanceof Boat) { - ent.remove(); - ++num; - } - break; - - case ITEMS: - if (ent instanceof Item) { - ent.remove(); - ++num; - } - break; - - case FALLING_BLOCKS: - if (ent instanceof FallingBlock) { - ent.remove(); - ++num; - } - break; - - case MINECARTS: - if (ent instanceof Minecart) { - ent.remove(); - ++num; - } - break; - - case PAINTINGS: - if (ent instanceof Painting) { - ent.remove(); - ++num; - } - break; - - case ITEM_FRAMES: - if (ent instanceof ItemFrame) { - ent.remove(); - ++num; - } - break; - - case TNT: - if (ent instanceof TNTPrimed || ent.getType() == tntMinecartType) { - ent.remove(); - ++num; - } - break; - - case XP_ORBS: - if (ent instanceof ExperienceOrb) { - ent.remove(); - ++num; - } - break; - } - } - - return num; - } - - /** - * Set a sign's text. - * - * @param pt - * @param text - * @return - */ - private boolean setSignText(Vector pt, String[] text) { - World world = getWorld(); - - Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (block == null) return false; - BlockState state = block.getState(); - if (state == null || !(state instanceof Sign)) return false; - Sign sign = (Sign) state; - sign.setLine(0, text[0]); - sign.setLine(1, text[1]); - sign.setLine(2, text[2]); - sign.setLine(3, text[3]); - sign.update(); - return true; - } - - /** - * Get a sign's text. - * - * @param pt - * @return - */ - private String[] getSignText(Vector pt) { - World world = getWorld(); - - Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (block == null) return new String[] { "", "", "", "" }; - BlockState state = block.getState(); - if (state == null || !(state instanceof Sign)) return new String[] { "", "", "", "" }; - Sign sign = (Sign) state; - String line0 = sign.getLine(0); - String line1 = sign.getLine(1); - String line2 = sign.getLine(2); - String line3 = sign.getLine(3); - return new String[] { - line0 != null ? line0 : "", - line1 != null ? line1 : "", - line2 != null ? line2 : "", - line3 != null ? line3 : "", - }; - } - - /** - * Get a container block's contents. - * - * @param pt - * @return - */ - private BaseItemStack[] getContainerBlockContents(Vector pt) { - Block block = getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (block == null) { - return new BaseItemStack[0]; - } - BlockState state = block.getState(); - if (!(state instanceof org.bukkit.inventory.InventoryHolder)) { - return new BaseItemStack[0]; - } - - org.bukkit.inventory.InventoryHolder container = (org.bukkit.inventory.InventoryHolder) state; - Inventory inven = container.getInventory(); - if (container instanceof Chest) { - inven = getBlockInventory((Chest) container); - } - int size = inven.getSize(); - BaseItemStack[] contents = new BaseItemStack[size]; - - for (int i = 0; i < size; ++i) { - ItemStack bukkitStack = inven.getItem(i); - if (bukkitStack != null && bukkitStack.getTypeId() > 0) { - contents[i] = new BaseItemStack( - bukkitStack.getTypeId(), - bukkitStack.getAmount(), - bukkitStack.getDurability()); - try { - for (Map.Entry entry : bukkitStack.getEnchantments().entrySet()) { - contents[i].getEnchantments().put(entry.getKey().getId(), entry.getValue()); - } - } catch (Throwable ignore) {} - } - } - - return contents; - } - - /** - * Set a container block's contents. - * - * @param pt - * @param contents - * @return - */ - private boolean setContainerBlockContents(Vector pt, BaseItemStack[] contents) { - Block block = getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); - if (block == null) { - return false; - } - BlockState state = block.getState(); - if (!(state instanceof org.bukkit.inventory.InventoryHolder)) { - return false; - } - - org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state; - Inventory inven = chest.getInventory(); - if (chest instanceof Chest) { - inven = getBlockInventory((Chest) chest); - } - int size = inven.getSize(); - - for (int i = 0; i < size; ++i) { - if (i >= contents.length) { - break; - } - - if (contents[i] != null) { - ItemStack toAdd = new ItemStack(contents[i].getType(), - contents[i].getAmount(), - contents[i].getData()); - try { - for (Map.Entry entry : contents[i].getEnchantments().entrySet()) { - toAdd.addEnchantment(Enchantment.getById(entry.getKey()), entry.getValue()); - } - } catch (Throwable ignore) {} - inven.setItem(i, toAdd); - } else { - inven.setItem(i, null); - } - } - - return true; - } - - /** - * Returns whether a block has a valid ID. - * - * @param type - * @return - */ + @SuppressWarnings("deprecation") @Override public boolean isValidBlockType(int type) { - if (!skipNmsValidBlockCheck) { - try { - return (Boolean) nmsValidBlockMethod.invoke(null, type); - } catch (Throwable e) { - skipNmsValidBlockCheck = true; - } - } return Material.getMaterial(type) != null && Material.getMaterial(type).isBlock(); } @@ -1139,13 +377,6 @@ public class BukkitWorld extends LocalWorld { } } - private static final Map effects = new HashMap(); - static { - for (Effect effect : Effect.values()) { - effects.put(effect.getId(), effect); - } - } - @Override public boolean playEffect(Vector position, int type, int data) { World world = getWorld(); @@ -1160,82 +391,36 @@ public class BukkitWorld extends LocalWorld { return true; } + @Override + public WorldData getWorldData() { + return BukkitWorldData.getInstance(); + } + @Override public void simulateBlockMine(Vector pt) { getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).breakNaturally(); } @Override - public LocalEntity[] getEntities(Region region) { - World world = getWorld(); - - List entities = new ArrayList(); - for (Vector2D pt : region.getChunks()) { - if (!world.isChunkLoaded(pt.getBlockX(), pt.getBlockZ())) { - continue; - } - - final Entity[] ents = world.getChunkAt(pt.getBlockX(), pt.getBlockZ()).getEntities(); - for (Entity ent : ents) { - if (region.contains(BukkitUtil.toVector(ent.getLocation()))) { - entities.add(BukkitUtil.toLocalEntity(ent)); - } - } + public BaseBlock getBlock(Vector position) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + return adapter.getBlock(BukkitAdapter.adapt(getWorld(), position)); + } else { + Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + return new BaseBlock(bukkitBlock.getTypeId(), bukkitBlock.getData()); } - return entities.toArray(new BukkitEntity[entities.size()]); } @Override - public int killEntities(LocalEntity... entities) { - World world = getWorld(); - - int amount = 0; - Set toKill = new HashSet(); - for (LocalEntity entity : entities) { - toKill.add(((BukkitEntity) entity).getEntityId()); + public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, notifyAndLight); + } else { + Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + return bukkitBlock.setTypeIdAndData(block.getType(), (byte) block.getData(), notifyAndLight); } - for (Entity entity : world.getEntities()) { - if (toKill.contains(entity.getUniqueId())) { - entity.remove(); - ++amount; - } - } - return amount; - } - - @Override - public BaseBlock getBlock(Vector pt) { - int type = getBlockType(pt); - int data = getBlockData(pt); - - switch (type) { - case BlockID.WALL_SIGN: - case BlockID.SIGN_POST: - //case BlockID.CHEST: // Prevent data loss for now - //case BlockID.FURNACE: - //case BlockID.BURNING_FURNACE: - //case BlockID.DISPENSER: - //case BlockID.MOB_SPAWNER: - case BlockID.NOTE_BLOCK: - case BlockID.HEAD: - return super.getBlock(pt); - default: - if (!skipNmsAccess) { - try { - NmsBlock block = null; - block = (NmsBlock) nmsGetMethod.invoke(null, getWorld(), pt, type, data); - if (block != null) { - return block; - } - } catch (Throwable t) { - logger.log(Level.WARNING, - "WorldEdit: Failed to do NMS access for direct NBT data copy", t); - skipNmsAccess = true; - } - } - } - - return super.getBlock(pt); } @SuppressWarnings("deprecation") @@ -1247,17 +432,26 @@ public class BukkitWorld extends LocalWorld { } @Override - public boolean setBlock(Vector pt, BaseBlock block, boolean notifyAdjacent) throws WorldEditException { - if (!skipNmsSafeSet) { - try { - return (Boolean) nmsSetSafeMethod.invoke(null, this, pt, block, notifyAdjacent); - } catch (Throwable t) { - logger.log(Level.WARNING, "WorldEdit: Failed to do NMS safe block set", t); - skipNmsSafeSet = true; - } + public BaseBiome getBiome(Vector2D position) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + int id = adapter.getBiomeId(getWorld().getBiome(position.getBlockX(), position.getBlockZ())); + return new BaseBiome(id); + } else { + return new BaseBiome(0); } + } - return super.setBlock(pt, block, notifyAdjacent); + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + Biome bukkitBiome = adapter.getBiome(biome.getId()); + getWorld().setBiome(position.getBlockX(), position.getBlockZ(), bukkitBiome); + return true; + } else { + return false; + } } /** @@ -1267,5 +461,4 @@ public class BukkitWorld extends LocalWorld { public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) throws WorldEditException { return setBlock(pt, (BaseBlock) block, notifyAdjacent); } - } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorldData.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorldData.java new file mode 100644 index 000000000..6d747f4aa --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorldData.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.bukkit; + +import com.sk89q.worldedit.world.registry.BiomeRegistry; +import com.sk89q.worldedit.world.registry.LegacyWorldData; + +/** + * World data for the Bukkit platform. + */ +class BukkitWorldData extends LegacyWorldData { + + private static final BukkitWorldData INSTANCE = new BukkitWorldData(); + private final BiomeRegistry biomeRegistry = new BukkitBiomeRegistry(); + + /** + * Create a new instance. + */ + BukkitWorldData() { + } + + @Override + public BiomeRegistry getBiomeRegistry() { + return biomeRegistry; + } + + /** + * Get a static instance. + * + * @return an instance + */ + public static BukkitWorldData getInstance() { + return INSTANCE; + } + +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/DefaultNmsBlock.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/DefaultNmsBlock.java deleted file mode 100644 index 23d5e64e8..000000000 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/DefaultNmsBlock.java +++ /dev/null @@ -1,472 +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.bukkit; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.blocks.TileEntityBlock; -import com.sk89q.worldedit.world.DataException; -import com.sk89q.worldedit.foundation.Block; -import net.minecraft.server.v1_7_R3.*; -import org.bukkit.World; -import org.bukkit.craftbukkit.v1_7_R3.CraftWorld; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.*; -import java.util.logging.Logger; - -/** - * A blind handler of blocks with TileEntity data that directly access Minecraft's - * classes through CraftBukkit. - *

- * Usage of this class may break terribly in the future, and therefore usage should - * be trapped in a handler for {@link Throwable}. - */ -public class DefaultNmsBlock extends NmsBlock { - - private static final Logger logger = WorldEdit.logger; - private static Field compoundMapField; - private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why. - private NBTTagCompound nbtData = null; - - static { - Field field; - try { - field = net.minecraft.server.v1_7_R3.Block.class.getDeclaredField("isTileEntity"); - field.setAccessible(true); - } catch (NoSuchFieldException e) { - // logger.severe("Could not find NMS block tile entity field!"); - field = null; - } - nmsBlock_isTileEntityField = field; - } - - public static boolean verify() { - return nmsBlock_isTileEntityField != null; - } - - /** - * Create a new instance with a given type ID, data value, and previous - * {@link TileEntityBlock}-implementing object. - * - * @param type block type ID - * @param data data value - * @param tileEntityBlock tile entity block - */ - public DefaultNmsBlock(int type, int data, TileEntityBlock tileEntityBlock) { - super(type, data); - - nbtData = (NBTTagCompound) fromNative(tileEntityBlock.getNbtData()); - } - - /** - * Create a new instance with a given type ID, data value, and raw - * {@link NBTTagCompound} copy. - * - * @param type block type ID - * @param data data value - * @param nbtData raw NBT data - */ - public DefaultNmsBlock(int type, int data, NBTTagCompound nbtData) { - super(type, data); - - this.nbtData = nbtData; - } - - /** - * Build a {@link NBTTagCompound} that has valid coordinates. - * - * @param pt coordinates to set - * @return the tag compound - */ - private NBTTagCompound getNmsData(Vector pt) { - if (nbtData == null) { - return null; - } - - nbtData.set("x", new NBTTagInt(pt.getBlockX())); - nbtData.set("y", new NBTTagInt(pt.getBlockY())); - nbtData.set("z", new NBTTagInt(pt.getBlockZ())); - - return nbtData; - } - - @Override - public boolean hasNbtData() { - return nbtData != null; - } - - @Override - public String getNbtId() { - if (nbtData == null) { - return ""; - } - - return nbtData.getString("id"); - } - - @Override - public CompoundTag getNbtData() { - if (nbtData == null) { - return new CompoundTag(getNbtId(), - new HashMap()); - } - return (CompoundTag) toNative(nbtData); - } - - @Override - public void setNbtData(CompoundTag tag) throws DataException { - if (tag == null) { - this.nbtData = null; - } - this.nbtData = (NBTTagCompound) fromNative(tag); - } - - /** - * Build an instance from the given information. - * - * @param world world to get the block from - * @param position position to get the block at - * @param type type ID of block - * @param data data value of block - * @return the block, or null - */ - public static DefaultNmsBlock get(World world, Vector position, int type, int data) { - if (!hasTileEntity(type)) { - return null; - } - - TileEntity te = ((CraftWorld) world).getHandle().getTileEntity( - position.getBlockX(), position.getBlockY(), position.getBlockZ()); - - if (te != null) { - NBTTagCompound tag = new NBTTagCompound(); - te.b(tag); // Load data - return new DefaultNmsBlock(type, data, tag); - } - - return null; - } - - /** - * Set an instance or a {@link TileEntityBlock} to the given position. - * - * @param world world to set the block in - * @param position position to set the block at - * @param block the block to set - * @return true if tile entity data was copied to the world - */ - public static boolean set(World world, Vector position, BaseBlock block) { - NBTTagCompound data = null; - if (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) { - return false; - } - - if (block instanceof DefaultNmsBlock) { - DefaultNmsBlock nmsProxyBlock = (DefaultNmsBlock) block; - data = nmsProxyBlock.getNmsData(position); - } else if (block instanceof TileEntityBlock) { - DefaultNmsBlock nmsProxyBlock = new DefaultNmsBlock( - block.getId(), block.getData(), block); - data = nmsProxyBlock.getNmsData(position); - } - - if (data != null) { - TileEntity te = ((CraftWorld) world).getHandle().getTileEntity( - position.getBlockX(), position.getBlockY(), position.getBlockZ()); - if (te != null) { - te.a(data); // Load data - return true; - } - } - - return false; - } - - /** - * Tries to set a block 'safely', as in setting the block data to the location, and - * then triggering physics only at the end. - * - * @param world world to set the block in - * @param position position to set the block at - * @param block the block to set - * @param notifyAdjacent true to notify physics and what not - * @return true if block id or data was changed - */ - public static boolean setSafely(BukkitWorld world, Vector position, - Block block, boolean notifyAdjacent) { - - int x = position.getBlockX(); - int y = position.getBlockY(); - int z = position.getBlockZ(); - - CraftWorld craftWorld = ((CraftWorld) world.getWorld()); -// TileEntity te = craftWorld.getHandle().getTileEntity(x, y, z); -// craftWorld.getHandle().tileEntityList.remove(te); - - boolean changed = craftWorld.getHandle().setTypeAndData(x, y, z, getNmsBlock(block.getId()), block.getData(), 0); - - if (block instanceof BaseBlock) { - world.copyToWorld(position, (BaseBlock) block); - } - - changed = craftWorld.getHandle().setData(x, y, z, block.getData(), 0) || changed; - if (changed && notifyAdjacent) { - craftWorld.getHandle().notify(x, y, z); - craftWorld.getHandle().update(x, y, z, getNmsBlock(block.getId())); - } - - return changed; - } - - public static boolean hasTileEntity(int type) { - net.minecraft.server.v1_7_R3.Block nmsBlock = getNmsBlock(type); - if (nmsBlock == null) { - return false; - } - - try { - return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast - } catch (IllegalAccessException e) { - return false; - } - } - - public static net.minecraft.server.v1_7_R3.Block getNmsBlock(int type) { - return net.minecraft.server.v1_7_R3.Block.e(type); - } - - /** - * Converts from a non-native NMS NBT structure to a native WorldEdit NBT - * structure. - * - * @param foreign non-native NMS NBT structure - * @return native WorldEdit NBT structure - */ - private static Tag toNative(NBTBase foreign) { - // temporary fix since mojang removed names from tags - // our nbt spec will need to be updated to theirs - return toNative(getTagName(foreign.getTypeId()), foreign); - } - - // seriously these two methods are hacky - our jnbt spec needs updating - // copied from NMS 1.7.5- code, since it was removed in 1.7.8 - private static String getTagName(int i) { - switch (i) { - case 0: - return "TAG_End"; - case 1: - return "TAG_Byte"; - case 2: - return "TAG_Short"; - case 3: - return "TAG_Int"; - case 4: - return "TAG_Long"; - case 5: - return "TAG_Float"; - case 6: - return "TAG_Double"; - case 7: - return "TAG_Byte_Array"; - case 8: - return "TAG_String"; - case 9: - return "TAG_List"; - case 10: - return "TAG_Compound"; - case 11: - return "TAG_Int_Array"; - case 99: - return "Any Numeric Tag"; - default: - return "UNKNOWN"; - } - } - - /** - * Converts from a non-native NMS NBT structure to a native WorldEdit NBT - * structure. - * - * @param foreign non-native NMS NBT structure - * @param name name for the tag, if it has one - * @return native WorldEdit NBT structure - */ - @SuppressWarnings("unchecked") - private static Tag toNative(String name, NBTBase foreign) { - if (foreign == null) { - return null; - } - if (foreign instanceof NBTTagCompound) { - Map values = new HashMap(); - Collection foreignKeys = null; - - if (compoundMapField == null) { - try { - // Method name may change! - foreignKeys = ((NBTTagCompound) foreign).c(); - } catch (Throwable t) { - try { - logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " + - "so we're going to try to get at the 'map' field directly from now on"); - - if (compoundMapField == null) { - compoundMapField = NBTTagCompound.class.getDeclaredField("map"); - compoundMapField.setAccessible(true); - } - } catch (Throwable e) { - // Can't do much beyond this - throw new RuntimeException(e); - } - } - } - - if (compoundMapField != null) { - try { - foreignKeys = ((HashMap) compoundMapField.get(foreign)).keySet(); - } catch (Throwable e) { - // Can't do much beyond this - throw new RuntimeException(e); - } - } - - for (Object obj : foreignKeys) { - String key = (String) obj; - NBTBase base = (NBTBase) ((NBTTagCompound) foreign).get(key); - values.put(key, toNative(key, base)); - } - return new CompoundTag(name, values); - } else if (foreign instanceof NBTTagByte) { - return new ByteTag(name, ((NBTTagByte) foreign).f()); // getByte - } else if (foreign instanceof NBTTagByteArray) { - return new ByteArrayTag(name, - ((NBTTagByteArray) foreign).c()); // data - } else if (foreign instanceof NBTTagDouble) { - return new DoubleTag(name, - ((NBTTagDouble) foreign).g()); // getDouble - } else if (foreign instanceof NBTTagFloat) { - return new FloatTag(name, ((NBTTagFloat) foreign).h()); // getFloat - } else if (foreign instanceof NBTTagInt) { - return new IntTag(name, ((NBTTagInt) foreign).d()); // getInt - } else if (foreign instanceof NBTTagIntArray) { - return new IntArrayTag(name, - ((NBTTagIntArray) foreign).c()); // data - } else if (foreign instanceof NBTTagList) { - try { - return transmorgifyList(name, (NBTTagList) foreign); - } catch (NoSuchFieldException e) { - } catch (SecurityException e) { - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) {} - return new ListTag(name, ByteTag.class, new ArrayList()); - } else if (foreign instanceof NBTTagLong) { - return new LongTag(name, ((NBTTagLong) foreign).c()); // getLong - } else if (foreign instanceof NBTTagShort) { - return new ShortTag(name, ((NBTTagShort) foreign).e()); // getShort - } else if (foreign instanceof NBTTagString) { - return new StringTag(name, - ((NBTTagString) foreign).a_()); // data - } else if (foreign instanceof NBTTagEnd) { - return new EndTag(); - } else { - throw new IllegalArgumentException("Don't know how to make native " - + foreign.getClass().getCanonicalName()); - } - } - - private static ListTag transmorgifyList(String name, NBTTagList foreign) - throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { - List values = new ArrayList(); - int type = foreign.d(); - Field listField = NBTTagList.class.getDeclaredField("list"); - listField.setAccessible(true); - List foreignList; - foreignList = (List) listField.get(foreign); - for (int i = 0; i < foreign.size(); i++) { - NBTBase element = (NBTBase) foreignList.get(i); - values.add(toNative(null, element)); // list elements shouldn't have names - } - Class cls = NBTConstants.getClassFromType(type); - return new ListTag(name, cls, values); - } - - /** - * Converts a WorldEdit-native NBT structure to a NMS structure. - * - * @param foreign structure to convert - * @return non-native structure - */ - private static NBTBase fromNative(Tag foreign) { - if (foreign == null) { - return null; - } - if (foreign instanceof CompoundTag) { - NBTTagCompound tag = new NBTTagCompound(); - for (Map.Entry entry : ((CompoundTag) foreign) - .getValue().entrySet()) { - tag.set(entry.getKey(), fromNative(entry.getValue())); - } - return tag; - } else if (foreign instanceof ByteTag) { - return new NBTTagByte(((ByteTag) foreign).getValue()); - } else if (foreign instanceof ByteArrayTag) { - return new NBTTagByteArray(((ByteArrayTag) foreign).getValue()); - } else if (foreign instanceof DoubleTag) { - return new NBTTagDouble(((DoubleTag) foreign).getValue()); - } else if (foreign instanceof FloatTag) { - return new NBTTagFloat(((FloatTag) foreign).getValue()); - } else if (foreign instanceof IntTag) { - return new NBTTagInt(((IntTag) foreign).getValue()); - } else if (foreign instanceof IntArrayTag) { - return new NBTTagIntArray(((IntArrayTag) foreign).getValue()); - } else if (foreign instanceof ListTag) { - NBTTagList tag = new NBTTagList(); - ListTag foreignList = (ListTag) foreign; - for (Tag t : foreignList.getValue()) { - tag.add(fromNative(t)); - } - return tag; - } else if (foreign instanceof LongTag) { - return new NBTTagLong(((LongTag) foreign).getValue()); - } else if (foreign instanceof ShortTag) { - return new NBTTagShort(((ShortTag) foreign).getValue()); - } else if (foreign instanceof StringTag) { - return new NBTTagString(((StringTag) foreign).getValue()); - } else if (foreign instanceof EndTag) { - try { - Method tagMaker = NBTBase.class.getDeclaredMethod("createTag", byte.class); - tagMaker.setAccessible(true); - return (NBTBase) tagMaker.invoke(null, (byte) 0); - } catch (Exception e) { - return null; - } - } else { - throw new IllegalArgumentException("Don't know how to make NMS " - + foreign.getClass().getCanonicalName()); - } - } - - public static boolean isValidBlockType(int type) throws NoClassDefFoundError { - return type == 0 || (type >= 1 && net.minecraft.server.v1_7_R3.Block.e(type) != null); - } - -} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/NmsBlock.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/NmsBlock.java deleted file mode 100644 index 088de8547..000000000 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/NmsBlock.java +++ /dev/null @@ -1,61 +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.bukkit; - -import org.bukkit.World; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.foundation.Block; - -public abstract class NmsBlock extends BaseBlock { - - protected NmsBlock(int type) { - super(type); - } - - protected NmsBlock(int type, int data) { - super(type, data); - } - - public static boolean verify() { - return false; - } - - public static NmsBlock get(World world, Vector vector, int type, int data) { - return null; - } - - public static boolean set(World world, Vector vector, Block block) { - return false; - } - - public static boolean setSafely(World world, Vector vector, Block block, boolean notify) { - return false; - } - - public static boolean hasTileEntity(int type) { - return false; - } - - public static boolean isValidBlockType(int type) { - return false; - } -} \ No newline at end of file diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/TameableAdapter.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/TameableAdapter.java new file mode 100644 index 000000000..b514f1b29 --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/TameableAdapter.java @@ -0,0 +1,40 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit; + +import com.sk89q.worldedit.entity.metadata.Tameable; +import com.sk89q.worldedit.internal.util.AbstractAdapter; + +/** + * Adapts a Bukkit {@link org.bukkit.entity.Tameable} into a WorldEdit + * equivalent. + */ +public class TameableAdapter extends AbstractAdapter implements Tameable { + + TameableAdapter(org.bukkit.entity.Tameable entity) { + super(entity); + } + + @Override + public boolean isTamed() { + return getHandle().isTamed(); + } + +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 0cdaf470d..eac26306b 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -22,7 +22,16 @@ package com.sk89q.worldedit.bukkit; import com.google.common.base.Joiner; import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.wepif.PermissionsResolverManager; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditOperation; +import com.sk89q.worldedit.bukkit.adapter.AdapterLoadException; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplLoader; import com.sk89q.worldedit.bukkit.selections.CuboidSelection; import com.sk89q.worldedit.bukkit.selections.CylinderSelection; import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; @@ -32,7 +41,11 @@ import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.regions.*; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.CylinderRegion; +import com.sk89q.worldedit.regions.Polygonal2DRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionSelector; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -40,13 +53,20 @@ import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; -import java.io.*; -import java.util.Enumeration; +import javax.annotation.Nullable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.List; -import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.zip.ZipEntry; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Plugin for Bukkit. * @@ -54,27 +74,13 @@ import java.util.zip.ZipEntry; */ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { - /** - * The name of the CUI's plugin channel registration - */ + private static final Logger log = Logger.getLogger(WorldEditPlugin.class.getCanonicalName()); public static final String CUI_PLUGIN_CHANNEL = "WECUI"; + private static WorldEditPlugin INSTANCE; - /** - * The server interface that all server-related API goes through. - */ + private BukkitImplAdapter bukkitAdapter; private BukkitServerInterface server; - /** - * Main WorldEdit instance. - */ - private WorldEdit controller; - /** - * Deprecated API. - */ - private WorldEditAPI api; - - /** - * Holds the configuration for WorldEdit. - */ + private final WorldEditAPI api = new WorldEditAPI(this); private BukkitConfiguration config; /** @@ -82,41 +88,27 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { */ @Override public void onEnable() { - final String pluginYmlVersion = getDescription().getVersion(); - final String manifestVersion = WorldEdit.getVersion(); + this.INSTANCE = this; - if (!manifestVersion.equalsIgnoreCase(pluginYmlVersion)) { - WorldEdit.setVersion(manifestVersion + " (" + pluginYmlVersion + ")"); - } + WorldEdit worldEdit = WorldEdit.getInstance(); - // Make the data folders that WorldEdit uses - getDataFolder().mkdirs(); - File targetDir = new File(getDataFolder() + File.separator + "nmsblocks"); - targetDir.mkdir(); - copyNmsBlockClasses(targetDir); + loadAdapter(); // Need an adapter to work with special blocks with NBT data + loadConfig(); // Load configuration + PermissionsResolverManager.initialize(this); // Setup permission resolver - // Create the default configuration file - createDefaultConfiguration("config.yml"); - - // Set up configuration and such, including the permissions - // resolver - config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config.yml"), true), this); - PermissionsResolverManager.initialize(this); - - // Load the configuration - config.load(); - - // Setup interfaces + // Setup platform server = new BukkitServerInterface(this, getServer()); - controller = WorldEdit.getInstance(); - controller.getPlatformManager().register(server); - api = new WorldEditAPI(this); + worldEdit.getPlatformManager().register(server); + + // Register CUI getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this)); getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL); - // Now we can register events! + + // Now we can register events getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); - getServer().getScheduler().runTaskTimerAsynchronously(this, new SessionTimer(controller, getServer()), 120, 120); + // Register session timer + getServer().getScheduler().runTaskTimerAsynchronously(this, new SessionTimer(worldEdit, getServer()), 120, 120); // If we are on MCPC+/Cauldron, then Forge will have already loaded // Forge WorldEdit and there's (probably) not going to be any other @@ -124,31 +116,34 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); } - private void copyNmsBlockClasses(File target) { + private void loadConfig() { + createDefaultConfiguration("config.yml"); // Create the default configuration file + + config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config.yml"), true), this); + config.load(); + } + + private void loadAdapter() { + // Attempt to load a Bukkit adapter + BukkitImplLoader adapterLoader = new BukkitImplLoader(); + try { - JarFile jar = new JarFile(getFile()); - @SuppressWarnings("rawtypes") - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry jarEntry = (JarEntry) entries.nextElement(); - if (!jarEntry.getName().startsWith("nmsblocks") || jarEntry.isDirectory()) continue; + adapterLoader.addFromPath(getClass().getClassLoader()); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to search path for Bukkit adapters"); + } - File file = new File(target + File.separator + jarEntry.getName().replace("nmsblocks", "")); - if (file.exists()) continue; - - InputStream is = jar.getInputStream(jarEntry); - FileOutputStream fos = new FileOutputStream(file); - - fos = new FileOutputStream(file); - byte[] buf = new byte[8192]; - int length = 0; - while ((length = is.read(buf)) > 0) { - fos.write(buf, 0, length); - } - fos.close(); - is.close(); - } - } catch (Throwable e) {} + try { + adapterLoader.addFromJar(getFile()); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to search " + getFile() + " for Bukkit adapters", e); + } + try { + bukkitAdapter = adapterLoader.loadAdapter(); + log.log(Level.INFO, "Using " + bukkitAdapter.getClass().getCanonicalName() + " as the Bukkit adapter"); + } catch (AdapterLoadException e) { + log.log(Level.WARNING, e.getMessage()); + } } /** @@ -156,10 +151,13 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { */ @Override public void onDisable() { - if (controller != null) { - controller.clearSessions(); - controller.getPlatformManager().unregister(server); + WorldEdit worldEdit = WorldEdit.getInstance(); + worldEdit.clearSessions(); + worldEdit.getPlatformManager().unregister(server); + if (config != null) { config.unload(); + } + if (server != null) { server.unregisterCommands(); } this.getServer().getScheduler().cancelTasks(this); @@ -177,7 +175,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { /** * Create a default configuration file from the .jar. * - * @param name + * @param name the filename */ protected void createDefaultConfiguration(String name) { File actual = new File(getDataFolder(), name); @@ -257,7 +255,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { * @return */ public LocalSession getSession(Player player) { - return controller.getSession(wrapPlayer(player)); + return WorldEdit.getInstance().getSession(wrapPlayer(player)); } /** @@ -268,10 +266,10 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { */ public EditSession createEditSession(Player player) { LocalPlayer wePlayer = wrapPlayer(player); - LocalSession session = controller.getSession(wePlayer); + LocalSession session = WorldEdit.getInstance().getSession(wePlayer); BlockBag blockBag = session.getBlockBag(wePlayer); - EditSession editSession = controller.getEditSessionFactory() + EditSession editSession = WorldEdit.getInstance().getEditSessionFactory() .getEditSession(wePlayer.getWorld(), session.getBlockChangeLimit(), blockBag, wePlayer); editSession.enableQueue(); @@ -286,12 +284,12 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { */ public void remember(Player player, EditSession editSession) { LocalPlayer wePlayer = wrapPlayer(player); - LocalSession session = controller.getSession(wePlayer); + LocalSession session = WorldEdit.getInstance().getSession(wePlayer); session.remember(editSession); editSession.flushQueue(); - controller.flushBlockBag(wePlayer, editSession); + WorldEdit.getInstance().flushBlockBag(wePlayer, editSession); } /** @@ -301,10 +299,9 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { * @param op * @throws Throwable */ - public void perform(Player player, WorldEditOperation op) - throws Throwable { + public void perform(Player player, WorldEditOperation op) throws Throwable { LocalPlayer wePlayer = wrapPlayer(player); - LocalSession session = controller.getSession(wePlayer); + LocalSession session = WorldEdit.getInstance().getSession(wePlayer); EditSession editSession = createEditSession(player); try { @@ -379,7 +376,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { * @return */ public WorldEdit getWorldEdit() { - return controller; + return WorldEdit.getInstance(); } /** @@ -396,7 +393,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { throw new IllegalArgumentException("Offline player not allowed"); } - LocalSession session = controller.getSession(wrapPlayer(player)); + LocalSession session = WorldEdit.getInstance().getSession(wrapPlayer(player)); RegionSelector selector = session.getRegionSelector(BukkitUtil.getLocalWorld(player.getWorld())); try { @@ -434,9 +431,30 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { throw new IllegalArgumentException("Null selection not allowed"); } - LocalSession session = controller.getSession(wrapPlayer(player)); + LocalSession session = WorldEdit.getInstance().getSession(wrapPlayer(player)); RegionSelector sel = selection.getRegionSelector(); session.setRegionSelector(BukkitUtil.getLocalWorld(player.getWorld()), sel); session.dispatchCUISelection(wrapPlayer(player)); } + + /** + * Gets the instance of this plugin. + * + * @return an instance of the plugin + * @throws NullPointerException if the plugin hasn't been enabled + */ + static WorldEditPlugin getInstance() { + return checkNotNull(INSTANCE); + } + + /** + * Get the Bukkit implementation adapter. + * + * @return the adapter + */ + @Nullable + BukkitImplAdapter getBukkitImplAdapter() { + return bukkitAdapter; + } + } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/AdapterLoadException.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/AdapterLoadException.java new file mode 100644 index 000000000..d8459d691 --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/AdapterLoadException.java @@ -0,0 +1,41 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter; + +/** + * Thrown when no adapter can be found. + */ +public class AdapterLoadException extends Exception { + + public AdapterLoadException() { + } + + public AdapterLoadException(String message) { + super(message); + } + + public AdapterLoadException(String message, Throwable cause) { + super(message, cause); + } + + public AdapterLoadException(Throwable cause) { + super(cause); + } +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java new file mode 100644 index 000000000..04113a6d5 --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -0,0 +1,114 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter; + +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.entity.Entity; + +import javax.annotation.Nullable; + +/** + * An interface for adapters of various Bukkit implementations. + */ +public interface BukkitImplAdapter { + + /** + * Get the block ID for the given material. + * + *

Returns 0 if it is not known or it doesn't exist.

+ * + * @param material the material + * @return the block ID + */ + int getBlockId(Material material); + + /** + * Get the material for the given block ID. + * + *

Returns {@link Material#AIR} if it is not known or it doesn't exist.

+ * + * @param id the block ID + * @return the material + */ + Material getMaterial(int id); + + /** + * Get the biome ID for the given biome. + * + *

Returns 0 if it is not known or it doesn't exist.

+ * + * @param biome biome + * @return the biome ID + */ + int getBiomeId(Biome biome); + + /** + * Get the biome ID for the given biome ID.. + * + *

Returns {@link Biome#OCEAN} if it is not known or it doesn't exist.

+ * + * @param id the biome ID + * @return the biome + */ + Biome getBiome(int id); + + /** + * Get the block at the given location. + * + * @param location the location + * @return the block + */ + BaseBlock getBlock(Location location); + + /** + * Set the block at the given location. + * + * @param location the location + * @param state the block + * @param notifyAndLight notify and light if set + * @return true if a block was likely changed + */ + boolean setBlock(Location location, BaseBlock state, boolean notifyAndLight); + + /** + * Get the state for the given entity. + * + * @param entity the entity + * @return the state, or null + */ + @Nullable + BaseEntity getEntity(Entity entity); + + /** + * Create the given entity. + * + * @param location the location + * @param state the state + * @return the created entity or null + */ + @Nullable + Entity createEntity(Location location, BaseEntity state); + + +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java new file mode 100644 index 000000000..28704a8ca --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java @@ -0,0 +1,197 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.util.io.Closer; +import org.bukkit.Bukkit; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Loads Bukkit implementation adapters. + */ +public class BukkitImplLoader { + + private static final Logger log = Logger.getLogger(BukkitImplLoader.class.getCanonicalName()); + private final List adapterCandidates = new ArrayList(); + private String customCandidate; + + private static final String SEARCH_PACKAGE = "com.sk89q.worldedit.bukkit.adapter.impl"; + private static final String SEARCH_PACKAGE_DOT = SEARCH_PACKAGE + "."; + private static final String SEARCH_PATH = SEARCH_PACKAGE.replace(".", "/"); + private static final String CLASS_SUFFIX = ".class"; + + private static final String LOAD_ERROR_MESSAGE = + "Failed to find an adapter for Bukkit!\n\n" + + "This version of WorldEdit (%s) does not fully support your version of Bukkit (%s).\n\n" + + "What this means:\n" + + "1) Block operations will work, but chests will be empty, signs will be blank, and so on.\n" + + "2) You won't be able to save and load chests, signs, etc. with .schematic files.\n" + + "3) You won't be able to work with entities properly.\n" + + "4) Undo will will not be able to restore chests, signs, and etc.\n\n" + + "Possible solutions:\n" + + "1) If this is a new version of Minecraft, please wait for us to update. " + + "You can also put in a ticket at http://youtrack.sk89q.com (check for an existing ticket first).\n" + + "2) If you are using an older version of Minecraft, you may need to downgrade WorldEdit.\n" + + "3) If you are using an older version of WorldEdit, you may need to update your WorldEdit.\n" + + "4) If you are not using CraftBukkit, then report this issue to http://youtrack.sk89q.com " + + "(check for an existing ticket first).\n" + + "5) If you are developing WorldEdit, you can force an adapter with " + + "-Dworldedit.bukkit.adapter=the_class_name.\n\n" + + "Can I ignore this error? Yes! Just be aware of the undo issue.\n" + + "Am I using CraftBukkit? %s.\n"; + + /** + * Create a new instance. + */ + public BukkitImplLoader() { + addDefaults(); + } + + /** + * Add default candidates, such as any defined with + * {@code -Dworldedit.bukkit.adapter}. + */ + private void addDefaults() { + String className = System.getProperty("worldedit.bukkit.adapter"); + if (className != null) { + customCandidate = className; + adapterCandidates.add(className); + log.log(Level.INFO, "-Dworldedit.bukkit.adapter used to add " + className + " to the list of available Bukkit adapters"); + } + } + + /** + * Search the given JAR for candidate implementations. + * + * @param file the file + * @throws IOException thrown on I/O error + */ + public void addFromJar(File file) throws IOException { + Closer closer = Closer.create(); + JarFile jar = closer.register(new JarFile(file)); + try { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry jarEntry = (JarEntry) entries.nextElement(); + + String className = jarEntry.getName().replaceAll("[/\\\\]+", "."); + + if (!className.startsWith(SEARCH_PACKAGE_DOT) || jarEntry.isDirectory()) continue; + + int beginIndex = 0; + int endIndex = className.length() - CLASS_SUFFIX.length(); + className = className.substring(beginIndex, endIndex); + adapterCandidates.add(className); + } + } finally { + closer.close(); + } + } + + /** + * Search for classes stored as separate files available via the given + * class loader. + * + * @param classLoader the class loader + * @throws IOException thrown on error + */ + public void addFromPath(ClassLoader classLoader) throws IOException { + Enumeration resources = classLoader.getResources(SEARCH_PATH); + while (resources.hasMoreElements()) { + File file = new File(resources.nextElement().getFile()); + addFromPath(file); + } + } + + /** + * Search for classes stored as separate files available via the given + * path. + * + * @param file the path + */ + private void addFromPath(File file) { + String resource = SEARCH_PACKAGE_DOT + file.getName(); + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File child : files) { + addFromPath(child); + } + } + } else if (resource.endsWith(CLASS_SUFFIX)) { + int beginIndex = 0; + int endIndex = resource.length() - CLASS_SUFFIX.length(); + String className = resource.substring(beginIndex, endIndex); + adapterCandidates.add(className); + } + } + + /** + * Iterate through the list of candidates and load an adapter. + * + * @return an adapter + * @throws AdapterLoadException thrown if no adapter could be found + */ + public BukkitImplAdapter loadAdapter() throws AdapterLoadException { + for (String className : adapterCandidates) { + try { + Class cls = Class.forName(className); + if (BukkitImplAdapter.class.isAssignableFrom(cls)) { + return (BukkitImplAdapter) cls.newInstance(); + } else { + log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className + + "' because it does not implement " + BukkitImplAdapter.class.getCanonicalName()); + } + } catch (ClassNotFoundException e) { + log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className + + "' that is not supposed to be missing", e); + } catch (IllegalAccessException e) { + log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className + + "' that is not supposed to be raising this error", e); + } catch (Throwable e) { + if (className.equals(customCandidate)) { + log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className + "'", e); + } + } + } + + String weVersion = WorldEdit.getVersion(); + String bukkitVersion = Bukkit.getBukkitVersion() + " implemented by " + Bukkit.getName() + " " + Bukkit.getVersion(); + String usingCraftBukkit = + Bukkit.getName().equals("CraftBukkit") + ? "Probably (if you got it from dl.bukkit.org, then yes)" + : "No! You are using " + Bukkit.getName(); + + throw new AdapterLoadException( + String.format(LOAD_ERROR_MESSAGE, weVersion, bukkitVersion, usingCraftBukkit)); + } + +} diff --git a/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_6_R3.class b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_6_R3.class new file mode 100644 index 000000000..6d5c96592 Binary files /dev/null and b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_6_R3.class differ diff --git a/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R2.class b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R2.class new file mode 100644 index 000000000..20b3c1930 Binary files /dev/null and b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R2.class differ diff --git a/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R3.class b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R3.class new file mode 100644 index 000000000..1f8cc436f Binary files /dev/null and b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R3.class differ diff --git a/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R4.class b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R4.class new file mode 100644 index 000000000..f9eb46a5d Binary files /dev/null and b/src/bukkit/resources/com/sk89q/worldedit/bukkit/adapter/impl/CraftBukkit_v1_7_R4.class differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_145.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_145.class deleted file mode 100644 index b8d6c51f3..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_145.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_146.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_146.class deleted file mode 100644 index e89c4a70b..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_146.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_147.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_147.class deleted file mode 100644 index e3c8c20fd..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_147.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_15.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_15.class deleted file mode 100644 index 68781e198..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_15.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_152.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_152.class deleted file mode 100644 index 8dda57ec9..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_152.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_161.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_161.class deleted file mode 100644 index 336c611cb..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_161.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_162.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_162.class deleted file mode 100644 index 2d70cfbcb..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_162.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_164.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_164.class deleted file mode 100644 index 7520a85a1..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_164.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_172.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_172.class deleted file mode 100644 index a7fb7e100..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_172.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_175.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_175.class deleted file mode 100644 index 6afbcdcc5..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_175.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_prePackage.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_prePackage.class deleted file mode 100644 index 07a94b131..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_prePackage.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/CBXNmsBlock_v152_2.class b/src/bukkit/resources/nmsblocks/CBXNmsBlock_v152_2.class deleted file mode 100644 index 2a4acd48e..000000000 Binary files a/src/bukkit/resources/nmsblocks/CBXNmsBlock_v152_2.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_147.class b/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_147.class deleted file mode 100644 index 86da91a7f..000000000 Binary files a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_147.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_151dv.class b/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_151dv.class deleted file mode 100644 index b68088e83..000000000 Binary files a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_151dv.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_162.class b/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_162.class deleted file mode 100644 index 7f1326865..000000000 Binary files a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_162.class and /dev/null differ diff --git a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_164.class b/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_164.class deleted file mode 100644 index ac6c27721..000000000 Binary files a/src/bukkit/resources/nmsblocks/MCPCPlusXNmsBlock_164.class and /dev/null differ diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeType.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeAdapter.java similarity index 69% rename from src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeType.java rename to src/forge/java/com/sk89q/worldedit/forge/ForgeAdapter.java index 5cd816ad7..5a1d6120f 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeType.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeAdapter.java @@ -1,36 +1,39 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.forge; - -import net.minecraft.world.biome.BiomeGenBase; - -import com.sk89q.worldedit.BiomeType; - -public class ForgeBiomeType implements BiomeType { - private BiomeGenBase biome; - - public ForgeBiomeType(BiomeGenBase biome) { - this.biome = biome; - } - - public String getName() { - return this.biome.biomeName; - } -} \ No newline at end of file +/* + * 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.sk89q.worldedit.Vector; +import com.sk89q.worldedit.world.World; +import net.minecraft.util.Vec3; + +final class ForgeAdapter { + + private ForgeAdapter() { + } + + public static World adapt(net.minecraft.world.World world) { + return new ForgeWorld(world); + } + + public static Vector adapt(Vec3 vector) { + return new Vector(vector.xCoord, vector.yCoord, vector.zCoord); + } + +} diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeRegistry.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeRegistry.java new file mode 100644 index 000000000..7f0597313 --- /dev/null +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeRegistry.java @@ -0,0 +1,107 @@ +/* + * 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.HashBiMap; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.BiomeData; +import com.sk89q.worldedit.world.registry.BiomeRegistry; +import net.minecraft.world.biome.BiomeGenBase; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Provides access to biome data in Forge. + */ +class ForgeBiomeRegistry implements BiomeRegistry { + + private static Map biomes = Collections.emptyMap(); + private static Map biomeData = Collections.emptyMap(); + + @Nullable + @Override + public BaseBiome createFromId(int id) { + return new BaseBiome(id); + } + + @Override + public List getBiomes() { + List list = new ArrayList(); + for (int biome : biomes.keySet()) { + list.add(new BaseBiome(biome)); + } + return list; + } + + @Nullable + @Override + public BiomeData getData(BaseBiome biome) { + return biomeData.get(biome.getId()); + } + + /** + * Populate the internal static list of biomes. + * + *

If called repeatedly, the last call will overwrite all previous + * calls.

+ */ + static void populate() { + Map biomes = HashBiMap.create(); + Map biomeData = new HashMap(); + + for (BiomeGenBase biome : BiomeGenBase.biomeList) { + if ((biome == null) || (biomes.containsValue(biome))) { + continue; + } + biomes.put(biome.biomeID, biome); + biomeData.put(biome.biomeID, new ForgeBiomeData(biome)); + } + + ForgeBiomeRegistry.biomes = biomes; + ForgeBiomeRegistry.biomeData = biomeData; + } + + /** + * Cached biome data information. + */ + private static class ForgeBiomeData implements BiomeData { + private final BiomeGenBase biome; + + /** + * Create a new instance. + * + * @param biome the base biome + */ + private ForgeBiomeData(BiomeGenBase biome) { + this.biome = biome; + } + + @Override + public String getName() { + return biome.biomeName; + } + } + +} \ No newline at end of file diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeTypes.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeTypes.java deleted file mode 100644 index 68275e49d..000000000 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeBiomeTypes.java +++ /dev/null @@ -1,87 +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.forge; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import net.minecraft.world.biome.BiomeGenBase; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.sk89q.worldedit.BiomeType; -import com.sk89q.worldedit.BiomeTypes; -import com.sk89q.worldedit.UnknownBiomeTypeException; - -public class ForgeBiomeTypes implements BiomeTypes { - private static BiMap biomes = HashBiMap.create(); - - public ForgeBiomeTypes() { - all(); - } - - public boolean has(String name) { - for (BiomeGenBase biome : BiomeGenBase.biomeList) { - if ((biome != null) && (biome.biomeName.equalsIgnoreCase(name))) { - return true; - } - } - return false; - } - - public BiomeType get(String name) throws UnknownBiomeTypeException { - if (biomes == null) { - all(); - } - Iterator it = biomes.keySet().iterator(); - while (it.hasNext()) { - BiomeType test = (BiomeType) it.next(); - if (test.getName().equalsIgnoreCase(name)) { - return test; - } - } - throw new UnknownBiomeTypeException(name); - } - - public List all() { - if (biomes.isEmpty()) { - biomes = HashBiMap.create(new HashMap()); - for (BiomeGenBase biome : BiomeGenBase.biomeList) { - if ((biome == null) || (biomes.containsValue(biome))) { - continue; - } - biomes.put(new ForgeBiomeType(biome), biome); - } - } - List retBiomes = new ArrayList(); - retBiomes.addAll(biomes.keySet()); - return retBiomes; - } - - public static BiomeType getFromBaseBiome(BiomeGenBase biome) { - return biomes.containsValue(biome) ? (BiomeType) biomes.inverse().get(biome) : BiomeType.UNKNOWN; - } - - public static BiomeGenBase getFromBiomeType(BiomeType biome) { - return (BiomeGenBase) biomes.get(biome); - } -} \ No newline at end of file diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeEntity.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeEntity.java new file mode 100644 index 000000000..44ca0a666 --- /dev/null +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeEntity.java @@ -0,0 +1,95 @@ +/* + * 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.sk89q.worldedit.Vector; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.metadata.EntityType; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; +import net.minecraft.entity.EntityList; +import net.minecraft.nbt.NBTTagCompound; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class ForgeEntity implements Entity { + + private final net.minecraft.entity.Entity entity; + + ForgeEntity(net.minecraft.entity.Entity entity) { + checkNotNull(entity); + this.entity = entity; + } + + /** + * Return the underlying entity. + * + * @return the underlying entity + */ + net.minecraft.entity.Entity getEntity() { + return entity; + } + + @Override + public BaseEntity getState() { + String id = EntityList.getEntityString(entity); + if (id != null) { + NBTTagCompound tag = new NBTTagCompound(); + entity.writeToNBT(tag); + return new BaseEntity(id, NBTConverter.fromNative(tag)); + } else { + return null; + } + } + + @Override + public Location getLocation() { + Vector position = new Vector(entity.posX, entity.posY, entity.posZ); + float yaw = entity.rotationYaw; + float pitch = entity.rotationPitch; + + return new Location(ForgeAdapter.adapt(entity.worldObj), position, yaw, pitch); + } + + @Override + public Extent getExtent() { + return ForgeAdapter.adapt(entity.worldObj); + } + + @Override + public boolean remove() { + entity.setDead(); + return true; + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T getFacet(Class cls) { + if (EntityType.class.isAssignableFrom(cls)) { + return (T) new ForgeEntityType(entity); + } else { + return null; + } + } +} diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeEntityType.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeEntityType.java new file mode 100644 index 000000000..a2fc3952e --- /dev/null +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeEntityType.java @@ -0,0 +1,138 @@ +/* + * 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.sk89q.worldedit.entity.metadata.EntityType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.IMerchant; +import net.minecraft.entity.INpc; +import net.minecraft.entity.IProjectile; +import net.minecraft.entity.item.EntityBoat; +import net.minecraft.entity.item.EntityEnderEye; +import net.minecraft.entity.item.EntityFallingSand; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.item.EntityItemFrame; +import net.minecraft.entity.item.EntityMinecart; +import net.minecraft.entity.item.EntityPainting; +import net.minecraft.entity.item.EntityTNTPrimed; +import net.minecraft.entity.item.EntityXPOrb; +import net.minecraft.entity.monster.EntityGolem; +import net.minecraft.entity.passive.EntityAmbientCreature; +import net.minecraft.entity.passive.EntityTameable; +import net.minecraft.entity.passive.IAnimals; +import net.minecraft.entity.player.EntityPlayer; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class ForgeEntityType implements EntityType { + + private final Entity entity; + + public ForgeEntityType(Entity entity) { + checkNotNull(entity); + this.entity = entity; + } + + @Override + public boolean isPlayerDerived() { + return entity instanceof EntityPlayer; + } + + @Override + public boolean isProjectile() { + return entity instanceof EntityEnderEye || entity instanceof IProjectile; + } + + @Override + public boolean isItem() { + return entity instanceof EntityItem; + } + + @Override + public boolean isFallingBlock() { + return entity instanceof EntityFallingSand; + } + + @Override + public boolean isPainting() { + return entity instanceof EntityPainting; + } + + @Override + public boolean isItemFrame() { + return entity instanceof EntityItemFrame; + } + + @Override + public boolean isBoat() { + return entity instanceof EntityBoat; + } + + @Override + public boolean isMinecart() { + return entity instanceof EntityMinecart; + } + + @Override + public boolean isTNT() { + return entity instanceof EntityTNTPrimed; + } + + @Override + public boolean isExperienceOrb() { + return entity instanceof EntityXPOrb; + } + + @Override + public boolean isLiving() { + return entity instanceof EntityLiving; + } + + @Override + public boolean isAnimal() { + return entity instanceof IAnimals; + } + + @Override + public boolean isAmbient() { + return entity instanceof EntityAmbientCreature; + } + + @Override + public boolean isNPC() { + return entity instanceof INpc || entity instanceof IMerchant; + } + + @Override + public boolean isGolem() { + return entity instanceof EntityGolem; + } + + @Override + public boolean isTamed() { + return entity instanceof EntityTameable && ((EntityTameable) entity).isTamed(); + } + + @Override + public boolean isTagged() { + return entity instanceof EntityLiving && ((EntityLiving) entity).hasCustomNameTag(); + } +} diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java index 62c04afb3..3f4f9b19b 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.forge; -import com.sk89q.worldedit.BiomeTypes; +import com.sk89q.worldedit.world.registry.BiomeRegistry; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.ServerInterface; import com.sk89q.worldedit.entity.Player; @@ -56,13 +56,13 @@ class ForgePlatform extends ServerInterface implements MultiUserPlatform { private final ForgeWorldEdit mod; private final MinecraftServer server; - private final ForgeBiomeTypes biomes; + private final ForgeBiomeRegistry biomes; private boolean hookingEvents = false; ForgePlatform(ForgeWorldEdit mod) { this.mod = mod; this.server = FMLCommonHandler.instance().getMinecraftServerInstance(); - this.biomes = new ForgeBiomeTypes(); + this.biomes = new ForgeBiomeRegistry(); } boolean isHookingEvents() { @@ -95,11 +95,6 @@ class ForgePlatform extends ServerInterface implements MultiUserPlatform { public void reload() { } - @Override - public BiomeTypes getBiomes() { - return this.biomes; - } - @Override public int schedule(long delay, long period, Runnable task) { return -1; diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java index 21978c270..327b03602 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java @@ -20,21 +20,23 @@ package com.sk89q.worldedit.forge; import com.sk89q.util.StringUtil; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.ServerInterface; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extension.platform.AbstractPlayerActor; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.internal.LocalWorldAdapter; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.Vectors; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.network.packet.Packet250CustomPayload; import net.minecraft.util.ChatMessageComponent; -public class ForgePlayer extends LocalPlayer { +import javax.annotation.Nullable; + +public class ForgePlayer extends AbstractPlayerActor { + private EntityPlayerMP player; protected ForgePlayer(EntityPlayerMP player) { @@ -50,11 +52,19 @@ public class ForgePlayer extends LocalPlayer { return this.player.username; } + @Override + public BaseEntity getState() { + throw new UnsupportedOperationException("Cannot create a state from this object"); + } + @Override public Location getLocation() { Vector position = new Vector(this.player.posX, this.player.posY, this.player.posZ); - Vector direction = Vectors.fromEulerDeg(this.player.cameraYaw, this.player.cameraPitch); - return new Location(ForgeWorldEdit.inst.getWorld(this.player.worldObj), position, direction); + return new Location( + ForgeWorldEdit.inst.getWorld(this.player.worldObj), + position, + this.player.cameraYaw, + this.player.cameraPitch); } public WorldVector getPosition() { @@ -126,4 +136,11 @@ public class ForgePlayer extends LocalPlayer { public boolean hasPermission(String perm) { return ForgeUtil.hasPermission(this.player, perm); } + + @Nullable + @Override + public T getFacet(Class cls) { + return null; + } + } \ No newline at end of file diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java index 9271a1ce2..fdab7bcf1 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -20,24 +20,26 @@ package com.sk89q.worldedit.forge; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.LazyBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.AbstractWorld; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.WorldData; import net.minecraft.block.Block; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityHanging; -import net.minecraft.entity.EntityLiving; -import net.minecraft.entity.IProjectile; -import net.minecraft.entity.item.*; -import net.minecraft.entity.monster.EntityGolem; -import net.minecraft.entity.passive.EntityAmbientCreature; -import net.minecraft.entity.passive.EntityAnimal; -import net.minecraft.entity.passive.EntityTameable; -import net.minecraft.entity.passive.EntityVillager; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.item.EntityItem; import net.minecraft.inventory.IInventory; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; @@ -48,8 +50,10 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.gen.ChunkProviderServer; +import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.logging.Level; @@ -176,22 +180,23 @@ public class ForgeWorld extends AbstractWorld { } @Override - public BiomeType getBiome(Vector2D position) { + public BaseBiome getBiome(Vector2D position) { checkNotNull(position); - return ForgeBiomeTypes.getFromBaseBiome(getWorld().getBiomeGenForCoords(position.getBlockX(), position.getBlockZ())); + return new BaseBiome(getWorld().getBiomeGenForCoords(position.getBlockX(), position.getBlockZ()).biomeID); } @Override - public void setBiome(Vector2D position, BiomeType biome) { + public boolean setBiome(Vector2D position, BaseBiome biome) { checkNotNull(position); checkNotNull(biome); - if (getWorld().getChunkProvider().chunkExists(position.getBlockX(), position.getBlockZ())) { - Chunk chunk = getWorld().getChunkFromBlockCoords(position.getBlockX(), position.getBlockZ()); - if ((chunk != null) && (chunk.isChunkLoaded)) { - chunk.getBiomeArray()[((position.getBlockZ() & 0xF) << 4 | position.getBlockX() & 0xF)] = (byte) ForgeBiomeTypes.getFromBiomeType(biome).biomeID; - } + Chunk chunk = getWorld().getChunkFromBlockCoords(position.getBlockX(), position.getBlockZ()); + if ((chunk != null) && (chunk.isChunkLoaded)) { + chunk.getBiomeArray()[((position.getBlockZ() & 0xF) << 4 | position.getBlockX() & 0xF)] = (byte) biome.getId(); + return true; } + + return false; } @Override @@ -199,7 +204,7 @@ public class ForgeWorld extends AbstractWorld { checkNotNull(position); checkNotNull(item); - if ((item == null) || (item.getType() == 0)) { + if (item.getType() == 0) { return; } @@ -208,121 +213,6 @@ public class ForgeWorld extends AbstractWorld { getWorld().spawnEntityInWorld(entity); } - @Override - @SuppressWarnings({"unchecked", "ConstantConditions"}) - public int killMobs(Vector origin, double radius, int flags) { - boolean killPets = (flags & 0x1) != 0; - boolean killNPCs = (flags & 0x2) != 0; - boolean killAnimals = (flags & 0x4) != 0; - - boolean killGolems = (flags & 0x8) != 0; - boolean killAmbient = (flags & 0x10) != 0; - - int num = 0; - double radiusSq = radius * radius; - - for (Entity obj : (Iterable) getWorld().loadedEntityList) { - if ((obj instanceof EntityLiving)) { - EntityLiving ent = (EntityLiving) obj; - - if (!killAnimals && ent instanceof EntityAnimal) { - continue; - } - - if (!killPets && ent instanceof EntityTameable && ((EntityTameable) ent).isTamed()) { - continue; // tamed pet - } - - if (!killGolems && ent instanceof EntityGolem) { - continue; - } - - if (!killNPCs && ent instanceof EntityVillager) { - continue; - } - - if (!killAmbient && ent instanceof EntityAmbientCreature) { - continue; - } - - if ((radius < 0.0D) || (origin.distanceSq(new Vector(ent.posX, ent.posY, ent.posZ)) <= radiusSq)) { - ent.isDead = true; - num++; - } - } - } - - return num; - } - - @Override - @SuppressWarnings("unchecked") - public int removeEntities(EntityType type, Vector origin, int radius) { - checkNotNull(type); - checkNotNull(origin); - - int num = 0; - double radiusSq = Math.pow(radius, 2.0D); - - for (Entity ent : (Iterable) getWorld().loadedEntityList) { - if ((radius != -1) && (origin.distanceSq(new Vector(ent.posX, ent.posY, ent.posZ)) > radiusSq)) { - continue; - } - if (type == EntityType.ALL) { - if (((ent instanceof EntityBoat)) || ((ent instanceof EntityItem)) || ((ent instanceof EntityFallingSand)) || ((ent instanceof EntityMinecart)) || ((ent instanceof EntityHanging)) || ((ent instanceof EntityTNTPrimed)) || ((ent instanceof EntityXPOrb)) || ((ent instanceof EntityEnderEye)) || ((ent instanceof IProjectile))) { - ent.isDead = true; - num++; - } - } else if ((type == EntityType.PROJECTILES) || (type == EntityType.ARROWS)) { - if (((ent instanceof EntityEnderEye)) || ((ent instanceof IProjectile))) { - ent.isDead = true; - num++; - } - } else if (type == EntityType.BOATS) { - if ((ent instanceof EntityBoat)) { - ent.isDead = true; - num++; - } - } else if (type == EntityType.ITEMS) { - if ((ent instanceof EntityItem)) { - ent.isDead = true; - num++; - } - } else if (type == EntityType.FALLING_BLOCKS) { - if ((ent instanceof EntityFallingSand)) { - ent.isDead = true; - num++; - } - } else if (type == EntityType.MINECARTS) { - if ((ent instanceof EntityMinecart)) { - ent.isDead = true; - num++; - } - } else if (type == EntityType.PAINTINGS) { - if ((ent instanceof EntityPainting)) { - ent.isDead = true; - num++; - } - } else if (type == EntityType.ITEM_FRAMES) { - if ((ent instanceof EntityItemFrame)) { - ent.isDead = true; - num++; - } - } else if (type == EntityType.TNT) { - if ((ent instanceof EntityTNTPrimed)) { - ent.isDead = true; - num++; - } - } else if ((type == EntityType.XP_ORBS) && ((ent instanceof EntityXPOrb))) { - ent.isDead = true; - num++; - } - - } - - return num; - } - @Override public boolean regenerate(Region region, EditSession editSession) { BaseBlock[] history = new BaseBlock[256 * (getMaxY() + 1)]; @@ -425,6 +315,11 @@ public class ForgeWorld extends AbstractWorld { return false; } + @Override + public WorldData getWorldData() { + return ForgeWorldData.getInstance(); + } + @Override public boolean isValidBlockType(int id) { return (id == 0) || (net.minecraft.block.Block.blocksList[id] != null); @@ -462,10 +357,10 @@ public class ForgeWorld extends AbstractWorld { if (o == null) { return false; } else if ((o instanceof ForgeWorld)) { - ForgeWorld other = ((ForgeWorld) o); - World otherWorld = other.worldRef.get(); - World thisWorld = other.worldRef.get(); - return otherWorld != null && thisWorld != null && otherWorld.equals(thisWorld); + ForgeWorld other = ((ForgeWorld) o); + World otherWorld = other.worldRef.get(); + World thisWorld = other.worldRef.get(); + return otherWorld != null && thisWorld != null && otherWorld.equals(thisWorld); } else if (o instanceof com.sk89q.worldedit.world.World) { return ((com.sk89q.worldedit.world.World) o).getName().equals(getName()); } else { @@ -473,6 +368,61 @@ public class ForgeWorld extends AbstractWorld { } } + @Override + @SuppressWarnings("unchecked") + public List getEntities(Region region) { + List entities = new ArrayList(); + World world = getWorld(); + for (Vector2D pt : region.getChunks()) { + if (!world.getChunkProvider().chunkExists(pt.getBlockX(), pt.getBlockZ())) { + continue; + } + + Chunk chunk = world.getChunkProvider().provideChunk(pt.getBlockX(), pt.getBlockZ()); + for (List entitySubList : chunk.entityLists) { + for (net.minecraft.entity.Entity entity : entitySubList) { + if (region.contains(new Vector(entity.posX, entity.posY, entity.posZ))) { + entities.add(new ForgeEntity(entity)); + } + } + } + } + return entities; + } + + @Override + public List getEntities() { + List entities = new ArrayList(); + for (Object entity : getWorld().getLoadedEntityList()) { + entities.add(new ForgeEntity((net.minecraft.entity.Entity) entity)); + } + return entities; + } + + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity entity) { + World world = getWorld(); + net.minecraft.entity.Entity createdEntity = EntityList.createEntityByName(entity.getTypeId(), world); + if (createdEntity != null) { + CompoundTag nativeTag = entity.getNbtData(); + if (nativeTag != null) { + NBTTagCompound tag = NBTConverter.toNative(entity.getNbtData()); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.removeTag(name); + } + createdEntity.readFromNBT(tag); + } + + createdEntity.setLocationAndAngles(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.spawnEntityInWorld(createdEntity); + return new ForgeEntity(createdEntity); + } else { + return null; + } + } + /** * Thrown when the reference to the world is lost. */ diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldData.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldData.java new file mode 100644 index 000000000..c6f3d553c --- /dev/null +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldData.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.forge; + +import com.sk89q.worldedit.world.registry.BiomeRegistry; +import com.sk89q.worldedit.world.registry.LegacyWorldData; + +/** + * World data for the Forge platform. + */ +class ForgeWorldData extends LegacyWorldData { + + private static final ForgeWorldData INSTANCE = new ForgeWorldData(); + private final BiomeRegistry biomeRegistry = new ForgeBiomeRegistry(); + + /** + * Create a new instance. + */ + ForgeWorldData() { + } + + @Override + public BiomeRegistry getBiomeRegistry() { + return biomeRegistry; + } + + /** + * Get a static instance. + * + * @return an instance + */ + public static ForgeWorldData getInstance() { + return INSTANCE; + } + +} diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index dbc53eb1d..e6e740533 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -103,6 +103,8 @@ public class ForgeWorldEdit { WorldEdit.getInstance().getPlatformManager().unregister(platform); } + ForgeBiomeRegistry.populate(); + this.platform = new ForgePlatform(this); WorldEdit.getInstance().getPlatformManager().register(platform); diff --git a/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java b/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java index 1a46607a8..a17ac5d43 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java +++ b/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.forge; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.TileEntityBlock; -import com.sk89q.worldedit.world.DataException; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; @@ -29,10 +28,7 @@ public class TileEntityBaseBlock extends BaseBlock implements TileEntityBlock { public TileEntityBaseBlock(int type, int data, TileEntity tile) { super(type, data); - try { - setNbtData(NBTConverter.fromNative(copyNbtData(tile))); - } catch (DataException ignored) { - } + setNbtData(NBTConverter.fromNative(copyNbtData(tile))); } private static NBTTagCompound copyNbtData(TileEntity tile) { diff --git a/src/forge/resources/defaults/worldedit.properties b/src/forge/resources/defaults/worldedit.properties index f7e362f20..9c3f5d327 100644 --- a/src/forge/resources/defaults/worldedit.properties +++ b/src/forge/resources/defaults/worldedit.properties @@ -1,32 +1,32 @@ -#Don't put comments; they get removed -default-max-polygon-points=-1 -schematic-save-dir=schematics -allow-extra-data-values=false -super-pickaxe-many-drop-items=true -register-help=true -nav-wand-item=345 -profile=false -super-pickaxe-drop-items=true -disallowed-blocks=6,26,27,28,31,32,34,36,37,38,39,40,46,50,51,55,59,66,69,75,76,93,94,77,81,83,7,14,15,16,56 -max-super-pickaxe-size=5 -max-brush-radius=10 -craftscript-dir=craftscripts -no-double-slash=false -wand-item=271 -shell-save-type= -scripting-timeout=3000 -snapshots-dir= -use-inventory-creative-override=false -log-file=worldedit.log -max-changed-blocks=-1 -nav-wand-distance=50 -butcher-default-radius=-1 -default-max-changed-blocks=-1 -history-size=15 -use-inventory=false -allow-symbolic-links=false -use-inventory-override=false -log-commands=false -butcher-max-radius=-1 -max-polygon-points=20 -max-radius=-1 +#Don't put comments; they get removed +default-max-polygon-points=-1 +schematic-save-dir=schematics +allow-extra-data-values=false +super-pickaxe-many-drop-items=true +register-help=true +nav-wand-item=345 +profile=false +super-pickaxe-drop-items=true +disallowed-blocks=6,26,27,28,31,32,34,36,37,38,39,40,46,50,51,55,59,66,69,75,76,93,94,77,81,83,7,14,15,16,56 +max-super-pickaxe-size=5 +max-brush-radius=10 +craftscript-dir=craftscripts +no-double-slash=false +wand-item=271 +shell-save-type= +scripting-timeout=3000 +snapshots-dir= +use-inventory-creative-override=false +log-file=worldedit.log +max-changed-blocks=-1 +nav-wand-distance=50 +butcher-default-radius=-1 +default-max-changed-blocks=-1 +history-size=15 +use-inventory=false +allow-symbolic-links=false +use-inventory-override=false +log-commands=false +butcher-max-radius=-1 +max-polygon-points=20 +max-radius=-1 diff --git a/src/main/java/com/sk89q/worldedit/blocks/ChestBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/ChestBlock.java similarity index 78% rename from src/main/java/com/sk89q/worldedit/blocks/ChestBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/ChestBlock.java index fcc9f0a32..d5547e661 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/ChestBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/ChestBlock.java @@ -19,17 +19,18 @@ package com.sk89q.worldedit.blocks; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTUtils; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.world.DataException; +import com.sk89q.worldedit.world.storage.InvalidFormatException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Represents a chest block. @@ -78,7 +79,7 @@ public class ChestBlock extends ContainerBlock { } @Override - public void setNbtData(CompoundTag rootTag) throws DataException { + public void setNbtData(CompoundTag rootTag) { if (rootTag == null) { return; } @@ -87,19 +88,25 @@ public class ChestBlock extends ContainerBlock { Tag t = values.get("id"); if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Chest")) { - throw new DataException("'Chest' tile entity expected"); + throw new RuntimeException("'Chest' tile entity expected"); } List items = new ArrayList(); - - for (Tag tag : NBTUtils.getChildTag(values, "Items", ListTag.class).getValue()) { - if (!(tag instanceof CompoundTag)) { - throw new DataException("CompoundTag expected as child tag of Chest's Items"); + + try { + for (Tag tag : NBTUtils.getChildTag(values, "Items", ListTag.class).getValue()) { + if (!(tag instanceof CompoundTag)) { + throw new RuntimeException("CompoundTag expected as child tag of Chest's Items"); + } + + items.add((CompoundTag) tag); } - items.add((CompoundTag) tag); + setItems(deserializeInventory(items)); + } catch (InvalidFormatException e) { + throw new RuntimeException(e); + } catch (DataException e) { + throw new RuntimeException(e); } - - setItems(deserializeInventory(items)); } } diff --git a/src/main/java/com/sk89q/worldedit/blocks/ContainerBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/ContainerBlock.java similarity index 97% rename from src/main/java/com/sk89q/worldedit/blocks/ContainerBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/ContainerBlock.java index 9e0d69fb1..aae973c44 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/ContainerBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/ContainerBlock.java @@ -1,142 +1,142 @@ -/* - * 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.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.sk89q.jnbt.ByteTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.DataException; - -/** - * Represents a block that stores items. - * - * @author sk89q - */ -public abstract class ContainerBlock extends BaseBlock implements TileEntityBlock { - - private BaseItemStack[] items; - - public ContainerBlock(int type, int inventorySize) { - super(type); - this.items = new BaseItemStack[inventorySize]; - } - - public ContainerBlock(int type, int data, int inventorySize) { - super(type, data); - this.items = new BaseItemStack[inventorySize]; - } - - /** - * Get the list of items. - * - * @return - */ - public BaseItemStack[] getItems() { - return this.items; - } - - /** - * Set the list of items. - * - * @param items - */ - public void setItems(BaseItemStack[] items) { - this.items = items; - } - - @Override - public boolean hasNbtData() { - return true; - } - - public Map serializeItem(BaseItemStack item) { - Map data = new HashMap(); - data.put("id", new ShortTag("id", (short) item.getType())); - data.put("Damage", new ShortTag("Damage", item.getData())); - data.put("Count", new ByteTag("Count", (byte) item.getAmount())); - if (item.getEnchantments().size() > 0) { - List enchantmentList = new ArrayList(); - for(Map.Entry entry : item.getEnchantments().entrySet()) { - Map enchantment = new HashMap(); - enchantment.put("id", new ShortTag("id", entry.getKey().shortValue())); - enchantment.put("lvl", new ShortTag("lvl", entry.getValue().shortValue())); - enchantmentList.add(new CompoundTag(null, enchantment)); - } - - Map auxData = new HashMap(); - auxData.put("ench", new ListTag("ench", CompoundTag.class, enchantmentList)); - data.put("tag", new CompoundTag("tag", auxData)); - } - return data; - } - - public BaseItemStack deserializeItem(Map data) throws DataException { - short id = NBTUtils.getChildTag(data, "id", ShortTag.class).getValue(); - short damage = NBTUtils.getChildTag(data, "Damage", ShortTag.class).getValue(); - byte count = NBTUtils.getChildTag(data, "Count", ByteTag.class).getValue(); - - BaseItemStack stack = new BaseItemStack(id, count, damage); - - if (data.containsKey("tag")) { - Map auxData = NBTUtils.getChildTag(data, "tag", CompoundTag.class).getValue(); - ListTag ench = (ListTag)auxData.get("ench"); - for(Tag e : ench.getValue()) { - Map vars = ((CompoundTag) e).getValue(); - short enchId = NBTUtils.getChildTag(vars, "id", ShortTag.class).getValue(); - short enchLevel = NBTUtils.getChildTag(vars, "lvl", ShortTag.class).getValue(); - stack.getEnchantments().put((int) enchId, (int) enchLevel); - } - } - return stack; - } - - public BaseItemStack[] deserializeInventory(List items) throws DataException { - BaseItemStack[] stacks = new BaseItemStack[items.size()]; - for (CompoundTag tag : items) { - Map item = tag.getValue(); - BaseItemStack stack = deserializeItem(item); - byte slot = NBTUtils.getChildTag(item, "Slot", ByteTag.class).getValue(); - if (slot >= 0 && slot < stacks.length) { - stacks[slot] = stack; - } - } - return stacks; - } - - public List serializeInventory(BaseItemStack[] items) { - List tags = new ArrayList(); - for (int i = 0; i < items.length; ++i) { - if (items[i] != null) { - Map tagData = serializeItem(items[i]); - tagData.put("Slot", new ByteTag("Slot", (byte) i)); - tags.add(new CompoundTag("", tagData)); - } - } - return tags; - } -} +/* + * 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.world.DataException; + +/** + * Represents a block that stores items. + * + * @author sk89q + */ +public abstract class ContainerBlock extends BaseBlock implements TileEntityBlock { + + private BaseItemStack[] items; + + public ContainerBlock(int type, int inventorySize) { + super(type); + this.items = new BaseItemStack[inventorySize]; + } + + public ContainerBlock(int type, int data, int inventorySize) { + super(type, data); + this.items = new BaseItemStack[inventorySize]; + } + + /** + * Get the list of items. + * + * @return + */ + public BaseItemStack[] getItems() { + return this.items; + } + + /** + * Set the list of items. + * + * @param items + */ + public void setItems(BaseItemStack[] items) { + this.items = items; + } + + @Override + public boolean hasNbtData() { + return true; + } + + public Map serializeItem(BaseItemStack item) { + Map data = new HashMap(); + data.put("id", new ShortTag("id", (short) item.getType())); + data.put("Damage", new ShortTag("Damage", item.getData())); + data.put("Count", new ByteTag("Count", (byte) item.getAmount())); + if (item.getEnchantments().size() > 0) { + List enchantmentList = new ArrayList(); + for(Map.Entry entry : item.getEnchantments().entrySet()) { + Map enchantment = new HashMap(); + enchantment.put("id", new ShortTag("id", entry.getKey().shortValue())); + enchantment.put("lvl", new ShortTag("lvl", entry.getValue().shortValue())); + enchantmentList.add(new CompoundTag(null, enchantment)); + } + + Map auxData = new HashMap(); + auxData.put("ench", new ListTag("ench", CompoundTag.class, enchantmentList)); + data.put("tag", new CompoundTag("tag", auxData)); + } + return data; + } + + public BaseItemStack deserializeItem(Map data) throws DataException { + short id = NBTUtils.getChildTag(data, "id", ShortTag.class).getValue(); + short damage = NBTUtils.getChildTag(data, "Damage", ShortTag.class).getValue(); + byte count = NBTUtils.getChildTag(data, "Count", ByteTag.class).getValue(); + + BaseItemStack stack = new BaseItemStack(id, count, damage); + + if (data.containsKey("tag")) { + Map auxData = NBTUtils.getChildTag(data, "tag", CompoundTag.class).getValue(); + ListTag ench = (ListTag)auxData.get("ench"); + for(Tag e : ench.getValue()) { + Map vars = ((CompoundTag) e).getValue(); + short enchId = NBTUtils.getChildTag(vars, "id", ShortTag.class).getValue(); + short enchLevel = NBTUtils.getChildTag(vars, "lvl", ShortTag.class).getValue(); + stack.getEnchantments().put((int) enchId, (int) enchLevel); + } + } + return stack; + } + + public BaseItemStack[] deserializeInventory(List items) throws DataException { + BaseItemStack[] stacks = new BaseItemStack[items.size()]; + for (CompoundTag tag : items) { + Map item = tag.getValue(); + BaseItemStack stack = deserializeItem(item); + byte slot = NBTUtils.getChildTag(item, "Slot", ByteTag.class).getValue(); + if (slot >= 0 && slot < stacks.length) { + stacks[slot] = stack; + } + } + return stacks; + } + + public List serializeInventory(BaseItemStack[] items) { + List tags = new ArrayList(); + for (int i = 0; i < items.length; ++i) { + if (items[i] != null) { + Map tagData = serializeItem(items[i]); + tagData.put("Slot", new ByteTag("Slot", (byte) i)); + tags.add(new CompoundTag("", tagData)); + } + } + return tags; + } +} diff --git a/src/main/java/com/sk89q/worldedit/blocks/DispenserBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/DispenserBlock.java similarity index 72% rename from src/main/java/com/sk89q/worldedit/blocks/DispenserBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/DispenserBlock.java index 0608e4935..c13c7f163 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/DispenserBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/DispenserBlock.java @@ -1,105 +1,109 @@ -/* - * 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.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.DataException; - -/** - * Represents dispensers. - * - * @author sk89q - */ -public class DispenserBlock extends ContainerBlock { - - /** - * Construct an empty dispenser block. - */ - public DispenserBlock() { - super(BlockID.DISPENSER, 9); - } - - /** - * Construct an empty dispenser block. - * - * @param data data value (orientation) - */ - public DispenserBlock(int data) { - super(BlockID.DISPENSER, data, 9); - } - - /** - * Construct a dispenser block with the given orientation and inventory. - * - * @param data data value (orientation) - * @param items array of items in the inventory - */ - public DispenserBlock(int data, BaseItemStack[] items) { - super(BlockID.DISPENSER, data, 9); - this.setItems(items); - } - - @Override - public String getNbtId() { - return "Trap"; - } - - @Override - public CompoundTag getNbtData() { - Map values = new HashMap(); - values.put("Items", new ListTag("Items", CompoundTag.class, - serializeInventory(getItems()))); - return new CompoundTag(getNbtId(), values); - } - - @Override - public void setNbtData(CompoundTag rootTag) throws DataException { - if (rootTag == null) { - return; - } - - Map values = rootTag.getValue(); - - Tag t = values.get("id"); - if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Trap")) { - throw new DataException("'Trap' tile entity expected"); - } - - List items = new ArrayList(); - for (Tag tag : NBTUtils.getChildTag(values, "Items", ListTag.class).getValue()) { - if (!(tag instanceof CompoundTag)) { - throw new DataException("CompoundTag expected as child tag of Trap Items"); - } - - items.add((CompoundTag) tag); - } - - setItems(deserializeInventory(items)); - } -} +/* + * 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 com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.world.DataException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents dispensers. + * + * @author sk89q + */ +public class DispenserBlock extends ContainerBlock { + + /** + * Construct an empty dispenser block. + */ + public DispenserBlock() { + super(BlockID.DISPENSER, 9); + } + + /** + * Construct an empty dispenser block. + * + * @param data data value (orientation) + */ + public DispenserBlock(int data) { + super(BlockID.DISPENSER, data, 9); + } + + /** + * Construct a dispenser block with the given orientation and inventory. + * + * @param data data value (orientation) + * @param items array of items in the inventory + */ + public DispenserBlock(int data, BaseItemStack[] items) { + super(BlockID.DISPENSER, data, 9); + this.setItems(items); + } + + @Override + public String getNbtId() { + return "Trap"; + } + + @Override + public CompoundTag getNbtData() { + Map values = new HashMap(); + values.put("Items", new ListTag("Items", CompoundTag.class, + serializeInventory(getItems()))); + return new CompoundTag(getNbtId(), values); + } + + @Override + public void setNbtData(CompoundTag rootTag) { + try { + if (rootTag == null) { + return; + } + + Map values = rootTag.getValue(); + + Tag t = values.get("id"); + if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Trap")) { + throw new DataException("'Trap' tile entity expected"); + } + + List items = new ArrayList(); + for (Tag tag : NBTUtils.getChildTag(values, "Items", ListTag.class).getValue()) { + if (!(tag instanceof CompoundTag)) { + throw new DataException("CompoundTag expected as child tag of Trap Items"); + } + + items.add((CompoundTag) tag); + } + + setItems(deserializeInventory(items)); + } catch (DataException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/sk89q/worldedit/blocks/FurnaceBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/FurnaceBlock.java similarity index 73% rename from src/main/java/com/sk89q/worldedit/blocks/FurnaceBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/FurnaceBlock.java index db4d3e210..07b38c78b 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/FurnaceBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/FurnaceBlock.java @@ -1,164 +1,167 @@ -/* - * 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.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.DataException; - -/** - * Represents a furnace block. - * - * @author sk89q - */ -public class FurnaceBlock extends ContainerBlock { - - private short burnTime; - private short cookTime; - - /** - * Construct an empty furnace block with the default orientation. - * - * @param type type ID - */ - public FurnaceBlock(int type) { - super(type, 2); - } - - /** - * Construct an empty furnace block with a given orientation. - * - * @param type type ID - * @param data orientation - */ - public FurnaceBlock(int type, int data) { - super(type, data, 2); - } - - /** - * Construct an furnace block with a given orientation and inventory. - * - * @param type type ID - * @param data orientation - * @param items inventory items - */ - public FurnaceBlock(int type, int data, BaseItemStack[] items) { - super(type, data, 2); - setItems(items); - } - - /** - * Get the burn time. - * - * @return the burn time - */ - public short getBurnTime() { - return burnTime; - } - - /** - * Set the burn time. - * - * @param burnTime the burn time - */ - public void setBurnTime(short burnTime) { - this.burnTime = burnTime; - } - - /** - * Get the cook time. - * - * @return the cook time - */ - public short getCookTime() { - return cookTime; - } - - /** - * Set the cook time. - * - * @param cookTime the cook time to set - */ - public void setCookTime(short cookTime) { - this.cookTime = cookTime; - } - - @Override - public String getNbtId() { - return "Furnace"; - } - - @Override - public CompoundTag getNbtData() { - Map values = new HashMap(); - values.put("Items", new ListTag("Items", CompoundTag.class, - serializeInventory(getItems()))); - values.put("BurnTime", new ShortTag("BurnTime", burnTime)); - values.put("CookTime", new ShortTag("CookTime", cookTime)); - return new CompoundTag(getNbtId(), values); - } - - @Override - public void setNbtData(CompoundTag rootTag) throws DataException { - if (rootTag == null) { - return; - } - - Map values = rootTag.getValue(); - - Tag t = values.get("id"); - if (!(t instanceof StringTag) - || !((StringTag) t).getValue().equals("Furnace")) { - throw new DataException("'Furnace' tile entity expected"); - } - - ListTag items = NBTUtils.getChildTag(values, "Items", ListTag.class); - - List compound = new ArrayList(); - - for (Tag tag : items.getValue()) { - if (!(tag instanceof CompoundTag)) { - throw new DataException( - "CompoundTag expected as child tag of Furnace Items"); - } - compound.add((CompoundTag) tag); - } - setItems(deserializeInventory(compound)); - - t = values.get("BurnTime"); - if (t instanceof ShortTag) { - burnTime = ((ShortTag) t).getValue(); - } - - t = values.get("CookTime"); - if (t instanceof ShortTag) { - cookTime = ((ShortTag) t).getValue(); - } - } -} +/* + * 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 com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.world.DataException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents a furnace block. + * + * @author sk89q + */ +public class FurnaceBlock extends ContainerBlock { + + private short burnTime; + private short cookTime; + + /** + * Construct an empty furnace block with the default orientation. + * + * @param type type ID + */ + public FurnaceBlock(int type) { + super(type, 2); + } + + /** + * Construct an empty furnace block with a given orientation. + * + * @param type type ID + * @param data orientation + */ + public FurnaceBlock(int type, int data) { + super(type, data, 2); + } + + /** + * Construct an furnace block with a given orientation and inventory. + * + * @param type type ID + * @param data orientation + * @param items inventory items + */ + public FurnaceBlock(int type, int data, BaseItemStack[] items) { + super(type, data, 2); + setItems(items); + } + + /** + * Get the burn time. + * + * @return the burn time + */ + public short getBurnTime() { + return burnTime; + } + + /** + * Set the burn time. + * + * @param burnTime the burn time + */ + public void setBurnTime(short burnTime) { + this.burnTime = burnTime; + } + + /** + * Get the cook time. + * + * @return the cook time + */ + public short getCookTime() { + return cookTime; + } + + /** + * Set the cook time. + * + * @param cookTime the cook time to set + */ + public void setCookTime(short cookTime) { + this.cookTime = cookTime; + } + + @Override + public String getNbtId() { + return "Furnace"; + } + + @Override + public CompoundTag getNbtData() { + Map values = new HashMap(); + values.put("Items", new ListTag("Items", CompoundTag.class, + serializeInventory(getItems()))); + values.put("BurnTime", new ShortTag("BurnTime", burnTime)); + values.put("CookTime", new ShortTag("CookTime", cookTime)); + return new CompoundTag(getNbtId(), values); + } + + @Override + public void setNbtData(CompoundTag rootTag) { + if (rootTag == null) { + return; + } + + try { + Map values = rootTag.getValue(); + + Tag t = values.get("id"); + if (!(t instanceof StringTag) + || !((StringTag) t).getValue().equals("Furnace")) { + throw new RuntimeException("'Furnace' tile entity expected"); + } + + ListTag items = NBTUtils.getChildTag(values, "Items", ListTag.class); + + List compound = new ArrayList(); + + for (Tag tag : items.getValue()) { + if (!(tag instanceof CompoundTag)) { + throw new RuntimeException("CompoundTag expected as child tag of Furnace Items"); + } + compound.add((CompoundTag) tag); + } + setItems(deserializeInventory(compound)); + + t = values.get("BurnTime"); + if (t instanceof ShortTag) { + burnTime = ((ShortTag) t).getValue(); + } + + t = values.get("CookTime"); + if (t instanceof ShortTag) { + cookTime = ((ShortTag) t).getValue(); + } + } catch (DataException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/sk89q/worldedit/blocks/LazyBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/LazyBlock.java similarity index 91% rename from src/main/java/com/sk89q/worldedit/blocks/LazyBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/LazyBlock.java index 591744fac..bf9ad5d19 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/LazyBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/LazyBlock.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.blocks; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.world.DataException; import com.sk89q.worldedit.extent.Extent; import static com.google.common.base.Preconditions.checkNotNull; @@ -88,17 +87,13 @@ public class LazyBlock extends BaseBlock { public CompoundTag getNbtData() { if (!loaded) { BaseBlock loadedBlock = extent.getBlock(position); - try { - super.setNbtData(loadedBlock.getNbtData()); - } catch (DataException e) { - throw new RuntimeException(e); - } + super.setNbtData(loadedBlock.getNbtData()); } return super.getNbtData(); } @Override - public void setNbtData(CompoundTag nbtData) throws DataException { + public void setNbtData(CompoundTag nbtData) { throw new UnsupportedOperationException("This object is immutable"); } diff --git a/src/main/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java similarity index 91% rename from src/main/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java index 0004f4210..ed38b5bc8 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java @@ -19,9 +19,13 @@ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.*; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.MobType; -import com.sk89q.worldedit.world.DataException; import com.sk89q.worldedit.world.storage.InvalidFormatException; import java.util.HashMap; @@ -153,7 +157,7 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { } @Override - public void setNbtData(CompoundTag rootTag) throws DataException { + public void setNbtData(CompoundTag rootTag) { if (rootTag == null) { return; } @@ -162,11 +166,18 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { Tag t = values.get("id"); if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("MobSpawner")) { - throw new DataException("'MobSpawner' tile entity expected"); + throw new RuntimeException("'MobSpawner' tile entity expected"); } - StringTag mobTypeTag = NBTUtils.getChildTag(values, "EntityId", StringTag.class); - ShortTag delayTag = NBTUtils.getChildTag(values, "Delay", ShortTag.class); + StringTag mobTypeTag; + ShortTag delayTag; + + try { + mobTypeTag = NBTUtils.getChildTag(values, "EntityId", StringTag.class); + delayTag = NBTUtils.getChildTag(values, "Delay", ShortTag.class); + } catch (InvalidFormatException ignored) { + throw new RuntimeException("Invalid mob spawner data: no EntityId and/or no Delay"); + } this.mobType = mobTypeTag.getValue(); this.delay = delayTag.getValue(); diff --git a/src/main/java/com/sk89q/worldedit/blocks/NoteBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/NoteBlock.java similarity index 87% rename from src/main/java/com/sk89q/worldedit/blocks/NoteBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/NoteBlock.java index 19d3dc150..0f5c819e3 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/NoteBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/NoteBlock.java @@ -1,125 +1,123 @@ -/* - * 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.HashMap; -import java.util.Map; - -import com.sk89q.jnbt.ByteTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.DataException; - -/** - * A note block. - * - * @author sk89q - */ -public class NoteBlock extends BaseBlock implements TileEntityBlock { - - private byte note; - - /** - * Construct the note block with a data value of 0. - */ - public NoteBlock() { - super(BlockID.NOTE_BLOCK); - this.note = 0; - } - - /** - * Construct the note block with a given data value. - * - * @param data data value - */ - public NoteBlock(int data) { - super(BlockID.NOTE_BLOCK, data); - this.note = 0; - } - - /** - * Construct the note block with a given data value and note. - * - * @param data data value - * @param note note - */ - public NoteBlock(int data, byte note) { - super(BlockID.NOTE_BLOCK, data); - this.note = note; - } - - /** - * Get the note. - * - * @return the note - */ - public byte getNote() { - return note; - } - - /** - * Set the note. - * - * @param note the note to set - */ - public void setNote(byte note) { - this.note = note; - } - - @Override - public boolean hasNbtData() { - return true; - } - - @Override - public String getNbtId() { - return "Music"; - } - - @Override - public CompoundTag getNbtData() { - Map values = new HashMap(); - values.put("note", new ByteTag("note", note)); - return new CompoundTag(getNbtId(), values); - } - - @Override - public void setNbtData(CompoundTag rootTag) throws DataException { - if (rootTag == null) { - return; - } - - Map values = rootTag.getValue(); - - Tag t; - - t = values.get("id"); - if (!(t instanceof StringTag) - || !((StringTag) t).getValue().equals("Music")) { - throw new DataException("'Music' tile entity expected"); - } - - t = values.get("note"); - if (t instanceof ByteTag) { - note = ((ByteTag) t).getValue(); - } - } -} +/* + * 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 com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; + +import java.util.HashMap; +import java.util.Map; + +/** + * A note block. + * + * @author sk89q + */ +public class NoteBlock extends BaseBlock implements TileEntityBlock { + + private byte note; + + /** + * Construct the note block with a data value of 0. + */ + public NoteBlock() { + super(BlockID.NOTE_BLOCK); + this.note = 0; + } + + /** + * Construct the note block with a given data value. + * + * @param data data value + */ + public NoteBlock(int data) { + super(BlockID.NOTE_BLOCK, data); + this.note = 0; + } + + /** + * Construct the note block with a given data value and note. + * + * @param data data value + * @param note note + */ + public NoteBlock(int data, byte note) { + super(BlockID.NOTE_BLOCK, data); + this.note = note; + } + + /** + * Get the note. + * + * @return the note + */ + public byte getNote() { + return note; + } + + /** + * Set the note. + * + * @param note the note to set + */ + public void setNote(byte note) { + this.note = note; + } + + @Override + public boolean hasNbtData() { + return true; + } + + @Override + public String getNbtId() { + return "Music"; + } + + @Override + public CompoundTag getNbtData() { + Map values = new HashMap(); + values.put("note", new ByteTag("note", note)); + return new CompoundTag(getNbtId(), values); + } + + @Override + public void setNbtData(CompoundTag rootTag) { + if (rootTag == null) { + return; + } + + Map values = rootTag.getValue(); + + Tag t; + + t = values.get("id"); + if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Music")) { + throw new RuntimeException("'Music' tile entity expected"); + } + + t = values.get("note"); + if (t instanceof ByteTag) { + note = ((ByteTag) t).getValue(); + } + } +} diff --git a/src/main/java/com/sk89q/worldedit/blocks/SignBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/SignBlock.java similarity index 92% rename from src/main/java/com/sk89q/worldedit/blocks/SignBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/SignBlock.java index 8afff7001..e4d36c0d2 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/SignBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/SignBlock.java @@ -19,13 +19,12 @@ package com.sk89q.worldedit.blocks; -import java.util.HashMap; -import java.util.Map; - import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.DataException; + +import java.util.HashMap; +import java.util.Map; /** * Represents a sign block. @@ -104,7 +103,7 @@ public class SignBlock extends BaseBlock implements TileEntityBlock { } @Override - public void setNbtData(CompoundTag rootTag) throws DataException { + public void setNbtData(CompoundTag rootTag) { if (rootTag == null) { return; } @@ -116,9 +115,8 @@ public class SignBlock extends BaseBlock implements TileEntityBlock { text = new String[] { "", "", "", "" }; t = values.get("id"); - if (!(t instanceof StringTag) - || !((StringTag) t).getValue().equals("Sign")) { - throw new DataException("'Sign' tile entity expected"); + if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Sign")) { + throw new RuntimeException("'Sign' tile entity expected"); } t = values.get("Text1"); diff --git a/src/main/java/com/sk89q/worldedit/blocks/SkullBlock.java b/src/legacy/java/com/sk89q/worldedit/blocks/SkullBlock.java similarity index 91% rename from src/main/java/com/sk89q/worldedit/blocks/SkullBlock.java rename to src/legacy/java/com/sk89q/worldedit/blocks/SkullBlock.java index 02ab5ac6e..ac62da26c 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/SkullBlock.java +++ b/src/legacy/java/com/sk89q/worldedit/blocks/SkullBlock.java @@ -1,196 +1,194 @@ -/* - * 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.HashMap; -import java.util.Map; - -import com.sk89q.jnbt.ByteTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.DataException; - -/** - * A skull block. - */ -public class SkullBlock extends BaseBlock implements TileEntityBlock { - - private String owner = ""; // notchian - private byte skullType; // stored here for block, in damage value for item - private byte rot; // only matters if block data == 0x1 (on floor) - - /** - * Construct the skull block with a default type of skelton. - * @param data data value to set, controls placement - */ - public SkullBlock(int data) { - this(data, (byte) 0); - } - - /** - * Construct the skull block with a given type. - * 0 - skeleton - * 1 - wither skelly - * 2 - zombie - * 3 - human - * 4 - creeper - * @param data data value to set, controls placement - * @param type type of skull - */ - public SkullBlock(int data, byte type) { - this(data, type, (byte) 0); - } - - /** - * Construct the skull block with a given type and rotation. - * @param data data value to set, controls placement - * @param type type of skull - * @param rot rotation (if on floor) - */ - public SkullBlock(int data, byte type, byte rot) { - super(BlockID.HEAD, data); - if (type < (byte) 0 || type > (byte) 4) { - this.skullType = (byte) 0; - } else { - this.skullType = type; - } - this.rot = rot; - this.owner = ""; - } - - /** - * Construct the skull block with a given rotation and owner. - * The type is assumed to be player unless owner is null or empty. - * @param data data value to set, controls placement - * @param rot rotation of skull - * @param owner name of player - */ - public SkullBlock(int data, byte rot, String owner) { - super(BlockID.HEAD, data); - this.rot = rot; - this.setOwner(owner); - if (owner == null || owner.isEmpty()) this.skullType = (byte) 0; - } - - /** - * Set the skull's owner. Automatically sets type to player if not empty or null. - * @param owner player name to set the skull to - */ - public void setOwner(String owner) { - if (owner == null) { - this.owner = ""; - } else { - if (owner.length() > 16 || owner.isEmpty()) this.owner = ""; - else this.owner = owner; - } - if (this.owner != null && !this.owner.isEmpty()) this.skullType = (byte) 3; - } - - /** - * Get the skull's owner. Returns null if unset. - * @return player name or null - */ - public String getOwner() { - return owner; - } - - /** - * Get the type of skull. - * @return the skullType - */ - public byte getSkullType() { - return skullType; - } - - /** - * Set the type of skull; - * @param skullType the skullType to set - */ - public void setSkullType(byte skullType) { - this.skullType = skullType; - } - - /** - * Get rotation of skull. This only means anything if the block data is 1. - * @return the rotation - */ - public byte getRot() { - return rot; - } - - /** - * Set the rotation of skull. - * @param rot the rotation to set - */ - public void setRot(byte rot) { - this.rot = rot; - } - - @Override - public boolean hasNbtData() { - return true; - } - - @Override - public String getNbtId() { - return "Skull"; - } - - @Override - public CompoundTag getNbtData() { - Map values = new HashMap(); - values.put("SkullType", new ByteTag("SkullType", skullType)); - if (owner == null) owner = ""; - values.put("ExtraType", new StringTag("ExtraType", owner)); - values.put("Rot", new ByteTag("Rot", rot)); - return new CompoundTag(getNbtId(), values); - } - - @Override - public void setNbtData(CompoundTag rootTag) throws DataException { - if (rootTag == null) { - return; - } - - Map values = rootTag.getValue(); - - Tag t; - - t = values.get("id"); - if (!(t instanceof StringTag) - || !((StringTag) t).getValue().equals("Skull")) { - throw new DataException("'Skull' tile entity expected"); - } - - t = values.get("SkullType"); - if (t instanceof ByteTag) { - skullType = ((ByteTag) t).getValue(); - } - t = values.get("ExtraType"); - if (t != null && t instanceof StringTag) { - owner = ((StringTag) t).getValue(); - } - t = values.get("Rot"); - if (t instanceof ByteTag) { - rot = ((ByteTag) t).getValue(); - } - } -} +/* + * 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 com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; + +import java.util.HashMap; +import java.util.Map; + +/** + * A skull block. + */ +public class SkullBlock extends BaseBlock implements TileEntityBlock { + + private String owner = ""; // notchian + private byte skullType; // stored here for block, in damage value for item + private byte rot; // only matters if block data == 0x1 (on floor) + + /** + * Construct the skull block with a default type of skelton. + * @param data data value to set, controls placement + */ + public SkullBlock(int data) { + this(data, (byte) 0); + } + + /** + * Construct the skull block with a given type. + * 0 - skeleton + * 1 - wither skelly + * 2 - zombie + * 3 - human + * 4 - creeper + * @param data data value to set, controls placement + * @param type type of skull + */ + public SkullBlock(int data, byte type) { + this(data, type, (byte) 0); + } + + /** + * Construct the skull block with a given type and rotation. + * @param data data value to set, controls placement + * @param type type of skull + * @param rot rotation (if on floor) + */ + public SkullBlock(int data, byte type, byte rot) { + super(BlockID.HEAD, data); + if (type < (byte) 0 || type > (byte) 4) { + this.skullType = (byte) 0; + } else { + this.skullType = type; + } + this.rot = rot; + this.owner = ""; + } + + /** + * Construct the skull block with a given rotation and owner. + * The type is assumed to be player unless owner is null or empty. + * @param data data value to set, controls placement + * @param rot rotation of skull + * @param owner name of player + */ + public SkullBlock(int data, byte rot, String owner) { + super(BlockID.HEAD, data); + this.rot = rot; + this.setOwner(owner); + if (owner == null || owner.isEmpty()) this.skullType = (byte) 0; + } + + /** + * Set the skull's owner. Automatically sets type to player if not empty or null. + * @param owner player name to set the skull to + */ + public void setOwner(String owner) { + if (owner == null) { + this.owner = ""; + } else { + if (owner.length() > 16 || owner.isEmpty()) this.owner = ""; + else this.owner = owner; + } + if (this.owner != null && !this.owner.isEmpty()) this.skullType = (byte) 3; + } + + /** + * Get the skull's owner. Returns null if unset. + * @return player name or null + */ + public String getOwner() { + return owner; + } + + /** + * Get the type of skull. + * @return the skullType + */ + public byte getSkullType() { + return skullType; + } + + /** + * Set the type of skull; + * @param skullType the skullType to set + */ + public void setSkullType(byte skullType) { + this.skullType = skullType; + } + + /** + * Get rotation of skull. This only means anything if the block data is 1. + * @return the rotation + */ + public byte getRot() { + return rot; + } + + /** + * Set the rotation of skull. + * @param rot the rotation to set + */ + public void setRot(byte rot) { + this.rot = rot; + } + + @Override + public boolean hasNbtData() { + return true; + } + + @Override + public String getNbtId() { + return "Skull"; + } + + @Override + public CompoundTag getNbtData() { + Map values = new HashMap(); + values.put("SkullType", new ByteTag("SkullType", skullType)); + if (owner == null) owner = ""; + values.put("ExtraType", new StringTag("ExtraType", owner)); + values.put("Rot", new ByteTag("Rot", rot)); + return new CompoundTag(getNbtId(), values); + } + + @Override + public void setNbtData(CompoundTag rootTag) { + if (rootTag == null) { + return; + } + + Map values = rootTag.getValue(); + + Tag t; + + t = values.get("id"); + if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Skull")) { + throw new RuntimeException("'Skull' tile entity expected"); + } + + t = values.get("SkullType"); + if (t instanceof ByteTag) { + skullType = ((ByteTag) t).getValue(); + } + t = values.get("ExtraType"); + if (t != null && t instanceof StringTag) { + owner = ((StringTag) t).getValue(); + } + t = values.get("Rot"); + if (t instanceof ByteTag) { + rot = ((ByteTag) t).getValue(); + } + } +} diff --git a/src/main/build/import-control.xml b/src/main/build/import-control.xml index 9ef48920d..9bb8d7a3d 100644 --- a/src/main/build/import-control.xml +++ b/src/main/build/import-control.xml @@ -10,6 +10,8 @@ + + diff --git a/src/main/java/com/sk89q/jnbt/ByteArrayTag.java b/src/main/java/com/sk89q/jnbt/ByteArrayTag.java index 1d58427be..59e7568e4 100644 --- a/src/main/java/com/sk89q/jnbt/ByteArrayTag.java +++ b/src/main/java/com/sk89q/jnbt/ByteArrayTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Byte_Array tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Byte_Array} tag. */ public final class ByteArrayTag extends Tag { - /** - * The value. - */ private final byte[] value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public ByteArrayTag(byte[] value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public ByteArrayTag(String name, byte[] value) { super(name); @@ -67,7 +67,7 @@ public final class ByteArrayTag extends Tag { if (name != null && !name.equals("")) { append = "(\"" + this.getName() + "\")"; } - return "TAG_Byte_Array" + append + ": " + hex.toString(); + return "TAG_Byte_Array" + append + ": " + hex; } } diff --git a/src/main/java/com/sk89q/jnbt/ByteTag.java b/src/main/java/com/sk89q/jnbt/ByteTag.java index 3adf3ec8c..56b6d4857 100644 --- a/src/main/java/com/sk89q/jnbt/ByteTag.java +++ b/src/main/java/com/sk89q/jnbt/ByteTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Byte tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Byte} tag. */ public final class ByteTag extends Tag { - /** - * The value. - */ private final byte value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public ByteTag(byte value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public ByteTag(String name, byte value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/CompoundTag.java b/src/main/java/com/sk89q/jnbt/CompoundTag.java index 28b3d8da5..6f959e81a 100644 --- a/src/main/java/com/sk89q/jnbt/CompoundTag.java +++ b/src/main/java/com/sk89q/jnbt/CompoundTag.java @@ -20,40 +20,401 @@ package com.sk89q.jnbt; import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; -import com.sk89q.jnbt.Tag; /** - * The TAG_Compound tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Compound} tag. */ public final class CompoundTag extends Tag { - /** - * The value. - */ private final Map value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public CompoundTag(Map value) { + super(); + this.value = Collections.unmodifiableMap(value); + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public CompoundTag(String name, Map value) { super(name); this.value = Collections.unmodifiableMap(value); } + /** + * Returns whether this compound tag contains the given key. + * + * @param key the given key + * @return true if the tag contains the given key + */ + public boolean containsKey(String key) { + return value.containsKey(key); + } + @Override public Map getValue() { return value; } + /** + * Return a new compound tag with the given values. + * + * @param value the value + * @return the new compound tag + */ + public CompoundTag setValue(Map value) { + return new CompoundTag(getName(), value); + } + + /** + * Create a compound tag builder. + * + * @return the builder + */ + public CompoundTagBuilder createBuilder() { + return new CompoundTagBuilder(new HashMap(value)); + } + + /** + * Get a byte array named with the given key. + * + *

If the key does not exist or its value is not a byte array tag, + * then an empty byte array will be returned.

+ * + * @param key the key + * @return a byte array + */ + public byte[] getByteArray(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteArrayTag) { + return ((ByteArrayTag) tag).getValue(); + } else { + return new byte[0]; + } + } + + /** + * Get a byte named with the given key. + * + *

If the key does not exist or its value is not a byte tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a byte + */ + public byte getByte(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + } else { + return (byte) 0; + } + } + + /** + * Get a double named with the given key. + * + *

If the key does not exist or its value is not a double tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a double + */ + public double getDouble(String key) { + Tag tag = value.get(key); + if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a double named with the given key, even if it's another + * type of number. + * + *

If the key does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a double + */ + public double asDouble(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + + } else { + return 0; + } + } + + /** + * Get a float named with the given key. + * + *

If the key does not exist or its value is not a float tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a float + */ + public float getFloat(String key) { + Tag tag = value.get(key); + if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a {@code int[]} named with the given key. + * + *

If the key does not exist or its value is not an int array tag, + * then an empty array will be returned.

+ * + * @param key the key + * @return an int array + */ + public int[] getIntArray(String key) { + Tag tag = value.get(key); + if (tag instanceof IntArrayTag) { + return ((IntArrayTag) tag).getValue(); + } else { + return new int[0]; + } + } + + /** + * Get an int named with the given key. + * + *

If the key does not exist or its value is not an int tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return an int + */ + public int getInt(String key) { + Tag tag = value.get(key); + if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get an int named with the given key, even if it's another + * type of number. + * + *

If the key does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return an int + */ + public int asInt(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue().intValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().intValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().intValue(); + + } else { + return 0; + } + } + + /** + * Get a list of tags named with the given key. + * + *

If the key does not exist or its value is not a list tag, + * then an empty list will be returned.

+ * + * @param key the key + * @return a list of tags + */ + public List getList(String key) { + Tag tag = value.get(key); + if (tag instanceof ListTag) { + return ((ListTag) tag).getValue(); + } else { + return Collections.emptyList(); + } + } + + /** + * Get a {@code TagList} named with the given key. + * + *

If the key does not exist or its value is not a list tag, + * then an empty tag list will be returned.

+ * + * @param key the key + * @return a tag list instance + */ + public ListTag getListTag(String key) { + Tag tag = value.get(key); + if (tag instanceof ListTag) { + return (ListTag) tag; + } else { + return new ListTag(key, StringTag.class, Collections.emptyList()); + } + } + + /** + * Get a list of tags named with the given key. + * + *

If the key does not exist or its value is not a list tag, + * then an empty list will be returned. If the given key references + * a list but the list of of a different type, then an empty + * list will also be returned.

+ * + * @param key the key + * @return a list of tags + */ + @SuppressWarnings("unchecked") + public List getList(String key, Class listType) { + Tag tag = value.get(key); + if (tag instanceof ListTag) { + ListTag listTag = (ListTag) tag; + if (listTag.getType().equals(listType)) { + return (List) listTag.getValue(); + } else { + return Collections.emptyList(); + } + } else { + return Collections.emptyList(); + } + } + + /** + * Get a long named with the given key. + * + *

If the key does not exist or its value is not a long tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a long + */ + public long getLong(String key) { + Tag tag = value.get(key); + if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + } else { + return 0L; + } + } + + /** + * Get a long named with the given key, even if it's another + * type of number. + * + *

If the key does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a long + */ + public long asLong(String key) { + Tag tag = value.get(key); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().longValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().longValue(); + + } else { + return 0L; + } + } + + /** + * Get a short named with the given key. + * + *

If the key does not exist or its value is not a short tag, + * then {@code 0} will be returned.

+ * + * @param key the key + * @return a short + */ + public short getShort(String key) { + Tag tag = value.get(key); + if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a string named with the given key. + * + *

If the key does not exist or its value is not a string tag, + * then {@code ""} will be returned.

+ * + * @param key the key + * @return a string + */ + public String getString(String key) { + Tag tag = value.get(key); + if (tag instanceof StringTag) { + return ((StringTag) tag).getValue(); + } else { + return ""; + } + } + @Override public String toString() { String name = getName(); @@ -62,12 +423,9 @@ public final class CompoundTag extends Tag { append = "(\"" + this.getName() + "\")"; } StringBuilder bldr = new StringBuilder(); - bldr.append("TAG_Compound" + append + ": " + value.size() - + " entries\r\n{\r\n"); + bldr.append("TAG_Compound").append(append).append(": ").append(value.size()).append(" entries\r\n{\r\n"); for (Map.Entry entry : value.entrySet()) { - bldr.append(" " - + entry.getValue().toString().replaceAll("\r\n", "\r\n ") - + "\r\n"); + bldr.append(" ").append(entry.getValue().toString().replaceAll("\r\n", "\r\n ")).append("\r\n"); } bldr.append("}"); return bldr.toString(); diff --git a/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java b/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java new file mode 100644 index 000000000..187995a6b --- /dev/null +++ b/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java @@ -0,0 +1,214 @@ +/* + * 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.jnbt; + +import java.util.HashMap; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helps create compound tags. + */ +public class CompoundTagBuilder { + + private final Map entries; + + /** + * Create a new instance. + */ + CompoundTagBuilder() { + this.entries = new HashMap(); + } + + /** + * Create a new instance and use the given map (which will be modified). + * + * @param value the value + */ + CompoundTagBuilder(Map value) { + checkNotNull(value); + this.entries = value; + } + + /** + * Put the given key and tag into the compound tag. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder put(String key, Tag value) { + checkNotNull(key); + checkNotNull(value); + entries.put(key, value); + return this; + } + + /** + * Put the given key and value into the compound tag as a + * {@code ByteArrayTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putByteArray(String key, byte[] value) { + return put(key, new ByteArrayTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as a + * {@code ByteTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putByte(String key, byte value) { + return put(key, new ByteTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as a + * {@code DoubleTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putDouble(String key, double value) { + return put(key, new DoubleTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as a + * {@code FloatTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putFloat(String key, float value) { + return put(key, new FloatTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as a + * {@code IntArrayTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putIntArray(String key, int[] value) { + return put(key, new IntArrayTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as an {@code IntTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putInt(String key, int value) { + return put(key, new IntTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as a + * {@code LongTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putLong(String key, long value) { + return put(key, new LongTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as a + * {@code ShortTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putShort(String key, short value) { + return put(key, new ShortTag(key, value)); + } + + /** + * Put the given key and value into the compound tag as a + * {@code StringTag}. + * + * @param key they key + * @param value the value + * @return this object + */ + public CompoundTagBuilder putString(String key, String value) { + return put(key, new StringTag(key, value)); + } + + /** + * Put all the entries from the given map into this map. + * + * @param value the map of tags + * @return this object + */ + public CompoundTagBuilder putAll(Map value) { + checkNotNull(value); + for (Map.Entry entry : value.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * Build an unnamed compound tag with this builder's entries. + * + * @return the new compound tag + */ + public CompoundTag build() { + return new CompoundTag(new HashMap(entries)); + } + + /** + * Build a new compound tag with this builder's entries. + * + * @param name the name of the tag + * @return the created compound tag + */ + public CompoundTag build(String name) { + return new CompoundTag(name, new HashMap(entries)); + } + + /** + * Create a new builder instance. + * + * @return a new builder + */ + public static CompoundTagBuilder create() { + return new CompoundTagBuilder(); + } + +} diff --git a/src/main/java/com/sk89q/jnbt/DoubleTag.java b/src/main/java/com/sk89q/jnbt/DoubleTag.java index f3f69ea05..1ebcdf794 100644 --- a/src/main/java/com/sk89q/jnbt/DoubleTag.java +++ b/src/main/java/com/sk89q/jnbt/DoubleTag.java @@ -19,28 +19,29 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Double tag. - * - * @author Graham Edgecombe + * The {@code TAG_Double} tag. * */ public final class DoubleTag extends Tag { - /** - * The value. - */ private final double value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public DoubleTag(double value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public DoubleTag(String name, double value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/EndTag.java b/src/main/java/com/sk89q/jnbt/EndTag.java index ec5e167f1..5051cc939 100644 --- a/src/main/java/com/sk89q/jnbt/EndTag.java +++ b/src/main/java/com/sk89q/jnbt/EndTag.java @@ -19,8 +19,6 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** * The TAG_End tag. * @@ -33,7 +31,7 @@ public final class EndTag extends Tag { * Creates the tag. */ public EndTag() { - super(""); + super(); } @Override diff --git a/src/main/java/com/sk89q/jnbt/FloatTag.java b/src/main/java/com/sk89q/jnbt/FloatTag.java index bb005face..21b921b69 100644 --- a/src/main/java/com/sk89q/jnbt/FloatTag.java +++ b/src/main/java/com/sk89q/jnbt/FloatTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Float tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Float} tag. */ public final class FloatTag extends Tag { - /** - * The value. - */ private final float value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public FloatTag(float value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public FloatTag(String name, float value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/IntArrayTag.java b/src/main/java/com/sk89q/jnbt/IntArrayTag.java index 1765e50cf..b8dced001 100644 --- a/src/main/java/com/sk89q/jnbt/IntArrayTag.java +++ b/src/main/java/com/sk89q/jnbt/IntArrayTag.java @@ -19,31 +19,35 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; +import static com.google.common.base.Preconditions.checkNotNull; /** - * The TAG_Int_Array tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Int_Array} tag. */ public final class IntArrayTag extends Tag { - /** - * The value. - */ private final int[] value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public IntArrayTag(int[] value) { + super(); + checkNotNull(value); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public IntArrayTag(String name, int[] value) { super(name); + checkNotNull(value); this.value = value; } diff --git a/src/main/java/com/sk89q/jnbt/IntTag.java b/src/main/java/com/sk89q/jnbt/IntTag.java index 480b54ee9..5591408f6 100644 --- a/src/main/java/com/sk89q/jnbt/IntTag.java +++ b/src/main/java/com/sk89q/jnbt/IntTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Int tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Int} tag. */ public final class IntTag extends Tag { - /** - * The value. - */ private final int value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public IntTag(int value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public IntTag(String name, int value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/ListTag.java b/src/main/java/com/sk89q/jnbt/ListTag.java index 282791179..4125b43ee 100644 --- a/src/main/java/com/sk89q/jnbt/ListTag.java +++ b/src/main/java/com/sk89q/jnbt/ListTag.java @@ -19,41 +19,44 @@ package com.sk89q.jnbt; +import javax.annotation.Nullable; import java.util.Collections; import java.util.List; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.Tag; +import java.util.NoSuchElementException; + +import static com.google.common.base.Preconditions.checkNotNull; /** - * The TAG_List tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_List} tag. */ public final class ListTag extends Tag { - /** - * The type. - */ private final Class type; + private final List value; /** - * The value. + * Creates the tag with an empty name. + * + * @param type the type of tag + * @param value the value of the tag */ - private final List value; + public ListTag(Class type, List value) { + super(); + checkNotNull(value); + this.type = type; + this.value = Collections.unmodifiableList(value); + } /** * Creates the tag. * - * @param name - * The name. - * @param type - * The type of item in the list. - * @param value - * The value. + * @param name the name of the tag + * @param type the type of tag + * @param value the value of the tag */ public ListTag(String name, Class type, List value) { super(name); + checkNotNull(value); this.type = type; this.value = Collections.unmodifiableList(value); } @@ -72,6 +75,360 @@ public final class ListTag extends Tag { return value; } + /** + * Create a new list tag with this tag's name and type. + * + * @param list the new list + * @return a new list tag + */ + public ListTag setValue(List list) { + return new ListTag(getName(), getType(), list); + } + + /** + * Get the tag if it exists at the given index. + * + * @param index the index + * @return the tag or null + */ + @Nullable + public Tag getIfExists(int index) { + try { + return value.get(index); + } catch (NoSuchElementException e) { + return null; + } + } + + /** + * Get a byte array named with the given index. + * + *

If the index does not exist or its value is not a byte array tag, + * then an empty byte array will be returned.

+ * + * @param index the index + * @return a byte array + */ + public byte[] getByteArray(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteArrayTag) { + return ((ByteArrayTag) tag).getValue(); + } else { + return new byte[0]; + } + } + + /** + * Get a byte named with the given index. + * + *

If the index does not exist or its value is not a byte tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a byte + */ + public byte getByte(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + } else { + return (byte) 0; + } + } + + /** + * Get a double named with the given index. + * + *

If the index does not exist or its value is not a double tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a double + */ + public double getDouble(int index) { + Tag tag = getIfExists(index); + if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a double named with the given index, even if it's another + * type of number. + * + *

If the index does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a double + */ + public double asDouble(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue(); + + } else { + return 0; + } + } + + /** + * Get a float named with the given index. + * + *

If the index does not exist or its value is not a float tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a float + */ + public float getFloat(int index) { + Tag tag = getIfExists(index); + if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a {@code int[]} named with the given index. + * + *

If the index does not exist or its value is not an int array tag, + * then an empty array will be returned.

+ * + * @param index the index + * @return an int array + */ + public int[] getIntArray(int index) { + Tag tag = getIfExists(index); + if (tag instanceof IntArrayTag) { + return ((IntArrayTag) tag).getValue(); + } else { + return new int[0]; + } + } + + /** + * Get an int named with the given index. + * + *

If the index does not exist or its value is not an int tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return an int + */ + public int getInt(int index) { + Tag tag = getIfExists(index); + if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get an int named with the given index, even if it's another + * type of number. + * + *

If the index does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return an int + */ + public int asInt(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue().intValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().intValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().intValue(); + + } else { + return 0; + } + } + + /** + * Get a list of tags named with the given index. + * + *

If the index does not exist or its value is not a list tag, + * then an empty list will be returned.

+ * + * @param index the index + * @return a list of tags + */ + public List getList(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ListTag) { + return ((ListTag) tag).getValue(); + } else { + return Collections.emptyList(); + } + } + + /** + * Get a {@code TagList} named with the given index. + * + *

If the index does not exist or its value is not a list tag, + * then an empty tag list will be returned.

+ * + * @param index the index + * @return a tag list instance + */ + public ListTag getListTag(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ListTag) { + return (ListTag) tag; + } else { + return new ListTag(StringTag.class, Collections.emptyList()); + } + } + + /** + * Get a list of tags named with the given index. + * + *

If the index does not exist or its value is not a list tag, + * then an empty list will be returned. If the given index references + * a list but the list of of a different type, then an empty + * list will also be returned.

+ * + * @param index the index + * @return a list of tags + */ + @SuppressWarnings("unchecked") + public List getList(int index, Class listType) { + Tag tag = getIfExists(index); + if (tag instanceof ListTag) { + ListTag listTag = (ListTag) tag; + if (listTag.getType().equals(listType)) { + return (List) listTag.getValue(); + } else { + return Collections.emptyList(); + } + } else { + return Collections.emptyList(); + } + } + + /** + * Get a long named with the given index. + * + *

If the index does not exist or its value is not a long tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a long + */ + public long getLong(int index) { + Tag tag = getIfExists(index); + if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + } else { + return 0L; + } + } + + /** + * Get a long named with the given index, even if it's another + * type of number. + * + *

If the index does not exist or its value is not a number, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a long + */ + public long asLong(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ByteTag) { + return ((ByteTag) tag).getValue(); + + } else if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + + } else if (tag instanceof IntTag) { + return ((IntTag) tag).getValue(); + + } else if (tag instanceof LongTag) { + return ((LongTag) tag).getValue(); + + } else if (tag instanceof FloatTag) { + return ((FloatTag) tag).getValue().longValue(); + + } else if (tag instanceof DoubleTag) { + return ((DoubleTag) tag).getValue().longValue(); + + } else { + return 0; + } + } + + /** + * Get a short named with the given index. + * + *

If the index does not exist or its value is not a short tag, + * then {@code 0} will be returned.

+ * + * @param index the index + * @return a short + */ + public short getShort(int index) { + Tag tag = getIfExists(index); + if (tag instanceof ShortTag) { + return ((ShortTag) tag).getValue(); + } else { + return 0; + } + } + + /** + * Get a string named with the given index. + * + *

If the index does not exist or its value is not a string tag, + * then {@code ""} will be returned.

+ * + * @param index the index + * @return a string + */ + public String getString(int index) { + Tag tag = getIfExists(index); + if (tag instanceof StringTag) { + return ((StringTag) tag).getValue(); + } else { + return ""; + } + } + @Override public String toString() { String name = getName(); diff --git a/src/main/java/com/sk89q/jnbt/ListTagBuilder.java b/src/main/java/com/sk89q/jnbt/ListTagBuilder.java new file mode 100644 index 000000000..2934c2255 --- /dev/null +++ b/src/main/java/com/sk89q/jnbt/ListTagBuilder.java @@ -0,0 +1,129 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.jnbt; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helps create list tags. + */ +public class ListTagBuilder { + + private final Class type; + private final List entries; + + /** + * Create a new instance. + * + * @param type of tag contained in this list + */ + ListTagBuilder(Class type) { + checkNotNull(type); + this.type = type; + this.entries = new ArrayList(); + } + + /** + * Add the given tag. + * + * @param value the tag + * @return this object + */ + public ListTagBuilder add(Tag value) { + checkNotNull(value); + if (!type.isInstance(value)) { + throw new IllegalArgumentException(value.getClass().getCanonicalName() + " is not of expected type " + type.getCanonicalName()); + } + entries.add(value); + return this; + } + + /** + * Add all the tags in the given list. + * + * @param value a list of tags + * @return this object + */ + public ListTagBuilder addAll(Collection value) { + checkNotNull(value); + for (Tag v : value) { + add(v); + } + return this; + } + + /** + * Build an unnamed list tag with this builder's entries. + * + * @return the new list tag + */ + public ListTag build() { + return new ListTag(type, new ArrayList(entries)); + } + + /** + * Build a new list tag with this builder's entries. + * + * @param name the name of the tag + * @return the created list tag + */ + public ListTag build(String name) { + return new ListTag(name, type, new ArrayList(entries)); + } + + /** + * Create a new builder instance. + * + * @return a new builder + */ + public static ListTagBuilder create(Class type) { + return new ListTagBuilder(type); + } + + /** + * Create a new builder instance. + * + * @return a new builder + */ + public static ListTagBuilder createWith(T ... entries) { + checkNotNull(entries); + + if (entries.length == 0) { + throw new IllegalArgumentException("This method needs an array of at least one entry"); + } + + Class type = entries[0].getClass(); + for (int i = 1; i < entries.length; i++) { + if (!type.isInstance(entries[i])) { + throw new IllegalArgumentException("An array of different tag types was provided"); + } + } + + ListTagBuilder builder = new ListTagBuilder(type); + builder.addAll(Arrays.asList(entries)); + return builder; + } + +} diff --git a/src/main/java/com/sk89q/jnbt/LongTag.java b/src/main/java/com/sk89q/jnbt/LongTag.java index 121f1365d..26e1902a5 100644 --- a/src/main/java/com/sk89q/jnbt/LongTag.java +++ b/src/main/java/com/sk89q/jnbt/LongTag.java @@ -19,28 +19,29 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Long tag. - * - * @author Graham Edgecombe + * The {@code TAG_Long} tag. * */ public final class LongTag extends Tag { - /** - * The value. - */ private final long value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public LongTag(long value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public LongTag(String name, long value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/NBTConstants.java b/src/main/java/com/sk89q/jnbt/NBTConstants.java index f214f7dec..7c8001b43 100644 --- a/src/main/java/com/sk89q/jnbt/NBTConstants.java +++ b/src/main/java/com/sk89q/jnbt/NBTConstants.java @@ -29,14 +29,8 @@ import java.nio.charset.Charset; */ public final class NBTConstants { - /** - * The character set used by NBT (UTF-8). - */ public static final Charset CHARSET = Charset.forName("UTF-8"); - /** - * Tag type constants. - */ public static final int TYPE_END = 0, TYPE_BYTE = 1, TYPE_SHORT = 2, TYPE_INT = 3, TYPE_LONG = 4, TYPE_FLOAT = 5, TYPE_DOUBLE = 6, TYPE_BYTE_ARRAY = 7, TYPE_STRING = 8, TYPE_LIST = 9, diff --git a/src/main/java/com/sk89q/jnbt/NBTInputStream.java b/src/main/java/com/sk89q/jnbt/NBTInputStream.java index d50873d12..1d1c5215b 100644 --- a/src/main/java/com/sk89q/jnbt/NBTInputStream.java +++ b/src/main/java/com/sk89q/jnbt/NBTInputStream.java @@ -27,20 +27,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import com.sk89q.jnbt.ByteArrayTag; -import com.sk89q.jnbt.ByteTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.EndTag; -import com.sk89q.jnbt.FloatTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.LongTag; -import com.sk89q.jnbt.NBTConstants; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; /** *

@@ -60,19 +46,14 @@ import com.sk89q.jnbt.Tag; */ public final class NBTInputStream implements Closeable { - /** - * The data input stream. - */ private final DataInputStream is; /** - * Creates a new NBTInputStream, which will source its data + * Creates a new {@code NBTInputStream}, which will source its data * from the specified input stream. * - * @param is - * The input stream. - * @throws IOException - * if an I/O error occurs. + * @param is the input stream + * @throws IOException if an I/O error occurs */ public NBTInputStream(InputStream is) throws IOException { this.is = new DataInputStream(is); @@ -82,8 +63,7 @@ public final class NBTInputStream implements Closeable { * Reads an NBT tag from the stream. * * @return The tag that was read. - * @throws IOException - * if an I/O error occurs. + * @throws IOException if an I/O error occurs. */ public Tag readTag() throws IOException { return readTag(0); @@ -92,11 +72,9 @@ public final class NBTInputStream implements Closeable { /** * Reads an NBT from the stream. * - * @param depth - * The depth of this tag. + * @param depth the depth of this tag * @return The tag that was read. - * @throws IOException - * if an I/O error occurs. + * @throws IOException if an I/O error occurs. */ private Tag readTag(int depth) throws IOException { int type = is.readByte() & 0xFF; @@ -117,18 +95,13 @@ public final class NBTInputStream implements Closeable { /** * Reads the payload of a tag, given the name and type. * - * @param type - * The type. - * @param name - * The name. - * @param depth - * The depth. - * @return The tag. - * @throws IOException - * if an I/O error occurs. + * @param type the type + * @param name the name + * @param depth the depth + * @return the tag + * @throws IOException if an I/O error occurs. */ - private Tag readTagPayload(int type, String name, int depth) - throws IOException { + private Tag readTagPayload(int type, String name, int depth) throws IOException { switch (type) { case NBTConstants.TYPE_END: if (depth == 0) { @@ -197,6 +170,7 @@ public final class NBTInputStream implements Closeable { } } + @Override public void close() throws IOException { is.close(); } diff --git a/src/main/java/com/sk89q/jnbt/NBTOutputStream.java b/src/main/java/com/sk89q/jnbt/NBTOutputStream.java index 7c440ef31..d9a56f666 100644 --- a/src/main/java/com/sk89q/jnbt/NBTOutputStream.java +++ b/src/main/java/com/sk89q/jnbt/NBTOutputStream.java @@ -24,21 +24,6 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.List; -import java.util.zip.GZIPOutputStream; -import com.sk89q.jnbt.ByteArrayTag; -import com.sk89q.jnbt.ByteTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.EndTag; -import com.sk89q.jnbt.FloatTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.LongTag; -import com.sk89q.jnbt.NBTConstants; -import com.sk89q.jnbt.NBTUtils; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; /** *

@@ -72,7 +57,7 @@ public final class NBTOutputStream implements Closeable { * if an I/O error occurs. */ public NBTOutputStream(OutputStream os) throws IOException { - this.os = new DataOutputStream(new GZIPOutputStream(os)); + this.os = new DataOutputStream(os); } /** diff --git a/src/main/java/com/sk89q/jnbt/NBTUtils.java b/src/main/java/com/sk89q/jnbt/NBTUtils.java index 53983022c..20b3e5542 100644 --- a/src/main/java/com/sk89q/jnbt/NBTUtils.java +++ b/src/main/java/com/sk89q/jnbt/NBTUtils.java @@ -19,23 +19,31 @@ package com.sk89q.jnbt; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.storage.InvalidFormatException; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + /** * A class which contains NBT-related utility methods. * - * @author Graham Edgecombe - * */ public final class NBTUtils { + /** + * Default private constructor. + */ + private NBTUtils() { + } + /** * Gets the type name of a tag. * - * @param clazz - * The tag class. + * @param clazz the tag class * @return The type name. */ public static String getTypeName(Class clazz) { @@ -72,11 +80,9 @@ public final class NBTUtils { /** * Gets the type code of a tag class. * - * @param clazz - * The tag class. + * @param clazz the tag class * @return The type code. - * @throws IllegalArgumentException - * if the tag class is invalid. + * @throws IllegalArgumentException if the tag class is invalid. */ public static int getTypeCode(Class clazz) { if (clazz.equals(ByteArrayTag.class)) { @@ -112,11 +118,9 @@ public final class NBTUtils { /** * Gets the class of a type of tag. * - * @param type - * The type. + * @param type the type * @return The class. - * @throws IllegalArgumentException - * if the tag type is invalid. + * @throws IllegalArgumentException if the tag type is invalid. */ public static Class getTypeClass(int type) { switch (type) { @@ -151,10 +155,17 @@ public final class NBTUtils { } /** - * Default private constructor. + * Read a vector from a list tag containing ideally three values: the + * X, Y, and Z components. + * + *

For values that are unavailable, their values will be 0.

+ * + * @param listTag the list tag + * @return a vector */ - private NBTUtils() { - + public static Vector toVector(ListTag listTag) { + checkNotNull(listTag); + return new Vector(listTag.asDouble(0), listTag.asDouble(1), listTag.asDouble(2)); } /** diff --git a/src/main/java/com/sk89q/jnbt/ShortTag.java b/src/main/java/com/sk89q/jnbt/ShortTag.java index 0a658fa07..fd3ee3a4e 100644 --- a/src/main/java/com/sk89q/jnbt/ShortTag.java +++ b/src/main/java/com/sk89q/jnbt/ShortTag.java @@ -19,28 +19,28 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; - /** - * The TAG_Short tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_Short} tag. */ public final class ShortTag extends Tag { - /** - * The value. - */ private final short value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public ShortTag(short value) { + super(); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public ShortTag(String name, short value) { super(name); diff --git a/src/main/java/com/sk89q/jnbt/StringTag.java b/src/main/java/com/sk89q/jnbt/StringTag.java index b6687c6fd..409511ab5 100644 --- a/src/main/java/com/sk89q/jnbt/StringTag.java +++ b/src/main/java/com/sk89q/jnbt/StringTag.java @@ -19,31 +19,35 @@ package com.sk89q.jnbt; -import com.sk89q.jnbt.Tag; +import static com.google.common.base.Preconditions.checkNotNull; /** - * The TAG_String tag. - * - * @author Graham Edgecombe - * + * The {@code TAG_String} tag. */ public final class StringTag extends Tag { - /** - * The value. - */ private final String value; + /** + * Creates the tag with an empty name. + * + * @param value the value of the tag + */ + public StringTag(String value) { + super(); + checkNotNull(value); + this.value = value; + } + /** * Creates the tag. - * - * @param name - * The name. - * @param value - * The value. + * + * @param name the name of the tag + * @param value the value of the tag */ public StringTag(String name, String value) { super(name); + checkNotNull(value); this.value = value; } diff --git a/src/main/java/com/sk89q/jnbt/Tag.java b/src/main/java/com/sk89q/jnbt/Tag.java index 18a0a0420..e0d7d3f4b 100644 --- a/src/main/java/com/sk89q/jnbt/Tag.java +++ b/src/main/java/com/sk89q/jnbt/Tag.java @@ -20,32 +20,35 @@ package com.sk89q.jnbt; /** - * Represents a single NBT tag. - * - * @author Graham Edgecombe - * + * Represents a NBT tag. */ public abstract class Tag { - /** - * The name of this tag. - */ private final String name; + /** + * Create a new tag with an empty name. + */ + Tag() { + this(""); + } + /** * Creates the tag with the specified name. * - * @param name - * The name. + * @param name the name */ - public Tag(String name) { + Tag(String name) { + if (name == null) { + name = ""; + } this.name = name; } /** * Gets the name of this tag. * - * @return The name of this tag. + * @return the name of this tag */ public final String getName() { return name; @@ -54,7 +57,7 @@ public abstract class Tag { /** * Gets the value of this tag. * - * @return The value of this tag. + * @return the value */ public abstract Object getValue(); diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index a641f0089..f97f002ea 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -22,6 +22,8 @@ package com.sk89q.worldedit; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.extent.ChangeSetExtent; import com.sk89q.worldedit.extent.Extent; @@ -69,12 +71,12 @@ import com.sk89q.worldedit.regions.shape.ArbitraryBiomeShape; import com.sk89q.worldedit.regions.shape.ArbitraryShape; import com.sk89q.worldedit.regions.shape.RegionShape; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; -import com.sk89q.worldedit.util.Countable; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.*; import com.sk89q.worldedit.util.collection.DoubleArrayList; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.world.NullWorld; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BaseBiome; import javax.annotation.Nullable; import java.util.*; @@ -376,6 +378,16 @@ public class EditSession implements Extent { return changeSet.size(); } + @Override + public BaseBiome getBiome(Vector2D position) { + return bypassNone.getBiome(position); + } + + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + return bypassNone.setBiome(position, biome); + } + @Override public BaseBlock getLazyBlock(Vector position) { return world.getLazyBlock(position); @@ -581,6 +593,12 @@ public class EditSession implements Extent { return getBlock(position).isAir() && setBlock(position, block); } + @Override + @Nullable + public Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) { + return bypassNone.createEntity(location, entity); + } + /** * Insert a contrived block change into the history. * @@ -637,6 +655,16 @@ public class EditSession implements Extent { return getWorld().getMaximumPoint(); } + @Override + public List getEntities(Region region) { + return bypassNone.getEntities(region); + } + + @Override + public List getEntities() { + return bypassNone.getEntities(); + } + /** * Finish off the queue. */ @@ -1148,6 +1176,7 @@ public class EditSession implements Extent { ForwardExtentCopy copy = new ForwardExtentCopy(this, region, buffer, to); copy.setTransform(new AffineTransform().translate(dir.multiply(distance))); copy.setSourceFunction(remove); // Remove + copy.setRemovingEntities(true); if (!copyAir) { copy.setSourceMask(new ExistingBlockMask(this)); } @@ -2230,7 +2259,7 @@ public class EditSession implements Extent { } // while } - public int makeBiomeShape(final Region region, final Vector zero, final Vector unit, final BiomeType biomeType, final String expressionString, final boolean hollow) throws ExpressionException, MaxChangedBlocksException { + public int makeBiomeShape(final Region region, final Vector zero, final Vector unit, final BaseBiome biomeType, final String expressionString, final boolean hollow) throws ExpressionException, MaxChangedBlocksException { final Vector2D zero2D = zero.toVector2D(); final Vector2D unit2D = unit.toVector2D(); @@ -2243,7 +2272,7 @@ public class EditSession implements Extent { final ArbitraryBiomeShape shape = new ArbitraryBiomeShape(region) { @Override - protected BiomeType getBiome(int x, int z, BiomeType defaultBiomeType) { + protected BaseBiome getBiome(int x, int z, BaseBiome defaultBiomeType) { final Vector2D current = new Vector2D(x, z); environment.setCurrentBlock(current.toVector(0)); final Vector2D scaled = current.subtract(zero2D).divide(unit2D); diff --git a/src/main/java/com/sk89q/worldedit/LocalSession.java b/src/main/java/com/sk89q/worldedit/LocalSession.java index df38b58a5..0bf0bfeb3 100644 --- a/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -38,11 +38,17 @@ import com.sk89q.worldedit.internal.cui.SelectionShapeEvent; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.snapshot.Snapshot; -import java.util.*; +import javax.annotation.Nullable; +import java.util.Calendar; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.TimeZone; /** * An instance of this represents the WorldEdit session of a user. A session @@ -62,7 +68,7 @@ public class LocalSession { private boolean placeAtPos1 = false; private LinkedList history = new LinkedList(); private int historyPointer = 0; - private CuboidClipboard clipboard; + private ClipboardHolder clipboard; private boolean toolControl = true; private boolean superPickaxe = false; private BlockTool pickaxeMode = new SinglePickaxe(); @@ -324,9 +330,9 @@ public class LocalSession { * Gets the clipboard. * * @return clipboard, may be null - * @throws EmptyClipboardException + * @throws EmptyClipboardException thrown if no clipboard is set */ - public CuboidClipboard getClipboard() throws EmptyClipboardException { + public ClipboardHolder getClipboard() throws EmptyClipboardException { if (clipboard == null) { throw new EmptyClipboardException(); } @@ -336,9 +342,11 @@ public class LocalSession { /** * Sets the clipboard. * - * @param clipboard + *

Pass {@code null} to clear the clipboard.

+ * + * @param clipboard the clipboard, or null if the clipboard is to be cleared */ - public void setClipboard(CuboidClipboard clipboard) { + public void setClipboard(@Nullable ClipboardHolder clipboard) { this.clipboard = clipboard; } diff --git a/src/main/java/com/sk89q/worldedit/LocalWorld.java b/src/main/java/com/sk89q/worldedit/LocalWorld.java index 9dd3498cb..ae1112b95 100644 --- a/src/main/java/com/sk89q/worldedit/LocalWorld.java +++ b/src/main/java/com/sk89q/worldedit/LocalWorld.java @@ -19,13 +19,11 @@ package com.sk89q.worldedit; -import com.sk89q.worldedit.blocks.*; +import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.World; -import java.util.Random; - /** * A legacy abstract implementation of {@link World}. New implementations * should use {@link AbstractWorld} when possible. @@ -35,140 +33,11 @@ import java.util.Random; @Deprecated public abstract class LocalWorld extends AbstractWorld { - /** - * Named flags to use as parameters to {@link LocalWorld#killMobs(Vector, double, int)} - */ - @SuppressWarnings("PointlessBitwiseExpression") - public final class KillFlags { - public static final int PETS = 1 << 0; - public static final int NPCS = 1 << 1; - public static final int ANIMALS = 1 << 2; - public static final int GOLEMS = 1 << 3; - public static final int AMBIENT = 1 << 4; - public static final int TAGGED = 1 << 5; - public static final int FRIENDLY = PETS | NPCS | ANIMALS | GOLEMS | AMBIENT | TAGGED; - public static final int WITH_LIGHTNING = 1 << 20; - - private KillFlags() { - } - } - - /** - * @deprecated Don't use this anymore. It will be removed - */ - @SuppressWarnings("ProtectedField") - @Deprecated - protected Random random = new Random(); - - @Override - public BaseBlock getBlock(Vector pt) { - checkLoadedChunk(pt); - - @SuppressWarnings("deprecation") int type = getBlockType(pt); - @SuppressWarnings("deprecation") int data = getBlockData(pt); - - switch (type) { - case BlockID.WALL_SIGN: - case BlockID.SIGN_POST: { - SignBlock block = new SignBlock(type, data); - copyFromWorld(pt, block); - return block; - } - - case BlockID.CHEST: { - ChestBlock block = new ChestBlock(data); - copyFromWorld(pt, block); - return block; - } - - case BlockID.FURNACE: - case BlockID.BURNING_FURNACE: { - FurnaceBlock block = new FurnaceBlock(type, data); - copyFromWorld(pt, block); - return block; - } - - case BlockID.DISPENSER: { - DispenserBlock block = new DispenserBlock(data); - copyFromWorld(pt, block); - return block; - } - - case BlockID.MOB_SPAWNER: { - MobSpawnerBlock block = new MobSpawnerBlock(data); - copyFromWorld(pt, block); - return block; - } - - case BlockID.NOTE_BLOCK: { - NoteBlock block = new NoteBlock(data); - copyFromWorld(pt, block); - return block; - } - - case BlockID.HEAD: { - SkullBlock block = new SkullBlock(data); - copyFromWorld(pt, block); - return block; - } - - default: - return new BaseBlock(type, data); - } - } - - /** - * Given a block and a position, copy data from the world to the block - * based on the type of block. - *

- * The provided {@link BaseBlock} should match that of the one in the - * world. - * - * @param position the position - * @param block the block - * @return true if the copy operation succeeded, false otherwise - */ - public abstract boolean copyFromWorld(Vector position, BaseBlock block); - @Override public BaseBlock getLazyBlock(Vector position) { return getBlock(position); } - @Override - public boolean setBlock(Vector pt, BaseBlock block, boolean notifyAdjacent) throws WorldEditException { - boolean successful; - - // Default implementation will call the old deprecated methods - if (notifyAdjacent) { - successful = setTypeIdAndData(pt, block.getId(), block.getData()); - } else { - successful = setTypeIdAndDataFast(pt, block.getId(), block.getData()); - } - - copyToWorld(pt, block); - - return successful; - } - - /** - * Given a block and a position, copy data to the world from the block - * based on the type of block. - *

- * The provided {@link BaseBlock} should match that of the one in the - * world. - * - * @param position the position - * @param block the block - * @return true if the copy operation succeeded, false otherwise - */ - public abstract boolean copyToWorld(Vector position, BaseBlock block); - - @Override - public boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException { - return setBlock(pt, block, true); - } - @Override public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pt) throws MaxChangedBlocksException { switch (type) { diff --git a/src/main/java/com/sk89q/worldedit/Vector.java b/src/main/java/com/sk89q/worldedit/Vector.java index c028c5797..c1fd0475e 100644 --- a/src/main/java/com/sk89q/worldedit/Vector.java +++ b/src/main/java/com/sk89q/worldedit/Vector.java @@ -600,8 +600,7 @@ public class Vector implements Comparable { * @param translateZ what to add after rotation * @return */ - public Vector transform2D(double angle, - double aboutX, double aboutZ, double translateX, double translateZ) { + public Vector transform2D(double angle, double aboutX, double aboutZ, double translateX, double translateZ) { angle = Math.toRadians(angle); double x = this.x - aboutX; double z = this.z - aboutZ; @@ -652,6 +651,40 @@ public class Vector implements Comparable { throw new RuntimeException("This should not happen"); } + /** + * Get this vector's pitch as used within the game. + * + * @return pitch in radians + */ + public float toPitch() { + double x = getX(); + double z = getZ(); + + if (x == 0 && z == 0) { + return getY() > 0 ? -90 : 90; + } else { + double x2 = x * x; + double z2 = z * z; + double xz = Math.sqrt(x2 + z2); + return (float) Math.toDegrees(Math.atan(-getY() / xz)); + } + } + + /** + * Get this vector's yaw as used within the game. + * + * @return yaw in radians + */ + public float toYaw() { + double x = getX(); + double z = getZ(); + + double t = Math.atan2(-x, z); + double _2pi = 2 * Math.PI; + + return (float) Math.toDegrees(((t + _2pi) % _2pi)); + } + /** * Get a block point from a point. * @@ -792,4 +825,5 @@ public class Vector implements Comparable { (v1.z + v2.z) / 2 ); } + } diff --git a/src/main/java/com/sk89q/worldedit/Vector2D.java b/src/main/java/com/sk89q/worldedit/Vector2D.java index 6bb9d7eed..77ba862a1 100644 --- a/src/main/java/com/sk89q/worldedit/Vector2D.java +++ b/src/main/java/com/sk89q/worldedit/Vector2D.java @@ -517,8 +517,7 @@ public class Vector2D { * @param translateZ what to add after rotation * @return */ - public Vector2D transform2D(double angle, - double aboutX, double aboutZ, double translateX, double translateZ) { + public Vector2D transform2D(double angle, double aboutX, double aboutZ, double translateX, double translateZ) { angle = Math.toRadians(angle); double x = this.x - aboutX; double z = this.z - aboutZ; diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index 1a0d25a5e..2e3a4d2df 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -27,13 +27,13 @@ import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.event.platform.BlockInteractEvent; import com.sk89q.worldedit.event.platform.InputType; import com.sk89q.worldedit.event.platform.PlayerInputEvent; +import com.sk89q.worldedit.extension.factory.BlockFactory; +import com.sk89q.worldedit.extension.factory.MaskFactory; +import com.sk89q.worldedit.extension.factory.PatternFactory; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.PlatformManager; -import com.sk89q.worldedit.extension.registry.BlockRegistry; -import com.sk89q.worldedit.extension.registry.MaskRegistry; -import com.sk89q.worldedit.extension.registry.PatternRegistry; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.pattern.Patterns; @@ -46,9 +46,14 @@ import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.util.logging.WorldEditPrefixHandler; +import com.sk89q.worldedit.world.registry.BundledBlockData; import javax.script.ScriptException; -import java.io.*; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -84,13 +89,14 @@ public class WorldEdit { private final EditSessionFactory editSessionFactory = new EditSessionFactory.EditSessionFactoryImpl(eventBus); private final SessionManager sessions = new SessionManager(this); - private final BlockRegistry blockRegistry = new BlockRegistry(this); - private final MaskRegistry maskRegistry = new MaskRegistry(this); - private final PatternRegistry patternRegistry = new PatternRegistry(this); + private final BlockFactory blockFactory = new BlockFactory(this); + private final MaskFactory maskFactory = new MaskFactory(this); + private final PatternFactory patternFactory = new PatternFactory(this); static { WorldEditPrefixHandler.register("com.sk89q.worldedit"); getVersion(); + BundledBlockData.getInstance(); // Load block registry } private WorldEdit() { @@ -132,33 +138,33 @@ public class WorldEdit { } /** - * Get the block registry from which new {@link BaseBlock}s can be + * Get the block factory from which new {@link BaseBlock}s can be * constructed. * - * @return the block registry + * @return the block factory */ - public BlockRegistry getBlockRegistry() { - return blockRegistry; + public BlockFactory getBlockFactory() { + return blockFactory; } /** - * Get the mask registry from which new {@link com.sk89q.worldedit.function.mask.Mask}s + * Get the mask factory from which new {@link com.sk89q.worldedit.function.mask.Mask}s * can be constructed. * - * @return the mask registry + * @return the mask factory */ - public MaskRegistry getMaskRegistry() { - return maskRegistry; + public MaskFactory getMaskFactory() { + return maskFactory; } /** - * Get the pattern registry from which new {@link com.sk89q.worldedit.function.pattern.Pattern}s + * Get the pattern factory from which new {@link com.sk89q.worldedit.function.pattern.Pattern}s * can be constructed. * - * @return the pattern registry + * @return the pattern factory */ - public PatternRegistry getPatternRegistry() { - return patternRegistry; + public PatternFactory getPatternFactory() { + return patternFactory; } /** @@ -211,7 +217,7 @@ public class WorldEdit { } /** - * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + * @deprecated Use {@link #getBlockFactory()} and {@link BlockFactory#parseFromInput(String, ParserContext)} */ @SuppressWarnings("deprecation") @Deprecated @@ -220,7 +226,7 @@ public class WorldEdit { } /** - * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + * @deprecated Use {@link #getBlockFactory()} and {@link BlockFactory#parseFromInput(String, ParserContext)} */ @SuppressWarnings("deprecation") @Deprecated @@ -231,11 +237,11 @@ public class WorldEdit { context.setSession(getSession(player)); context.setRestricted(!allAllowed); context.setPreferringWildcard(allowNoData); - return getBlockRegistry().parseFromInput(arg, context); + return getBlockFactory().parseFromInput(arg, context); } /** - * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + * @deprecated Use {@link #getBlockFactory()} and {@link BlockFactory#parseFromInput(String, ParserContext)} */ @SuppressWarnings("deprecation") @Deprecated @@ -244,7 +250,7 @@ public class WorldEdit { } /** - * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromListInput(String, ParserContext)} + * @deprecated Use {@link #getBlockFactory()} and {@link BlockFactory#parseFromListInput(String, ParserContext)} */ @Deprecated @SuppressWarnings("deprecation") @@ -258,7 +264,7 @@ public class WorldEdit { } /** - * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + * @deprecated Use {@link #getBlockFactory()} and {@link BlockFactory#parseFromInput(String, ParserContext)} */ @Deprecated @SuppressWarnings("deprecation") @@ -267,7 +273,7 @@ public class WorldEdit { } /** - * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromListInput(String, ParserContext)} + * @deprecated Use {@link #getBlockFactory()} and {@link BlockFactory#parseFromListInput(String, ParserContext)} */ @Deprecated @SuppressWarnings("deprecation") @@ -276,7 +282,7 @@ public class WorldEdit { } /** - * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromListInput(String, ParserContext)} + * @deprecated Use {@link #getBlockFactory()} and {@link BlockFactory#parseFromListInput(String, ParserContext)} */ @Deprecated @SuppressWarnings("deprecation") @@ -290,7 +296,7 @@ public class WorldEdit { } /** - * @deprecated Use {@link #getPatternRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + * @deprecated Use {@link #getPatternFactory()} and {@link BlockFactory#parseFromInput(String, ParserContext)} */ @Deprecated @SuppressWarnings("deprecation") @@ -299,11 +305,11 @@ public class WorldEdit { context.setActor(player); context.setWorld(player.getWorld()); context.setSession(getSession(player)); - return Patterns.wrap(getPatternRegistry().parseFromInput(input, context)); + return Patterns.wrap(getPatternFactory().parseFromInput(input, context)); } /** - * @deprecated Use {@link #getMaskRegistry()} ()} and {@link MaskRegistry#parseFromInput(String, ParserContext)} + * @deprecated Use {@link #getMaskFactory()} ()} and {@link MaskFactory#parseFromInput(String, ParserContext)} */ @Deprecated @SuppressWarnings("deprecation") @@ -312,7 +318,7 @@ public class WorldEdit { context.setActor(player); context.setWorld(player.getWorld()); context.setSession(session); - return Masks.wrap(getMaskRegistry().parseFromInput(input, context)); + return Masks.wrap(getMaskFactory().parseFromInput(input, context)); } /** diff --git a/src/main/java/com/sk89q/worldedit/WorldVector.java b/src/main/java/com/sk89q/worldedit/WorldVector.java index 5e3d12b2e..d03137a69 100644 --- a/src/main/java/com/sk89q/worldedit/WorldVector.java +++ b/src/main/java/com/sk89q/worldedit/WorldVector.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit; import com.sk89q.worldedit.internal.LocalWorldAdapter; +import com.sk89q.worldedit.world.World; /** * @deprecated Use {@link com.sk89q.worldedit.util.Location} wherever possible @@ -97,7 +98,7 @@ public class WorldVector extends Vector { * @param location the location */ public WorldVector(com.sk89q.worldedit.util.Location location) { - this(LocalWorldAdapter.adapt(location.getWorld()), location.getX(), location.getY(), location.getZ()); + this(LocalWorldAdapter.adapt((World) location.getExtent()), location.getX(), location.getY(), location.getZ()); } /** diff --git a/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java b/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java index 497839125..d89ff8e03 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java @@ -100,17 +100,27 @@ public class BaseBlock extends Block implements TileEntityBlock { * @see #setData(int) * @see #setNbtData(CompoundTag) */ - public BaseBlock(int id, int data, CompoundTag nbtData) throws DataException { + public BaseBlock(int id, int data, CompoundTag nbtData) { setId(id); setData(data); setNbtData(nbtData); } + /** + * Create a clone of another block. + * + * @param other the other block + */ + public BaseBlock(BaseBlock other) { + this(other.getId(), other.getData(), other.getNbtData()); + } + /** * Get the ID of the block. * * @return ID (between 0 and {@link #MAX_ID}) */ + @Override public int getId() { return id; } @@ -138,6 +148,7 @@ public class BaseBlock extends Block implements TileEntityBlock { * * @param id block id (between 0 and {@link #MAX_ID}). */ + @Override public void setId(int id) { internalSetId(id); } @@ -147,6 +158,7 @@ public class BaseBlock extends Block implements TileEntityBlock { * * @return data value (0-15) */ + @Override public int getData() { return data; } @@ -175,6 +187,7 @@ public class BaseBlock extends Block implements TileEntityBlock { * * @param data block data value (between 0 and {@link #MAX_DATA}). */ + @Override public void setData(int data) { internalSetData(data); } @@ -187,6 +200,7 @@ public class BaseBlock extends Block implements TileEntityBlock { * @see #setId(int) * @see #setData(int) */ + @Override public void setIdAndData(int id, int data) { setId(id); setData(data); @@ -198,6 +212,7 @@ public class BaseBlock extends Block implements TileEntityBlock { * * @return true if the data value is -1 */ + @Override public boolean hasWildcardData() { return getData() == -1; } @@ -227,7 +242,7 @@ public class BaseBlock extends Block implements TileEntityBlock { } @Override - public void setNbtData(CompoundTag nbtData) throws DataException { + public void setNbtData(CompoundTag nbtData) { this.nbtData = nbtData; } diff --git a/src/main/java/com/sk89q/worldedit/blocks/BlockMaterial.java b/src/main/java/com/sk89q/worldedit/blocks/BlockMaterial.java new file mode 100644 index 000000000..09c9f8716 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/blocks/BlockMaterial.java @@ -0,0 +1,71 @@ +/* + * 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; + +/** + * Describes the material for a block. + */ +public interface BlockMaterial { + + boolean isRenderedAsNormalBlock(); + + boolean isFullCube(); + + boolean isOpaque(); + + boolean isPowerSource(); + + boolean isLiquid(); + + boolean isSolid(); + + float getHardness(); + + float getResistance(); + + float getSlipperiness(); + + boolean isGrassBlocking(); + + float getAmbientOcclusionLightValue(); + + int getLightOpacity(); + + int getLightValue(); + + boolean isFragileWhenPushed(); + + boolean isUnpushable(); + + boolean isAdventureModeExempt(); + + boolean isTicksRandomly(); + + boolean isUsingNeighborLight(); + + boolean isMovementBlocker(); + + boolean isBurnable(); + + boolean isToolRequired(); + + boolean isReplacedDuringPlacement(); + +} diff --git a/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index a08f70ab3..953168c3b 100644 --- a/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -23,15 +23,29 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.function.FlatRegionFunction; +import com.sk89q.worldedit.function.FlatRegionMaskingFilter; +import com.sk89q.worldedit.function.biome.BiomeReplace; import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.masks.BiomeTypeMask; -import com.sk89q.worldedit.masks.InvertedMask; +import com.sk89q.worldedit.function.mask.Mask2D; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.Regions; +import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.BiomeData; +import com.sk89q.worldedit.world.registry.BiomeRegistry; import java.util.HashSet; import java.util.List; @@ -64,7 +78,7 @@ public class BiomeCommands { max = 1 ) @CommandPermissions("worldedit.biome.list") - public void biomeList(Actor actor, CommandContext args) throws WorldEditException { + public void biomeList(Player player, CommandContext args) throws WorldEditException { int page; int offset; int count = 0; @@ -76,16 +90,22 @@ public class BiomeCommands { offset = (page - 1) * 19; } - List biomes = worldEdit.getServer().getBiomes().all(); + BiomeRegistry biomeRegistry = player.getWorld().getWorldData().getBiomeRegistry(); + List biomes = biomeRegistry.getBiomes(); int totalPages = biomes.size() / 19 + 1; - actor.print("Available Biomes (page " + page + "/" + totalPages + ") :"); - for (BiomeType biome : biomes) { + player.print("Available Biomes (page " + page + "/" + totalPages + ") :"); + for (BaseBiome biome : biomes) { if (offset > 0) { offset--; } else { - actor.print(" " + biome.getName()); - if (++count == 19) { - break; + BiomeData data = biomeRegistry.getData(biome); + if (data != null) { + player.print(" " + data.getName()); + if (++count == 19) { + break; + } + } else { + player.print(" "); } } } @@ -103,7 +123,11 @@ public class BiomeCommands { max = 0 ) @CommandPermissions("worldedit.biome.info") - public void biomeInfo(CommandContext args, Player player, LocalSession session) throws WorldEditException { + public void biomeInfo(Player player, LocalSession session, CommandContext args) throws WorldEditException { + BiomeRegistry biomeRegistry = player.getWorld().getWorldData().getBiomeRegistry(); + Set biomes = new HashSet(); + String qualifier; + if (args.hasFlag('t')) { Vector blockPosition = player.getBlockTrace(300); if (blockPosition == null) { @@ -111,15 +135,18 @@ public class BiomeCommands { return; } - BiomeType biome = player.getWorld().getBiome(blockPosition.toVector2D()); - player.print("Biome: " + biome.getName()); + BaseBiome biome = player.getWorld().getBiome(blockPosition.toVector2D()); + biomes.add(biome); + + qualifier = "at line of sight point"; } else if (args.hasFlag('p')) { - BiomeType biome = player.getWorld().getBiome(player.getPosition().toVector2D()); - player.print("Biome: " + biome.getName()); + BaseBiome biome = player.getWorld().getBiome(player.getPosition().toVector2D()); + biomes.add(biome); + + qualifier = "at your position"; } else { World world = player.getWorld(); Region region = session.getSelection(world); - Set biomes = new HashSet(); if (region instanceof FlatRegion) { for (Vector2D pt : ((FlatRegion) region).asFlatRegion()) { @@ -131,9 +158,16 @@ public class BiomeCommands { } } - player.print("Biomes:"); - for (BiomeType biome : biomes) { - player.print(" " + biome.getName()); + qualifier = "in your selection"; + } + + player.print(biomes.size() != 1 ? "Biomes " + qualifier + ":" : "Biome " + qualifier + ":"); + for (BaseBiome biome : biomes) { + BiomeData data = biomeRegistry.getData(biome); + if (data != null) { + player.print(" " + data.getName()); + } else { + player.print(" "); } } } @@ -145,65 +179,31 @@ public class BiomeCommands { desc = "Sets the biome of the player's current block or region.", help = "Set the biome of the region.\n" + - "By default use all the blocks contained in your selection.\n" + - "-p use the block you are currently in", - min = 1, - max = 1 + "By default use all the blocks contained in your selection.\n" + + "-p use the block you are currently in" ) @Logging(REGION) @CommandPermissions("worldedit.biome.set") - public void setBiome(CommandContext args, Player player, LocalSession session, EditSession editSession) throws WorldEditException { - final BiomeType target = worldEdit.getServer().getBiomes().get(args.getString(0)); - if (target == null) { - player.printError("Biome '" + args.getString(0) + "' does not exist!"); - return; - } - + public void setBiome(Player player, LocalSession session, EditSession editSession, BaseBiome target, @Switch('p') boolean atPosition) throws WorldEditException { + World world = player.getWorld(); + Region region; Mask mask = editSession.getMask(); - BiomeTypeMask biomeMask = null; - boolean inverted = false; - if (mask instanceof BiomeTypeMask) { - biomeMask = (BiomeTypeMask) mask; - } else if (mask instanceof InvertedMask && ((InvertedMask) mask).getInvertedMask() instanceof BiomeTypeMask) { - inverted = true; - biomeMask = (BiomeTypeMask) ((InvertedMask) mask).getInvertedMask(); - } + Mask2D mask2d = mask != null ? mask.toMask2D() : null; - if (args.hasFlag('p')) { - Vector2D pos = player.getPosition().toVector2D(); - if (biomeMask == null || (biomeMask.matches2D(editSession, pos) ^ inverted)) { - player.getWorld().setBiome(pos, target); - player.print("Biome changed to " + target.getName() + " at your current location."); - } else { - player.print("Your global mask doesn't match this biome. Type //gmask to disable it."); - } + if (atPosition) { + region = new CuboidRegion(player.getPosition(), player.getPosition()); } else { - int affected = 0; - World world = player.getWorld(); - Region region = session.getSelection(world); - - if (region instanceof FlatRegion) { - for (Vector2D pt : ((FlatRegion) region).asFlatRegion()) { - if (biomeMask == null || (biomeMask.matches2D(editSession, pt) ^ inverted)) { - world.setBiome(pt, target); - ++affected; - } - } - } else { - HashSet alreadyVisited = new HashSet(); - for (Vector pt : region) { - if (!alreadyVisited.contains((long)pt.getBlockX() << 32 | pt.getBlockZ())) { - alreadyVisited.add(((long)pt.getBlockX() << 32 | pt.getBlockZ())); - if (biomeMask == null || (biomeMask.matches(editSession, pt) ^ inverted)) { - world.setBiome(pt.toVector2D(), target); - ++affected; - } - } - } - } - - player.print("Biome changed to " + target.getName() + ". " + affected + " columns affected."); + region = session.getSelection(world); } + + FlatRegionFunction replace = new BiomeReplace(editSession, target); + if (mask2d != null) { + replace = new FlatRegionMaskingFilter(mask2d, replace); + } + FlatRegionVisitor visitor = new FlatRegionVisitor(Regions.asFlatRegion(region), replace); + Operations.completeLegacy(visitor); + + player.print("Biomes were changed in " + visitor.getAffected() + " columns. You may have to rejoin your game (or close and reopen your world) to see a change."); } } diff --git a/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index f954e26d8..1ce10523e 100644 --- a/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -22,17 +22,30 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.LocalWorld.KillFlags; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.command.UtilityCommands.FlagContainer; import com.sk89q.worldedit.command.tool.BrushTool; -import com.sk89q.worldedit.command.tool.brush.*; +import com.sk89q.worldedit.command.tool.brush.ButcherBrush; +import com.sk89q.worldedit.command.tool.brush.ClipboardBrush; +import com.sk89q.worldedit.command.tool.brush.CylinderBrush; +import com.sk89q.worldedit.command.tool.brush.GravityBrush; +import com.sk89q.worldedit.command.tool.brush.HollowCylinderBrush; +import com.sk89q.worldedit.command.tool.brush.HollowSphereBrush; +import com.sk89q.worldedit.command.tool.brush.SmoothBrush; +import com.sk89q.worldedit.command.tool.brush.SphereBrush; +import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; @@ -117,32 +130,27 @@ public class BrushCommands { @Command( aliases = { "clipboard", "copy" }, usage = "", - flags = "a", desc = "Choose the clipboard brush", help = "Chooses the clipboard brush.\n" + - "The -a flag makes it not paste air.", - min = 0, - max = 0 + "The -a flag makes it not paste air.\n" + + "Without the -p flag, the paste will appear centered at the target location. " + + "With the flag, then the paste will appear relative to where you had " + + "stood relative to the copied area when you copied it." ) @CommandPermissions("worldedit.brush.clipboard") - public void clipboardBrush(Player player, LocalSession session, EditSession editSession, @Switch('a') boolean ignoreAir) throws WorldEditException { + public void clipboardBrush(Player player, LocalSession session, EditSession editSession, @Switch('a') boolean ignoreAir, @Switch('p') boolean usingOrigin) throws WorldEditException { + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); - CuboidClipboard clipboard = session.getClipboard(); - - if (clipboard == null) { - player.printError("Copy something first."); - return; - } - - Vector size = clipboard.getSize(); + Vector size = clipboard.getDimensions(); worldEdit.checkMaxBrushRadius(size.getBlockX()); worldEdit.checkMaxBrushRadius(size.getBlockY()); worldEdit.checkMaxBrushRadius(size.getBlockZ()); BrushTool tool = session.getBrushTool(player.getItemInHand()); - tool.setBrush(new ClipboardBrush(clipboard, ignoreAir), "worldedit.brush.clipboard"); + tool.setBrush(new ClipboardBrush(holder, ignoreAir, usingOrigin), "worldedit.brush.clipboard"); player.print("Clipboard brush shape equipped."); } @@ -220,13 +228,21 @@ public class BrushCommands { @Command( aliases = { "butcher", "kill" }, - usage = "[radius] [command flags]", + usage = "[radius]", + flags = "plangbtf", desc = "Butcher brush", help = "Kills nearby mobs within the specified radius.\n" + - "Any number of 'flags' that the //butcher command uses\n" + - "may be specified as an argument", + "Flags:" + + " -p also kills pets.\n" + + " -n also kills NPCs.\n" + + " -g also kills Golems.\n" + + " -a also kills animals.\n" + + " -b also kills ambient mobs.\n" + + " -t also kills mobs with name tags.\n" + + " -f compounds all previous flags.\n" + + " -l currently does nothing.", min = 0, - max = 2 + max = 1 ) @CommandPermissions("worldedit.brush.butcher") public void butcherBrush(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { @@ -245,24 +261,13 @@ public class BrushCommands { return; } - FlagContainer flags = new FlagContainer(player); - if (args.argsLength() == 2) { - String flagString = args.getString(1); - // straight from the command, using contains instead of hasflag - flags.or(KillFlags.FRIENDLY , flagString.contains("f")); // No permission check here. Flags will instead be filtered by the subsequent calls. - flags.or(KillFlags.PETS , flagString.contains("p"), "worldedit.butcher.pets"); - flags.or(KillFlags.NPCS , flagString.contains("n"), "worldedit.butcher.npcs"); - flags.or(KillFlags.GOLEMS , flagString.contains("g"), "worldedit.butcher.golems"); - flags.or(KillFlags.ANIMALS , flagString.contains("a"), "worldedit.butcher.animals"); - flags.or(KillFlags.AMBIENT , flagString.contains("b"), "worldedit.butcher.ambient"); - flags.or(KillFlags.TAGGED , flagString.contains("t"), "worldedit.butcher.tagged"); - flags.or(KillFlags.WITH_LIGHTNING, flagString.contains("l"), "worldedit.butcher.lightning"); - } + CreatureButcher flags = new CreatureButcher(player); + flags.fromCommand(args); + BrushTool tool = session.getBrushTool(player.getItemInHand()); tool.setSize(radius); - tool.setBrush(new ButcherBrush(flags.flags), "worldedit.brush.butcher"); + tool.setBrush(new ButcherBrush(flags), "worldedit.brush.butcher"); - player.print(String.format("Butcher brush equipped (%.0f).", - radius)); + player.print(String.format("Butcher brush equipped (%.0f).", radius)); } } diff --git a/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index ffc765095..a5980cdc2 100644 --- a/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -20,19 +20,32 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.block.BlockReplace; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; -import com.sk89q.worldedit.world.World; import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; @@ -57,87 +70,63 @@ public class ClipboardCommands { @Command( aliases = { "/copy" }, - flags = "e", + flags = "em", desc = "Copy the selection to the clipboard", help = "Copy the selection to the clipboard\n" + "Flags:\n" + " -e controls whether entities are copied\n" + + " -m sets a source mask so that excluded blocks become air\n" + "WARNING: Pasting entities cannot yet be undone!", min = 0, max = 0 ) @CommandPermissions("worldedit.clipboard.copy") - public void copy(Player player, LocalSession session, EditSession editSession, @Switch('e') boolean copyEntities) throws WorldEditException { - Region region = session.getSelection(player.getWorld()); - Vector min = region.getMinimumPoint(); - Vector max = region.getMaximumPoint(); - Vector pos = session.getPlacementPosition(player); + public void copy(Player player, LocalSession session, EditSession editSession, + @Selection Region region, @Switch('e') boolean copyEntities, + @Switch('m') Mask mask) throws WorldEditException { - CuboidClipboard clipboard = new CuboidClipboard( - max.subtract(min).add(Vector.ONE), - min, min.subtract(pos)); - - if (region instanceof CuboidRegion) { - clipboard.copy(editSession); - } else { - clipboard.copy(editSession, region); + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + clipboard.setOrigin(session.getPlacementPosition(player)); + ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); + if (mask != null) { + copy.setSourceMask(mask); } + Operations.completeLegacy(copy); + session.setClipboard(new ClipboardHolder(clipboard, editSession.getWorld().getWorldData())); - if (copyEntities) { - for (LocalEntity entity : player.getWorld().getEntities(region)) { - clipboard.storeEntity(entity); - } - } - - session.setClipboard(clipboard); - - player.print("Block(s) copied."); + player.print(region.getArea() + " block(s) were copied."); } @Command( aliases = { "/cut" }, + flags = "em", usage = "[leave-id]", desc = "Cut the selection to the clipboard", help = "Copy the selection to the clipboard\n" + "Flags:\n" + " -e controls whether entities are copied\n" + + " -m sets a source mask so that excluded blocks become air\n" + "WARNING: Cutting and pasting entities cannot yet be undone!", - flags = "e", min = 0, max = 1 ) @CommandPermissions("worldedit.clipboard.cut") @Logging(REGION) - public void cut(Player player, LocalSession session, EditSession editSession, @Optional("air") BaseBlock block, @Switch('e') boolean copyEntities) throws WorldEditException { - World world = player.getWorld(); + public void cut(Player player, LocalSession session, EditSession editSession, + @Selection Region region, @Optional("air") Pattern leavePattern, @Switch('e') boolean copyEntities, + @Switch('m') Mask mask) throws WorldEditException { - Region region = session.getSelection(world); - Vector min = region.getMinimumPoint(); - Vector max = region.getMaximumPoint(); - Vector pos = session.getPlacementPosition(player); - - CuboidClipboard clipboard = new CuboidClipboard( - max.subtract(min).add(Vector.ONE), - min, min.subtract(pos)); - - if (region instanceof CuboidRegion) { - clipboard.copy(editSession); - } else { - clipboard.copy(editSession, region); + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + clipboard.setOrigin(session.getPlacementPosition(player)); + ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); + copy.setSourceFunction(new BlockReplace(editSession, leavePattern)); + if (mask != null) { + copy.setSourceMask(mask); } + Operations.completeLegacy(copy); + session.setClipboard(new ClipboardHolder(clipboard, editSession.getWorld().getWorldData())); - if (copyEntities) { - LocalEntity[] entities = world.getEntities(region); - for (LocalEntity entity : entities) { - clipboard.storeEntity(entity); - } - world.killEntities(entities); - } - - session.setClipboard(clipboard); - - editSession.setBlocks(region, block); - player.print("Block(s) cut."); + player.print(region.getArea() + " block(s) were copied."); } @Command( @@ -156,79 +145,76 @@ public class ClipboardCommands { ) @CommandPermissions("worldedit.clipboard.paste") @Logging(PLACEMENT) - public void paste(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public void paste(Player player, LocalSession session, EditSession editSession, + @Switch('a') boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, + @Switch('s') boolean selectPasted) throws WorldEditException { - boolean atOrigin = args.hasFlag('o'); - boolean pasteNoAir = args.hasFlag('a'); + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + Region region = clipboard.getRegion(); - CuboidClipboard clipboard = session.getClipboard(); + Vector to = atOrigin ? clipboard.getOrigin() : session.getPlacementPosition(player); + Operation operation = holder + .createPaste(editSession, editSession.getWorld().getWorldData()) + .to(to) + .ignoreAirBlocks(ignoreAirBlocks) + .build(); + Operations.completeLegacy(operation); - Vector pos = atOrigin ? session.getClipboard().getOrigin() - : session.getPlacementPosition(player); - - if (atOrigin) { - clipboard.place(editSession, pos, pasteNoAir); - clipboard.pasteEntities(pos); - player.findFreePosition(); - player.print("Pasted to copy origin. Undo with //undo"); - } else { - clipboard.paste(editSession, pos, pasteNoAir, true); - player.findFreePosition(); - player.print("Pasted relative to you. Undo with //undo"); + if (selectPasted) { + Vector max = to.add(region.getMaximumPoint().subtract(region.getMinimumPoint())); + RegionSelector selector = new CuboidRegionSelector(player.getWorld(), to, max); + session.setRegionSelector(player.getWorld(), selector); + selector.learnChanges(); + selector.explainRegionAdjust(player, session); } - if (args.hasFlag('s')) { - World world = player.getWorld(); - Vector pos2 = pos.add(clipboard.getSize().subtract(1, 1, 1)); - if (!atOrigin) { - pos2 = pos2.add(clipboard.getOffset()); - pos = pos.add(clipboard.getOffset()); - } - session.setRegionSelector(world, new CuboidRegionSelector(world, pos, pos2)); - session.getRegionSelector(world).learnChanges(); - session.getRegionSelector(world).explainRegionAdjust(player, session); - } + player.print("The clipboard has been pasted at " + to); } @Command( aliases = { "/rotate" }, - usage = "", + usage = " [] []", desc = "Rotate the contents of the clipboard", - min = 1, - max = 1 + help = "Non-destructively rotate the contents of the clipboard.\n" + + "Angles are provided in degrees and a positive angle will result in a clockwise rotation. " + + "Multiple rotations can be stacked. Interpolation is not performed so angles should be a multiple of 90 degrees.\n" ) @CommandPermissions("worldedit.clipboard.rotate") - public void rotate(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - int angle = args.getInteger(0); - - if (angle % 90 == 0) { - CuboidClipboard clipboard = session.getClipboard(); - clipboard.rotate2D(angle); - player.print("Clipboard rotated by " + angle + " degrees."); - } else { - player.printError("Angles must be divisible by 90 degrees."); + public void rotate(Player player, LocalSession session, Double yRotate, @Optional Double xRotate, @Optional Double zRotate) throws WorldEditException { + if ((yRotate != null && Math.abs(yRotate % 90) > 0.001) || + xRotate != null && Math.abs(xRotate % 90) > 0.001 || + zRotate != null && Math.abs(zRotate % 90) > 0.001) { + player.printDebug("Note: Interpolation is not yet supported, so angles that are multiples of 90 is recommended."); } + + ClipboardHolder holder = session.getClipboard(); + AffineTransform transform = new AffineTransform(); + transform = transform.rotateY(-(yRotate != null ? yRotate : 0)); + transform = transform.rotateX(-(xRotate != null ? xRotate : 0)); + transform = transform.rotateZ(-(zRotate != null ? zRotate : 0)); + holder.setTransform(holder.getTransform().combine(transform)); + player.print("The clipboard copy has been rotated."); } @Command( aliases = { "/flip" }, - usage = "[dir]", - flags = "p", - desc = "Flip the contents of the clipboard.", + usage = "[]", + desc = "Flip the contents of the clipboard", help = - "Flips the contents of the clipboard.\n" + - "The -p flag flips the selection around the player,\n" + - "instead of the selections center.", + "Flips the contents of the clipboard across the point from which the copy was made.\n", min = 0, max = 1 ) @CommandPermissions("worldedit.clipboard.flip") - public void flip(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - CuboidClipboard.FlipDirection dir = worldEdit.getFlipDirection(player, args.argsLength() > 0 ? args.getString(0).toLowerCase() : "me"); - CuboidClipboard clipboard = session.getClipboard(); - clipboard.flip(dir, args.hasFlag('p')); - player.print("Clipboard flipped."); + public void flip(Player player, LocalSession session, EditSession editSession, + @Optional(Direction.AIM) @Direction Vector direction) throws WorldEditException { + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + AffineTransform transform = new AffineTransform(); + transform = transform.scale(direction.positive().multiply(-2).add(1, 1, 1)); + holder.setTransform(holder.getTransform().combine(transform)); + player.print("The clipboard copy has been flipped."); } @Command( diff --git a/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java b/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java new file mode 100644 index 000000000..70adc7803 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java @@ -0,0 +1,143 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.transform.BlockTransformExtent; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.math.transform.AffineTransform; +import com.sk89q.worldedit.math.transform.CombinedTransform; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.registry.WorldData; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper class to 'bake' a transform into a clipboard. + * + *

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

+ * + * @see Clipboard + * @see Transform + */ +class FlattenedClipboardTransform { + + private final Clipboard original; + private final Transform transform; + private final WorldData worldData; + + /** + * Create a new instance. + * + * @param original the original clipboard + * @param transform the transform + * @param worldData the world data instance + */ + private FlattenedClipboardTransform(Clipboard original, Transform transform, WorldData worldData) { + checkNotNull(original); + checkNotNull(transform); + checkNotNull(worldData); + this.original = original; + this.transform = transform; + this.worldData = worldData; + } + + /** + * Get the transformed region. + * + * @return the transformed region + */ + public Region getTransformedRegion() { + Region region = original.getRegion(); + Vector minimum = region.getMinimumPoint(); + Vector maximum = region.getMaximumPoint(); + + Transform transformAround = + new CombinedTransform( + new AffineTransform().translate(original.getOrigin().multiply(-1)), + transform, + new AffineTransform().translate(original.getOrigin())); + + Vector[] corners = new Vector[] { + minimum, + maximum, + minimum.setX(maximum.getX()), + minimum.setY(maximum.getY()), + minimum.setZ(maximum.getZ()), + maximum.setX(minimum.getX()), + maximum.setY(minimum.getY()), + maximum.setZ(minimum.getZ()) }; + + for (int i = 0; i < corners.length; i++) { + corners[i] = transformAround.apply(corners[i]); + } + + Vector newMinimum = corners[0]; + Vector newMaximum = corners[0]; + + for (int i = 1; i < corners.length; i++) { + newMinimum = Vector.getMinimum(newMinimum, corners[i]); + newMaximum = Vector.getMaximum(newMaximum, corners[i]); + } + + // After transformation, the points may not really sit on a block, + // so we should expand the region for edge cases + newMinimum = newMinimum.setX(Math.floor(newMinimum.getX())); + newMinimum = newMinimum.setY(Math.floor(newMinimum.getY())); + newMinimum = newMinimum.setZ(Math.floor(newMinimum.getZ())); + + newMaximum = newMaximum.setX(Math.ceil(newMaximum.getX())); + newMaximum = newMaximum.setY(Math.ceil(newMaximum.getY())); + newMaximum = newMaximum.setZ(Math.ceil(newMaximum.getZ())); + + return new CuboidRegion(newMinimum, newMaximum); + } + + /** + * Create an operation to copy from the original clipboard to the given extent. + * + * @param target the target + * @return the operation + */ + public Operation copyTo(Extent target) { + BlockTransformExtent extent = new BlockTransformExtent(original, transform, worldData.getBlockRegistry()); + ForwardExtentCopy copy = new ForwardExtentCopy(extent, original.getRegion(), original.getOrigin(), target, original.getOrigin()); + copy.setTransform(transform); + return copy; + } + + /** + * Create a new instance to bake the transform with. + * + * @param original the original clipboard + * @param transform the transform + * @param worldData the world data instance + * @return a builder + */ + public static FlattenedClipboardTransform transform(Clipboard original, Transform transform, WorldData worldData) { + return new FlattenedClipboardTransform(original, transform, worldData); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index ffce9f7ec..09abf3b99 100644 --- a/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -22,7 +22,11 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Patterns; @@ -35,6 +39,7 @@ import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.world.biome.BaseBiome; import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; @@ -332,7 +337,7 @@ public class GenerationCommands { @Logging(ALL) public void generateBiome(Player player, LocalSession session, EditSession editSession, @Selection Region region, - BiomeType target, + BaseBiome target, @Text String expression, @Switch('h') boolean hollow, @Switch('r') boolean useRawCoords, @@ -368,7 +373,7 @@ public class GenerationCommands { try { final int affected = editSession.makeBiomeShape(region, zero, unit, target, expression, hollow); player.findFreePosition(); - player.print("Biome changed to " + target.getName() + ". " + affected + " columns affected."); + player.print("" + affected + " columns affected."); } catch (ExpressionException e) { player.printError(e.getMessage()); } diff --git a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 2b0d25526..67e8ba20f 100644 --- a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -19,18 +19,42 @@ package com.sk89q.worldedit.command; -import com.sk89q.minecraft.util.commands.*; -import com.sk89q.worldedit.*; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.FilenameException; +import com.sk89q.worldedit.FilenameResolutionException; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.schematic.SchematicFormat; -import com.sk89q.worldedit.world.DataException; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.io.Closer; +import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.world.registry.WorldData; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; +import java.util.logging.Level; +import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; @@ -39,6 +63,7 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class SchematicCommands { + private static final Logger log = Logger.getLogger(SchematicCommands.class.getCanonicalName()); private final WorldEdit worldEdit; /** @@ -52,113 +77,94 @@ public class SchematicCommands { } @Command( - aliases = { "load", "l" }, - usage = "[format] ", - desc = "Load a file into your clipboard", - help = "Load a schematic file into your clipboard\n" + - "Format is a format from \"//schematic formats\"\n" + - "If the format is not provided, WorldEdit will\n" + - "attempt to automatically detect the format of the schematic", - flags = "f", - min = 1, - max = 2 + aliases = { "load" }, + usage = "[] ", + desc = "Load a schematic into your clipboard" ) - @CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load"}) // TODO: Remove 'clipboard' perm - public void load(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - + @Deprecated + @CommandPermissions({ "worldedit.clipboard.load", "worldedit.schematic.load" }) + public void load(Player player, LocalSession session, @Optional("schematic") String formatName, String filename) throws FilenameException { LocalConfiguration config = worldEdit.getConfiguration(); - String fileName; - String formatName; - if (args.argsLength() == 1) { - formatName = null; - fileName = args.getString(0); - } else { - formatName = args.getString(0); - fileName = args.getString(1); - } File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); - File f = worldEdit.getSafeOpenFile(player, dir, fileName, "schematic", "schematic"); + File f = worldEdit.getSafeOpenFile(player, dir, filename, "schematic", "schematic"); if (!f.exists()) { - player.printError("Schematic " + fileName + " does not exist!"); + player.printError("Schematic " + filename + " does not exist!"); return; } - SchematicFormat format = formatName == null ? null : SchematicFormat.getFormat(formatName); - if (format == null) { - format = SchematicFormat.getFormat(f); - } - + ClipboardFormat format = ClipboardFormat.findByAlias(formatName); if (format == null) { player.printError("Unknown schematic format: " + formatName); return; } - if (!format.isOfFormat(f) && !args.hasFlag('f')) { - player.printError(fileName + " is not of the " + format.getName() + " schematic format!"); - return; - } - + Closer closer = Closer.create(); try { String filePath = f.getCanonicalPath(); String dirPath = dir.getCanonicalPath(); if (!filePath.substring(0, dirPath.length()).equals(dirPath)) { - player.printError("Schematic could not read or it does not exist."); + player.printError("Clipboard file could not read or it does not exist."); } else { - session.setClipboard(format.load(f)); - WorldEdit.logger.info(player.getName() + " loaded " + filePath); - player.print(fileName + " loaded. Paste it with //paste"); + FileInputStream fis = closer.register(new FileInputStream(f)); + BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); + ClipboardReader reader = format.getReader(bis); + + WorldData worldData = player.getWorld().getWorldData(); + Clipboard clipboard = reader.read(player.getWorld().getWorldData()); + session.setClipboard(new ClipboardHolder(clipboard, worldData)); + + log.info(player.getName() + " loaded " + filePath); + player.print(filename + " loaded. Paste it with //paste"); } - } catch (DataException e) { - player.printError("Load error: " + e.getMessage()); } catch (IOException e) { player.printError("Schematic could not read or it does not exist: " + e.getMessage()); + log.log(Level.WARNING, "Failed to load a saved clipboard", e); + } finally { + try { + closer.close(); + } catch (IOException ignored) { + } } } @Command( - aliases = { "save", "s" }, - usage = "[format] ", - desc = "Save your clipboard to file", - help = "Save your clipboard to file\n" + - "Format is a format from \"//schematic formats\"\n", - min = 1, - max = 2 + aliases = { "save" }, + usage = "[] ", + desc = "Save a schematic into your clipboard" ) - @CommandPermissions({"worldedit.clipboard.save", "worldedit.schematic.save"}) // TODO: Remove 'clipboard' perm - public void save(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException, CommandException { - + @Deprecated + @CommandPermissions({ "worldedit.clipboard.save", "worldedit.schematic.save" }) + public void save(Player player, LocalSession session, @Optional("schematic") String formatName, String filename) throws CommandException, WorldEditException { LocalConfiguration config = worldEdit.getConfiguration(); - SchematicFormat format; - if (args.argsLength() == 1) { - if (SchematicFormat.getFormats().size() == 1) { - format = SchematicFormat.getFormats().iterator().next(); - } else { - player.printError("More than one schematic format is available. Please provide the desired format"); - return; - } - } else { - format = SchematicFormat.getFormat(args.getString(0)); - if (format == null) { - player.printError("Unknown schematic format: " + args.getString(0)); - return; - } - } - - String filename = args.getString(args.argsLength() - 1); File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); File f = worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic"); - if (!dir.exists()) { - if (!dir.mkdir()) { - player.printError("The storage folder could not be created."); - return; - } + ClipboardFormat format = ClipboardFormat.findByAlias(formatName); + if (format == null) { + player.printError("Unknown schematic format: " + formatName); + return; } + 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, holder.getWorldData()); + target = new BlockArrayClipboard(result.getTransformedRegion()); + target.setOrigin(clipboard.getOrigin()); + Operations.completeLegacy(result.copyTo(target)); + } else { + target = clipboard; + } + + Closer closer = Closer.create(); try { // Create parent directories File parent = f.getParentFile(); @@ -168,13 +174,20 @@ public class SchematicCommands { } } - format.save(session.getClipboard(), f); - WorldEdit.logger.info(player.getName() + " saved " + f.getCanonicalPath()); + FileOutputStream fos = closer.register(new FileOutputStream(f)); + BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos)); + ClipboardWriter writer = closer.register(format.getWriter(bos)); + writer.write(target, holder.getWorldData()); + log.info(player.getName() + " saved " + f.getCanonicalPath()); player.print(filename + " saved."); - } catch (DataException se) { - player.printError("Save error: " + se.getMessage()); } catch (IOException e) { player.printError("Schematic could not written: " + e.getMessage()); + log.log(Level.WARNING, "Failed to write a saved clipboard", e); + } finally { + try { + closer.close(); + } catch (IOException ignored) { + } } } @@ -215,13 +228,13 @@ public class SchematicCommands { ) @CommandPermissions("worldedit.schematic.formats") public void formats(Actor actor) throws WorldEditException { - actor.print("Available schematic formats (Name: Lookup names)"); + actor.print("Available clipboard formats (Name: Lookup names)"); StringBuilder builder; boolean first = true; - for (SchematicFormat format : SchematicFormat.getFormats()) { + for (ClipboardFormat format : ClipboardFormat.values()) { builder = new StringBuilder(); - builder.append(format.getName()).append(": "); - for (String lookupName : format.getLookupNames()) { + builder.append(format.name()).append(": "); + for (String lookupName : format.getAliases()) { if (!first) { builder.append(", "); } @@ -292,9 +305,8 @@ public class SchematicCommands { } build.append("\n\u00a79"); - SchematicFormat format = SchematicFormat.getFormat(file); - build.append(prefix).append(file.getName()) - .append(": ").append(format == null ? "Unknown" : format.getName()); + ClipboardFormat format = ClipboardFormat.findByFile(file); + build.append(prefix).append(file.getName()).append(": ").append(format == null ? "Unknown" : format.name()); } return build.toString(); } diff --git a/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index ba5144f9e..4d8d165dc 100644 --- a/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -21,9 +21,9 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.Vector; @@ -33,6 +33,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.RegionSelector; @@ -43,6 +44,7 @@ import com.sk89q.worldedit.regions.selector.EllipsoidRegionSelector; import com.sk89q.worldedit.regions.selector.ExtendingCuboidRegionSelector; import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector; import com.sk89q.worldedit.regions.selector.SphereRegionSelector; +import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.storage.ChunkStore; @@ -582,15 +584,16 @@ public class SelectionCommands { public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { if (args.hasFlag('c')) { - CuboidClipboard clipboard = session.getClipboard(); - Vector size = clipboard.getSize(); - Vector offset = clipboard.getOffset(); + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + Region region = clipboard.getRegion(); + Vector size = region.getMaximumPoint().subtract(region.getMinimumPoint()); + Vector origin = clipboard.getOrigin(); - player.print("Size: " + size); - player.print("Offset: " + offset); + player.print("Cuboid dimensions (max - min): " + size); + player.print("Offset: " + origin); player.print("Cuboid distance: " + size.distance(Vector.ONE)); - player.print("# of blocks: " - + (int) (size.getX() * size.getY() * size.getZ())); + player.print("# of blocks: " + (int) (size.getX() * size.getY() * size.getZ())); return; } @@ -608,8 +611,7 @@ public class SelectionCommands { } player.print("Size: " + size); - player.print("Cuboid distance: " + region.getMaximumPoint() - .distance(region.getMinimumPoint())); + player.print("Cuboid distance: " + region.getMaximumPoint().distance(region.getMinimumPoint())); player.print("# of blocks: " + region.getArea()); } @@ -653,7 +655,7 @@ public class SelectionCommands { max = 0 ) @CommandPermissions("worldedit.analysis.distr") - public void distr(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public void distr(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException, CommandException { int size; boolean useData = args.hasFlag('d'); @@ -661,13 +663,8 @@ public class SelectionCommands { List> distributionData = null; if (args.hasFlag('c')) { - CuboidClipboard clip = session.getClipboard(); - if (useData) { - distributionData = clip.getBlockDistributionWithData(); - } else { - distribution = clip.getBlockDistribution(); - } - size = clip.getHeight() * clip.getLength() * clip.getWidth(); + // TODO: Update for new clipboard + throw new CommandException("Needs to be re-written again"); } else { if (useData) { distributionData = editSession.getBlockDistributionWithData(session.getSelection(player.getWorld())); diff --git a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index d1e0852d8..5dd5cce8d 100644 --- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -26,30 +26,35 @@ import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.EntityType; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.LocalWorld.KillFlags; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.command.util.CreatureButcher; +import com.sk89q.worldedit.command.util.EntityRemover; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.patterns.SingleBlockPattern; import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.PrimaryAliasComparator; import com.sk89q.worldedit.util.command.binding.Text; -import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; import com.sk89q.worldedit.util.formatting.Style; import com.sk89q.worldedit.util.formatting.StyledFragment; @@ -374,7 +379,7 @@ public class UtilityCommands { " -b also kills ambient mobs.\n" + " -t also kills mobs with name tags.\n" + " -f compounds all previous flags.\n" + - " -l strikes lightning on each killed mob.", + " -l currently does nothing.", min = 0, max = 1 ) @@ -399,26 +404,37 @@ public class UtilityCommands { } } - FlagContainer flags = new FlagContainer(actor); - flags.or(KillFlags.FRIENDLY , args.hasFlag('f')); // No permission check here. Flags will instead be filtered by the subsequent calls. - flags.or(KillFlags.PETS , args.hasFlag('p'), "worldedit.butcher.pets"); - flags.or(KillFlags.NPCS , args.hasFlag('n'), "worldedit.butcher.npcs"); - flags.or(KillFlags.GOLEMS , args.hasFlag('g'), "worldedit.butcher.golems"); - flags.or(KillFlags.ANIMALS , args.hasFlag('a'), "worldedit.butcher.animals"); - flags.or(KillFlags.AMBIENT , args.hasFlag('b'), "worldedit.butcher.ambient"); - flags.or(KillFlags.TAGGED , args.hasFlag('t'), "worldedit.butcher.tagged"); - flags.or(KillFlags.WITH_LIGHTNING, args.hasFlag('l'), "worldedit.butcher.lightning"); - // If you add flags here, please add them to com.sk89q.worldedit.commands.BrushCommands.butcherBrush() as well + CreatureButcher flags = new CreatureButcher(actor); + flags.fromCommand(args); + + List visitors = new ArrayList(); + LocalSession session = null; + EditSession editSession = null; - int killed; if (player != null) { - LocalSession session = we.getSessionManager().get(player); - killed = player.getWorld().killMobs(session.getPlacementPosition(player), radius, flags.flags); - } else { - killed = 0; - for (World world : we.getServer().getWorlds()) { - killed += world.killMobs(new Vector(), radius, flags.flags); + session = we.getSessionManager().get(player); + Vector center = session.getPlacementPosition(player); + editSession = session.createEditSession(player); + List entities; + if (radius >= 0) { + CylinderRegion region = CylinderRegion.createRadius(editSession, center, radius); + entities = editSession.getEntities(region); + } else { + entities = editSession.getEntities(); } + visitors.add(new EntityVisitor(entities.iterator(), flags.createFunction(editSession.getWorld().getWorldData().getEntityRegistry()))); + } else { + Platform platform = we.getPlatformManager().queryCapability(Capability.WORLD_EDITING); + for (World world : platform.getWorlds()) { + List entities = world.getEntities(); + visitors.add(new EntityVisitor(entities.iterator(), flags.createFunction(world.getWorldData().getEntityRegistry()))); + } + } + + int killed = 0; + for (EntityVisitor visitor : visitors) { + Operations.completeLegacy(visitor); + killed += visitor.getAffected(); } if (radius < 0) { @@ -426,25 +442,10 @@ public class UtilityCommands { } else { actor.print("Killed " + killed + " mobs in a radius of " + radius + "."); } - } - public static class FlagContainer { - private final Actor player; - public int flags = 0; - public FlagContainer(Actor player) { - this.player = player; - } - - public void or(int flag, boolean on) { - if (on) flags |= flag; - } - - public void or(int flag, boolean on, String permission) { - or(flag, on); - - if ((flags & flag) != 0 && !player.hasPermission(permission)) { - flags &= ~flag; - } + if (editSession != null) { + session.remember(editSession); + editSession.flushQueue(); } } @@ -457,56 +458,55 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.remove") @Logging(PLACEMENT) - public void remove(Actor actor, @Optional Player player, @Optional LocalSession session, CommandContext args) throws WorldEditException { - + public void remove(Actor actor, CommandContext args) throws WorldEditException, CommandException { String typeStr = args.getString(0); int radius = args.getInteger(1); + Player player = actor instanceof Player ? (Player) actor : null; if (radius < -1) { actor.printError("Use -1 to remove all entities in loaded chunks"); return; } - EntityType type = null; + EntityRemover remover = new EntityRemover(); + remover.fromString(typeStr); - if (typeStr.matches("all")) { - type = EntityType.ALL; - } else if (typeStr.matches("projectiles?|arrows?")) { - type = EntityType.PROJECTILES; - } else if (typeStr.matches("items?") - || typeStr.matches("drops?")) { - type = EntityType.ITEMS; - } else if (typeStr.matches("falling(blocks?|sand|gravel)")) { - type = EntityType.FALLING_BLOCKS; - } else if (typeStr.matches("paintings?") - || typeStr.matches("art")) { - type = EntityType.PAINTINGS; - } else if (typeStr.matches("(item)frames?")) { - type = EntityType.ITEM_FRAMES; - } else if (typeStr.matches("boats?")) { - type = EntityType.BOATS; - } else if (typeStr.matches("minecarts?") - || typeStr.matches("carts?")) { - type = EntityType.MINECARTS; - } else if (typeStr.matches("tnt")) { - type = EntityType.TNT; - } else if (typeStr.matches("xp")) { - type = EntityType.XP_ORBS; + List visitors = new ArrayList(); + LocalSession session = null; + EditSession editSession = null; + + if (player != null) { + session = we.getSessionManager().get(player); + Vector center = session.getPlacementPosition(player); + editSession = session.createEditSession(player); + List entities; + if (radius >= 0) { + CylinderRegion region = CylinderRegion.createRadius(editSession, center, radius); + entities = editSession.getEntities(region); + } else { + entities = editSession.getEntities(); + } + visitors.add(new EntityVisitor(entities.iterator(), remover.createFunction(editSession.getWorld().getWorldData().getEntityRegistry()))); } else { - actor.printError("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); - return; + Platform platform = we.getPlatformManager().queryCapability(Capability.WORLD_EDITING); + for (World world : platform.getWorlds()) { + List entities = world.getEntities(); + visitors.add(new EntityVisitor(entities.iterator(), remover.createFunction(world.getWorldData().getEntityRegistry()))); + } } int removed = 0; - if (player != null) { - Vector origin = session.getPlacementPosition(player); - removed = player.getWorld().removeEntities(type, origin, radius); - } else { - for (World world : we.getServer().getWorlds()) { - removed += world.removeEntities(type, new Vector(), radius); - } + for (EntityVisitor visitor : visitors) { + Operations.completeLegacy(visitor); + removed += visitor.getAffected(); + } + + actor.print("Marked " + (removed != 1 ? "entities" : "entity") + " for removal."); + + if (editSession != null) { + session.remember(editSession); + editSession.flushQueue(); } - player.print("Marked " + removed + " entit(ies) for removal."); } @Command( diff --git a/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java index fffa9a0be..04a25443a 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.world.World; /** * A super pickaxe mode that will remove blocks in an area. @@ -48,7 +49,7 @@ public class AreaPickaxe implements BlockTool { int ox = clicked.getBlockX(); int oy = clicked.getBlockY(); int oz = clicked.getBlockZ(); - int initialType = clicked.getWorld().getBlockType(clicked.toVector()); + int initialType = ((World) clicked.getExtent()).getBlockType(clicked.toVector()); if (initialType == 0) { return true; @@ -70,7 +71,7 @@ public class AreaPickaxe implements BlockTool { continue; } - clicked.getWorld().queueBlockBreakEffect(server, pos, initialType, clicked.toVector().distanceSq(pos)); + ((World) clicked.getExtent()).queueBlockBreakEffect(server, pos, initialType, clicked.toVector().distanceSq(pos)); editSession.setBlock(pos, air); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java b/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java index 252823561..41bff5f2a 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java @@ -41,7 +41,7 @@ public class BlockDataCyler implements DoubleActionBlockTool { private boolean handleCycle(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked, boolean forward) { - World world = clicked.getWorld(); + World world = (World) clicked.getExtent(); int type = world.getBlockType(clicked.toVector()); int data = world.getBlockData(clicked.toVector()); diff --git a/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java b/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java index 19abb8ea3..d1af045bb 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java @@ -48,7 +48,7 @@ public class BlockReplacer implements DoubleActionBlockTool { public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { BlockBag bag = session.getBlockBag(player); - World world = clicked.getWorld(); + World world = (World) clicked.getExtent(); EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1, bag, player); try { @@ -67,7 +67,7 @@ BlockBag bag = session.getBlockBag(player); @Override public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { - World world = clicked.getWorld(); + World world = (World) clicked.getExtent(); EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1, player); targetBlock = (editSession).getBlock(clicked.toVector()); BlockType type = BlockType.fromID(targetBlock.getType()); diff --git a/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java b/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java index df92fb5f0..14785c392 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java @@ -53,7 +53,7 @@ public class FloatingTreeRemover implements BlockTool { public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { - final World world = clicked.getWorld(); + final World world = (World) clicked.getExtent(); switch (world.getBlockType(clicked.toVector())) { case BlockID.LOG: diff --git a/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java b/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java index 4ccead8cf..3d484b18e 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java @@ -51,7 +51,7 @@ public class FloodFillTool implements BlockTool { @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { - World world = clicked.getWorld(); + World world = (World) clicked.getExtent(); int initialType = world.getBlockType(clicked.toVector()); diff --git a/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java b/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java index a68031f74..6aa5262a0 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java @@ -42,7 +42,7 @@ public class QueryTool implements BlockTool { @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { - World world = clicked.getWorld(); + World world = (World) clicked.getExtent(); EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, 0, player); BaseBlock block = (editSession).rawGetBlock(clicked.toVector()); BlockType type = BlockType.fromID(block.getType()); diff --git a/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java index e551b9f2e..7a8589b4c 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java @@ -50,7 +50,7 @@ public class RecursivePickaxe implements BlockTool { @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { - World world = clicked.getWorld(); + World world = (World) clicked.getExtent(); int initialType = world.getBlockType(clicked.toVector()); diff --git a/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java index 202c04c95..246a6f633 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java @@ -42,7 +42,7 @@ public class SinglePickaxe implements BlockTool { @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { - World world = clicked.getWorld(); + World world = (World) clicked.getExtent(); final int blockType = world.getBlockType(clicked.toVector()); if (blockType == BlockID.BEDROCK && !player.canDestroyBedrock()) { diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java index 1a8e4f9cd..637be6d2f 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java @@ -22,19 +22,28 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.command.util.CreatureButcher; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.visitor.EntityVisitor; +import com.sk89q.worldedit.regions.CylinderRegion; + +import java.util.List; public class ButcherBrush implements Brush { - private int flags; + private CreatureButcher flags; - public ButcherBrush(int flags) { + public ButcherBrush(CreatureButcher flags) { this.flags = flags; } @Override public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { - editSession.getWorld().killMobs(pos, size, flags); + CylinderRegion region = CylinderRegion.createRadius(editSession, pos, size); + List entities = editSession.getEntities(region); + Operations.completeLegacy(new EntityVisitor(entities.iterator(), flags.createFunction(editSession.getWorld().getWorldData().getEntityRegistry()))); } } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java index ef5fffbcc..7f772239d 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java @@ -19,24 +19,41 @@ package com.sk89q.worldedit.command.tool.brush; -import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; public class ClipboardBrush implements Brush { - private CuboidClipboard clipboard; - private boolean noAir; + private ClipboardHolder holder; + private boolean ignoreAirBlocks; + private boolean usingOrigin; - public ClipboardBrush(CuboidClipboard clipboard, boolean noAir) { - this.clipboard = clipboard; - this.noAir = noAir; + public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin) { + this.holder = holder; + this.ignoreAirBlocks = ignoreAirBlocks; + this.usingOrigin = usingOrigin; } + @Override public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { - clipboard.place(editSession, pos.subtract(clipboard.getSize().divide(2)), noAir); + Clipboard clipboard = holder.getClipboard(); + Region region = clipboard.getRegion(); + Vector centerOffset = region.getCenter().subtract(clipboard.getOrigin()); + + Operation operation = holder + .createPaste(editSession, editSession.getWorld().getWorldData()) + .to(usingOrigin ? pos : pos.subtract(centerOffset)) + .ignoreAirBlocks(ignoreAirBlocks) + .build(); + + Operations.completeLegacy(operation); } } diff --git a/src/main/java/com/sk89q/worldedit/command/util/CreatureButcher.java b/src/main/java/com/sk89q/worldedit/command/util/CreatureButcher.java new file mode 100644 index 000000000..822389bbe --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/command/util/CreatureButcher.java @@ -0,0 +1,135 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.metadata.EntityType; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.function.EntityFunction; +import com.sk89q.worldedit.world.registry.EntityRegistry; + +/** + * The implementation of /butcher. + */ +public class CreatureButcher { + + final class Flags { + @SuppressWarnings("PointlessBitwiseExpression") + public static final int PETS = 1 << 0; + public static final int NPCS = 1 << 1; + public static final int ANIMALS = 1 << 2; + public static final int GOLEMS = 1 << 3; + public static final int AMBIENT = 1 << 4; + public static final int TAGGED = 1 << 5; + public static final int FRIENDLY = PETS | NPCS | ANIMALS | GOLEMS | AMBIENT | TAGGED; + public static final int WITH_LIGHTNING = 1 << 20; + + private Flags() { + } + } + + private final Actor player; + public int flags = 0; + + public CreatureButcher(Actor player) { + this.player = player; + } + + public void or(int flag, boolean on) { + if (on) flags |= flag; + } + + public void or(int flag, boolean on, String permission) { + or(flag, on); + + if ((flags & flag) != 0 && !player.hasPermission(permission)) { + flags &= ~flag; + } + } + + public void fromCommand(CommandContext args) { + or(Flags.FRIENDLY , args.hasFlag('f')); // No permission check here. Flags will instead be filtered by the subsequent calls. + or(Flags.PETS , args.hasFlag('p'), "worldedit.butcher.pets"); + or(Flags.NPCS , args.hasFlag('n'), "worldedit.butcher.npcs"); + or(Flags.GOLEMS , args.hasFlag('g'), "worldedit.butcher.golems"); + or(Flags.ANIMALS , args.hasFlag('a'), "worldedit.butcher.animals"); + or(Flags.AMBIENT , args.hasFlag('b'), "worldedit.butcher.ambient"); + or(Flags.TAGGED , args.hasFlag('t'), "worldedit.butcher.tagged"); + or(Flags.WITH_LIGHTNING, args.hasFlag('l'), "worldedit.butcher.lightning"); + } + + public EntityFunction createFunction(final EntityRegistry entityRegistry) { + return new EntityFunction() { + @Override + public boolean apply(Entity entity) throws WorldEditException { + boolean killPets = (flags & Flags.PETS) != 0; + boolean killNPCs = (flags & Flags.NPCS) != 0; + boolean killAnimals = (flags & Flags.ANIMALS) != 0; + boolean killGolems = (flags & Flags.GOLEMS) != 0; + boolean killAmbient = (flags & Flags.AMBIENT) != 0; + boolean killTagged = (flags & Flags.TAGGED) != 0; + + EntityType type = entity.getFacet(EntityType.class); + + if (type == null) { + return false; + } + + if (type.isPlayerDerived()) { + return false; + } + + if (!type.isLiving()) { + return false; + } + + if (!killAnimals && type.isAnimal()) { + return false; + } + + if (!killPets && type.isTamed()) { + return false; + } + + if (!killGolems && type.isGolem()) { + return false; + } + + if (!killNPCs && type.isNPC()) { + return false; + } + + if (!killAmbient && type.isAmbient()) { + return false; + } + + if (!killTagged && type.isTagged()) { + return false; + } + + entity.remove(); + return true; + } + }; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java b/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java new file mode 100644 index 000000000..b6459b186 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java @@ -0,0 +1,160 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.metadata.EntityType; +import com.sk89q.worldedit.function.EntityFunction; +import com.sk89q.worldedit.world.registry.EntityRegistry; + +import javax.annotation.Nullable; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * The implementation of /remove. + */ +public class EntityRemover { + + public enum Type { + ALL("all") { + @Override + boolean matches(EntityType type) { + for (Type value : values()) { + if (value != this && value.matches(type)) { + return true; + } + } + return false; + } + }, + PROJECTILES("projectiles?|arrows?") { + @Override + boolean matches(EntityType type) { + return type.isProjectile(); + } + }, + ITEMS("items?|drops?") { + @Override + boolean matches(EntityType type) { + return type.isItem(); + } + }, + FALLING_BLOCKS("falling(blocks?|sand|gravel)") { + @Override + boolean matches(EntityType type) { + return type.isFallingBlock(); + } + }, + PAINTINGS("paintings?|art") { + @Override + boolean matches(EntityType type) { + return type.isPainting(); + } + }, + ITEM_FRAMES("(item)frames?") { + @Override + boolean matches(EntityType type) { + return type.isItemFrame(); + } + }, + BOATS("boats?") { + @Override + boolean matches(EntityType type) { + return type.isBoat(); + } + }, + MINECARTS("(mine)?carts?") { + @Override + boolean matches(EntityType type) { + return type.isMinecart(); + } + }, + TNT("tnt") { + @Override + boolean matches(EntityType type) { + return type.isTNT(); + } + }, + XP_ORBS("xp") { + @Override + boolean matches(EntityType type) { + return type.isExperienceOrb(); + } + }; + + private final Pattern pattern; + + Type(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + public boolean matches(String str) { + return pattern.matcher(str).matches(); + } + + abstract boolean matches(EntityType type); + + @Nullable + public static Type findByPattern(String str) { + for (Type type : values()) { + if (type.matches(str)) { + return type; + } + } + + return null; + } + } + + private Type type; + + public void fromString(String str) throws CommandException { + Type type = Type.findByPattern(str); + if (type != null) { + this.type = type; + } else { + throw new CommandException("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); + } + } + + public EntityFunction createFunction(final EntityRegistry entityRegistry) { + final Type type = this.type; + checkNotNull("type can't be null", type); + return new EntityFunction() { + @Override + public boolean apply(Entity entity) throws WorldEditException { + EntityType registryType = entity.getFacet(EntityType.class); + if (registryType != null) { + if (type.matches(registryType)) { + entity.remove(); + return true; + } + } + + return false; + } + }; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java b/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java index 2cb248a69..4f6c40731 100644 --- a/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java +++ b/src/main/java/com/sk89q/worldedit/entity/BaseEntity.java @@ -20,14 +20,8 @@ package com.sk89q.worldedit.entity; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.world.DataException; import com.sk89q.worldedit.world.NbtValued; -import java.util.HashMap; -import java.util.Map; - import static com.google.common.base.Preconditions.checkNotNull; /** @@ -35,52 +29,43 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class BaseEntity implements NbtValued { + private String id; private CompoundTag nbtData; /** - * Create a new entity with the given entity ID. + * Create a new base entity. * - * @param id the ID of the entity, which determines its type + * @param id the entity type ID + * @param nbtData NBT data + */ + public BaseEntity(String id, CompoundTag nbtData) { + setTypeId(id); + setNbtData(nbtData); + } + + /** + * Create a new base entity with no NBT data. + * + * @param id the entity type ID */ public BaseEntity(String id) { - checkNotNull(id); - Map map = new HashMap(); - map.put("id", new StringTag("id", id)); - this.nbtData = new CompoundTag("", map); + setTypeId(id); } /** - * Create a new entity with the given NBT data. + * Make a clone of a {@link BaseEntity}. * - * @param nbtData the NBT data + * @param other the object to clone */ - public BaseEntity(CompoundTag nbtData) { - checkNotNull(nbtData); - this.nbtData = nbtData; - } - - /** - * Get the ID of the entity, which determines the type of entity. - * An example of an entity ID would be "CaveSpider". - * - * @return the entity ID, which may be an empty string - */ - public String getEntityId() { - CompoundTag nbtData = getNbtData(); - if (nbtData == null) { - return ""; - } - Tag idTag = nbtData.getValue().get("id"); - if (idTag != null && idTag instanceof StringTag) { - return ((StringTag) idTag).getValue(); - } else { - return ""; - } + public BaseEntity(BaseEntity other) { + checkNotNull(other); + setTypeId(other.getTypeId()); + setNbtData(other.getNbtData()); } @Override public boolean hasNbtData() { - return getNbtData() != null; + return true; } @Override @@ -89,9 +74,28 @@ public class BaseEntity implements NbtValued { } @Override - public void setNbtData(CompoundTag nbtData) throws DataException { + public void setNbtData(CompoundTag nbtData) { checkNotNull(nbtData); this.nbtData = nbtData; } + /** + * Get the entity that determines the type of entity. + * + * @return the entity ID + */ + public String getTypeId() { + return id; + } + + /** + * Set the entity ID that determines the type of entity. + * + * @param id the id + */ + public void setTypeId(String id) { + checkNotNull(id); + this.id = id; + } + } diff --git a/src/main/java/com/sk89q/worldedit/entity/Entity.java b/src/main/java/com/sk89q/worldedit/entity/Entity.java index 950557bc3..4224e5de7 100644 --- a/src/main/java/com/sk89q/worldedit/entity/Entity.java +++ b/src/main/java/com/sk89q/worldedit/entity/Entity.java @@ -19,13 +19,11 @@ package com.sk89q.worldedit.entity; -import com.sk89q.worldedit.PlayerDirection; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldVector; -import com.sk89q.worldedit.WorldVectorFace; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Faceted; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.world.World; + +import javax.annotation.Nullable; /** * A reference to an instance of an entity that exists in an {@link Extent} @@ -37,138 +35,15 @@ import com.sk89q.worldedit.world.World; * can then be used to spawn new instances of that particular entity * description. */ -public interface Entity { +public interface Entity extends Faceted { /** - * Find a position for the actor to stand that is not inside a block. - * Blocks above the player will be iteratively tested until there is - * a series of two free blocks. The actor will be teleported to - * that free position. + * Get a copy of the entity's state. * - * @param searchPos search position + * @return the entity's state or null if one cannot be gotten */ - void findFreePosition(WorldVector searchPos); - - /** - * Set the actor on the ground. - * - * @param searchPos The location to start searching from - */ - void setOnGround(WorldVector searchPos); - - /** - * Find a position for the player to stand that is not inside a block. - * Blocks above the player will be iteratively tested until there is - * a series of two free blocks. The player will be teleported to - * that free position. - */ - void findFreePosition(); - - /** - * Go up one level to the next free space above. - * - * @return true if a spot was found - */ - boolean ascendLevel(); - - /** - * Go up one level to the next free space above. - * - * @return true if a spot was found - */ - boolean descendLevel(); - - /** - * Ascend to the ceiling above. - * - * @param clearance How many blocks to leave above the player's head - * @return whether the player was moved - */ - boolean ascendToCeiling(int clearance); - - /** - * Ascend to the ceiling above. - * - * @param clearance How many blocks to leave above the player's head - * @param alwaysGlass Always put glass under the player - * @return whether the player was moved - */ - boolean ascendToCeiling(int clearance, boolean alwaysGlass); - - /** - * Just go up. - * - * @param distance How far up to teleport - * @return whether the player was moved - */ - boolean ascendUpwards(int distance); - - /** - * Just go up. - * - * @param distance How far up to teleport - * @param alwaysGlass Always put glass under the player - * @return whether the player was moved - */ - boolean ascendUpwards(int distance, boolean alwaysGlass); - - /** - * Make the player float in the given blocks. - * - * @param x The X coordinate of the block to float in - * @param y The Y coordinate of the block to float in - * @param z The Z coordinate of the block to float in - */ - void floatAt(int x, int y, int z, boolean alwaysGlass); - - /** - * Get the point of the block that is being stood in. - * - * @return point - */ - WorldVector getBlockIn(); - - /** - * Get the point of the block that is being stood upon. - * - * @return point - */ - WorldVector getBlockOn(); - - /** - * Get the point of the block being looked at. May return null. - * Will return the farthest away air block 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 air block found. - * @return point - */ - WorldVector getBlockTrace(int range, boolean useLastBlock); - - WorldVectorFace getBlockTraceFace(int range, boolean useLastBlock); - - /** - * Get the point of the block being looked at. May return null. - * - * @param range How far to checks for blocks - * @return point - */ - WorldVector getBlockTrace(int range); - - /** - * Get the point of the block being looked at. May return null. - * - * @param range How far to checks for blocks - * @return point - */ - WorldVector getSolidBlockTrace(int range); - - /** - * Get the player's cardinal direction (N, W, NW, etc.). May return null. - * - * @return the direction - */ - PlayerDirection getCardinalDirection(); + @Nullable + BaseEntity getState(); /** * Get the location of this entity. @@ -178,57 +53,17 @@ public interface Entity { Location getLocation(); /** - * Get the actor's position. - *

- * If the actor has no permission, then return a dummy location. + * Get the extent that this entity is on. * - * @return the actor's position + * @return the extent */ - WorldVector getPosition(); + Extent getExtent(); /** - * Get the player's view pitch. + * Remove this entity from it container. * - * @return pitch + * @return true if removal was successful */ - double getPitch(); - - /** - * Get the player's view yaw. - * - * @return yaw - */ - double getYaw(); - - /** - * Pass through the wall that you are looking at. - * - * @param range How far to checks for blocks - * @return whether the player was pass through - */ - boolean passThroughForwardWall(int range); - - /** - * Move the player. - * - * @param pos Where to move them - * @param pitch The pitch (up/down) of the player's view - * @param yaw The yaw (left/right) of the player's view - */ - void setPosition(Vector pos, float pitch, float yaw); - - /** - * Move the player. - * - * @param pos Where to move them - */ - void setPosition(Vector pos); - - /** - * Get the world that this entity is on. - * - * @return the world - */ - World getWorld(); + boolean remove(); } diff --git a/src/main/java/com/sk89q/worldedit/entity/Player.java b/src/main/java/com/sk89q/worldedit/entity/Player.java index 0098dc2f4..c5e2e333e 100644 --- a/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -20,16 +20,27 @@ package com.sk89q.worldedit.entity; import com.sk89q.worldedit.PlayerDirection; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.WorldVectorFace; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.world.World; /** * A player. */ public interface Player extends Entity, Actor { + /** + * Return the world that the player is on. + * + * @return the world + */ + World getWorld(); + /** * Returns true if the entity is holding a pick axe. * @@ -81,4 +92,182 @@ public interface Player extends Entity, Actor { */ boolean hasCreativeMode(); + /** + * Find a position for the actor to stand that is not inside a block. + * Blocks above the player will be iteratively tested until there is + * a series of two free blocks. The actor will be teleported to + * that free position. + * + * @param searchPos search position + */ + void findFreePosition(WorldVector searchPos); + + /** + * Set the actor on the ground. + * + * @param searchPos The location to start searching from + */ + void setOnGround(WorldVector searchPos); + + /** + * Find a position for the player to stand that is not inside a block. + * Blocks above the player will be iteratively tested until there is + * a series of two free blocks. The player will be teleported to + * that free position. + */ + void findFreePosition(); + + /** + * Go up one level to the next free space above. + * + * @return true if a spot was found + */ + boolean ascendLevel(); + + /** + * Go up one level to the next free space above. + * + * @return true if a spot was found + */ + boolean descendLevel(); + + /** + * Ascend to the ceiling above. + * + * @param clearance How many blocks to leave above the player's head + * @return whether the player was moved + */ + boolean ascendToCeiling(int clearance); + + /** + * Ascend to the ceiling above. + * + * @param clearance How many blocks to leave above the player's head + * @param alwaysGlass Always put glass under the player + * @return whether the player was moved + */ + boolean ascendToCeiling(int clearance, boolean alwaysGlass); + + /** + * Just go up. + * + * @param distance How far up to teleport + * @return whether the player was moved + */ + boolean ascendUpwards(int distance); + + /** + * Just go up. + * + * @param distance How far up to teleport + * @param alwaysGlass Always put glass under the player + * @return whether the player was moved + */ + boolean ascendUpwards(int distance, boolean alwaysGlass); + + /** + * Make the player float in the given blocks. + * + * @param x The X coordinate of the block to float in + * @param y The Y coordinate of the block to float in + * @param z The Z coordinate of the block to float in + */ + void floatAt(int x, int y, int z, boolean alwaysGlass); + + /** + * Get the point of the block that is being stood in. + * + * @return point + */ + WorldVector getBlockIn(); + + /** + * Get the point of the block that is being stood upon. + * + * @return point + */ + WorldVector getBlockOn(); + + /** + * Get the point of the block being looked at. May return null. + * Will return the farthest away air block 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 air block found. + * @return point + */ + WorldVector getBlockTrace(int range, boolean useLastBlock); + + WorldVectorFace getBlockTraceFace(int range, boolean useLastBlock); + + /** + * Get the point of the block being looked at. May return null. + * + * @param range How far to checks for blocks + * @return point + */ + WorldVector getBlockTrace(int range); + + /** + * Get the point of the block being looked at. May return null. + * + * @param range How far to checks for blocks + * @return point + */ + WorldVector getSolidBlockTrace(int range); + + /** + * Get the player's cardinal direction (N, W, NW, etc.). May return null. + * + * @return the direction + */ + PlayerDirection getCardinalDirection(); + + /** + * Get the actor's position. + *

+ * If the actor has no permission, then return a dummy location. + * + * @return the actor's position + */ + WorldVector getPosition(); + + /** + * Get the player's view pitch. + * + * @return pitch + */ + double getPitch(); + + /** + * Get the player's view yaw. + * + * @return yaw + */ + double getYaw(); + + /** + * Pass through the wall that you are looking at. + * + * @param range How far to checks for blocks + * @return whether the player was pass through + */ + boolean passThroughForwardWall(int range); + + /** + * Move the player. + * + * @param pos Where to move them + * @param pitch The pitch (up/down) of the player's view + * @param yaw The yaw (left/right) of the player's view + */ + void setPosition(Vector pos, float pitch, float yaw); + + /** + * Move the player. + * + * @param pos Where to move them + */ + void setPosition(Vector pos); + } diff --git a/src/main/java/com/sk89q/worldedit/entity/metadata/Creature.java b/src/main/java/com/sk89q/worldedit/entity/metadata/Creature.java new file mode 100644 index 000000000..66ca80b17 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/entity/metadata/Creature.java @@ -0,0 +1,56 @@ +/* + * 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.entity.metadata; + +/** + * Describes a creature. + */ +public interface Creature { + + /** + * Returns whether the creature is a non-player character, such as + * a NPC human or a villager. + * + * @return true if the creature is an NPC + */ + boolean isNpc(); + + /** + * Returns whether the creature can be tamed. + * + * @return true if the creature can be tamed + */ + boolean isTameable(); + + /** + * Returns whether the creature is hostile. + * + * @return true if the creature is hostile + */ + boolean isHostile(); + + /** + * Returns whether the creature is passive. + * + * @return true if the creature is passive + */ + boolean isPassive(); + +} diff --git a/src/main/java/com/sk89q/worldedit/entity/metadata/EntityType.java b/src/main/java/com/sk89q/worldedit/entity/metadata/EntityType.java new file mode 100644 index 000000000..a12225414 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/entity/metadata/EntityType.java @@ -0,0 +1,151 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.entity.metadata; + +/** + * Describes various classes of entities. + */ +public interface EntityType { + + /** + * Test whether the entity is a player-derived entity. + * + * @return true if a player derived entity + */ + boolean isPlayerDerived(); + + /** + * Test whether the entity is a projectile. + * + * @return true if a projectile + */ + boolean isProjectile(); + + /** + * Test whether the entity is an item. + * + * @return true if an item + */ + boolean isItem(); + + /** + * Test whether the entity is a falling block. + * + * @return true if a falling block + */ + boolean isFallingBlock(); + + /** + * Test whether the entity is a painting. + * + * @return true if a painting + */ + boolean isPainting(); + + /** + * Test whether the entity is an item frame. + * + * @return true if an item frame + */ + boolean isItemFrame(); + + /** + * Test whether the entity is a boat. + * + * @return true if a boat + */ + boolean isBoat(); + + /** + * Test whether the entity is a minecart. + * + * @return true if a minecart + */ + boolean isMinecart(); + + /** + * Test whether the entity is a primed TNT block. + * + * @return true if TNT + */ + boolean isTNT(); + + /** + * Test whether the entity is an experience orb. + * + * @return true if an experience orb + */ + boolean isExperienceOrb(); + + /** + * Test whether the entity is a living entity. + * + *

A "living entity" is the superclass of many living entity classes + * in Minecraft.

+ * + * @return true if a living entity + */ + boolean isLiving(); + + /** + * Test whether the entity is an animal. + * + * @return true if an animal + */ + boolean isAnimal(); + + /** + * Test whether the entity is an ambient creature, which includes + * the bat. + * + * @return true if an ambient creature + */ + boolean isAmbient(); + + /** + * Test whether the entity is a non-player controlled character, which + * includes villagers, NPCs from mods, and so on. + * + * @return true if an NPC + */ + boolean isNPC(); + + /** + * Test whether the entity is the iron golem from Minecraft. + * + * @return true if an iron golem + */ + boolean isGolem(); + + /** + * Test whether the entity is tameable and is tamed. + * + * @return true if tamed + */ + boolean isTamed(); + + /** + * Test whether the entity has been named (tagged). + * + * @return true if named + */ + boolean isTagged(); + +} diff --git a/src/main/java/com/sk89q/worldedit/EntityType.java b/src/main/java/com/sk89q/worldedit/entity/metadata/Tameable.java similarity index 74% rename from src/main/java/com/sk89q/worldedit/EntityType.java rename to src/main/java/com/sk89q/worldedit/entity/metadata/Tameable.java index 4b4f83438..6f288a00f 100644 --- a/src/main/java/com/sk89q/worldedit/EntityType.java +++ b/src/main/java/com/sk89q/worldedit/entity/metadata/Tameable.java @@ -1,37 +1,34 @@ -/* - * 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; - -/** - * List of removable entity types. - */ -public enum EntityType { - ALL, - @Deprecated ARROWS, - PROJECTILES, - ITEMS, - FALLING_BLOCKS, - PAINTINGS, - ITEM_FRAMES, - BOATS, - MINECARTS, - TNT, - XP_ORBS -} +/* + * 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.entity.metadata; + +/** + * Indicates a creature that can be tamed. + */ +public interface Tameable { + + /** + * Returns whether the creature is tamed. + * + * @return true if the creature is tamed + */ + boolean isTamed(); + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/BlockRegistry.java b/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java similarity index 88% rename from src/main/java/com/sk89q/worldedit/extension/registry/BlockRegistry.java rename to src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java index 2bd118015..2cb5c9d1d 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/BlockRegistry.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java @@ -17,13 +17,13 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.input.InputParseException; -import com.sk89q.worldedit.internal.registry.AbstractRegistry; +import com.sk89q.worldedit.internal.registry.AbstractFactory; import java.util.HashSet; import java.util.Set; @@ -33,16 +33,16 @@ import java.util.Set; * new blocks from input. *

* Instances of this class can be taken from - * {@link WorldEdit#getBlockRegistry()}. + * {@link WorldEdit#getBlockFactory()}. */ -public class BlockRegistry extends AbstractRegistry { +public class BlockFactory extends AbstractFactory { /** * Create a new instance. * * @param worldEdit the WorldEdit instance. */ - public BlockRegistry(WorldEdit worldEdit) { + public BlockFactory(WorldEdit worldEdit) { super(worldEdit); parsers.add(new DefaultBlockParser(worldEdit)); diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultBlockParser.java b/src/main/java/com/sk89q/worldedit/extension/factory/DefaultBlockParser.java similarity index 99% rename from src/main/java/com/sk89q/worldedit/extension/registry/DefaultBlockParser.java rename to src/main/java/com/sk89q/worldedit/extension/factory/DefaultBlockParser.java index fe7a63b09..3ecbc15fb 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultBlockParser.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/DefaultBlockParser.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.*; diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java b/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java similarity index 77% rename from src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java rename to src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java index 304a49b60..2d697fbbb 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java @@ -17,19 +17,32 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.function.mask.*; +import com.sk89q.worldedit.function.mask.BiomeMask2D; +import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.mask.NoiseFilter; +import com.sk89q.worldedit.function.mask.OffsetMask; +import com.sk89q.worldedit.function.mask.RegionMask; +import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.internal.registry.InputParser; -import com.sk89q.worldedit.masks.BiomeTypeMask; import com.sk89q.worldedit.math.noise.RandomNoise; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.session.request.RequestSelection; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.Biomes; +import com.sk89q.worldedit.world.registry.BiomeRegistry; import java.util.ArrayList; import java.util.HashSet; @@ -109,18 +122,19 @@ class DefaultMaskParser extends InputParser { return new MaskIntersection(offsetMask, Masks.negate(submask)); case '$': - Set biomes = new HashSet(); + Set biomes = new HashSet(); String[] biomesList = component.substring(1).split(","); + BiomeRegistry biomeRegistry = context.requireWorld().getWorldData().getBiomeRegistry(); + List knownBiomes = biomeRegistry.getBiomes(); for (String biomeName : biomesList) { - try { - BiomeType biome = worldEdit.getServer().getBiomes().get(biomeName); - biomes.add(biome); - } catch (UnknownBiomeTypeException e) { + BaseBiome biome = Biomes.findBiomeByName(knownBiomes, biomeName, biomeRegistry); + if (biome == null) { throw new InputParseException("Unknown biome '" + biomeName + "'"); } + biomes.add(biome); } - return Masks.wrap(new BiomeTypeMask(biomes)); + return Masks.asMask(new BiomeMask2D(context.requireExtent(), biomes)); case '%': int i = Integer.parseInt(component.substring(1)); @@ -135,7 +149,7 @@ class DefaultMaskParser extends InputParser { ParserContext tempContext = new ParserContext(context); tempContext.setRestricted(false); tempContext.setPreferringWildcard(true); - return new BlockMask(extent, worldEdit.getBlockRegistry().parseFromListInput(component, tempContext)); + return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(component, tempContext)); } } diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/HashTagPatternParser.java b/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java similarity index 86% rename from src/main/java/com/sk89q/worldedit/extension/registry/HashTagPatternParser.java rename to src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java index 1d4f66d35..1c7a0d646 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/HashTagPatternParser.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java @@ -17,16 +17,18 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.EmptyClipboardException; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.pattern.ClipboardPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.session.ClipboardHolder; class HashTagPatternParser extends InputParser { @@ -45,7 +47,9 @@ class HashTagPatternParser extends InputParser { if (session != null) { try { - return new ClipboardPattern(session.getClipboard()); + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + return new ClipboardPattern(clipboard); } catch (EmptyClipboardException e) { throw new InputParseException("To use #clipboard, please first copy something to your clipboard"); } diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/MaskRegistry.java b/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java similarity index 82% rename from src/main/java/com/sk89q/worldedit/extension/registry/MaskRegistry.java rename to src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java index fa4fb9e58..309992bcb 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/MaskRegistry.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java @@ -17,27 +17,27 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.internal.registry.AbstractRegistry; +import com.sk89q.worldedit.internal.registry.AbstractFactory; /** * A registry of known {@link Mask}s. Provides methods to instantiate * new masks from input. *

* Instances of this class can be taken from - * {@link WorldEdit#getMaskRegistry()}. + * {@link WorldEdit#getMaskFactory()}. */ -public final class MaskRegistry extends AbstractRegistry { +public final class MaskFactory extends AbstractFactory { /** * Create a new mask registry. * * @param worldEdit the WorldEdit instance */ - public MaskRegistry(WorldEdit worldEdit) { + public MaskFactory(WorldEdit worldEdit) { super(worldEdit); parsers.add(new DefaultMaskParser(worldEdit)); diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/PatternRegistry.java b/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java similarity index 83% rename from src/main/java/com/sk89q/worldedit/extension/registry/PatternRegistry.java rename to src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java index daab33cac..e19891148 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/PatternRegistry.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java @@ -17,27 +17,27 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.internal.registry.AbstractRegistry; +import com.sk89q.worldedit.internal.registry.AbstractFactory; /** * A registry of known {@link Pattern}s. Provides methods to instantiate * new patterns from input. *

* Instances of this class can be taken from - * {@link WorldEdit#getPatternRegistry()}. + * {@link WorldEdit#getPatternFactory()}. */ -public final class PatternRegistry extends AbstractRegistry { +public final class PatternFactory extends AbstractFactory { /** * Create a new instance. * * @param worldEdit the WorldEdit instance */ - public PatternRegistry(WorldEdit worldEdit) { + public PatternFactory(WorldEdit worldEdit) { super(worldEdit); parsers.add(new HashTagPatternParser(worldEdit)); diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/RandomPatternParser.java b/src/main/java/com/sk89q/worldedit/extension/factory/RandomPatternParser.java similarity index 95% rename from src/main/java/com/sk89q/worldedit/extension/registry/RandomPatternParser.java rename to src/main/java/com/sk89q/worldedit/extension/factory/RandomPatternParser.java index d07f94e03..a850b98ff 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/RandomPatternParser.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/RandomPatternParser.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseBlock; @@ -36,7 +36,7 @@ class RandomPatternParser extends InputParser { @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { - BlockRegistry blockRegistry = worldEdit.getBlockRegistry(); + BlockFactory blockRegistry = worldEdit.getBlockFactory(); RandomPattern randomPattern = new RandomPattern(); for (String token : input.split(",")) { diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/SingleBlockPatternParser.java b/src/main/java/com/sk89q/worldedit/extension/factory/SingleBlockPatternParser.java similarity index 91% rename from src/main/java/com/sk89q/worldedit/extension/registry/SingleBlockPatternParser.java rename to src/main/java/com/sk89q/worldedit/extension/factory/SingleBlockPatternParser.java index a1a11fa10..17833cf97 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/SingleBlockPatternParser.java +++ b/src/main/java/com/sk89q/worldedit/extension/factory/SingleBlockPatternParser.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.registry; +package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; @@ -37,7 +37,7 @@ class SingleBlockPatternParser extends InputParser { String[] items = input.split(","); if (items.length == 1) { - return new BlockPattern(worldEdit.getBlockRegistry().parseFromInput(items[0], context)); + return new BlockPattern(worldEdit.getBlockFactory().parseFromInput(items[0], context)); } else { return null; } diff --git a/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java b/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java index 9d2b56f19..dd3a487af 100644 --- a/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java +++ b/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java @@ -20,8 +20,8 @@ package com.sk89q.worldedit.extension.input; import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.extension.factory.MaskFactory; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.registry.MaskRegistry; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.World; @@ -29,7 +29,7 @@ import javax.annotation.Nullable; /** * Contains contextual information that may be useful when constructing - * objects from a registry (such as {@link MaskRegistry}). + * objects from a registry (such as {@link MaskFactory}). *

* By default, {@link #isRestricted()} will return true. */ @@ -114,6 +114,7 @@ public class ParserContext { */ public void setWorld(@Nullable World world) { this.world = world; + setExtent(world); } /** diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index 9e4c9843c..6cc034891 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -19,26 +19,38 @@ package com.sk89q.worldedit.extension.platform; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.BlockWorldVector; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.NotABlockException; +import com.sk89q.worldedit.PlayerDirection; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.WorldEditPermissionException; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.WorldVectorFace; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.blocks.ItemID; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.util.TargetBlock; import com.sk89q.worldedit.world.World; import java.io.File; -import static com.google.common.base.Preconditions.checkNotNull; - /** * An abstract implementation of both a {@link Actor} and a {@link Player} * that is intended for implementations of WorldEdit to use to wrap * players that make use of WorldEdit. */ -public abstract class AbstractPlayerActor implements Actor, Player { +public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { + + @Override + public final Extent getExtent() { + return getWorld(); + } /** * Returns direction according to rotation. May return null. @@ -468,4 +480,15 @@ public abstract class AbstractPlayerActor implements Actor, Player { return false; } + @SuppressWarnings("CloneDoesntCallSuperClone") + @Override + public Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException("Not supported"); + } + + @Override + public boolean remove() { + return false; + } + } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index bc90eaa5d..412afd9ea 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.extension.platform; -import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.util.command.Dispatcher; @@ -58,13 +57,6 @@ public interface Platform { */ void reload(); - /** - * Returns all available biomes. - * - * @return an object containing all the biomes - */ - BiomeTypes getBiomes(); - /** * Schedules the given task to be invoked once every period ticks * after an initial delay of delay ticks. diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java index b2569490f..b1d8979aa 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -21,12 +21,15 @@ package com.sk89q.worldedit.extension.platform; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; class PlayerProxy extends AbstractPlayerActor { @@ -67,6 +70,11 @@ class PlayerProxy extends AbstractPlayerActor { return basePlayer.getName(); } + @Override + public BaseEntity getState() { + throw new UnsupportedOperationException("Can't getState() on a player"); + } + @Override public Location getLocation() { return basePlayer.getLocation(); @@ -131,4 +139,10 @@ class PlayerProxy extends AbstractPlayerActor { public void dispatchCUIEvent(CUIEvent event) { cuiActor.dispatchCUIEvent(event); } + + @Nullable + @Override + public T getFacet(Class cls) { + return basePlayer.getFacet(cls); + } } diff --git a/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index b9c4c1158..7a1bd25f9 100644 --- a/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -20,13 +20,21 @@ package com.sk89q.worldedit.extent; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.OperationQueue; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.biome.BaseBiome; import javax.annotation.Nullable; +import java.util.List; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -70,6 +78,32 @@ public abstract class AbstractDelegateExtent implements Extent { return extent.setBlock(location, block); } + @Override + @Nullable + public Entity createEntity(Location location, BaseEntity entity) { + return extent.createEntity(location, entity); + } + + @Override + public List getEntities() { + return extent.getEntities(); + } + + @Override + public List getEntities(Region region) { + return extent.getEntities(region); + } + + @Override + public BaseBiome getBiome(Vector2D position) { + return extent.getBiome(position); + } + + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + return extent.setBiome(position, biome); + } + @Override public Vector getMinimumPoint() { return extent.getMinimumPoint(); diff --git a/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java b/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java index 87dfeac3d..84af07ab3 100644 --- a/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java +++ b/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java @@ -22,8 +22,19 @@ package com.sk89q.worldedit.extent; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.history.change.BlockChange; +import com.sk89q.worldedit.history.change.EntityCreate; +import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.history.changeset.ChangeSet; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; + +import javax.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -53,4 +64,71 @@ public class ChangeSetExtent extends AbstractDelegateExtent { return super.setBlock(location, block); } + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity state) { + Entity entity = super.createEntity(location, state); + if (state != null) { + changeSet.add(new EntityCreate(location, state, entity)); + } + return entity; + } + + @Override + public List getEntities() { + return wrapEntities(super.getEntities()); + } + + @Override + public List getEntities(Region region) { + return wrapEntities(super.getEntities(region)); + } + + private List wrapEntities(List entities) { + List newList = new ArrayList(entities.size()); + for (Entity entity : entities) { + newList.add(new TrackedEntity(entity)); + } + return newList; + } + + private class TrackedEntity implements Entity { + private final Entity entity; + + private TrackedEntity(Entity entity) { + this.entity = entity; + } + + @Override + public BaseEntity getState() { + return entity.getState(); + } + + @Override + public Location getLocation() { + return entity.getLocation(); + } + + @Override + public Extent getExtent() { + return entity.getExtent(); + } + + @Override + public boolean remove() { + Location location = entity.getLocation(); + BaseEntity state = entity.getState(); + boolean success = entity.remove(); + if (state != null && success) { + changeSet.add(new EntityRemove(location, state)); + } + return success; + } + + @Nullable + @Override + public T getFacet(Class cls) { + return entity.getFacet(cls); + } + } } diff --git a/src/main/java/com/sk89q/worldedit/extent/Extent.java b/src/main/java/com/sk89q/worldedit/extent/Extent.java index 34d86d298..51ae4fc84 100644 --- a/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -20,6 +20,13 @@ package com.sk89q.worldedit.extent; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.regions.Region; + +import javax.annotation.Nullable; +import java.util.List; /** * A world, portion of a world, clipboard, or other object that can have blocks @@ -50,4 +57,35 @@ public interface Extent extends InputExtent, OutputExtent { */ Vector getMaximumPoint(); + /** + * Get a list of all entities within the given region. + *

+ * If the extent is not wholly loaded (i.e. a world being simulated in the + * game will not have every chunk loaded), then this list may not be + * incomplete. + * + * @return a list of entities + */ + List getEntities(Region region); + + /** + * Get a list of all entities. + *

+ * If the extent is not wholly loaded (i.e. a world being simulated in the + * game will not have every chunk loaded), then this list may not be + * incomplete. + * + * @return a list of entities + */ + List getEntities(); + + /** + * Create an entity at the given location. + * + * @param entity the entity + * @param location the location + * @return a reference to the created entity, or null if the entity could not be created + */ + @Nullable Entity createEntity(Location location, BaseEntity entity); + } diff --git a/src/main/java/com/sk89q/worldedit/extent/InputExtent.java b/src/main/java/com/sk89q/worldedit/extent/InputExtent.java index f40750720..81fcce5c9 100644 --- a/src/main/java/com/sk89q/worldedit/extent/InputExtent.java +++ b/src/main/java/com/sk89q/worldedit/extent/InputExtent.java @@ -20,9 +20,10 @@ package com.sk89q.worldedit.extent; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.world.biome.BaseBiome; /** * Provides the current state of blocks, entities, and so on. @@ -74,4 +75,15 @@ public interface InputExtent { */ BaseBlock getLazyBlock(Vector position); + /** + * Get the biome at the given location. + * + *

If there is no biome available, then the ocean biome should be + * returned.

+ * + * @param position the (x, z) location to check the biome at + * @return the biome at the location + */ + BaseBiome getBiome(Vector2D position); + } diff --git a/src/main/java/com/sk89q/worldedit/extent/NullExtent.java b/src/main/java/com/sk89q/worldedit/extent/NullExtent.java index f291bc2f2..1979bdaaf 100644 --- a/src/main/java/com/sk89q/worldedit/extent/NullExtent.java +++ b/src/main/java/com/sk89q/worldedit/extent/NullExtent.java @@ -20,11 +20,19 @@ package com.sk89q.worldedit.extent; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.biome.BaseBiome; import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; /** * An extent that returns air blocks for all blocks and does not @@ -44,6 +52,22 @@ public class NullExtent implements Extent { return nullPoint; } + @Override + public List getEntities(Region region) { + return Collections.emptyList(); + } + + @Override + public List getEntities() { + return Collections.emptyList(); + } + + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity entity) { + return null; + } + @Override public BaseBlock getBlock(Vector position) { return new BaseBlock(0); @@ -54,11 +78,22 @@ public class NullExtent implements Extent { return new BaseBlock(0); } + @Nullable + @Override + public BaseBiome getBiome(Vector2D position) { + return null; + } + @Override public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { return false; } + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + return false; + } + @Nullable @Override public Operation commit() { diff --git a/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java b/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java index 1468982f2..e0268495d 100644 --- a/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java +++ b/src/main/java/com/sk89q/worldedit/extent/OutputExtent.java @@ -20,9 +20,11 @@ package com.sk89q.worldedit.extent; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.world.biome.BaseBiome; import javax.annotation.Nullable; @@ -49,6 +51,15 @@ public interface OutputExtent { */ boolean setBlock(Vector position, BaseBlock block) throws WorldEditException; + /** + * Set the biome. + * + * @param position the (x, z) location to set the biome at + * @param biome the biome to set to + * @return true if the biome was successfully set (return value may not be accurate) + */ + boolean setBiome(Vector2D position, BaseBiome biome); + /** * Return an {@link Operation} that should be called to tie up loose ends * (such as to commit changes in a buffer). diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java new file mode 100644 index 000000000..1b819e036 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -0,0 +1,187 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BaseBiome; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stores block data as a multi-dimensional array of {@link BaseBlock}s and + * other data as lists or maps. + */ +public class BlockArrayClipboard implements Clipboard { + + private final Region region; + private Vector origin = new Vector(); + private final BaseBlock[][][] blocks; + private final List entities = new ArrayList(); + + /** + * Create a new instance. + *

+ * The origin will be placed at the region's lowest minimum point. + * + * @param region the bounding region + */ + public BlockArrayClipboard(Region region) { + checkNotNull(region); + this.region = region.clone(); + this.origin = region.getMinimumPoint(); + + Vector dimensions = getDimensions(); + blocks = new BaseBlock[dimensions.getBlockX()][dimensions.getBlockY()][dimensions.getBlockZ()]; + } + + @Override + public Region getRegion() { + return region.clone(); + } + + @Override + public Vector getOrigin() { + return origin; + } + + @Override + public void setOrigin(Vector origin) { + this.origin = origin; + } + + @Override + public Vector getDimensions() { + return region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1); + } + + @Override + public Vector getMinimumPoint() { + return region.getMinimumPoint(); + } + + @Override + public Vector getMaximumPoint() { + return region.getMaximumPoint(); + } + + @Override + public List getEntities(Region region) { + List filtered = new ArrayList(); + for (Entity entity : entities) { + if (region.contains(entity.getLocation().toVector())) { + filtered.add(entity); + } + } + return Collections.unmodifiableList(filtered); + } + + @Override + public List getEntities() { + return Collections.unmodifiableList(entities); + } + + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity entity) { + ClipboardEntity ret = new ClipboardEntity(location, entity); + entities.add(ret); + return ret; + } + + @Override + public BaseBlock getBlock(Vector position) { + if (region.contains(position)) { + Vector v = position.subtract(region.getMinimumPoint()); + BaseBlock block = blocks[v.getBlockX()][v.getBlockY()][v.getBlockZ()]; + if (block != null) { + return new BaseBlock(block); + } + } + + return new BaseBlock(BlockID.AIR); + } + + @Override + public BaseBlock getLazyBlock(Vector position) { + return getBlock(position); + } + + @Override + public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { + if (region.contains(position)) { + Vector v = position.subtract(region.getMinimumPoint()); + blocks[v.getBlockX()][v.getBlockY()][v.getBlockZ()] = new BaseBlock(block); + return true; + } else { + return false; + } + } + + @Override + public BaseBiome getBiome(Vector2D position) { + return new BaseBiome(0); + } + + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + return false; + } + + @Nullable + @Override + public Operation commit() { + return null; + } + + /** + * Stores entity data. + */ + private class ClipboardEntity extends StoredEntity { + ClipboardEntity(Location location, BaseEntity entity) { + super(location, entity); + } + + @Override + public boolean remove() { + return entities.remove(this); + } + + @Nullable + @Override + public T getFacet(Class cls) { + return null; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java new file mode 100644 index 000000000..e68d5b2c0 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -0,0 +1,61 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.regions.Region; + +/** + * Specifies an object that implements something suitable as a "clipboard." + */ +public interface Clipboard extends Extent { + + /** + * Get the bounding region of this extent. + *

+ * Implementations should return a copy of the region. + * + * @return the bounding region + */ + Region getRegion(); + + /** + * Get the dimensions of the copy, which is at minimum (1, 1, 1). + * + * @return the dimensions + */ + Vector getDimensions(); + + /** + * Get the origin point from which the copy was made from. + * + * @return the origin + */ + Vector getOrigin(); + + /** + * Set the origin point from which the copy was made from. + * + * @param origin the origin + */ + void setOrigin(Vector origin); + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/StoredEntity.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/StoredEntity.java new file mode 100644 index 000000000..a38d2d412 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/StoredEntity.java @@ -0,0 +1,76 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard; + +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An implementation of {@link Entity} that stores a {@link BaseEntity} with it. + *

+ * Calls to {@link #getState()} return a clone. + */ +abstract class StoredEntity implements Entity { + + private final Location location; + private final BaseEntity entity; + + /** + * Create a new instance. + * + * @param location the location + * @param entity the entity (which will be copied) + */ + StoredEntity(Location location, BaseEntity entity) { + checkNotNull(location); + checkNotNull(entity); + this.location = location; + this.entity = new BaseEntity(entity); + } + + /** + * Get the entity state. This is not a copy. + * + * @return the entity + */ + BaseEntity getEntity() { + return entity; + } + + @Override + public BaseEntity getState() { + return new BaseEntity(entity); + } + + @Override + public Location getLocation() { + return location; + } + + @Override + public Extent getExtent() { + return location.getExtent(); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java new file mode 100644 index 000000000..460d23662 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java @@ -0,0 +1,178 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io; + +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.NBTOutputStream; + +import javax.annotation.Nullable; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A collection of supported clipboard formats. + */ +public enum ClipboardFormat { + + /** + * The Schematic format used by many software. + */ + SCHEMATIC("mcedit", "mce", "schematic") { + @Override + public ClipboardReader getReader(InputStream inputStream) throws IOException { + NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream)); + return new SchematicReader(nbtStream); + } + + @Override + public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { + NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream)); + return new SchematicWriter(nbtStream); + } + + @Override + public boolean isFormat(File file) { + DataInputStream str = null; + try { + str = new DataInputStream(new GZIPInputStream(new FileInputStream(file))); + if ((str.readByte() & 0xFF) != NBTConstants.TYPE_COMPOUND) { + return false; + } + byte[] nameBytes = new byte[str.readShort() & 0xFFFF]; + str.readFully(nameBytes); + String name = new String(nameBytes, NBTConstants.CHARSET); + return name.equals("Schematic"); + } catch (IOException e) { + return false; + } finally { + if (str != null) { + try { + str.close(); + } catch (IOException ignored) { + } + } + } + } + }; + + private static final Map aliasMap = new HashMap(); + + private final String[] aliases; + + /** + * Create a new instance. + * + * @param aliases an array of aliases by which this format may be referred to + */ + private ClipboardFormat(String ... aliases) { + this.aliases = aliases; + } + + /** + * Get a set of aliases. + * + * @return a set of aliases + */ + public Set getAliases() { + return Collections.unmodifiableSet(new HashSet(Arrays.asList(aliases))); + } + + /** + * Create a reader. + * + * @param inputStream the input stream + * @return a reader + * @throws IOException thrown on I/O error + */ + public abstract ClipboardReader getReader(InputStream inputStream) throws IOException; + + /** + * Create a writer. + * + * @param outputStream the output stream + * @return a writer + * @throws IOException thrown on I/O error + */ + public abstract ClipboardWriter getWriter(OutputStream outputStream) throws IOException; + + /** + * Return whether the given file is of this format. + * + * @param file the file + * @return true if the given file is of this format + */ + public abstract boolean isFormat(File file); + + static { + for (ClipboardFormat format : EnumSet.allOf(ClipboardFormat.class)) { + for (String key : format.aliases) { + aliasMap.put(key, format); + } + } + } + + /** + * Find the clipboard format named by the given alias. + * + * @param alias the alias + * @return the format, otherwise null if none is matched + */ + @Nullable + public static ClipboardFormat findByAlias(String alias) { + checkNotNull(alias); + return aliasMap.get(alias.toLowerCase().trim()); + } + + /** + * Detect the format given a file. + * + * @param file the file + * @return the format, otherwise null if one cannot be detected + */ + @Nullable + public static ClipboardFormat findByFile(File file) { + checkNotNull(file); + + for (ClipboardFormat format : EnumSet.allOf(ClipboardFormat.class)) { + if (format.isFormat(file)) { + return format; + } + } + + return null; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardReader.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardReader.java new file mode 100644 index 000000000..ccb74ee5b --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardReader.java @@ -0,0 +1,43 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io; + +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.world.registry.WorldData; + +import java.io.IOException; + +/** + * Reads {@code Clipboard}s. + * + * @see Clipboard + */ +public interface ClipboardReader { + + /** + * Read a {@code Clipboard}. + * + * @param data the world data space to convert the blocks to + * @return the read clipboard + * @throws IOException thrown on I/O error + */ + Clipboard read(WorldData data) throws IOException; + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardWriter.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardWriter.java new file mode 100644 index 000000000..9f5dc307b --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardWriter.java @@ -0,0 +1,44 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io; + +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.world.registry.WorldData; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Writes {@code Clipboard}s. + * + * @see Clipboard + */ +public interface ClipboardWriter extends Closeable { + + /** + * Writes a clipboard. + * + * @param clipboard the clipboard + * @param data the world data instance + * @throws IOException thrown on I/O error + */ + void write(Clipboard clipboard, WorldData data) throws IOException; + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java new file mode 100644 index 000000000..bae3ae753 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java @@ -0,0 +1,278 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io; + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.registry.WorldData; +import com.sk89q.worldedit.world.storage.NBTConversions; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Reads schematic files based that are compatible with MCEdit and other editors. + */ +public class SchematicReader implements ClipboardReader { + + private static final Logger log = Logger.getLogger(SchematicReader.class.getCanonicalName()); + private final NBTInputStream inputStream; + + /** + * Create a new instance. + * + * @param inputStream the input stream to read from + */ + public SchematicReader(NBTInputStream inputStream) { + checkNotNull(inputStream); + this.inputStream = inputStream; + } + + @Override + public Clipboard read(WorldData data) throws IOException { + // Schematic tag + CompoundTag schematicTag = (CompoundTag) inputStream.readTag(); + if (!schematicTag.getName().equals("Schematic")) { + throw new IOException("Tag 'Schematic' does not exist or is not first"); + } + + // Check + Map schematic = schematicTag.getValue(); + if (!schematic.containsKey("Blocks")) { + throw new IOException("Schematic file is missing a 'Blocks' tag"); + } + + // Check type of Schematic + String materials = requireTag(schematic, "Materials", StringTag.class).getValue(); + if (!materials.equals("Alpha")) { + throw new IOException("Schematic file is not an Alpha schematic"); + } + + // ==================================================================== + // Metadata + // ==================================================================== + + Vector origin; + Region region; + + // Get information + short width = requireTag(schematic, "Width", ShortTag.class).getValue(); + short height = requireTag(schematic, "Height", ShortTag.class).getValue(); + short length = requireTag(schematic, "Length", ShortTag.class).getValue(); + + try { + int originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue(); + int originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue(); + int originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue(); + Vector min = new Vector(originX, originY, originZ); + + int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue(); + int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue(); + int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue(); + Vector offset = new Vector(offsetX, offsetY, offsetZ); + + origin = min.subtract(offset); + region = new CuboidRegion(min, min.add(width, height, length).subtract(Vector.ONE)); + } catch (IOException ignored) { + origin = new Vector(0, 0, 0); + region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE)); + } + + // ==================================================================== + // Blocks + // ==================================================================== + + // Get blocks + byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue(); + byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue(); + byte[] addId = new byte[0]; + short[] blocks = new short[blockId.length]; // Have to later combine IDs + + // We support 4096 block IDs using the same method as vanilla Minecraft, where + // the highest 4 bits are stored in a separate byte array. + if (schematic.containsKey("AddBlocks")) { + addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue(); + } + + // Combine the AddBlocks data with the first 8-bit block ID + for (int index = 0; index < blockId.length; index++) { + if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index + blocks[index] = (short) (blockId[index] & 0xFF); + } else { + if ((index & 1) == 0) { + blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF)); + } else { + blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF)); + } + } + } + + // Need to pull out tile entities + List tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue(); + Map> tileEntitiesMap = new HashMap>(); + + for (Tag tag : tileEntities) { + if (!(tag instanceof CompoundTag)) continue; + CompoundTag t = (CompoundTag) tag; + + int x = 0; + int y = 0; + int z = 0; + + Map values = new HashMap(); + + for (Map.Entry entry : t.getValue().entrySet()) { + if (entry.getKey().equals("x")) { + if (entry.getValue() instanceof IntTag) { + x = ((IntTag) entry.getValue()).getValue(); + } + } else if (entry.getKey().equals("y")) { + if (entry.getValue() instanceof IntTag) { + y = ((IntTag) entry.getValue()).getValue(); + } + } else if (entry.getKey().equals("z")) { + if (entry.getValue() instanceof IntTag) { + z = ((IntTag) entry.getValue()).getValue(); + } + } + + values.put(entry.getKey(), entry.getValue()); + } + + BlockVector vec = new BlockVector(x, y, z); + tileEntitiesMap.put(vec, values); + } + + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + clipboard.setOrigin(origin); + + // Don't log a torrent of errors + int failedBlockSets = 0; + + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + for (int z = 0; z < length; ++z) { + int index = y * width * length + z * width + x; + BlockVector pt = new BlockVector(x, y, z); + BaseBlock block = new BaseBlock(blocks[index], blockData[index]); + + if (tileEntitiesMap.containsKey(pt)) { + block.setNbtData(new CompoundTag("", tileEntitiesMap.get(pt))); + } + + try { + clipboard.setBlock(region.getMinimumPoint().add(pt), block); + } catch (WorldEditException e) { + switch (failedBlockSets) { + case 0: + log.log(Level.WARNING, "Failed to set block on a Clipboard", e); + break; + case 1: + log.log(Level.WARNING, "Failed to set block on a Clipboard (again) -- no more messages will be logged", e); + break; + default: + } + + failedBlockSets++; + } + } + } + } + + // ==================================================================== + // Entities + // ==================================================================== + + try { + List entityTags = requireTag(schematic, "Entities", ListTag.class).getValue(); + + for (Tag tag : entityTags) { + if (tag instanceof CompoundTag) { + CompoundTag compound = (CompoundTag) tag; + String id = compound.getString("id"); + Location location = NBTConversions.toLocation(clipboard, compound.getListTag("Pos"), compound.getListTag("Rotation")); + + if (!id.isEmpty()) { + BaseEntity state = new BaseEntity(id, compound); + clipboard.createEntity(location, state); + } + } + } + } catch (IOException ignored) { // No entities? No problem + } + + return clipboard; + } + + private static T requireTag(Map items, String key, Class expected) throws IOException { + if (!items.containsKey(key)) { + throw new IOException("Schematic file is missing a \"" + key + "\" tag"); + } + + Tag tag = items.get(key); + if (!expected.isInstance(tag)) { + throw new IOException(key + " tag is not of tag type " + expected.getName()); + } + + return expected.cast(tag); + } + + @Nullable + private static T getTag(CompoundTag tag, Class expected, String key) { + Map items = tag.getValue(); + + if (!items.containsKey(key)) { + return null; + } + + Tag test = items.get(key); + if (!expected.isInstance(test)) { + return null; + } + + return expected.cast(test); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java new file mode 100644 index 000000000..6e907cb0e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java @@ -0,0 +1,218 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io; + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.registry.WorldData; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Writes schematic files based that are compatible with MCEdit and other editors. + */ +public class SchematicWriter implements ClipboardWriter { + + private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; + private final NBTOutputStream outputStream; + + /** + * Create a new schematic writer. + * + * @param outputStream the output stream to write to + */ + public SchematicWriter(NBTOutputStream outputStream) { + checkNotNull(outputStream); + this.outputStream = outputStream; + } + + @Override + public void write(Clipboard clipboard, WorldData data) throws IOException { + Region region = clipboard.getRegion(); + Vector origin = clipboard.getOrigin(); + Vector min = region.getMinimumPoint(); + Vector offset = min.subtract(origin); + int width = region.getWidth(); + int height = region.getHeight(); + int length = region.getLength(); + + if (width > MAX_SIZE) { + throw new IllegalArgumentException("Width of region too large for a .schematic"); + } + if (height > MAX_SIZE) { + throw new IllegalArgumentException("Height of region too large for a .schematic"); + } + if (length > MAX_SIZE) { + throw new IllegalArgumentException("Length of region too large for a .schematic"); + } + + // ==================================================================== + // Metadata + // ==================================================================== + + HashMap schematic = new HashMap(); + schematic.put("Width", new ShortTag("Width", (short) width)); + schematic.put("Length", new ShortTag("Length", (short) length)); + schematic.put("Height", new ShortTag("Height", (short) height)); + schematic.put("Materials", new StringTag("Materials", "Alpha")); + schematic.put("WEOriginX", new IntTag("WEOriginX", min.getBlockX())); + schematic.put("WEOriginY", new IntTag("WEOriginY", min.getBlockY())); + schematic.put("WEOriginZ", new IntTag("WEOriginZ", min.getBlockZ())); + schematic.put("WEOffsetX", new IntTag("WEOffsetX", offset.getBlockX())); + schematic.put("WEOffsetY", new IntTag("WEOffsetY", offset.getBlockY())); + schematic.put("WEOffsetZ", new IntTag("WEOffsetZ", offset.getBlockZ())); + + // ==================================================================== + // Block handling + // ==================================================================== + + byte[] blocks = new byte[width * height * length]; + byte[] addBlocks = null; + byte[] blockData = new byte[width * height * length]; + List tileEntities = new ArrayList(); + + for (Vector point : region) { + Vector relative = point.subtract(min); + int x = relative.getBlockX(); + int y = relative.getBlockY(); + int z = relative.getBlockZ(); + + int index = y * width * length + z * width + x; + BaseBlock block = clipboard.getBlock(point); + + // Save 4096 IDs in an AddBlocks section + if (block.getType() > 255) { + if (addBlocks == null) { // Lazily create section + addBlocks = new byte[(blocks.length >> 1) + 1]; + } + + addBlocks[index >> 1] = (byte) (((index & 1) == 0) ? + addBlocks[index >> 1] & 0xF0 | (block.getType() >> 8) & 0xF + : addBlocks[index >> 1] & 0xF | ((block.getType() >> 8) & 0xF) << 4); + } + + blocks[index] = (byte) block.getType(); + blockData[index] = (byte) block.getData(); + + // Store TileEntity data + CompoundTag rawTag = block.getNbtData(); + if (rawTag != null) { + Map values = new HashMap(); + for (Entry entry : rawTag.getValue().entrySet()) { + values.put(entry.getKey(), entry.getValue()); + } + + values.put("id", new StringTag("id", block.getNbtId())); + values.put("x", new IntTag("x", x)); + values.put("y", new IntTag("y", y)); + values.put("z", new IntTag("z", z)); + + CompoundTag tileEntityTag = new CompoundTag("TileEntity", values); + tileEntities.add(tileEntityTag); + } + } + + schematic.put("Blocks", new ByteArrayTag("Blocks", blocks)); + schematic.put("Data", new ByteArrayTag("Data", blockData)); + schematic.put("TileEntities", new ListTag("TileEntities", CompoundTag.class, tileEntities)); + + if (addBlocks != null) { + schematic.put("AddBlocks", new ByteArrayTag("AddBlocks", addBlocks)); + } + + // ==================================================================== + // Entities + // ==================================================================== + + List entities = new ArrayList(); + for (Entity entity : clipboard.getEntities()) { + BaseEntity state = entity.getState(); + + if (state != null) { + Map values = new HashMap(); + + // Put NBT provided data + CompoundTag rawTag = state.getNbtData(); + if (rawTag != null) { + values.putAll(rawTag.getValue()); + } + + // Store our location data, overwriting any + values.put("id", new StringTag("id", state.getTypeId())); + values.put("Pos", writeVector(entity.getLocation().toVector(), "Pos")); + values.put("Rotation", writeRotation(entity.getLocation(), "Rotation")); + + CompoundTag entityTag = new CompoundTag("Entity", values); + entities.add(entityTag); + } + } + + schematic.put("Entities", new ListTag("Entities", CompoundTag.class, entities)); + + // ==================================================================== + // Output + // ==================================================================== + + CompoundTag schematicTag = new CompoundTag("Schematic", schematic); + outputStream.writeTag(schematicTag); + } + + private Tag writeVector(Vector vector, String name) { + List list = new ArrayList(); + list.add(new DoubleTag("", vector.getX())); + list.add(new DoubleTag("", vector.getY())); + list.add(new DoubleTag("", vector.getZ())); + return new ListTag(name, DoubleTag.class, list); + } + + private Tag writeRotation(Location location, String name) { + List list = new ArrayList(); + list.add(new FloatTag("", location.getYaw())); + list.add(new FloatTag("", location.getPitch())); + return new ListTag(name, FloatTag.class, list); + } + + @Override + public void close() throws IOException { + outputStream.close(); + } +} diff --git a/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java b/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java new file mode 100644 index 000000000..dc634ed2f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java @@ -0,0 +1,181 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.transform; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.registry.BlockRegistry; +import com.sk89q.worldedit.world.registry.State; +import com.sk89q.worldedit.world.registry.StateValue; + +import javax.annotation.Nullable; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Transforms blocks themselves (but not their position) according to a + * given transform. + */ +public class BlockTransformExtent extends AbstractDelegateExtent { + + private static final double RIGHT_ANGLE = Math.toRadians(90); + + private final Transform transform; + private final BlockRegistry blockRegistry; + + /** + * Create a new instance. + * + * @param extent the extent + * @param blockRegistry the block registry used for block direction data + */ + public BlockTransformExtent(Extent extent, Transform transform, BlockRegistry blockRegistry) { + super(extent); + checkNotNull(transform); + checkNotNull(blockRegistry); + this.transform = transform; + this.blockRegistry = blockRegistry; + } + + /** + * Get the transform. + * + * @return the transform + */ + public Transform getTransform() { + return transform; + } + + /** + * Transform a block without making a copy. + * + * @param block the block + * @param reverse true to transform in the opposite direction + * @return the same block + */ + private BaseBlock transformBlock(BaseBlock block, boolean reverse) { + transform(block, reverse ? transform.inverse() : transform, blockRegistry); + return block; + } + + @Override + public BaseBlock getBlock(Vector position) { + return transformBlock(super.getBlock(position), false); + } + + @Override + public BaseBlock getLazyBlock(Vector position) { + return transformBlock(super.getLazyBlock(position), false); + } + + @Override + public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException { + return super.setBlock(location, transformBlock(new BaseBlock(block), true)); + } + + + /** + * Transform the given block using the given transform. + * + *

The provided block is modified.

+ * + * @param block the block + * @param transform the transform + * @param registry the registry + * @return the same block + */ + public static BaseBlock transform(BaseBlock block, Transform transform, BlockRegistry registry) { + return transform(block, transform, registry, block); + } + + /** + * Transform the given block using the given transform. + * + * @param block the block + * @param transform the transform + * @param registry the registry + * @param changedBlock the block to change + * @return the changed block + */ + private static BaseBlock transform(BaseBlock block, Transform transform, BlockRegistry registry, BaseBlock changedBlock) { + checkNotNull(block); + checkNotNull(transform); + checkNotNull(registry); + + Map states = registry.getStates(block); + + if (states == null) { + return changedBlock; + } + + for (State state : states.values()) { + if (state.hasDirection()) { + StateValue value = state.getValue(block); + if (value != null && value.getDirection() != null) { + StateValue newValue = getNewStateValue(state, transform, value.getDirection()); + if (newValue != null) { + newValue.set(changedBlock); + } + } + } + } + + return changedBlock; + } + + /** + * Get the new value with the transformed direction. + * + * @param state the state + * @param transform the transform + * @param oldDirection the old direction to transform + * @return a new state or null if none could be found + */ + @Nullable + private static StateValue getNewStateValue(State state, Transform transform, Vector oldDirection) { + Vector newDirection = transform.apply(oldDirection).subtract(transform.apply(Vector.ZERO)).normalize(); + StateValue newValue = null; + double closest = -2; + boolean found = false; + + for (StateValue v : state.valueMap().values()) { + if (v.getDirection() != null) { + double dot = v.getDirection().normalize().dot(newDirection); + if (dot >= closest) { + closest = dot; + newValue = v; + found = true; + } + } + } + + if (found) { + return newValue; + } else { + return null; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/EntityFunction.java b/src/main/java/com/sk89q/worldedit/function/EntityFunction.java new file mode 100644 index 000000000..f7048cd1a --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/EntityFunction.java @@ -0,0 +1,39 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; + +/** + * Applies a function to entities. + */ +public interface EntityFunction { + + /** + * Apply the function to the entity. + * + * @param entity the entity + * @return true if something was changed + * @throws WorldEditException thrown on an error + */ + public boolean apply(Entity entity) throws WorldEditException; + +} diff --git a/src/main/java/com/sk89q/worldedit/function/biome/BiomeReplace.java b/src/main/java/com/sk89q/worldedit/function/biome/BiomeReplace.java new file mode 100644 index 000000000..f877adce5 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/biome/BiomeReplace.java @@ -0,0 +1,56 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.biome; + +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.FlatRegionFunction; +import com.sk89q.worldedit.world.biome.BaseBiome; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Replaces the biome at the locations that this function is applied to. + */ +public class BiomeReplace implements FlatRegionFunction { + + private final Extent extent; + private BaseBiome biome; + + /** + * Create a new instance. + * + * @param extent an extent + * @param biome a biome + */ + public BiomeReplace(Extent extent, BaseBiome biome) { + checkNotNull(extent); + checkNotNull(biome); + this.extent = extent; + this.biome = biome; + } + + @Override + public boolean apply(Vector2D position) throws WorldEditException { + return extent.setBiome(position, biome); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java b/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java index b8d50bcc0..f7bdf8e11 100644 --- a/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java +++ b/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java @@ -64,7 +64,9 @@ public class ExtentBlockCopy implements RegionFunction { @Override public boolean apply(Vector position) throws WorldEditException { BaseBlock block = source.getBlock(position); - return destination.setBlock(transform.apply(position.subtract(from)).add(to), block); + Vector orig = position.subtract(from); + Vector transformed = transform.apply(orig); + return destination.setBlock(transformed.add(to), block); } } diff --git a/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java b/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java new file mode 100644 index 000000000..d172d10c6 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java @@ -0,0 +1,157 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.entity; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.CompoundTagBuilder; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.EntityFunction; +import com.sk89q.worldedit.internal.helper.MCDirections; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.Direction.Flag; +import com.sk89q.worldedit.util.Location; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Copies entities provided to the function to the provided destination + * {@code Extent}. + */ +public class ExtentEntityCopy implements EntityFunction { + + private final Extent destination; + private final Vector from; + private final Vector to; + private final Transform transform; + private boolean removing; + + /** + * Create a new instance. + * + * @param from the from position + * @param destination the destination {@code Extent} + * @param to the destination position + * @param transform the transformation to apply to both position and orientation + */ + public ExtentEntityCopy(Vector from, Extent destination, Vector to, Transform transform) { + checkNotNull(from); + checkNotNull(destination); + checkNotNull(to); + checkNotNull(transform); + this.destination = destination; + this.from = from; + this.to = to; + this.transform = transform; + } + + /** + * Return whether entities that are copied should be removed. + * + * @return true if removing + */ + public boolean isRemoving() { + return removing; + } + + /** + * Set whether entities that are copied should be removed. + * + * @param removing true if removing + */ + public void setRemoving(boolean removing) { + this.removing = removing; + } + + @Override + public boolean apply(Entity entity) throws WorldEditException { + BaseEntity state = entity.getState(); + if (state != null) { + Location location = entity.getLocation(); + Vector newPosition = transform.apply(location.toVector().subtract(from)); + Vector newDirection = transform.apply(location.getDirection()).subtract(transform.apply(Vector.ZERO)).normalize(); + Location newLocation = new Location(destination, newPosition.add(to), newDirection); + + // Some entities store their position data in NBT + state = transformNbtData(state); + + boolean success = destination.createEntity(newLocation, state) != null; + + // Remove + if (isRemoving() && success) { + entity.remove(); + } + + return success; + } else { + return false; + } + } + + /** + * Transform NBT data in the given entity state and return a new instance + * if the NBT data needs to be transformed. + * + * @param state the existing state + * @return a new state or the existing one + */ + private BaseEntity transformNbtData(BaseEntity state) { + CompoundTag tag = state.getNbtData(); + + if (tag != null) { + // Handle hanging entities (paintings, item frames, etc.) + boolean hasTilePosition = tag.containsKey("TileX") && tag.containsKey("TileY") && tag.containsKey("TileZ"); + boolean hasDirection = tag.containsKey("Direction"); + boolean hasLegacyDirection = tag.containsKey("Dir"); + + if (hasTilePosition) { + Vector tilePosition = new Vector(tag.asInt("TileX"), tag.asInt("TileY"), tag.asInt("TileZ")); + Vector newTilePosition = transform.apply(tilePosition.subtract(from)).add(to); + + CompoundTagBuilder builder = tag.createBuilder() + .putInt("TileX", newTilePosition.getBlockX()) + .putInt("TileY", newTilePosition.getBlockY()) + .putInt("TileZ", newTilePosition.getBlockZ()); + + if (hasDirection || hasLegacyDirection) { + int d = hasDirection ? tag.asInt("Direction") : MCDirections.fromLegacyHanging((byte) tag.asInt("Dir")); + Direction direction = MCDirections.fromHanging(d); + + if (direction != null) { + Vector vector = transform.apply(direction.toVector()).subtract(transform.apply(Vector.ZERO)).normalize(); + Direction newDirection = Direction.findClosest(vector, Flag.CARDINAL); + + builder.putByte("Direction", (byte) MCDirections.toHanging(newDirection)); + builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection))); + } + } + + return new BaseEntity(state.getTypeId(), builder.build()); + } + } + + return state; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask2D.java b/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask2D.java new file mode 100644 index 000000000..75240db18 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask2D.java @@ -0,0 +1,98 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.mask; + +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.biome.BaseBiome; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Tests true if the biome at applied points is the same as the one given. + */ +public class BiomeMask2D extends AbstractMask2D { + + private final Extent extent; + private final Set biomes = new HashSet(); + + /** + * Create a new biome mask. + * + * @param extent the extent + * @param biomes a list of biomes to match + */ + public BiomeMask2D(Extent extent, Collection biomes) { + checkNotNull(extent); + checkNotNull(biomes); + this.extent = extent; + this.biomes.addAll(biomes); + } + + /** + * Create a new biome mask. + * + * @param extent the extent + * @param biome an array of biomes to match + */ + public BiomeMask2D(Extent extent, BaseBiome... biome) { + this(extent, Arrays.asList(checkNotNull(biome))); + } + + /** + * Add the given biomes to the list of criteria. + * + * @param biomes a list of biomes + */ + public void add(Collection biomes) { + checkNotNull(biomes); + this.biomes.addAll(biomes); + } + + /** + * Add the given biomes to the list of criteria. + * + * @param biome an array of biomes + */ + public void add(BaseBiome... biome) { + add(Arrays.asList(checkNotNull(biome))); + } + + /** + * Get the list of biomes that are tested with. + * + * @return a list of biomes + */ + public Collection getBiomes() { + return biomes; + } + + @Override + public boolean test(Vector2D vector) { + BaseBiome biome = extent.getBiome(vector); + return biomes.contains(biome); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java b/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java index e05532438..91ac89c14 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -96,4 +97,11 @@ public class BlockMask extends AbstractExtentMask { BaseBlock block = getExtent().getBlock(vector); return blocks.contains(block) || blocks.contains(new BaseBlock(block.getType(), -1)); } + + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java b/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java index 81d143c3d..fe35ff1ef 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java @@ -21,6 +21,8 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.Vector; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkArgument; /** @@ -49,4 +51,10 @@ public class BoundedHeightMask extends AbstractMask { return vector.getY() >= minY && vector.getY() <= maxY; } + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java b/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java index 80e9719fc..c3d8f1037 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java @@ -23,6 +23,8 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockID; +import javax.annotation.Nullable; + /** * A mask that returns true whenever the block at the location is not * an air block (it contains some other block). @@ -43,4 +45,10 @@ public class ExistingBlockMask extends AbstractExtentMask { return getExtent().getLazyBlock(vector).getType() != BlockID.AIR; } + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index 611b1b662..96b4af82b 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -21,6 +21,8 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.Vector; +import javax.annotation.Nullable; + /** * Tests whether a given vector meets a criteria. */ @@ -34,4 +36,12 @@ public interface Mask { */ boolean test(Vector vector); + /** + * Get the 2D version of this mask if one exists. + * + * @return a 2D mask version or {@code null} if this mask can't be 2D + */ + @Nullable + Mask2D toMask2D(); + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java b/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java index d1a600ef4..d98fdadac 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java @@ -21,9 +21,12 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.Vector; +import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; @@ -86,7 +89,7 @@ public class MaskIntersection extends AbstractMask { @Override public boolean test(Vector vector) { - if (masks.size() == 0) { + if (masks.isEmpty()) { return false; } @@ -99,4 +102,19 @@ public class MaskIntersection extends AbstractMask { return true; } + @Nullable + @Override + public Mask2D toMask2D() { + List mask2dList = new ArrayList(); + for (Mask mask : masks) { + Mask2D mask2d = mask.toMask2D(); + if (mask2d != null) { + mask2dList.add(mask2d); + } else { + return null; + } + } + return new MaskIntersection2D(mask2dList); + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection2D.java b/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection2D.java new file mode 100644 index 000000000..02e605ff9 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection2D.java @@ -0,0 +1,100 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.mask; + +import com.sk89q.worldedit.Vector2D; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Tests true if all contained masks test true. + */ +public class MaskIntersection2D implements Mask2D { + + private final Set masks = new HashSet(); + + /** + * Create a new intersection. + * + * @param masks a list of masks + */ + public MaskIntersection2D(Collection masks) { + checkNotNull(masks); + this.masks.addAll(masks); + } + + /** + * Create a new intersection. + * + * @param mask a list of masks + */ + public MaskIntersection2D(Mask2D... mask) { + this(Arrays.asList(checkNotNull(mask))); + } + + /** + * Add some masks to the list. + * + * @param masks the masks + */ + public void add(Collection masks) { + checkNotNull(masks); + this.masks.addAll(masks); + } + + /** + * Add some masks to the list. + * + * @param mask the masks + */ + public void add(Mask2D... mask) { + add(Arrays.asList(checkNotNull(mask))); + } + + /** + * Get the masks that are tested with. + * + * @return the masks + */ + public Collection getMasks() { + return masks; + } + + @Override + public boolean test(Vector2D vector) { + if (masks.isEmpty()) { + return false; + } + + for (Mask2D mask : masks) { + if (!mask.test(vector)) { + return false; + } + } + + return true; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java b/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java index a02983f4a..955ab779e 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java @@ -21,7 +21,10 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.Vector; +import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** * Combines several masks and requires that one or more masks return true @@ -61,4 +64,18 @@ public class MaskUnion extends MaskIntersection { return false; } + @Nullable + @Override + public Mask2D toMask2D() { + List mask2dList = new ArrayList(); + for (Mask mask : getMasks()) { + Mask2D mask2d = mask.toMask2D(); + if (mask2d != null) { + mask2dList.add(mask2d); + } else { + return null; + } + } + return new MaskUnion2D(mask2dList); + } } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion2D.java b/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion2D.java new file mode 100644 index 000000000..099dc9a05 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion2D.java @@ -0,0 +1,62 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.mask; + +import com.sk89q.worldedit.Vector2D; + +import java.util.Collection; + +/** + * Tests true if any contained mask is true, even if it just one. + */ +public class MaskUnion2D extends MaskIntersection2D { + + /** + * Create a new union. + * + * @param masks a list of masks + */ + public MaskUnion2D(Collection masks) { + super(masks); + } + + /** + * Create a new union. + * + * @param mask a list of masks + */ + public MaskUnion2D(Mask2D... mask) { + super(mask); + } + + @Override + public boolean test(Vector2D vector) { + Collection masks = getMasks(); + + for (Mask2D mask : masks) { + if (mask.test(vector)) { + return true; + } + } + + return false; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/mask/Masks.java b/src/main/java/com/sk89q/worldedit/function/mask/Masks.java index 689b127a7..837bcd2ae 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/Masks.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/Masks.java @@ -22,6 +22,8 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.*; import com.sk89q.worldedit.session.request.Request; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -29,6 +31,9 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public final class Masks { + private static final AlwaysTrue ALWAYS_TRUE = new AlwaysTrue(); + private static final AlwaysFalse ALWAYS_FALSE = new AlwaysFalse(); + private Masks() { } @@ -38,12 +43,7 @@ public final class Masks { * @return a mask */ public static Mask alwaysTrue() { - return new AbstractMask() { - @Override - public boolean test(Vector vector) { - return true; - } - }; + return ALWAYS_TRUE; } /** @@ -52,12 +52,7 @@ public final class Masks { * @return a mask */ public static Mask2D alwaysTrue2D() { - return new AbstractMask2D() { - @Override - public boolean test(Vector2D vector) { - return true; - } - }; + return ALWAYS_TRUE; } /** @@ -67,12 +62,29 @@ public final class Masks { * @return a new mask */ public static Mask negate(final Mask mask) { + if (mask instanceof AlwaysTrue) { + return ALWAYS_FALSE; + } else if (mask instanceof AlwaysFalse) { + return ALWAYS_TRUE; + } + checkNotNull(mask); return new AbstractMask() { @Override public boolean test(Vector vector) { return !mask.test(vector); } + + @Nullable + @Override + public Mask2D toMask2D() { + Mask2D mask2d = mask.toMask2D(); + if (mask2d != null) { + return negate(mask2d); + } else { + return null; + } + } }; } @@ -83,6 +95,12 @@ public final class Masks { * @return a new mask */ public static Mask2D negate(final Mask2D mask) { + if (mask instanceof AlwaysTrue) { + return ALWAYS_FALSE; + } else if (mask instanceof AlwaysFalse) { + return ALWAYS_TRUE; + } + checkNotNull(mask); return new AbstractMask2D() { @Override @@ -92,6 +110,27 @@ public final class Masks { }; } + /** + * Return a 3-dimensional version of a 2D mask. + * + * @param mask the mask to make 3D + * @return a 3D mask + */ + public static Mask asMask(final Mask2D mask) { + return new AbstractMask() { + @Override + public boolean test(Vector vector) { + return mask.test(vector.toVector2D()); + } + + @Nullable + @Override + public Mask2D toMask2D() { + return mask; + } + }; + } + /** * Wrap an old-style mask and convert it to a new mask. *

@@ -113,6 +152,12 @@ public final class Masks { public boolean test(Vector vector) { return mask.matches(editSession, vector); } + + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } }; } @@ -135,6 +180,12 @@ public final class Masks { EditSession editSession = Request.request().getEditSession(); return editSession != null && mask.matches(editSession, vector); } + + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } }; } @@ -156,4 +207,40 @@ public final class Masks { }; } + private static class AlwaysTrue implements Mask, Mask2D { + @Override + public boolean test(Vector vector) { + return true; + } + + @Override + public boolean test(Vector2D vector) { + return true; + } + + @Nullable + @Override + public Mask2D toMask2D() { + return this; + } + } + + private static class AlwaysFalse implements Mask, Mask2D { + @Override + public boolean test(Vector vector) { + return false; + } + + @Override + public boolean test(Vector2D vector) { + return false; + } + + @Nullable + @Override + public Mask2D toMask2D() { + return this; + } + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java b/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java index f5aa09807..7c5b658d4 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java @@ -22,6 +22,8 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.math.noise.NoiseGenerator; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -89,4 +91,10 @@ public class NoiseFilter extends AbstractMask { return noiseGenerator.noise(vector) <= density; } + @Nullable + @Override + public Mask2D toMask2D() { + return new NoiseFilter2D(getNoiseGenerator(), getDensity()); + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java b/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java index 833a543ef..b5358cb30 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java @@ -21,10 +21,13 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.Vector; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; /** - * Checks whether the provided mask tests true for an offset position. + * Checks whether another mask tests true for a position that is offset + * a given vector. */ public class OffsetMask extends AbstractMask { @@ -87,4 +90,15 @@ public class OffsetMask extends AbstractMask { return getMask().test(vector.add(offset)); } + @Nullable + @Override + public Mask2D toMask2D() { + Mask2D childMask = getMask().toMask2D(); + if (childMask != null) { + return new OffsetMask2D(childMask, getOffset().toVector2D()); + } else { + return null; + } + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask2D.java b/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask2D.java new file mode 100644 index 000000000..50d2835ba --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask2D.java @@ -0,0 +1,91 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.mask; + +import com.sk89q.worldedit.Vector2D; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Checks whether another mask tests true for a position that is offset + * a given vector. + */ +public class OffsetMask2D extends AbstractMask2D { + + private Mask2D mask; + private Vector2D offset; + + /** + * Create a new instance. + * + * @param mask the mask + * @param offset the offset + */ + public OffsetMask2D(Mask2D mask, Vector2D offset) { + checkNotNull(mask); + checkNotNull(offset); + this.mask = mask; + this.offset = offset; + } + + /** + * Get the mask. + * + * @return the mask + */ + public Mask2D getMask() { + return mask; + } + + /** + * Set the mask. + * + * @param mask the mask + */ + public void setMask(Mask2D mask) { + checkNotNull(mask); + this.mask = mask; + } + + /** + * Get the offset. + * + * @return the offset + */ + public Vector2D getOffset() { + return offset; + } + + /** + * Set the offset. + * + * @param offset the offset + */ + public void setOffset(Vector2D offset) { + checkNotNull(offset); + this.offset = offset; + } + + @Override + public boolean test(Vector2D vector) { + return getMask().test(vector.add(offset)); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java b/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java index 04e1c4dc3..1ddf891fe 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java @@ -22,6 +22,8 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.regions.Region; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -64,4 +66,10 @@ public class RegionMask extends AbstractMask { return region.contains(vector); } + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java b/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java index 2428643c2..b6a10972a 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java @@ -24,6 +24,8 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockType; +import javax.annotation.Nullable; + public class SolidBlockMask extends AbstractExtentMask { public SolidBlockMask(Extent extent) { @@ -37,4 +39,10 @@ public class SolidBlockMask extends AbstractExtentMask { return !BlockType.canPassThrough(lazyBlock.getType(), lazyBlock.getData()); } + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 2c3c5aee8..3f9887b28 100644 --- a/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -19,20 +19,25 @@ package com.sk89q.worldedit.function.operation; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.CombinedRegionFunction; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; import com.sk89q.worldedit.function.block.ExtentBlockCopy; +import com.sk89q.worldedit.function.entity.ExtentEntityCopy; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.Region; +import java.util.List; + import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -48,9 +53,11 @@ public class ForwardExtentCopy implements Operation { private final Extent source; private final Extent destination; private final Region region; + private final Vector from; private final Vector to; private int repetitions = 1; private Mask sourceMask = Masks.alwaysTrue(); + private boolean removingEntities; private RegionFunction sourceFunction = null; private Transform transform = new Identity(); private Transform currentTransform = null; @@ -58,21 +65,38 @@ public class ForwardExtentCopy implements Operation { private int affected; /** - * Create a new copy. + * Create a new copy using the region's lowest minimum point as the + * "from" position. * * @param source the source extent * @param region the region to copy * @param destination the destination extent - * @param to the destination position, starting from the the lowest X, Y, Z + * @param to the destination position + * @see #ForwardExtentCopy(Extent, Region, Vector, Extent, Vector) the main constructor */ public ForwardExtentCopy(Extent source, Region region, Extent destination, Vector to) { + this(source, region, region.getMinimumPoint(), destination, to); + } + + /** + * Create a new copy. + * + * @param source the source extent + * @param region the region to copy + * @param from the source position + * @param destination the destination extent + * @param to the destination position + */ + public ForwardExtentCopy(Extent source, Region region, Vector from, Extent destination, Vector to) { checkNotNull(source); checkNotNull(region); + checkNotNull(from); checkNotNull(destination); checkNotNull(to); this.source = source; this.destination = destination; this.region = region; + this.from = from; this.to = to; } @@ -159,6 +183,24 @@ public class ForwardExtentCopy implements Operation { this.repetitions = repetitions; } + /** + * Return whether entities that are copied should be removed. + * + * @return true if removing + */ + public boolean isRemovingEntities() { + return removingEntities; + } + + /** + * Set whether entities that are copied should be removed. + * + * @param removing true if removing + */ + public void setRemovingEntities(boolean removingEntities) { + this.removingEntities = removingEntities; + } + /** * Get the number of affected objects. * @@ -182,13 +224,19 @@ public class ForwardExtentCopy implements Operation { currentTransform = transform; } - ExtentBlockCopy copy = new ExtentBlockCopy(source, region.getMinimumPoint(), destination, to, currentTransform); - RegionMaskingFilter filter = new RegionMaskingFilter(sourceMask, copy); + ExtentBlockCopy blockCopy = new ExtentBlockCopy(source, from, destination, to, currentTransform); + RegionMaskingFilter filter = new RegionMaskingFilter(sourceMask, blockCopy); RegionFunction function = sourceFunction != null ? new CombinedRegionFunction(filter, sourceFunction) : filter; - RegionVisitor visitor = new RegionVisitor(region, function); - lastVisitor = visitor; + RegionVisitor blockVisitor = new RegionVisitor(region, function); + + ExtentEntityCopy entityCopy = new ExtentEntityCopy(from, destination, to, currentTransform); + entityCopy.setRemoving(removingEntities); + List entities = source.getEntities(region); + EntityVisitor entityVisitor = new EntityVisitor(entities.iterator(), entityCopy); + + lastVisitor = blockVisitor; currentTransform = currentTransform.combine(transform); - return new DelegateOperation(this, visitor); + return new DelegateOperation(this, new OperationQueue(blockVisitor, entityVisitor)); } else { return null; } diff --git a/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java b/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java index 21df293d5..c16718330 100644 --- a/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java +++ b/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java @@ -19,21 +19,18 @@ package com.sk89q.worldedit.function.pattern; -import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import static com.google.common.base.Preconditions.checkNotNull; /** - * A pattern that reads from {@link CuboidClipboard}. - * - * @deprecated May be removed without notice, but there is no direct replacement yet + * A pattern that reads from {@link Clipboard}. */ -@Deprecated public class ClipboardPattern extends AbstractPattern { - private final CuboidClipboard clipboard; + private final Clipboard clipboard; private final Vector size; /** @@ -41,10 +38,10 @@ public class ClipboardPattern extends AbstractPattern { * * @param clipboard the clipboard */ - public ClipboardPattern(CuboidClipboard clipboard) { + public ClipboardPattern(Clipboard clipboard) { checkNotNull(clipboard); this.clipboard = clipboard; - this.size = clipboard.getSize(); + this.size = clipboard.getMaximumPoint().subtract(clipboard.getMinimumPoint()).add(1, 1, 1); } @Override @@ -53,7 +50,7 @@ public class ClipboardPattern extends AbstractPattern { int yp = Math.abs(position.getBlockY()) % size.getBlockY(); int zp = Math.abs(position.getBlockZ()) % size.getBlockZ(); - return clipboard.getPoint(new Vector(xp, yp, zp)); + return clipboard.getBlock(clipboard.getMinimumPoint().add(new Vector(xp, yp, zp))); } } diff --git a/src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java b/src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java index ae9d37221..024c674fa 100644 --- a/src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java +++ b/src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java @@ -85,7 +85,7 @@ public class RepeatingExtentPattern extends AbstractPattern { @Override public BaseBlock apply(Vector position) { Vector base = position.add(offset); - Vector size = extent.getMaximumPoint().subtract(extent.getMinimumPoint()); + Vector size = extent.getMaximumPoint().subtract(extent.getMinimumPoint()).add(1, 1, 1); int x = base.getBlockX() % size.getBlockX(); int y = base.getBlockY() % size.getBlockY(); int z = base.getBlockZ() % size.getBlockZ(); diff --git a/src/main/java/com/sk89q/worldedit/function/visitor/EntityVisitor.java b/src/main/java/com/sk89q/worldedit/function/visitor/EntityVisitor.java new file mode 100644 index 000000000..d412103e9 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/visitor/EntityVisitor.java @@ -0,0 +1,78 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.visitor; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.function.EntityFunction; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.RunContext; + +import java.util.Iterator; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Visits entities as provided by an {@code Iterator}. + */ +public class EntityVisitor implements Operation { + + private final Iterator iterator; + private final EntityFunction function; + private int affected = 0; + + /** + * Create a new instance. + * + * @param iterator the iterator + * @param function the function + */ + public EntityVisitor(Iterator iterator, EntityFunction function) { + checkNotNull(iterator); + checkNotNull(function); + this.iterator = iterator; + this.function = function; + } + + /** + * Get the number of affected objects. + * + * @return the number of affected + */ + public int getAffected() { + return affected; + } + + @Override + public Operation resume(RunContext run) throws WorldEditException { + while (iterator.hasNext()) { + if (function.apply(iterator.next())) { + affected++; + } + } + + return null; + } + + @Override + public void cancel() { + } + +} diff --git a/src/main/java/com/sk89q/worldedit/history/change/EntityCreate.java b/src/main/java/com/sk89q/worldedit/history/change/EntityCreate.java new file mode 100644 index 000000000..80d0bbcaa --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/history/change/EntityCreate.java @@ -0,0 +1,68 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.history.change; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.history.UndoContext; +import com.sk89q.worldedit.util.Location; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logs the creation of an entity and removes the entity upon undo. + */ +public class EntityCreate implements Change { + + private final Location location; + private final BaseEntity state; + private Entity entity; + + /** + * Create a new instance. + * + * @param location the location + * @param state the state of the created entity + * @param entity the entity that was created + */ + public EntityCreate(Location location, BaseEntity state, Entity entity) { + checkNotNull(location); + checkNotNull(state); + checkNotNull(entity); + this.location = location; + this.state = state; + this.entity = entity; + } + + @Override + public void undo(UndoContext context) throws WorldEditException { + if (entity != null) { + entity.remove(); + entity = null; + } + } + + @Override + public void redo(UndoContext context) throws WorldEditException { + entity = checkNotNull(context.getExtent()).createEntity(location, state); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/history/change/EntityRemove.java b/src/main/java/com/sk89q/worldedit/history/change/EntityRemove.java new file mode 100644 index 000000000..eef480a91 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/history/change/EntityRemove.java @@ -0,0 +1,65 @@ +/* + * 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.history.change; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.history.UndoContext; +import com.sk89q.worldedit.util.Location; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Tracks the removal of an entity. + */ +public class EntityRemove implements Change { + + private final Location location; + private final BaseEntity state; + private Entity entity; + + /** + * Create a new instance. + * + * @param location the location + * @param state the state of the created entity + */ + public EntityRemove(Location location, BaseEntity state) { + checkNotNull(location); + checkNotNull(state); + this.location = location; + this.state = state; + } + + @Override + public void undo(UndoContext context) throws WorldEditException { + entity = checkNotNull(context.getExtent()).createEntity(location, state); + } + + @Override + public void redo(UndoContext context) throws WorldEditException { + if (entity != null) { + entity.remove(); + entity = null; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/Constants.java b/src/main/java/com/sk89q/worldedit/internal/Constants.java new file mode 100644 index 000000000..9194866b4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/Constants.java @@ -0,0 +1,45 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public final class Constants { + + private Constants() { + } + + /** + * List of top level NBT fields that should not be copied to a world, + * such as UUIDLeast and UUIDMost. + */ + public static final List NO_COPY_ENTITY_NBT_FIELDS; + + static { + NO_COPY_ENTITY_NBT_FIELDS = Collections.unmodifiableList(Arrays.asList( + "UUIDLeast", "UUIDMost", // Bukkit and Vanilla + "WorldUUIDLeast", "WorldUUIDMost", // Bukkit and Vanilla + "PersistentIDMSB", "PersistentIDLSB" // Forge + )); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java index a14d168f1..d800de00a 100644 --- a/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java @@ -19,17 +19,28 @@ package com.sk89q.worldedit.internal; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.WorldData; import javax.annotation.Nullable; +import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -87,42 +98,6 @@ public class LocalWorldAdapter extends LocalWorld { return world.setBlock(position, block, notifyAndLight); } - @Override - @Deprecated - public boolean setBlockType(Vector position, int type) { - return world.setBlockType(position, type); - } - - @Override - @Deprecated - public boolean setBlockTypeFast(Vector position, int type) { - return world.setBlockTypeFast(position, type); - } - - @Override - @Deprecated - public void setBlockData(Vector position, int data) { - world.setBlockData(position, data); - } - - @Override - @Deprecated - public void setBlockDataFast(Vector position, int data) { - world.setBlockDataFast(position, data); - } - - @Override - @Deprecated - public boolean setTypeIdAndData(Vector position, int type, int data) { - return world.setTypeIdAndData(position, type, data); - } - - @Override - @Deprecated - public boolean setTypeIdAndDataFast(Vector position, int type, int data) { - return world.setTypeIdAndDataFast(position, type, data); - } - @Override public int getBlockLightLevel(Vector position) { return world.getBlockLightLevel(position); @@ -134,13 +109,13 @@ public class LocalWorldAdapter extends LocalWorld { } @Override - public BiomeType getBiome(Vector2D position) { + public BaseBiome getBiome(Vector2D position) { return world.getBiome(position); } @Override - public void setBiome(Vector2D position, BiomeType biome) { - world.setBiome(position, biome); + public boolean setBiome(Vector2D position, BaseBiome biome) { + return world.setBiome(position, biome); } @Override @@ -158,38 +133,6 @@ public class LocalWorldAdapter extends LocalWorld { world.simulateBlockMine(position); } - @Override - public LocalEntity[] getEntities(Region region) { - return world.getEntities(region); - } - - @Override - public int killEntities(LocalEntity... entity) { - return world.killEntities(entity); - } - - @Override - @Deprecated - public int killMobs(Vector origin, int radius) { - return world.killMobs(origin, radius); - } - - @Override - @Deprecated - public int killMobs(Vector origin, int radius, boolean killPets) { - return world.killMobs(origin, radius, killPets); - } - - @Override - public int killMobs(Vector origin, double radius, int flags) { - return world.killMobs(origin, radius, flags); - } - - @Override - public int removeEntities(EntityType type, Vector origin, int radius) { - return world.removeEntities(type, origin, radius); - } - @Override public boolean regenerate(Region region, EditSession editSession) { return world.regenerate(region, editSession); @@ -255,6 +198,11 @@ public class LocalWorldAdapter extends LocalWorld { return world.queueBlockBreakEffect(server, position, blockId, priority); } + @Override + public WorldData getWorldData() { + return world.getWorldData(); + } + @Override public boolean equals(Object other) { return world.equals(other); @@ -275,39 +223,36 @@ public class LocalWorldAdapter extends LocalWorld { return world.getMaximumPoint(); } + @Override + public List getEntities(Region region) { + return world.getEntities(region); + } + @Override public BaseBlock getBlock(Vector position) { return world.getBlock(position); } - @Override - public boolean copyFromWorld(Vector position, BaseBlock block) { - return false; - } - - @Override - public boolean copyToWorld(Vector position, BaseBlock block) { - return false; - } - @Override public BaseBlock getLazyBlock(Vector position) { return world.getLazyBlock(position); } @Override - public boolean setBlock(Vector position, BaseBlock block) { - try { - return world.setBlock(position, block); - } catch (WorldEditException e) { - throw new RuntimeException(e); - } + @Nullable + public Operation commit() { + return world.commit(); } @Override @Nullable - public Operation commit() { - return world.commit(); + public Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) { + return world.createEntity(location, entity); + } + + @Override + public List getEntities() { + return world.getEntities(); } public static LocalWorldAdapter adapt(World world) { diff --git a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java index 16d86ee5e..32d0922cc 100644 --- a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.internal; -import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.ServerInterface; import com.sk89q.worldedit.entity.Player; @@ -67,11 +66,6 @@ public class ServerInterfaceAdapter extends ServerInterface { platform.reload(); } - @Override - public BiomeTypes getBiomes() { - return platform.getBiomes(); - } - @Override public int schedule(long delay, long period, Runnable task) { return platform.schedule(delay, period, task); diff --git a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index ed29e8fb5..fbccb8204 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -19,13 +19,20 @@ package com.sk89q.worldedit.internal.command; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.UnknownDirectionException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.annotation.Direction; @@ -33,9 +40,18 @@ import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.TreeGenerator.TreeType; -import com.sk89q.worldedit.util.command.parametric.*; +import com.sk89q.worldedit.util.command.parametric.ArgumentStack; +import com.sk89q.worldedit.util.command.parametric.BindingBehavior; +import com.sk89q.worldedit.util.command.parametric.BindingHelper; +import com.sk89q.worldedit.util.command.parametric.BindingMatch; +import com.sk89q.worldedit.util.command.parametric.ParameterException; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.Biomes; +import com.sk89q.worldedit.world.registry.BiomeRegistry; import java.util.Arrays; +import java.util.List; /** * Binds standard WorldEdit classes such as {@link Player} and {@link LocalSession}. @@ -158,11 +174,14 @@ public class WorldEditBinding extends BindingHelper { ParserContext parserContext = new ParserContext(); parserContext.setActor(context.getContext().getLocals().get(Actor.class)); if (actor instanceof Entity) { - parserContext.setWorld(((Entity) actor).getWorld()); + Extent extent = ((Entity) actor).getExtent(); + if (extent instanceof World) { + parserContext.setWorld((World) extent); + } } parserContext.setSession(worldEdit.getSessionManager().get(actor)); try { - return worldEdit.getBlockRegistry().parseFromInput(context.next(), parserContext); + return worldEdit.getBlockFactory().parseFromInput(context.next(), parserContext); } catch (NoMatchException e) { throw new ParameterException(e.getMessage(), e); } @@ -184,11 +203,14 @@ public class WorldEditBinding extends BindingHelper { ParserContext parserContext = new ParserContext(); parserContext.setActor(context.getContext().getLocals().get(Actor.class)); if (actor instanceof Entity) { - parserContext.setWorld(((Entity) actor).getWorld()); + Extent extent = ((Entity) actor).getExtent(); + if (extent instanceof World) { + parserContext.setWorld((World) extent); + } } parserContext.setSession(worldEdit.getSessionManager().get(actor)); try { - return worldEdit.getPatternRegistry().parseFromInput(context.next(), parserContext); + return worldEdit.getPatternFactory().parseFromInput(context.next(), parserContext); } catch (NoMatchException e) { throw new ParameterException(e.getMessage(), e); } @@ -210,11 +232,14 @@ public class WorldEditBinding extends BindingHelper { ParserContext parserContext = new ParserContext(); parserContext.setActor(context.getContext().getLocals().get(Actor.class)); if (actor instanceof Entity) { - parserContext.setWorld(((Entity) actor).getWorld()); + Extent extent = ((Entity) actor).getExtent(); + if (extent instanceof World) { + parserContext.setWorld((World) extent); + } } parserContext.setSession(worldEdit.getSessionManager().get(actor)); try { - return worldEdit.getMaskRegistry().parseFromInput(context.next(), parserContext); + return worldEdit.getMaskFactory().parseFromInput(context.next(), parserContext); } catch (NoMatchException e) { throw new ParameterException(e.getMessage(), e); } @@ -266,25 +291,40 @@ public class WorldEditBinding extends BindingHelper { } /** - * Gets an {@link BiomeType} from a {@link ArgumentStack}. + * Gets an {@link BaseBiome} from a {@link ArgumentStack}. * * @param context the context * @return a pattern * @throws ParameterException on error * @throws WorldEditException on error */ - @BindingMatch(type = BiomeType.class, + @BindingMatch(type = BaseBiome.class, behavior = BindingBehavior.CONSUMES, consumedCount = 1) - public BiomeType getBiomeType(ArgumentStack context) throws ParameterException, WorldEditException { + public BaseBiome getBiomeType(ArgumentStack context) throws ParameterException, WorldEditException { String input = context.next(); if (input != null) { - BiomeType type = worldEdit.getServer().getBiomes().get(input); - if (type != null) { - return type; + Actor actor = context.getContext().getLocals().get(Actor.class); + World world; + if (actor instanceof Entity) { + Extent extent = ((Entity) actor).getExtent(); + if (extent instanceof World) { + world = (World) extent; + } else { + throw new ParameterException("A world is required."); + } + } else { + throw new ParameterException("An entity is required."); + } + + BiomeRegistry biomeRegistry = world.getWorldData().getBiomeRegistry(); + List knownBiomes = biomeRegistry.getBiomes(); + BaseBiome biome = Biomes.findBiomeByName(knownBiomes, input, biomeRegistry); + if (biome != null) { + return biome; } else { throw new ParameterException( - String.format("Can't recognize biome type '%s' -- use //biomelist to list available types", input)); + String.format("Can't recognize biome type '%s' -- use /biomelist to list available types", input)); } } else { throw new ParameterException( diff --git a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java index 6b8831d13..393e8c134 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java @@ -150,7 +150,7 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper { @ExceptionMatch public void convert(WorldEditException e) throws CommandException { - throw new CommandException(e.getMessage()); + throw new CommandException(e.getMessage(), e); } } diff --git a/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java b/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java new file mode 100644 index 000000000..d9e75d62f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java @@ -0,0 +1,80 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.helper; + +import com.sk89q.worldedit.util.Direction; + +/** + * Utility methods for working with directions in Minecraft. + */ +public final class MCDirections { + + private MCDirections() { + } + + public static Direction fromHanging(int i) { + switch (i) { + case 0: + return Direction.SOUTH; + case 1: + return Direction.WEST; + case 2: + return Direction.NORTH; + case 3: + return Direction.EAST; + default: + return Direction.NORTH; + } + } + + public static int toHanging(Direction direction) { + switch (direction) { + case SOUTH: + return 0; + case WEST: + return 1; + case NORTH: + return 2; + case EAST: + return 3; + default: + return 0; + } + } + + public static int fromLegacyHanging(byte i) { + switch (i) { + case 0: return 2; + case 1: return 1; + case 2: return 0; + default: return 3; + } + } + + public static byte toLegacyHanging(int i) { + switch (i) { + case 0: return (byte) 2; + case 1: return (byte) 1; + case 2: return (byte) 0; + default: return (byte) 3; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/registry/AbstractRegistry.java b/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java similarity index 88% rename from src/main/java/com/sk89q/worldedit/internal/registry/AbstractRegistry.java rename to src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java index b466b5bb9..95563fcc0 100644 --- a/src/main/java/com/sk89q/worldedit/internal/registry/AbstractRegistry.java +++ b/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java @@ -30,22 +30,22 @@ import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; /** - * An abstract implementation of a registry for internal usage. + * An abstract implementation of a factory for internal usage. * - * @param the element that the registry returns + * @param the element that the factory returns */ @SuppressWarnings("ProtectedField") -public abstract class AbstractRegistry { +public abstract class AbstractFactory { protected final WorldEdit worldEdit; protected final List> parsers = new ArrayList>(); /** - * Create a new registry. + * Create a new factory. * * @param worldEdit the WorldEdit instance */ - protected AbstractRegistry(WorldEdit worldEdit) { + protected AbstractFactory(WorldEdit worldEdit) { checkNotNull(worldEdit); this.worldEdit = worldEdit; } diff --git a/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java b/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java index afe702852..059c95ba4 100644 --- a/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java +++ b/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java @@ -24,7 +24,7 @@ import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.input.InputParseException; /** - * Input parser interface for {@link AbstractRegistry}. + * Input parser interface for {@link AbstractFactory}. * * @param the element */ diff --git a/src/main/java/com/sk89q/worldedit/internal/util/AbstractAdapter.java b/src/main/java/com/sk89q/worldedit/internal/util/AbstractAdapter.java new file mode 100644 index 000000000..c4ab6305e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/util/AbstractAdapter.java @@ -0,0 +1,52 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.util; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Abstract class for adapters. + * + * @param class of adapted objects + */ +public abstract class AbstractAdapter { + + private final E object; + + /** + * Create a new instance. + * + * @param object the object to adapt + */ + public AbstractAdapter(E object) { + checkNotNull(object); + this.object = object; + } + + /** + * Get the object. + * + * @return the object + */ + public E getHandle() { + return object; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/math/MathUtils.java b/src/main/java/com/sk89q/worldedit/math/MathUtils.java index 992a5e955..568019499 100644 --- a/src/main/java/com/sk89q/worldedit/math/MathUtils.java +++ b/src/main/java/com/sk89q/worldedit/math/MathUtils.java @@ -24,6 +24,15 @@ package com.sk89q.worldedit.math; */ public final class MathUtils { + /** + * Safe minimum, such that 1 / SAFE_MIN does not overflow. + *

+ * In IEEE 754 arithmetic, this is also the smallest normalized number + * 2-1022. The value of this constant is from Apache Commons + * Math 2.2. + */ + public static final double SAFE_MIN = 0x1.0p-1022; + private MathUtils() { } diff --git a/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java b/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java index df50071f3..3540daa77 100644 --- a/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java +++ b/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java @@ -109,6 +109,7 @@ public class AffineTransform implements Transform { // =================================================================== // accessors + @Override public boolean isIdentity() { if (m00 != 1) return false; @@ -243,6 +244,7 @@ public class AffineTransform implements Transform { } public AffineTransform rotateX(double theta) { + theta = Math.toRadians(theta); double cot = Math.cos(theta); double sit = Math.sin(theta); return concatenate( @@ -253,6 +255,7 @@ public class AffineTransform implements Transform { } public AffineTransform rotateY(double theta) { + theta = Math.toRadians(theta); double cot = Math.cos(theta); double sit = Math.sin(theta); return concatenate( @@ -263,6 +266,7 @@ public class AffineTransform implements Transform { } public AffineTransform rotateZ(double theta) { + theta = Math.toRadians(theta); double cot = Math.cos(theta); double sit = Math.sin(theta); return concatenate( @@ -280,6 +284,10 @@ public class AffineTransform implements Transform { return concatenate(new AffineTransform(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0)); } + public AffineTransform scale(Vector vec) { + return scale(vec.getX(), vec.getY(), vec.getZ()); + } + @Override public Vector apply(Vector vector) { return new Vector( @@ -288,6 +296,10 @@ public class AffineTransform implements Transform { vector.getX() * m20 + vector.getY() * m21 + vector.getZ() * m22 + m23); } + public AffineTransform combine(AffineTransform other) { + return concatenate(other); + } + @Override public Transform combine(Transform other) { if (other instanceof AffineTransform) { @@ -296,4 +308,11 @@ public class AffineTransform implements Transform { return new CombinedTransform(this, other); } } + + @Override + public String toString() { + return String.format("Affine[%g %g %g %g, %g %g %g %g, %g %g %g %g]}", m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23); + } + + } \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java b/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java index 84b1a474b..eab410b99 100644 --- a/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java +++ b/src/main/java/com/sk89q/worldedit/math/transform/CombinedTransform.java @@ -54,6 +54,17 @@ public class CombinedTransform implements Transform { this(transforms.toArray(new Transform[checkNotNull(transforms).size()])); } + @Override + public boolean isIdentity() { + for (Transform transform : transforms) { + if (!transform.isIdentity()) { + return false; + } + } + + return true; + } + @Override public Vector apply(Vector vector) { for (Transform transform : transforms) { diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Identity.java b/src/main/java/com/sk89q/worldedit/math/transform/Identity.java index a5f22038d..cac70ba36 100644 --- a/src/main/java/com/sk89q/worldedit/math/transform/Identity.java +++ b/src/main/java/com/sk89q/worldedit/math/transform/Identity.java @@ -26,6 +26,11 @@ import com.sk89q.worldedit.Vector; */ public class Identity implements Transform { + @Override + public boolean isIdentity() { + return true; + } + @Override public Vector apply(Vector vector) { return vector; diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Transform.java b/src/main/java/com/sk89q/worldedit/math/transform/Transform.java index 05a7d5196..b8df8bcbd 100644 --- a/src/main/java/com/sk89q/worldedit/math/transform/Transform.java +++ b/src/main/java/com/sk89q/worldedit/math/transform/Transform.java @@ -19,13 +19,29 @@ package com.sk89q.worldedit.math.transform; -import com.google.common.base.Function; import com.sk89q.worldedit.Vector; /** * Makes a transformation of {@link Vector}s. */ -public interface Transform extends Function { +public interface Transform { + + /** + * Return whether this transform is an identity. + * + *

If it is not known, then {@code false} must be returned.

+ * + * @return true if identity + */ + boolean isIdentity(); + + /** + * Returns the result of applying the function to the input. + * + * @param input the input + * @return the result + */ + Vector apply(Vector input); /** * Create a new inverse transform. diff --git a/src/main/java/com/sk89q/worldedit/math/transform/Transforms.java b/src/main/java/com/sk89q/worldedit/math/transform/Transforms.java index 35f8a3df8..cfb6356af 100644 --- a/src/main/java/com/sk89q/worldedit/math/transform/Transforms.java +++ b/src/main/java/com/sk89q/worldedit/math/transform/Transforms.java @@ -43,7 +43,7 @@ public final class Transforms { public static Location transform(Location location, Transform transform) { checkNotNull(location); checkNotNull(transform); - return new Location(location.getWorld(), transform.apply(location.toVector()), location.getDirection()); + return new Location(location.getExtent(), transform.apply(location.toVector()), location.getDirection()); } } diff --git a/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java b/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java index e6eea8cff..2e9a12817 100644 --- a/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java +++ b/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java @@ -19,7 +19,12 @@ package com.sk89q.worldedit.regions; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.geom.Polygons; import com.sk89q.worldedit.regions.iterator.FlatRegion3DIterator; import com.sk89q.worldedit.regions.iterator.FlatRegionIterator; @@ -28,6 +33,8 @@ import com.sk89q.worldedit.world.World; import java.util.Iterator; import java.util.List; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Represents a cylindrical region. * @@ -84,6 +91,23 @@ public class CylinderRegion extends AbstractRegion implements FlatRegion { hasY = true; } + /** + * Construct the region. + * + * @param center the center position + * @param radius the radius along the X and Z axes + * @param minY the minimum Y, inclusive + * @param maxY the maximum Y, inclusive + */ + public CylinderRegion(Vector center, Vector2D radius, int minY, int maxY) { + super(null); + setCenter(center.toVector2D()); + setRadius(radius); + this.minY = minY; + this.maxY = maxY; + hasY = true; + } + public CylinderRegion(CylinderRegion region) { this(region.world, region.getCenter(), region.getRadius(), region.minY, region.maxY); hasY = region.hasY; @@ -377,4 +401,24 @@ public class CylinderRegion extends AbstractRegion implements FlatRegion { public List polygonize(int maxPoints) { return Polygons.polygonizeCylinder(center, radius, maxPoints); } + + /** + * Return a new instance with the given center and radius in the X and Z + * axes with a Y that extends from the bottom of the extent to the top + * of the extent. + * + * @param extent the extent + * @param center the center position + * @param radius the radius in the X and Z axes + * @return a region + */ + public static CylinderRegion createRadius(Extent extent, Vector center, double radius) { + checkNotNull(extent); + checkNotNull(center); + Vector2D radiusVec = new Vector2D(radius, radius); + int minY = extent.getMinimumPoint().getBlockY(); + int maxY = extent.getMaximumPoint().getBlockY(); + return new CylinderRegion(center, radiusVec, minY, maxY); + } + } diff --git a/src/main/java/com/sk89q/worldedit/regions/TransformRegion.java b/src/main/java/com/sk89q/worldedit/regions/TransformRegion.java new file mode 100644 index 000000000..b024bf867 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/regions/TransformRegion.java @@ -0,0 +1,190 @@ +/* + * 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.regions; + +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.math.transform.Identity; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.World; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Transforms another region according to a provided vector {@code Transform}. + * + * @see Transform + */ +public class TransformRegion extends AbstractRegion { + + private final Region region; + private Transform transform = new Identity(); + + /** + * Create a new instance. + * + * @param region the region + * @param transform the transform + */ + public TransformRegion(Region region, Transform transform) { + this(null, region, transform); + } + + /** + * Create a new instance. + * + * @param world the world, which may be null + * @param region the region + * @param transform the transform + */ + public TransformRegion(@Nullable World world, Region region, Transform transform) { + super(world); + checkNotNull(region); + checkNotNull(transform); + this.region = region; + this.transform = transform; + } + + /** + * Get the untransformed, base region. + * + * @return the base region + */ + public Region getRegion() { + return region; + } + + /** + * Get the transform that is applied. + * + * @return the transform + */ + public Transform getTransform() { + return transform; + } + + /** + * Set the transform that is applied. + * + * @param transform the transform + */ + public void setTransform(Transform transform) { + checkNotNull(transform); + this.transform = transform; + } + + @Override + public Vector getMinimumPoint() { + return transform.apply(region.getMinimumPoint()); + } + + @Override + public Vector getMaximumPoint() { + return transform.apply(region.getMaximumPoint()); + } + + @Override + public Vector getCenter() { + return transform.apply(region.getCenter()); + } + + @Override + public int getArea() { + return region.getArea(); // Cannot transform this + } + + @Override + public int getWidth() { + return getMaximumPoint().subtract(getMinimumPoint()).getBlockX() + 1; + } + + @Override + public int getHeight() { + return getMaximumPoint().subtract(getMinimumPoint()).getBlockY() + 1; + } + + @Override + public int getLength() { + return getMaximumPoint().subtract(getMinimumPoint()).getBlockZ() + 1; + } + + @Override + public void expand(Vector... changes) throws RegionOperationException { + throw new RegionOperationException("Can't expand a TransformedRegion"); + } + + @Override + public void contract(Vector... changes) throws RegionOperationException { + throw new RegionOperationException("Can't contract a TransformedRegion"); + } + + @Override + public void shift(Vector change) throws RegionOperationException { + throw new RegionOperationException("Can't change a TransformedRegion"); + } + + @Override + public boolean contains(Vector pt) { + return region.contains(transform.inverse().apply(pt)); + } + + @Override + public List polygonize(int maxPoints) { + List origPoints = region.polygonize(maxPoints); + List transformedPoints = new ArrayList(); + for (BlockVector2D vector : origPoints) { + transformedPoints.add(transform.apply(vector.toVector(0)).toVector2D().toBlockVector2D()); + } + return transformedPoints; + } + + @Override + public Iterator iterator() { + final Iterator it = region.iterator(); + + return new Iterator() { + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public BlockVector next() { + BlockVector next = it.next(); + if (next != null) { + return transform.apply(next).toBlockVector(); + } else { + return null; + } + } + + @Override + public void remove() { + it.remove(); + } + }; + } +} diff --git a/src/main/java/com/sk89q/worldedit/regions/shape/ArbitraryBiomeShape.java b/src/main/java/com/sk89q/worldedit/regions/shape/ArbitraryBiomeShape.java index 2da795df9..a5a3b883d 100644 --- a/src/main/java/com/sk89q/worldedit/regions/shape/ArbitraryBiomeShape.java +++ b/src/main/java/com/sk89q/worldedit/regions/shape/ArbitraryBiomeShape.java @@ -19,12 +19,12 @@ package com.sk89q.worldedit.regions.shape; -import com.sk89q.worldedit.BiomeType; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.biome.BaseBiome; /** * Generates solid and hollow shapes according to materials returned by the @@ -58,7 +58,7 @@ public abstract class ArbitraryBiomeShape { cacheSizeX = (int) (max.getX() - cacheOffsetX + 2); cacheSizeZ = (int) (max.getZ() - cacheOffsetZ + 2); - cache = new BiomeType[cacheSizeX * cacheSizeZ]; + cache = new BaseBiome[cacheSizeX * cacheSizeZ]; } protected Iterable getExtent() { @@ -72,24 +72,24 @@ public abstract class ArbitraryBiomeShape { * OUTSIDE = outside * else = inside */ - private final BiomeType[] cache; + private final BaseBiome[] cache; /** * Override this function to specify the shape to generate. * * @param x X coordinate to be queried * @param z Z coordinate to be queried - * @param defaultBiomeType The default biome for the current column. + * @param defaultBaseBiome The default biome for the current column. * @return material to place or null to not place anything. */ - protected abstract BiomeType getBiome(int x, int z, BiomeType defaultBiomeType); + protected abstract BaseBiome getBiome(int x, int z, BaseBiome defaultBaseBiome); - private BiomeType getBiomeCached(int x, int z, BiomeType biomeType) { + private BaseBiome getBiomeCached(int x, int z, BaseBiome baseBiome) { final int index = (z - cacheOffsetZ) + (x - cacheOffsetX) * cacheSizeZ; - final BiomeType cacheEntry = cache[index]; + final BaseBiome cacheEntry = cache[index]; if (cacheEntry == null) {// unknown, fetch material - final BiomeType material = getBiome(x, z, biomeType); + final BaseBiome material = getBiome(x, z, baseBiome); if (material == null) { // outside cache[index] = OUTSIDE; @@ -108,13 +108,13 @@ public abstract class ArbitraryBiomeShape { return cacheEntry; } - private boolean isInsideCached(int x, int z, BiomeType biomeType) { + private boolean isInsideCached(int x, int z, BaseBiome baseBiome) { final int index = (z - cacheOffsetZ) + (x - cacheOffsetX) * cacheSizeZ; - final BiomeType cacheEntry = cache[index]; + final BaseBiome cacheEntry = cache[index]; if (cacheEntry == null) { // unknown block, meaning they must be outside the extent at this stage, but might still be inside the shape - return getBiomeCached(x, z, biomeType) != null; + return getBiomeCached(x, z, baseBiome) != null; } return cacheEntry != OUTSIDE; @@ -124,11 +124,11 @@ public abstract class ArbitraryBiomeShape { * Generates the shape. * * @param editSession The EditSession to use. - * @param biomeType The default biome type. + * @param baseBiome The default biome type. * @param hollow Specifies whether to generate a hollow shape. * @return number of affected blocks. */ - public int generate(EditSession editSession, BiomeType biomeType, boolean hollow) { + public int generate(EditSession editSession, BaseBiome baseBiome, boolean hollow) { int affected = 0; for (Vector2D position : getExtent()) { @@ -136,7 +136,7 @@ public abstract class ArbitraryBiomeShape { int z = position.getBlockZ(); if (!hollow) { - final BiomeType material = getBiome(x, z, biomeType); + final BaseBiome material = getBiome(x, z, baseBiome); if (material != OUTSIDE) { editSession.getWorld().setBiome(position, material); ++affected; @@ -145,26 +145,26 @@ public abstract class ArbitraryBiomeShape { continue; } - final BiomeType material = getBiomeCached(x, z, biomeType); + final BaseBiome material = getBiomeCached(x, z, baseBiome); if (material == null) { continue; } boolean draw = false; do { - if (!isInsideCached(x + 1, z, biomeType)) { + if (!isInsideCached(x + 1, z, baseBiome)) { draw = true; break; } - if (!isInsideCached(x - 1, z, biomeType)) { + if (!isInsideCached(x - 1, z, baseBiome)) { draw = true; break; } - if (!isInsideCached(x, z + 1, biomeType)) { + if (!isInsideCached(x, z + 1, baseBiome)) { draw = true; break; } - if (!isInsideCached(x, z - 1, biomeType)) { + if (!isInsideCached(x, z - 1, baseBiome)) { draw = true; break; } @@ -181,9 +181,15 @@ public abstract class ArbitraryBiomeShape { return affected; } - private static final BiomeType OUTSIDE = new BiomeType() { - public String getName() { - throw new UnsupportedOperationException(); + private static final BaseBiome OUTSIDE = new BaseBiome(0) { + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object o) { + return this == o; } }; } diff --git a/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java b/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java index 77f0c7036..3491b8f55 100644 --- a/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java +++ b/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; /** * @author zml2008 @@ -169,12 +170,8 @@ public class MCEditSchematicFormat extends SchematicFormat { BlockVector pt = new BlockVector(x, y, z); BaseBlock block = getBlockForId(blocks[index], blockData[index]); - if (block instanceof TileEntityBlock && tileEntitiesMap.containsKey(pt)) { - try { - ((TileEntityBlock) block).setNbtData(new CompoundTag("", tileEntitiesMap.get(pt))); - } catch (com.sk89q.worldedit.world.DataException e) { - throw new DataException(e.getMessage()); - } + if (tileEntitiesMap.containsKey(pt)) { + block.setNbtData(new CompoundTag("", tileEntitiesMap.get(pt))); } clipboard.setBlock(pt, block); } @@ -278,7 +275,7 @@ public class MCEditSchematicFormat extends SchematicFormat { // Build and output CompoundTag schematicTag = new CompoundTag("Schematic", schematic); - NBTOutputStream stream = new NBTOutputStream(new FileOutputStream(file)); + NBTOutputStream stream = new NBTOutputStream(new GZIPOutputStream(new FileOutputStream(file))); stream.writeTag(schematicTag); stream.close(); } diff --git a/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java b/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java new file mode 100644 index 000000000..37bb50de8 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java @@ -0,0 +1,101 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.session; + +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.transform.Identity; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.registry.WorldData; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Holds the clipboard and the current transform on the clipboard. + */ +public class ClipboardHolder { + + private final WorldData worldData; + private final Clipboard clipboard; + private Transform transform = new Identity(); + + /** + * Create a new instance with the given clipboard. + * + * @param clipboard the clipboard + * @param worldData the mapping of blocks, entities, and so on + */ + public ClipboardHolder(Clipboard clipboard, WorldData worldData) { + checkNotNull(clipboard); + checkNotNull(worldData); + this.clipboard = clipboard; + this.worldData = worldData; + } + + /** + * Get the mapping used for blocks, entities, and so on. + * + * @return the mapping + */ + public WorldData getWorldData() { + return worldData; + } + + /** + * Get the clipboard. + *

+ * If there is a transformation applied, the returned clipboard will + * not contain its effect. + * + * @return the clipboard + */ + public Clipboard getClipboard() { + return clipboard; + } + + /** + * Set the transform. + * + * @param transform the transform + */ + public void setTransform(Transform transform) { + checkNotNull(transform); + this.transform = transform; + } + + /** + * Get the transform. + * + * @return the transform + */ + public Transform getTransform() { + return transform; + } + + /** + * Create a builder for an operation to paste this clipboard. + * + * @return a builder + */ + public PasteBuilder createPaste(Extent targetExtent, WorldData targetWorldData) { + return new PasteBuilder(this, targetExtent, targetWorldData); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java b/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java new file mode 100644 index 000000000..571387cf5 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java @@ -0,0 +1,102 @@ +/* + * 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.session; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.transform.BlockTransformExtent; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.registry.WorldData; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Builds an operation to paste the contents of a clipboard. + */ +public class PasteBuilder { + + private final Clipboard clipboard; + private final WorldData worldData; + private final Transform transform; + private final Extent targetExtent; + private final WorldData targetWorldData; + + private Vector to = new Vector(); + private boolean ignoreAirBlocks; + + /** + * Create a new instance. + * + * @param holder the clipboard holder + * @param targetExtent + * @param targetWorldData + */ + PasteBuilder(ClipboardHolder holder, Extent targetExtent, WorldData targetWorldData) { + checkNotNull(holder); + checkNotNull(targetExtent); + checkNotNull(targetWorldData); + this.clipboard = holder.getClipboard(); + this.worldData = holder.getWorldData(); + this.transform = holder.getTransform(); + this.targetExtent = targetExtent; + this.targetWorldData = targetWorldData; + } + + /** + * Set the target location. + * + * @param to the target location + * @return this builder instance + */ + public PasteBuilder to(Vector to) { + this.to = to; + return this; + } + + /** + * Set whether air blocks in the source are skipped over when pasting. + * + * @return this builder instance + */ + public PasteBuilder ignoreAirBlocks(boolean ignoreAirBlocks) { + this.ignoreAirBlocks = ignoreAirBlocks; + return this; + } + + /** + * Build the operation. + * + * @return the operation + */ + public Operation build() { + BlockTransformExtent extent = new BlockTransformExtent(clipboard, transform, targetWorldData.getBlockRegistry()); + ForwardExtentCopy copy = new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(), targetExtent, to); + copy.setTransform(transform); + if (ignoreAirBlocks) { + copy.setSourceMask(new ExistingBlockMask(clipboard)); + } + return copy; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/Direction.java b/src/main/java/com/sk89q/worldedit/util/Direction.java new file mode 100644 index 000000000..bf1997f19 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/Direction.java @@ -0,0 +1,159 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.sk89q.worldedit.Vector; + +import javax.annotation.Nullable; + +/** + * A collection of cardinal, ordinal, and secondary-ordinal directions. + */ +public enum Direction { + + NORTH(new Vector(0, 0, -1), Flag.CARDINAL), + EAST(new Vector(1, 0, 0), Flag.CARDINAL), + SOUTH(new Vector(0, 0, 1), Flag.CARDINAL), + WEST(new Vector(-1, 0, 0), Flag.CARDINAL), + + UP(new Vector(0, 1, 0), Flag.UPRIGHT), + DOWN(new Vector(0, -1, 0), Flag.UPRIGHT), + + NORTHEAST(new Vector(1, 0, -1), Flag.ORDINAL), + NORTHWEST(new Vector(-1, 0, -1), Flag.ORDINAL), + SOUTHEAST(new Vector(1, 0, 1), Flag.ORDINAL), + SOUTHWEST(new Vector(-1, 0, 1), Flag.ORDINAL), + + WEST_NORTHWEST(new Vector(-Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL), + WEST_SOUTHWEST(new Vector(-Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL), + NORTH_NORTHWEST(new Vector(Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL), + NORTH_NORTHEAST(new Vector(Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL), + EAST_NORTHEAST(new Vector(Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL), + EAST_SOUTHEAST(new Vector(Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL), + SOUTH_SOUTHEAST(new Vector(Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL), + SOUTH_SOUTHWEST(new Vector(Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL); + + private final Vector direction; + private final int flags; + + private Direction(Vector vector, int flags) { + this.direction = vector.normalize(); + this.flags = flags; + } + + /** + * Return true if the direction is of a cardinal direction (north, west + * east, and south). + * + *

This evaluates as false for directions that have a non-zero + * Y-component.

+ * + * @return true if cardinal + */ + public boolean isCardinal() { + return (flags & Flag.CARDINAL) > 0; + } + + /** + * Return true if the direction is of an ordinal direction (northwest, + * southwest, southeast, northeaast). + * + * @return true if ordinal + */ + public boolean isOrdinal() { + return (flags & Flag.ORDINAL) > 0; + } + + /** + * Return true if the direction is of a secondary ordinal direction + * (north-northwest, north-northeast, south-southwest, etc.). + * + * @return true if secondary ordinal + */ + public boolean isSecondaryOrdinal() { + return (flags & Flag.SECONDARY_ORDINAL) > 0; + } + + /** + * Return whether Y component is non-zero. + * + * @return true if the Y component is non-zero + */ + public boolean isUpright() { + return (flags & Flag.UPRIGHT) > 0; + } + + /** + * Get the vector. + * + * @return the vector + */ + public Vector toVector() { + return direction; + } + + /** + * Find the closest direction to the given direction vector. + * + * @param vector the vector + * @param flags the only flags that are permitted (use bitwise math) + * @return the closest direction, or null if no direction can be returned + */ + @Nullable + public static Direction findClosest(Vector vector, int flags) { + if ((flags & Flag.UPRIGHT) == 0) { + vector = vector.setY(0); + } + vector = vector.normalize(); + + Direction closest = null; + double closestDot = -2; + for (Direction direction : values()) { + if ((~flags & direction.flags) > 0) { + continue; + } + + double dot = direction.toVector().dot(vector); + if (dot >= closestDot) { + closest = direction; + closestDot = dot; + } + } + + return closest; + } + + /** + * Flags to use with {@link #findClosest(Vector, int)}. + */ + public static final class Flag { + public static int CARDINAL = 0x1; + public static int ORDINAL = 0x2; + public static int SECONDARY_ORDINAL = 0x4; + public static int UPRIGHT = 0x8; + + public static int ALL = CARDINAL | ORDINAL | SECONDARY_ORDINAL | UPRIGHT; + + private Flag() { + } + } + +} + diff --git a/src/main/java/com/sk89q/worldedit/util/Enums.java b/src/main/java/com/sk89q/worldedit/util/Enums.java new file mode 100644 index 000000000..9f397c127 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/Enums.java @@ -0,0 +1,54 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper methods for enums. + */ +public final class Enums { + + private Enums() { + } + + /** + * Search the given enum for a value that is equal to the one of the + * given values, searching in an ascending manner. + * + * @param enumType the enum type + * @param values the list of values + * @param the type of enum + * @return the found value or null + */ + @Nullable + public static > T findByValue(Class enumType, String... values) { + checkNotNull(enumType); + checkNotNull(values); + for (String val : values) { + try { + return Enum.valueOf(enumType, val); + } catch (IllegalArgumentException ignored) {} + } + return null; + } +} diff --git a/src/main/java/com/sk89q/worldedit/util/Faceted.java b/src/main/java/com/sk89q/worldedit/util/Faceted.java new file mode 100644 index 000000000..2b54442b3 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/Faceted.java @@ -0,0 +1,52 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util; + +import com.sk89q.worldedit.entity.Entity; + +import javax.annotation.Nullable; + +/** + * Indicates that an object can provide various "facets," which are + * specific distinct interfaces that can represent a portion of the object. + * + *

For example, an instance of an {@link Entity} may have a facet + * for accessing its inventory (if it contains an inventory) or a facet + * for accessing its health (if it has health).

+ * + *

Facets are referred to by their interface or abstract class and + * it is dependent on the implementation of the object specifying this + * interface to return the most applicable implementation. However, in + * many cases, such an implementation may not apply or it has not been + * implemented so a request for a facet may return {@code null}.

+ */ +public interface Faceted { + + /** + * Get the facet corresponding to the given class or interface. + * + * @param cls the class or interface + * @param the type + * @return an implementation of the facet or {@code null} if one is unavailable + */ + @Nullable + T getFacet(Class cls); + +} diff --git a/src/main/java/com/sk89q/worldedit/util/Location.java b/src/main/java/com/sk89q/worldedit/util/Location.java index e5ae26909..f1fbcc8ce 100644 --- a/src/main/java/com/sk89q/worldedit/util/Location.java +++ b/src/main/java/com/sk89q/worldedit/util/Location.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.util; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.extent.Extent; import static com.google.common.base.Preconditions.checkNotNull; @@ -36,104 +36,185 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class Location { - private final World world; + private final Extent extent; private final Vector position; - private final Vector direction; + private final float pitch; + private final float yaw; /** - * Create a new instance in the given world at 0, 0, 0 with a + * Create a new instance in the given extent at 0, 0, 0 with a * direction vector of 0, 0, 0. * - * @param world the world + * @param extent the extent */ - public Location(World world) { - this(world, new Vector(), new Vector()); + public Location(Extent extent) { + this(extent, new Vector(), new Vector()); } /** - * Create a new instance in the given world with the given coordinates + * Create a new instance in the given extent with the given coordinates * with a direction vector of 0, 0, 0. * - * @param world the world + * @param extent the extent * @param x the X coordinate * @param y the Y coordinate * @param z the Z coordinate */ - public Location(World world, double x, double y, double z) { - this(world, new Vector(x, y, z), new Vector()); + public Location(Extent extent, double x, double y, double z) { + this(extent, new Vector(x, y, z), new Vector()); } /** - * Create a new instance in the given world with the given position + * Create a new instance in the given extent with the given position * vector and a direction vector of 0, 0, 0. * - * @param world the world + * @param extent the extent * @param position the position vector */ - public Location(World world, Vector position) { - this(world, position, new Vector()); + public Location(Extent extent, Vector position) { + this(extent, position, new Vector()); } /** - * Create a new instance in the given world with the given coordinates + * Create a new instance in the given extent with the given coordinates * and the given direction vector. * - * @param world the world + * @param extent the extent * @param x the X coordinate * @param y the Y coordinate * @param z the Z coordinate * @param direction the direction vector */ - public Location(World world, double x, double y, double z, Vector direction) { - this(world, new Vector(x, y, z), direction); + public Location(Extent extent, double x, double y, double z, Vector direction) { + this(extent, new Vector(x, y, z), direction); } /** - * Create a new instance in the given world with the given position vector + * Create a new instance in the given extent with the given coordinates * and the given direction vector. * - * @param world the world + * @param extent the extent + * @param x the X coordinate + * @param y the Y coordinate + * @param z the Z coordinate + * @param yaw the yaw, in degrees + * @param pitch the pitch, in degrees + */ + public Location(Extent extent, double x, double y, double z, float yaw, float pitch) { + this(extent, new Vector(x, y, z), yaw, pitch); + } + + /** + * Create a new instance in the given extent with the given position vector + * and the given direction vector. + * + * @param extent the extent * @param position the position vector * @param direction the direction vector */ - public Location(World world, Vector position, Vector direction) { - checkNotNull(world); - checkNotNull(position); - checkNotNull(direction); - this.world = world; - this.position = position; - this.direction = direction; + public Location(Extent extent, Vector position, Vector direction) { + this(extent, position, direction.toYaw(), direction.toPitch()); } /** - * Get the world. + * Create a new instance in the given extent with the given position vector + * and the given direction vector. * - * @return the world + * @param extent the extent + * @param position the position vector + * @param yaw the yaw, in degrees + * @param pitch the pitch, in degrees */ - public World getWorld() { - return world; + public Location(Extent extent, Vector position, float yaw, float pitch) { + checkNotNull(extent); + checkNotNull(position); + this.extent = extent; + this.position = position; + this.pitch = pitch; + this.yaw = yaw; } /** - * Create a clone of this object with the given world. + * Get the extent. * - * @param world the new world + * @return the extent + */ + public Extent getExtent() { + return extent; + } + + /** + * Create a clone of this object with the given extent. + * + * @param extent the new extent * @return the new instance */ - public Location setWorld(World world) { - return new Location(world, position, getDirection()); + public Location setExtent(Extent extent) { + return new Location(extent, position, getDirection()); } /** - * Get the direction. - *

- * The direction vector may be a null vector. It may or may not - * be a unit vector. + * Get the yaw in degrees. * - * @return the direction + * @return the yaw in degrees + */ + public float getYaw() { + return yaw; + } + + /** + * Create a clone of this object with the given yaw. + * + * @param yaw the new yaw + * @return the new instance + */ + public Location setYaw(float yaw) { + return new Location(extent, position, yaw, pitch); + } + + /** + * Get the pitch in degrees. + * + * @return the pitch in degrees + */ + public float getPitch() { + return pitch; + } + + /** + * Create a clone of this object with the given pitch. + * + * @param pitch the new yaw + * @return the new instance + */ + public Location setPitch(float pitch) { + return new Location(extent, position, yaw, pitch); + } + + /** + * Create a clone of this object with the given yaw and pitch. + * + * @param yaw the new yaw + * @param pitch the new pitch + * @return the new instance + */ + public Location setDirection(float yaw, float pitch) { + return new Location(extent, position, yaw, pitch); + } + + /** + * Get the direction vector. + * + * @return the direction vector */ public Vector getDirection() { - return direction; + double yaw = Math.toRadians(this.getYaw()); + double pitch = Math.toRadians(this.getPitch()); + double xz = Math.cos(pitch); + return new Vector( + -xz * Math.sin(yaw), + -Math.sin(pitch), + xz * Math.cos(yaw)); } /** @@ -143,7 +224,7 @@ public class Location { * @return the new instance */ public Location setDirection(Vector direction) { - return new Location(world, position, direction); + return new Location(extent, position, direction.toYaw(), direction.toPitch()); } /** @@ -181,7 +262,7 @@ public class Location { * @return a new immutable instance */ public Location setX(double x) { - return new Location(world, position.setX(x), direction); + return new Location(extent, position.setX(x), yaw, pitch); } /** @@ -192,7 +273,7 @@ public class Location { * @return a new immutable instance */ public Location setX(int x) { - return new Location(world, position.setX(x), direction); + return new Location(extent, position.setX(x), yaw, pitch); } /** @@ -221,7 +302,7 @@ public class Location { * @return a new immutable instance */ public Location setY(double y) { - return new Location(world, position.setY(y), direction); + return new Location(extent, position.setY(y), yaw, pitch); } /** @@ -232,7 +313,7 @@ public class Location { * @return a new immutable instance */ public Location setY(int y) { - return new Location(world, position.setY(y), direction); + return new Location(extent, position.setY(y), yaw, pitch); } /** @@ -261,7 +342,7 @@ public class Location { * @return a new immutable instance */ public Location setZ(double z) { - return new Location(world, position.setZ(z), direction); + return new Location(extent, position.setZ(z), yaw, pitch); } /** @@ -272,7 +353,7 @@ public class Location { * @return a new immutable instance */ public Location setZ(int z) { - return new Location(world, position.setZ(z), direction); + return new Location(extent, position.setZ(z), yaw, pitch); } @Override @@ -282,18 +363,20 @@ public class Location { Location location = (Location) o; - if (!direction.equals(location.direction)) return false; + if (Double.doubleToLongBits(pitch) != Double.doubleToLongBits(location.pitch)) return false; + if (Double.doubleToLongBits(yaw) != Double.doubleToLongBits(location.yaw)) return false; if (!position.equals(location.position)) return false; - if (!world.equals(location.world)) return false; + if (!extent.equals(location.extent)) return false; return true; } @Override public int hashCode() { - int result = world.hashCode(); + int result = extent.hashCode(); result = 31 * result + position.hashCode(); - result = 31 * result + direction.hashCode(); + result = 31 * result + Float.floatToIntBits(this.pitch); + result = 31 * result + Float.floatToIntBits(this.yaw); return result; } diff --git a/src/main/java/com/sk89q/worldedit/util/TargetBlock.java b/src/main/java/com/sk89q/worldedit/util/TargetBlock.java index 2d62e8b37..a97a182a0 100644 --- a/src/main/java/com/sk89q/worldedit/util/TargetBlock.java +++ b/src/main/java/com/sk89q/worldedit/util/TargetBlock.java @@ -22,7 +22,7 @@ package com.sk89q.worldedit.util; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; -import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.internal.LocalWorldAdapter; /** @@ -62,7 +62,7 @@ public class TargetBlock { * @param checkDistance how often to check for blocks, the smaller the more precise */ public TargetBlock(LocalPlayer player, int maxDistance, double checkDistance) { - this((Entity) player, maxDistance, checkDistance); + this((Player) player, maxDistance, checkDistance); } /** @@ -72,10 +72,9 @@ public class TargetBlock { * @param maxDistance how far it checks for blocks * @param checkDistance how often to check for blocks, the smaller the more precise */ - public TargetBlock(Entity player, int maxDistance, double checkDistance) { + public TargetBlock(Player player, int maxDistance, double checkDistance) { this.world = LocalWorldAdapter.adapt(player.getWorld()); - this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), - maxDistance, 1.65, checkDistance); + this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), maxDistance, 1.65, checkDistance); } /** diff --git a/src/main/java/com/sk89q/worldedit/util/Vectors.java b/src/main/java/com/sk89q/worldedit/util/Vectors.java deleted file mode 100644 index 277bf9c3d..000000000 --- a/src/main/java/com/sk89q/worldedit/util/Vectors.java +++ /dev/null @@ -1,65 +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; - -import com.sk89q.worldedit.Vector; - -/** - * Utility methods for {@link Vector}s. - */ -public final class Vectors { - - private Vectors() { - } - - /** - * Create a new {@link Vector} using Euler angles specified in degrees, - * but with no roll. - * - * @param yaw the yaw - * @param pitch the pitch - * @return a new {@link Vector} - */ - public static Vector fromEulerDeg(double yaw, double pitch) { - final double yawRadians = Math.toRadians(yaw); - final double pitchRadians = Math.toRadians(pitch); - return fromEulerRad(yawRadians, pitchRadians); - } - - /** - * Create a new {@link Vector} using Euler angles specified in radians, - * but with no roll. - * - * @param yaw the yaw - * @param pitch the pitch - * @return a new {@link Vector} - */ - public static Vector fromEulerRad(double yaw, double pitch) { - final double y = -Math.sin(pitch); - - final double h = Math.cos(pitch); - - final double x = -h * Math.sin(yaw); - final double z = h * Math.cos(yaw); - - return new Vector(x, y, z); - } - -} diff --git a/src/main/java/com/sk89q/worldedit/util/WeightedChoice.java b/src/main/java/com/sk89q/worldedit/util/WeightedChoice.java new file mode 100644 index 000000000..3e5d5d440 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/WeightedChoice.java @@ -0,0 +1,118 @@ +/* + * 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; + +import com.google.common.base.Function; +import com.google.common.base.Optional; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Returns the best choice given a weighting function and a target weight. + * + *

A function must be supplied that returns a numeric score for each + * choice. The function can return null to mean that the choice should + * not be considered.

+ * + * @param the type of choice + */ +public class WeightedChoice { + + private final Function function; + private double target; + private double best; + private T current; + + /** + * Create a new instance. + * + * @param function a function that assigns a score for each choice + * @param target the target score that the best choice should be closest to + */ + public WeightedChoice(Function function, double target) { + checkNotNull(function); + this.function = function; + this.target = target; + } + + /** + * Consider the given object. + * + * @param object the choice + */ + public void consider(T object) { + checkNotNull(object); + Number value = checkNotNull(function.apply(object)); + if (value != null) { + double distance = Math.abs(target - value.doubleValue()); + if (current == null || distance <= best) { + best = distance; + current = object; + } + } + } + + /** + * Get the best choice. + * + * @return the best choice + */ + public Optional> getChoice() { + if (current != null) { + return Optional.of(new Choice(current, best)); + } else { + return Optional.absent(); + } + } + + /** + * A tuple of choice and score. + * + * @param the choice type + */ + public static class Choice { + private final T object; + private final double value; + + private Choice(T object, double value) { + this.object = object; + this.value = value; + } + + /** + * Get the chosen value. + * + * @return the value + */ + public T getValue() { + return object; + } + + /** + * Get the score. + * + * @return the score + */ + public double getScore() { + return value; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/collection/FastListIterator.java b/src/main/java/com/sk89q/worldedit/util/collection/FastListIterator.java index 1342b91eb..688ce9e55 100644 --- a/src/main/java/com/sk89q/worldedit/util/collection/FastListIterator.java +++ b/src/main/java/com/sk89q/worldedit/util/collection/FastListIterator.java @@ -104,7 +104,11 @@ public class FastListIterator implements Iterator { * @return an iterator */ public static Iterator reverseIterator(List list) { - return new FastListIterator(list, list.size() - 1, list.size(), -1); + if (!list.isEmpty()) { + return new FastListIterator(list, list.size() - 1, list.size(), -1); + } else { + return new FastListIterator(list, 0, 0, -1); + } } } diff --git a/src/main/java/com/sk89q/worldedit/util/function/LevenshteinDistance.java b/src/main/java/com/sk89q/worldedit/util/function/LevenshteinDistance.java new file mode 100644 index 000000000..8767f3a2f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/function/LevenshteinDistance.java @@ -0,0 +1,192 @@ +/* + * 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.function; + +import com.google.common.base.Function; + +import javax.annotation.Nullable; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Provides a Levenshtein distance between a given string and each string + * that this function is applied to. + */ +public class LevenshteinDistance implements Function { + + public final static Pattern STANDARD_CHARS = Pattern.compile("[ _\\-]"); + + private final String baseString; + private final boolean caseSensitive; + private final Pattern replacePattern; + + /** + * Create a new instance. + * + * @param baseString the string to compare to + * @param caseSensitive true to make case sensitive comparisons + */ + public LevenshteinDistance(String baseString, boolean caseSensitive) { + this(baseString, caseSensitive, null); + } + + /** + * Create a new instance. + * + * @param baseString the string to compare to + * @param caseSensitive true to make case sensitive comparisons + * @param replacePattern pattern to match characters to be removed in both the input and test strings (may be null) + */ + public LevenshteinDistance(String baseString, boolean caseSensitive, @Nullable Pattern replacePattern) { + checkNotNull(baseString); + this.caseSensitive = caseSensitive; + this.replacePattern = replacePattern; + baseString = caseSensitive ? baseString : baseString.toLowerCase(); + baseString = replacePattern != null ? replacePattern.matcher(baseString).replaceAll("") : baseString; + this.baseString = baseString; + } + + @Nullable + @Override + public Integer apply(String input) { + if (input == null) { + return null; + } + + if (replacePattern != null) { + input = replacePattern.matcher(input).replaceAll(""); + } + + if (caseSensitive) { + return distance(baseString, input); + } else { + return distance(baseString, input.toLowerCase()); + } + } + + /** + *

Find the Levenshtein distance between two Strings.

+ * + *

This is the number of changes needed to change one String into + * another, where each change is a single character modification (deletion, + * insertion or substitution).

+ * + *

The previous implementation of the Levenshtein distance algorithm + * was from http://www.merriampark.com/ld.htm

+ * + *

Chas Emerick has written an implementation in Java, which avoids an OutOfMemoryError + * which can occur when my Java implementation is used with very large strings.
+ * This implementation of the Levenshtein distance algorithm + * is from http://www.merriampark.com/ldjava.htm

+ * + *
+     * distance(null, *)             = IllegalArgumentException
+     * distance(*, null)             = IllegalArgumentException
+     * distance("","")               = 0
+     * distance("","a")              = 1
+     * distance("aaapppp", "")       = 7
+     * distance("frog", "fog")       = 1
+     * distance("fly", "ant")        = 3
+     * distance("elephant", "hippo") = 7
+     * distance("hippo", "elephant") = 7
+     * distance("hippo", "zzzzzzzz") = 8
+     * distance("hello", "hallo")    = 1
+     * 
+ * + * @param s the first String, must not be null + * @param t the second String, must not be null + * @return result distance + * @throws IllegalArgumentException if either String input null + */ + public static int distance(String s, String t) { + if (s == null || t == null) { + throw new IllegalArgumentException("Strings must not be null"); + } + + /* + * The difference between this impl. and the previous is that, rather + * than creating and retaining a matrix of size s.length()+1 by + * t.length()+1, we maintain two single-dimensional arrays of length + * s.length()+1. The first, d, is the 'current working' distance array + * that maintains the newest distance cost counts as we iterate through + * the characters of String s. Each time we increment the index of + * String t we are comparing, d is copied to p, the second int[]. Doing + * so allows us to retain the previous cost counts as required by the + * algorithm (taking the minimum of the cost count to the left, up one, + * and diagonally up and to the left of the current cost count being + * calculated). (Note that the arrays aren't really copied anymore, just + * switched...this is clearly much better than cloning an array or doing + * a System.arraycopy() each time through the outer loop.) + * + * Effectively, the difference between the two implementations is this + * one does not cause an out of memory condition when calculating the LD + * over two very large strings. + */ + + int n = s.length(); // length of s + int m = t.length(); // length of t + + if (n == 0) { + return m; + } else if (m == 0) { + return n; + } + + int p[] = new int[n + 1]; // 'previous' cost array, horizontally + int d[] = new int[n + 1]; // cost array, horizontally + int _d[]; // placeholder to assist in swapping p and d + + // indexes into strings s and t + int i; // iterates through s + int j; // iterates through t + + char tj; // jth character of t + + int cost; // cost + + for (i = 0; i <= n; ++i) { + p[i] = i; + } + + for (j = 1; j <= m; ++j) { + tj = t.charAt(j - 1); + d[0] = j; + + for (i = 1; i <= n; ++i) { + cost = s.charAt(i - 1) == tj ? 0 : 1; + // minimum of cell to the left+1, to the top+1, diagonally left + // and up +cost + d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + + cost); + } + + // copy current distance counts to 'previous row' distance counts + _d = p; + p = d; + d = _d; + } + + // our last action in the above loop was to switch d and p, so p now + // actually has the most recent cost counts + return p[n]; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/gson/VectorAdapter.java b/src/main/java/com/sk89q/worldedit/util/gson/VectorAdapter.java new file mode 100644 index 000000000..a1e876dfe --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/gson/VectorAdapter.java @@ -0,0 +1,49 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.gson; + +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.sk89q.worldedit.Vector; + +import java.lang.reflect.Type; + +/** + * Deserializes {@code Vector}s for GSON. + */ +public class VectorAdapter implements JsonDeserializer { + + @Override + public Vector deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonArray jsonArray = json.getAsJsonArray(); + if (jsonArray.size() != 3) { + throw new JsonParseException("Expected array of 3 length for Vector"); + } + + double x = jsonArray.get(0).getAsDouble(); + double y = jsonArray.get(1).getAsDouble(); + double z = jsonArray.get(2).getAsDouble(); + + return new Vector(x, y, z); + } +} diff --git a/src/main/java/com/sk89q/worldedit/util/io/Closer.java b/src/main/java/com/sk89q/worldedit/util/io/Closer.java new file mode 100644 index 000000000..2e733c6ab --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/io/Closer.java @@ -0,0 +1,233 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.io; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +public final class Closer implements Closeable { + + private static final Logger logger = Logger.getLogger(Closer.class.getCanonicalName()); + + /** + * The suppressor implementation to use for the current Java version. + */ + private static final Suppressor SUPPRESSOR = SuppressingSuppressor.isAvailable() + ? SuppressingSuppressor.INSTANCE + : LoggingSuppressor.INSTANCE; + + /** + * Creates a new {@link Closer}. + */ + public static Closer create() { + return new Closer(SUPPRESSOR); + } + + @VisibleForTesting + final Suppressor suppressor; + + // only need space for 2 elements in most cases, so try to use the smallest array possible + private final Deque stack = new ArrayDeque(4); + private Throwable thrown; + + @VisibleForTesting Closer(Suppressor suppressor) { + this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests + } + + /** + * Registers the given {@code closeable} to be closed when this {@code Closer} is + * {@linkplain #close closed}. + * + * @return the given {@code closeable} + */ + // close. this word no longer has any meaning to me. + public C register(C closeable) { + stack.push(closeable); + return closeable; + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an + * {@code IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown + * wrapped in a {@code RuntimeException}. Note: Be sure to declare all of the checked + * exception types your try block can throw when calling an overload of this method so as to avoid + * losing the original exception type. + * + *

This method always throws, and as such should be called as + * {@code throw closer.rethrow(e);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + */ + public RuntimeException rethrow(Throwable e) throws IOException { + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + throw Throwables.propagate(e); + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an + * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the + * given type. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. Note: + * Be sure to declare all of the checked exception types your try block can throw when calling an + * overload of this method so as to avoid losing the original exception type. + * + *

This method always throws, and as such should be called as + * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + * @throws X when the given throwable is of the declared type X + */ + public RuntimeException rethrow(Throwable e, + Class declaredType) throws IOException, X { + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType); + throw Throwables.propagate(e); + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an + * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either + * of the given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. + * Note: Be sure to declare all of the checked exception types your try block can throw + * when calling an overload of this method so as to avoid losing the original exception type. + * + *

This method always throws, and as such should be called as + * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + * @throws X1 when the given throwable is of the declared type X1 + * @throws X2 when the given throwable is of the declared type X2 + */ + public RuntimeException rethrow( + Throwable e, Class declaredType1, Class declaredType2) throws IOException, X1, X2 { + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType1, declaredType2); + throw Throwables.propagate(e); + } + + /** + * Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an + * exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods, + * any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the + * first exception to be thrown from an attempt to close a closeable will be thrown and any + * additional exceptions that are thrown after that will be suppressed. + */ + @Override + public void close() throws IOException { + Throwable throwable = thrown; + + // close closeables in LIFO order + while (!stack.isEmpty()) { + Closeable closeable = stack.pop(); + try { + closeable.close(); + } catch (Throwable e) { + if (throwable == null) { + throwable = e; + } else { + suppressor.suppress(closeable, throwable, e); + } + } + } + + if (thrown == null && throwable != null) { + Throwables.propagateIfPossible(throwable, IOException.class); + throw new AssertionError(throwable); // not possible + } + } + + /** + * Suppression strategy interface. + */ + @VisibleForTesting interface Suppressor { + /** + * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close + * the given closeable. {@code thrown} is the exception that is actually being thrown from the + * method. Implementations of this method should not throw under any circumstances. + */ + void suppress(Closeable closeable, Throwable thrown, Throwable suppressed); + } + + /** + * Suppresses exceptions by logging them. + */ + @VisibleForTesting static final class LoggingSuppressor implements Suppressor { + + static final LoggingSuppressor INSTANCE = new LoggingSuppressor(); + + @Override + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + // log to the same place as Closeables + logger.log(Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed); + } + } + + /** + * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's + * addSuppressed(Throwable) mechanism. + */ + @VisibleForTesting static final class SuppressingSuppressor implements Suppressor { + + static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor(); + + static boolean isAvailable() { + return addSuppressed != null; + } + + static final Method addSuppressed = getAddSuppressed(); + + private static Method getAddSuppressed() { + try { + return Throwable.class.getMethod("addSuppressed", Throwable.class); + } catch (Throwable e) { + return null; + } + } + + @Override + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + // ensure no exceptions from addSuppressed + if (thrown == suppressed) { + return; + } + try { + addSuppressed.invoke(thrown, suppressed); + } catch (Throwable e) { + // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging + LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed); + } + } + } +} diff --git a/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java b/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java index e5876107c..b51a1cac8 100644 --- a/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java +++ b/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java @@ -19,8 +19,11 @@ package com.sk89q.worldedit.world; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.LocalWorld.KillFlags; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BlockID; @@ -29,7 +32,6 @@ import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; -import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import javax.annotation.Nullable; @@ -43,6 +45,37 @@ public abstract class AbstractWorld implements World { private final PriorityQueue effectQueue = new PriorityQueue(); private int taskId = -1; + @Override + public final boolean setBlockType(Vector position, int type) { + try { + return setBlock(position, new BaseBlock(type)); + } catch (WorldEditException ignored) { + return false; + } + } + + @Override + public final void setBlockData(Vector position, int data) { + try { + setBlock(position, new BaseBlock(getLazyBlock(position).getType(), data)); + } catch (WorldEditException ignored) { + } + } + + @Override + public final boolean setTypeIdAndData(Vector position, int type, int data) { + try { + return setBlock(position, new BaseBlock(type, data)); + } catch (WorldEditException ignored) { + return false; + } + } + + @Override + public final boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException { + return setBlock(pt, block, true); + } + @Override public int getMaxY() { return getMaximumPoint().getBlockY(); @@ -78,56 +111,6 @@ public abstract class AbstractWorld implements World { return getLazyBlock(pt).getData(); } - @Override - public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { - return setBlock(position, block, true); - } - - @Override - public boolean setBlockType(Vector position, int type) { - try { - return setBlock(position, new BaseBlock(type)); - } catch (WorldEditException e) { - throw new RuntimeException(e); - } - } - - @Override - public void setBlockData(Vector position, int data) { - try { - setBlock(position, new BaseBlock(getLazyBlock(position).getId(), data)); - } catch (WorldEditException e) { - throw new RuntimeException(e); - } - } - - @Override - public void setBlockDataFast(Vector position, int data) { - setBlockData(position, data); - } - - @SuppressWarnings("deprecation") - @Override - public boolean setBlockTypeFast(Vector pt, int type) { - return setBlockType(pt, type); - } - - @SuppressWarnings("deprecation") - @Override - public boolean setTypeIdAndData(Vector pt, int type, int data) { - boolean ret = setBlockType(pt, type); - setBlockData(pt, data); - return ret; - } - - @SuppressWarnings("deprecation") - @Override - public boolean setTypeIdAndDataFast(Vector pt, int type, int data) { - boolean ret = setBlockTypeFast(pt, type); - setBlockDataFast(pt, data); - return ret; - } - @Override public void dropItem(Vector pt, BaseItemStack item, int times) { for (int i = 0; i < times; ++i) { @@ -156,26 +139,6 @@ public abstract class AbstractWorld implements World { } } - @Override - public LocalEntity[] getEntities(Region region) { - return new LocalEntity[0]; - } - - @Override - public int killEntities(LocalEntity... entities) { - return 0; - } - - @Override - public int killMobs(Vector origin, int radius) { - return killMobs(origin, radius, false); - } - - @Override - public int killMobs(Vector origin, int radius, boolean killPets) { - return killMobs(origin, radius, killPets ? KillFlags.PETS : 0); - } - @Override public boolean generateTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException { return generateTree(TreeType.TREE, editSession, pt); diff --git a/src/main/java/com/sk89q/worldedit/world/NbtValued.java b/src/main/java/com/sk89q/worldedit/world/NbtValued.java index 525744b78..dc312a447 100644 --- a/src/main/java/com/sk89q/worldedit/world/NbtValued.java +++ b/src/main/java/com/sk89q/worldedit/world/NbtValued.java @@ -20,7 +20,8 @@ package com.sk89q.worldedit.world; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.world.DataException; + +import javax.annotation.Nullable; /** * Indicates an object that contains extra data identified as an NBT structure. This @@ -47,6 +48,7 @@ public interface NbtValued { * * @return compound tag, or null */ + @Nullable CompoundTag getNbtData(); /** @@ -55,6 +57,6 @@ public interface NbtValued { * @param nbtData NBT data, or null if no data * @throws DataException if possibly the data is invalid */ - void setNbtData(CompoundTag nbtData) throws DataException; + void setNbtData(@Nullable CompoundTag nbtData); } diff --git a/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/src/main/java/com/sk89q/worldedit/world/NullWorld.java index 62edc3352..cfb141a42 100644 --- a/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -19,12 +19,26 @@ package com.sk89q.worldedit.world; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator.TreeType; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.LegacyWorldData; +import com.sk89q.worldedit.world.registry.WorldData; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; /** * A null implementation of {@link World} that drops all changes and @@ -53,28 +67,19 @@ public class NullWorld extends AbstractWorld { } @Override - public BiomeType getBiome(Vector2D position) { + public BaseBiome getBiome(Vector2D position) { return null; } @Override - public void setBiome(Vector2D position, BiomeType biome) { + public boolean setBiome(Vector2D position, BaseBiome biome) { + return false; } @Override public void dropItem(Vector position, BaseItemStack item) { } - @Override - public int killMobs(Vector origin, double radius, int flags) { - return 0; - } - - @Override - public int removeEntities(EntityType type, Vector origin, int radius) { - return 0; - } - @Override public boolean regenerate(Region region, EditSession editSession) { return false; @@ -85,6 +90,11 @@ public class NullWorld extends AbstractWorld { return false; } + @Override + public WorldData getWorldData() { + return LegacyWorldData.getInstance(); + } + @Override public BaseBlock getBlock(Vector position) { return new BaseBlock(BlockID.AIR); @@ -95,4 +105,19 @@ public class NullWorld extends AbstractWorld { return new BaseBlock(BlockID.AIR); } + @Override + public List getEntities(Region region) { + return Collections.emptyList(); + } + + @Override + public List getEntities() { + return Collections.emptyList(); + } + + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity entity) { + return null; + } } diff --git a/src/main/java/com/sk89q/worldedit/world/World.java b/src/main/java/com/sk89q/worldedit/world/World.java index 8accbe122..657f510e3 100644 --- a/src/main/java/com/sk89q/worldedit/world/World.java +++ b/src/main/java/com/sk89q/worldedit/world/World.java @@ -19,7 +19,11 @@ package com.sk89q.worldedit.world; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Platform; @@ -28,6 +32,7 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.TreeGenerator.TreeType; +import com.sk89q.worldedit.world.registry.WorldData; /** * Represents a world (dimension). @@ -112,36 +117,18 @@ public interface World extends Extent { @Deprecated boolean setBlockType(Vector position, int type); - /** - * @deprecated Use {@link #setBlock(Vector, BaseBlock)} - */ - @Deprecated - boolean setBlockTypeFast(Vector position, int type); - /** * @deprecated Use {@link #setBlock(Vector, BaseBlock)} */ @Deprecated void setBlockData(Vector position, int data); - /** - * @deprecated Use {@link #setBlock(Vector, BaseBlock)} - */ - @Deprecated - void setBlockDataFast(Vector position, int data); - /** * @deprecated Use {@link #setBlock(Vector, BaseBlock)} */ @Deprecated boolean setTypeIdAndData(Vector position, int type, int data); - /** - * @deprecated Use {@link #setBlock(Vector, BaseBlock)} - */ - @Deprecated - boolean setTypeIdAndDataFast(Vector position, int type, int data); - /** * Get the light level at the given block. * @@ -158,22 +145,6 @@ public interface World extends Extent { */ boolean clearContainerBlockContents(Vector position); - /** - * Get the biome type. - * - * @param position the (x, z) location to check the biome at - * @return the biome type at the location - */ - BiomeType getBiome(Vector2D position); - - /** - * Set the biome type. - * - * @param position the (x, z) location to set the biome at - * @param biome the biome type to set to - */ - void setBiome(Vector2D position, BiomeType biome); - /** * Drop an item at the given position. * @@ -199,54 +170,6 @@ public interface World extends Extent { */ void simulateBlockMine(Vector position); - /** - * Get a list of entities in the given region. - * - * @param region the region - * @return a list of entities - */ - LocalEntity[] getEntities(Region region); - - /** - * Kill the entities listed in the provided array. - * - * @param entity an array of entities - * @return the number of entities that were removed - */ - int killEntities(LocalEntity... entity); - - /** - * @deprecated Use {@link #killMobs(Vector, double, int)} - */ - @Deprecated - int killMobs(Vector origin, int radius); - - /** - * @deprecated Use {@link #killMobs(Vector, double, int)} - */ - @Deprecated - int killMobs(Vector origin, int radius, boolean killPets); - - /** - * Kill mobs at the given location with the given radius. - * - * @param origin the origin - * @param radius the radius - * @param flags kill flags (see {@link LocalWorld.KillFlags}) - * @return the number of mobs that were killed - */ - int killMobs(Vector origin, double radius, int flags); - - /** - * Remove entities in an area. - * - * @param type the type of entity - * @param origin the origin - * @param radius the radius - * @return the number of mobs that were killed - */ - int removeEntities(EntityType type, Vector origin, int radius); - /** * Regenerate an area. * @@ -345,6 +268,13 @@ public interface World extends Extent { */ boolean queueBlockBreakEffect(Platform server, Vector position, int blockId, double priority); + /** + * Get the data for blocks and so on for this world. + * + * @return the world data + */ + WorldData getWorldData(); + @Override boolean equals(Object other); diff --git a/src/main/java/com/sk89q/worldedit/world/biome/BaseBiome.java b/src/main/java/com/sk89q/worldedit/world/biome/BaseBiome.java new file mode 100644 index 000000000..f60299f66 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/biome/BaseBiome.java @@ -0,0 +1,82 @@ +/* + * 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.biome; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Basic storage object to represent a given biome. + */ +public class BaseBiome { + + private int id; + + /** + * Create a new biome with the given biome ID. + * + * @param id the biome ID + */ + public BaseBiome(int id) { + this.id = id; + } + + /** + * Create a clone of the given biome. + * + * @param biome the biome to clone + */ + public BaseBiome(BaseBiome biome) { + checkNotNull(biome); + this.id = biome.getId(); + } + + /** + * Get the biome ID. + * + * @return the biome ID + */ + public int getId() { + return id; + } + + /** + * Set the biome id. + * + * @param id the biome ID + */ + public void setId(int id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BaseBiome baseBiome = (BaseBiome) o; + + return id == baseBiome.id; + } + + @Override + public int hashCode() { + return id; + } +} diff --git a/src/main/java/com/sk89q/worldedit/BiomeType.java b/src/main/java/com/sk89q/worldedit/world/biome/BiomeData.java similarity index 74% rename from src/main/java/com/sk89q/worldedit/BiomeType.java rename to src/main/java/com/sk89q/worldedit/world/biome/BiomeData.java index 93d9efe4a..5f21dd978 100644 --- a/src/main/java/com/sk89q/worldedit/BiomeType.java +++ b/src/main/java/com/sk89q/worldedit/world/biome/BiomeData.java @@ -17,20 +17,19 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit; +package com.sk89q.worldedit.world.biome; -public interface BiomeType { - - public static final BiomeType UNKNOWN = new BiomeType() { - public String getName() { - return "Unknown"; - } - }; +/** + * Provides information about a biome. + */ +public interface BiomeData { /** - * Get the name of this biome type. + * Get the name of the biome, which does not have to follow any + * particular convention. * - * @return String + * @return the biome's name */ - public String getName(); + String getName(); + } diff --git a/src/main/java/com/sk89q/worldedit/world/biome/BiomeName.java b/src/main/java/com/sk89q/worldedit/world/biome/BiomeName.java new file mode 100644 index 000000000..145d891cb --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/biome/BiomeName.java @@ -0,0 +1,57 @@ +/* + * 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.biome; + +import com.google.common.base.Function; +import com.sk89q.worldedit.world.registry.BiomeRegistry; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Returns the name of a biome using a given {@code BiomeRegistry}. + */ +class BiomeName implements Function { + + private final BiomeRegistry registry; + + /** + * Create a new instance. + * + * @param registry the biome registry + */ + BiomeName(BiomeRegistry registry) { + checkNotNull(registry); + this.registry = registry; + } + + @Nullable + @Override + public String apply(BaseBiome input) { + BiomeData data = registry.getData(input); + if (data != null) { + return data.getName(); + } else { + return null; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/world/biome/Biomes.java b/src/main/java/com/sk89q/worldedit/world/biome/Biomes.java new file mode 100644 index 000000000..c19c2ac93 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/biome/Biomes.java @@ -0,0 +1,70 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.biome; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Optional; +import com.sk89q.worldedit.util.WeightedChoice; +import com.sk89q.worldedit.util.WeightedChoice.Choice; +import com.sk89q.worldedit.util.function.LevenshteinDistance; +import com.sk89q.worldedit.world.registry.BiomeRegistry; + +import javax.annotation.Nullable; +import java.util.Collection; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility methods related to biomes. + */ +public final class Biomes { + + private Biomes() { + } + + /** + * Find a biome that matches the given input name. + * + * @param biomes a list of biomes + * @param name the name to test + * @param registry a biome registry + * @return a biome or null + */ + @Nullable + public static BaseBiome findBiomeByName(Collection biomes, String name, BiomeRegistry registry) { + checkNotNull(biomes); + checkNotNull(name); + checkNotNull(registry); + + Function compare = new LevenshteinDistance(name, false, LevenshteinDistance.STANDARD_CHARS); + WeightedChoice chooser = new WeightedChoice(Functions.compose(compare, new BiomeName(registry)), 0); + for (BaseBiome biome : biomes) { + chooser.consider(biome); + } + Optional> choice = chooser.getChoice(); + if (choice.isPresent() && choice.get().getScore() <= 1) { + return choice.get().getValue(); + } else { + return null; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/BiomeRegistry.java b/src/main/java/com/sk89q/worldedit/world/registry/BiomeRegistry.java new file mode 100644 index 000000000..ab3942463 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/BiomeRegistry.java @@ -0,0 +1,58 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.registry; + +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.BiomeData; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * Provides information on biomes. + */ +public interface BiomeRegistry { + + /** + * Create a new biome given its biome ID. + * + * @param id its biome ID + * @return a new biome or null if it can't be created + */ + @Nullable + BaseBiome createFromId(int id); + + /** + * Get a list of available biomes. + * + * @return a list of biomes + */ + List getBiomes(); + + /** + * Get data about a biome. + * + * @param biome the biome + * @return a data object or null if information is not known + */ + @Nullable + BiomeData getData(BaseBiome biome); + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java b/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java new file mode 100644 index 000000000..7448835cf --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/BlockRegistry.java @@ -0,0 +1,69 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.registry; + +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockMaterial; + +import javax.annotation.Nullable; +import java.util.Map; + +/** + * Provides information on blocks and provides methods to create them. + */ +public interface BlockRegistry { + + /** + * Create a new block using its ID. + * + * @param id the id + * @return the block, which may be null if no block exists + */ + @Nullable + BaseBlock createFromId(String id); + + /** + * Create a new block using its legacy numeric ID. + * + * @param id the id + * @return the block, which may be null if no block exists + */ + @Nullable + BaseBlock createFromId(int id); + + /** + * Get the material for the given block. + * + * @param block the block + * @return the material, or null if the material information is not known + */ + @Nullable + BlockMaterial getMaterial(BaseBlock block); + + /** + * Get an unmodifiable map of states for this block. + * + * @param block the block + * @return a map of states where the key is the state's ID + */ + @Nullable + Map getStates(BaseBlock block); + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java b/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java new file mode 100644 index 000000000..a111d2592 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java @@ -0,0 +1,189 @@ +/* + * 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.registry; + +import com.google.common.io.Resources; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BlockMaterial; +import com.sk89q.worldedit.util.gson.VectorAdapter; +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntObjectHashMap; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Provides block data based on the built-in block database that is bundled + * with WorldEdit. + * + *

A new instance cannot be created. Use {@link #getInstance()} to get + * an instance.

+ * + *

The data is read from a JSON file that is bundled with WorldEdit. If + * reading fails (which occurs when this class is first instantiated), then + * the methods will return {@code null}s for all blocks.

+ */ +public class BundledBlockData { + + private static final Logger log = Logger.getLogger(BundledBlockData.class.getCanonicalName()); + private static final BundledBlockData INSTANCE = new BundledBlockData(); + + private final Map idMap = new HashMap(); + private final TIntObjectMap legacyMap = new TIntObjectHashMap(); + + /** + * Create a new instance. + */ + private BundledBlockData() { + try { + loadFromResource(); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to load the built-in block registry", e); + } + } + + /** + * Attempt to load the data from file. + * + * @throws IOException thrown on I/O error + */ + private void loadFromResource() throws IOException { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(Vector.class, new VectorAdapter()); + Gson gson = gsonBuilder.create(); + URL url = BundledBlockData.class.getResource("blocks.json"); + if (url == null) { + throw new IOException("Could not find blocks.json"); + } + String data = Resources.toString(url, Charset.defaultCharset()); + List entries = gson.fromJson(data, new TypeToken>() {}.getType()); + + for (BlockEntry entry : entries) { + entry.postDeserialization(); + idMap.put(entry.id, entry); + legacyMap.put(entry.legacyId, entry); + } + } + + /** + * Return the entry for the given block ID. + * + * @param id the ID + * @return the entry, or null + */ + @Nullable + private BlockEntry findById(String id) { + return idMap.get(id); + } + + /** + * Return the entry for the given block legacy numeric ID. + * + * @param id the ID + * @return the entry, or null + */ + @Nullable + private BlockEntry findById(int id) { + return legacyMap.get(id); + } + + /** + * Convert the given string ID to a legacy numeric ID. + * + * @param id the ID + * @return the legacy ID, which may be null if the block does not have a legacy ID + */ + @Nullable + public Integer toLegacyId(String id) { + BlockEntry entry = findById(id); + if (entry != null) { + return entry.legacyId; + } else { + return null; + } + } + + /** + * Get the material properties for the given block. + * + * @param id the legacy numeric ID + * @return the material's properties, or null + */ + @Nullable + public BlockMaterial getMaterialById(int id) { + BlockEntry entry = findById(id); + if (entry != null) { + return entry.material; + } else { + return null; + } + } + + /** + * Get the states for the given block. + * + * @param id the legacy numeric ID + * @return the block's states, or null if no information is available + */ + @Nullable + public Map getStatesById(int id) { + BlockEntry entry = findById(id); + if (entry != null) { + return entry.states; + } else { + return null; + } + } + + /** + * Get a singleton instance of this object. + * + * @return the instance + */ + public static BundledBlockData getInstance() { + return INSTANCE; + } + + private static class BlockEntry { + private int legacyId; + private String id; + private String unlocalizedName; + private List aliases; + private Map states = new HashMap(); + private SimpleBlockMaterial material = new SimpleBlockMaterial(); + + void postDeserialization() { + for (SimpleState state : states.values()) { + state.postDeserialization(); + } + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/EntityRegistry.java b/src/main/java/com/sk89q/worldedit/world/registry/EntityRegistry.java new file mode 100644 index 000000000..787585327 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/EntityRegistry.java @@ -0,0 +1,42 @@ +/* + * 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.registry; + +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.metadata.EntityType; + +import javax.annotation.Nullable; + +/** + * Provides information on entities. + */ +public interface EntityRegistry { + + /** + * Create a new entity using its ID. + * + * @param id the id + * @return the entity, which may be null if the entity does not exist + */ + @Nullable + BaseEntity createFromId(String id); + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/LegacyBlockRegistry.java b/src/main/java/com/sk89q/worldedit/world/registry/LegacyBlockRegistry.java new file mode 100644 index 000000000..e846e7984 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/LegacyBlockRegistry.java @@ -0,0 +1,63 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.registry; + +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockMaterial; + +import javax.annotation.Nullable; +import java.util.Map; + +/** + * A block registry that uses {@link BundledBlockData} to serve information + * about blocks. + */ +public class LegacyBlockRegistry implements BlockRegistry { + + @Nullable + @Override + public BaseBlock createFromId(String id) { + Integer legacyId = BundledBlockData.getInstance().toLegacyId(id); + if (legacyId != null) { + return createFromId(legacyId); + } else { + return null; + } + } + + @Nullable + @Override + public BaseBlock createFromId(int id) { + return new BaseBlock(id); + } + + @Nullable + @Override + public BlockMaterial getMaterial(BaseBlock block) { + return BundledBlockData.getInstance().getMaterialById(block.getId()); + } + + @Nullable + @Override + public Map getStates(BaseBlock block) { + return BundledBlockData.getInstance().getStatesById(block.getId()); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/LegacyWorldData.java b/src/main/java/com/sk89q/worldedit/world/registry/LegacyWorldData.java new file mode 100644 index 000000000..2fabd886d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/LegacyWorldData.java @@ -0,0 +1,63 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.registry; + +/** + * An implementation of {@link WorldData} that uses legacy numeric IDs and + * a built-in block database. + */ +public class LegacyWorldData implements WorldData { + + private static final LegacyWorldData INSTANCE = new LegacyWorldData(); + private final LegacyBlockRegistry blockRegistry = new LegacyBlockRegistry(); + private final NullEntityRegistry entityRegistry = new NullEntityRegistry(); + private final NullBiomeRegistry biomeRegistry = new NullBiomeRegistry(); + + /** + * Create a new instance. + */ + protected LegacyWorldData() { + } + + @Override + public BlockRegistry getBlockRegistry() { + return blockRegistry; + } + + @Override + public EntityRegistry getEntityRegistry() { + return entityRegistry; + } + + @Override + public BiomeRegistry getBiomeRegistry() { + return biomeRegistry; + } + + /** + * Get a singleton instance. + * + * @return an instance + */ + public static LegacyWorldData getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/NullBiomeRegistry.java b/src/main/java/com/sk89q/worldedit/world/registry/NullBiomeRegistry.java new file mode 100644 index 000000000..8bbf7c1ff --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/NullBiomeRegistry.java @@ -0,0 +1,57 @@ +/* + * 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.registry; + +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.BiomeData; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +/** + * A biome registry that knows nothing. + */ +public class NullBiomeRegistry implements BiomeRegistry { + + /** + * Create a new instance. + */ + public NullBiomeRegistry() { + } + + @Nullable + @Override + public BaseBiome createFromId(int id) { + return null; + } + + @Override + public List getBiomes() { + return Collections.emptyList(); + } + + @Nullable + @Override + public BiomeData getData(BaseBiome biome) { + return null; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/NullEntityRegistry.java b/src/main/java/com/sk89q/worldedit/world/registry/NullEntityRegistry.java new file mode 100644 index 000000000..b3eadcc10 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/NullEntityRegistry.java @@ -0,0 +1,37 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.registry; + +import com.sk89q.worldedit.entity.BaseEntity; + +import javax.annotation.Nullable; + +/** + * An implementation of an entity registry that knows nothing. + */ +public class NullEntityRegistry implements EntityRegistry { + + @Nullable + @Override + public BaseEntity createFromId(String id) { + return null; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java b/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java new file mode 100644 index 000000000..7b190c740 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/SimpleBlockMaterial.java @@ -0,0 +1,246 @@ +/* + * 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.registry; + +import com.sk89q.worldedit.blocks.BlockMaterial; + +class SimpleBlockMaterial implements BlockMaterial { + + private boolean renderedAsNormalBlock; + private boolean fullCube; + private boolean opaque; + private boolean powerSource; + private boolean liquid; + private boolean solid; + private float hardness; + private float resistance; + private float slipperiness; + private boolean grassBlocking; + private float ambientOcclusionLightValue; + private int lightOpacity; + private int lightValue; + private boolean fragileWhenPushed; + private boolean unpushable; + private boolean adventureModeExempt; + private boolean ticksRandomly; + private boolean usingNeighborLight; + private boolean movementBlocker; + private boolean burnable; + private boolean toolRequired; + private boolean replacedDuringPlacement; + + @Override + public boolean isRenderedAsNormalBlock() { + return renderedAsNormalBlock; + } + + public void setRenderedAsNormalBlock(boolean renderedAsNormalBlock) { + this.renderedAsNormalBlock = renderedAsNormalBlock; + } + + @Override + public boolean isFullCube() { + return fullCube; + } + + public void setFullCube(boolean fullCube) { + this.fullCube = fullCube; + } + + @Override + public boolean isOpaque() { + return opaque; + } + + public void setOpaque(boolean opaque) { + this.opaque = opaque; + } + + @Override + public boolean isPowerSource() { + return powerSource; + } + + public void setPowerSource(boolean powerSource) { + this.powerSource = powerSource; + } + + @Override + public boolean isLiquid() { + return liquid; + } + + public void setLiquid(boolean liquid) { + this.liquid = liquid; + } + + @Override + public boolean isSolid() { + return solid; + } + + public void setSolid(boolean solid) { + this.solid = solid; + } + + @Override + public float getHardness() { + return hardness; + } + + public void setHardness(float hardness) { + this.hardness = hardness; + } + + @Override + public float getResistance() { + return resistance; + } + + public void setResistance(float resistance) { + this.resistance = resistance; + } + + @Override + public float getSlipperiness() { + return slipperiness; + } + + public void setSlipperiness(float slipperiness) { + this.slipperiness = slipperiness; + } + + @Override + public boolean isGrassBlocking() { + return grassBlocking; + } + + public void setGrassBlocking(boolean grassBlocking) { + this.grassBlocking = grassBlocking; + } + + @Override + public float getAmbientOcclusionLightValue() { + return ambientOcclusionLightValue; + } + + public void setAmbientOcclusionLightValue(float ambientOcclusionLightValue) { + this.ambientOcclusionLightValue = ambientOcclusionLightValue; + } + + @Override + public int getLightOpacity() { + return lightOpacity; + } + + public void setLightOpacity(int lightOpacity) { + this.lightOpacity = lightOpacity; + } + + @Override + public int getLightValue() { + return lightValue; + } + + public void setLightValue(int lightValue) { + this.lightValue = lightValue; + } + + @Override + public boolean isFragileWhenPushed() { + return fragileWhenPushed; + } + + public void setFragileWhenPushed(boolean fragileWhenPushed) { + this.fragileWhenPushed = fragileWhenPushed; + } + + @Override + public boolean isUnpushable() { + return unpushable; + } + + public void setUnpushable(boolean unpushable) { + this.unpushable = unpushable; + } + + @Override + public boolean isAdventureModeExempt() { + return adventureModeExempt; + } + + public void setAdventureModeExempt(boolean adventureModeExempt) { + this.adventureModeExempt = adventureModeExempt; + } + + @Override + public boolean isTicksRandomly() { + return ticksRandomly; + } + + public void setTicksRandomly(boolean ticksRandomly) { + this.ticksRandomly = ticksRandomly; + } + + @Override + public boolean isUsingNeighborLight() { + return usingNeighborLight; + } + + public void setUsingNeighborLight(boolean usingNeighborLight) { + this.usingNeighborLight = usingNeighborLight; + } + + @Override + public boolean isMovementBlocker() { + return movementBlocker; + } + + public void setMovementBlocker(boolean movementBlocker) { + this.movementBlocker = movementBlocker; + } + + @Override + public boolean isBurnable() { + return burnable; + } + + public void setBurnable(boolean burnable) { + this.burnable = burnable; + } + + @Override + public boolean isToolRequired() { + return toolRequired; + } + + public void setToolRequired(boolean toolRequired) { + this.toolRequired = toolRequired; + } + + @Override + public boolean isReplacedDuringPlacement() { + return replacedDuringPlacement; + } + + public void setReplacedDuringPlacement(boolean replacedDuringPlacement) { + this.replacedDuringPlacement = replacedDuringPlacement; + } +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/SimpleState.java b/src/main/java/com/sk89q/worldedit/world/registry/SimpleState.java new file mode 100644 index 000000000..4cc78de9b --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/SimpleState.java @@ -0,0 +1,71 @@ +/* + * 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.registry; + +import com.sk89q.worldedit.blocks.BaseBlock; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.Map; + +class SimpleState implements State { + + private Byte dataMask; + private Map values; + + @Override + public Map valueMap() { + return Collections.unmodifiableMap(values); + } + + @Nullable + @Override + public StateValue getValue(BaseBlock block) { + for (StateValue value : values.values()) { + if (value.isSet(block)) { + return value; + } + } + + return null; + } + + byte getDataMask() { + return dataMask != null ? dataMask : 0xF; + } + + @Override + public boolean hasDirection() { + for (SimpleStateValue value : values.values()) { + if (value.getDirection() != null) { + return true; + } + } + + return false; + } + + void postDeserialization() { + for (SimpleStateValue v : values.values()) { + v.setState(this); + } + } + +} diff --git a/src/legacy/java/com/sk89q/worldedit/masks/BiomeTypeMask.java b/src/main/java/com/sk89q/worldedit/world/registry/SimpleStateValue.java similarity index 55% rename from src/legacy/java/com/sk89q/worldedit/masks/BiomeTypeMask.java rename to src/main/java/com/sk89q/worldedit/world/registry/SimpleStateValue.java index 97ed58e09..791bc684a 100644 --- a/src/legacy/java/com/sk89q/worldedit/masks/BiomeTypeMask.java +++ b/src/main/java/com/sk89q/worldedit/world/registry/SimpleStateValue.java @@ -17,34 +17,39 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.masks; +package com.sk89q.worldedit.world.registry; -import java.util.HashSet; -import java.util.Set; - -import com.sk89q.worldedit.BiomeType; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.blocks.BaseBlock; -public class BiomeTypeMask extends AbstractMask { - private final Set biomes; +class SimpleStateValue implements StateValue { - public BiomeTypeMask() { - this(new HashSet()); - } + private SimpleState state; + private Byte data; + private Vector direction; - public BiomeTypeMask(Set biomes) { - this.biomes = biomes; - } - - public boolean matches2D(EditSession editSession, Vector2D pos) { - BiomeType biome = editSession.getWorld().getBiome(pos); - return biomes.contains(biome); + void setState(SimpleState state) { + this.state = state; } @Override - public boolean matches(EditSession editSession, Vector pos) { - return matches2D(editSession, pos.toVector2D()); + public boolean isSet(BaseBlock block) { + return data != null && (block.getData() & state.getDataMask()) == data; } + + @Override + public boolean set(BaseBlock block) { + if (data != null) { + block.setData((block.getData() & ~state.getDataMask()) | data); + return true; + } else { + return false; + } + } + + @Override + public Vector getDirection() { + return direction; + } + } diff --git a/src/main/java/com/sk89q/worldedit/world/registry/State.java b/src/main/java/com/sk89q/worldedit/world/registry/State.java new file mode 100644 index 000000000..a8cbfde3c --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/State.java @@ -0,0 +1,61 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.registry; + +import com.sk89q.worldedit.blocks.BaseBlock; + +import javax.annotation.Nullable; +import java.util.Map; + +/** + * Describes a state property of a block. + * + *

Example states include "variant" (indicating material or type) and + * "facing" (indicating orientation).

+ */ +public interface State { + + /** + * Return a map of available values for this state. + * + *

Keys are the value of state and map values describe that + * particular state value.

+ * + * @return the map of state values + */ + Map valueMap(); + + /** + * Get the value that the block is set to. + * + * @param block the block + * @return the state, otherwise null if the block isn't set to any of the values + */ + @Nullable + StateValue getValue(BaseBlock block); + + /** + * Returns whether this state contains directional data. + * + * @return true if directional data is available + */ + boolean hasDirection(); + +} diff --git a/src/main/java/com/sk89q/worldedit/world/registry/StateValue.java b/src/main/java/com/sk89q/worldedit/world/registry/StateValue.java new file mode 100644 index 000000000..4e565cb19 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/registry/StateValue.java @@ -0,0 +1,56 @@ +/* + * 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.registry; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; + +import javax.annotation.Nullable; + +/** + * Describes a possible value for a {@code State}. + */ +public interface StateValue { + + /** + * Return whether this state is set on the given block. + * + * @param block the block + * @return true if this value is set + */ + boolean isSet(BaseBlock block); + + /** + * Set the state to this value on the given block. + * + * @param block the block to change + * @return true if the value was set successfully + */ + boolean set(BaseBlock block); + + /** + * Return the direction associated with this value. + * + * @return the direction, otherwise null + */ + @Nullable + Vector getDirection(); + +} diff --git a/src/main/java/com/sk89q/worldedit/BiomeTypes.java b/src/main/java/com/sk89q/worldedit/world/registry/WorldData.java similarity index 65% rename from src/main/java/com/sk89q/worldedit/BiomeTypes.java rename to src/main/java/com/sk89q/worldedit/world/registry/WorldData.java index 0d61b4c80..d6a4b21f8 100644 --- a/src/main/java/com/sk89q/worldedit/BiomeTypes.java +++ b/src/main/java/com/sk89q/worldedit/world/registry/WorldData.java @@ -17,31 +17,33 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit; +package com.sk89q.worldedit.world.registry; -import java.util.List; - -public interface BiomeTypes { +/** + * Describes the necessary data for blocks, entities, and other objects + * on a world. + */ +public interface WorldData { /** - * Returns if a biome with the given name is available. + * Get the block registry. * - * @param name - * @return + * @return the block registry */ - boolean has(String name); + BlockRegistry getBlockRegistry(); /** - * Returns the biome type for the given name + * Get the entity registry. * - * @return + * @return the entity registry */ - BiomeType get(String name) throws UnknownBiomeTypeException; + EntityRegistry getEntityRegistry(); /** - * Returns a list of all available biome types. + * Get the biome registry. * - * @return + * @return the biome registry */ - List all(); + BiomeRegistry getBiomeRegistry(); + } diff --git a/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java b/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java new file mode 100644 index 000000000..ad10929ae --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java @@ -0,0 +1,58 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.storage; + +import com.sk89q.jnbt.ListTag; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility methods for working with NBT data used in Minecraft. + */ +public final class NBTConversions { + + private NBTConversions() { + } + + /** + * Read a {@code Location} from two list tags, the first of which contains + * three numbers for the X, Y, and Z components, and the second of + * which contains two numbers, the yaw and pitch in degrees. + * + *

For values that are unavailable, their values will be 0.

+ * + * @param extent the extent + * @param positionTag the position tag + * @param directionTag the direction tag + * @return a location + */ + public static Location toLocation(Extent extent, ListTag positionTag, ListTag directionTag) { + checkNotNull(extent); + checkNotNull(positionTag); + checkNotNull(directionTag); + return new Location( + extent, + positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), + (float) directionTag.asDouble(0), (float) directionTag.asDouble(1)); + } + +} diff --git a/src/main/resources/com/sk89q/worldedit/world/registry/blocks.json b/src/main/resources/com/sk89q/worldedit/world/registry/blocks.json new file mode 100644 index 000000000..f4a08d736 --- /dev/null +++ b/src/main/resources/com/sk89q/worldedit/world/registry/blocks.json @@ -0,0 +1,6013 @@ +[ + { + "legacyId": 0, + "id": "minecraft:air", + "unlocalizedName": "tile.air", + "material": { + "renderedAsNormalBlock": true, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 1, + "id": "minecraft:stone", + "unlocalizedName": "tile.stone", + "states": { + "variant": { + "values": { + "stone": { "data": 0 }, + "granite": { "data": 1 }, + "polished_granite": { "data": 2 }, + "diorite": { "data": 3 }, + "polished_diorite": { "data": 4 }, + "andesite": { "data": 5 }, + "polished_andesite": { "data": 6 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.5, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 2, + "id": "minecraft:grass", + "unlocalizedName": "tile.grass", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.6, + "resistance": 3.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 3, + "id": "minecraft:dirt", + "unlocalizedName": "tile.dirt", + "states": { + "variant": { + "values": { + "dirt": { "data": 0 }, + "coarse_dirt": { "data": 1 }, + "podzol": { "data": 2 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 4, + "id": "minecraft:cobblestone", + "unlocalizedName": "tile.stonebrick", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 5, + "id": "minecraft:planks", + "unlocalizedName": "tile.wood", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 6, + "id": "minecraft:sapling", + "unlocalizedName": "tile.sapling", + "states": { + "variant": { + "values": { + "oak": { "data": 0 }, + "spruce": { "data": 1 }, + "birch": { "data": 2 }, + "jungle": { "data": 3 }, + "acacia": { "data": 4 }, + "darkoak": { "data": 5 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 7, + "id": "minecraft:bedrock", + "unlocalizedName": "tile.bedrock", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": -1.0, + "resistance": 1.8E7, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 8, + "id": "minecraft:flowing_water", + "unlocalizedName": "tile.water", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": true, + "solid": false, + "hardness": 100.0, + "resistance": 500.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 3, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 9, + "id": "minecraft:water", + "unlocalizedName": "tile.water", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": true, + "solid": false, + "hardness": 100.0, + "resistance": 500.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 3, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 10, + "id": "minecraft:flowing_lava", + "unlocalizedName": "tile.lava", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": true, + "solid": false, + "hardness": 100.0, + "resistance": 500.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 15, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 11, + "id": "minecraft:lava", + "unlocalizedName": "tile.lava", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": true, + "solid": false, + "hardness": 100.0, + "resistance": 500.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 15, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 12, + "id": "minecraft:sand", + "unlocalizedName": "tile.sand", + "states": { + "variant": { + "values": { + "sand": { "data": 0 }, + "red_sand": { "data": 1 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 13, + "id": "minecraft:gravel", + "unlocalizedName": "tile.gravel", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.6, + "resistance": 3.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 14, + "id": "minecraft:gold_ore", + "unlocalizedName": "tile.oreGold", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 15, + "id": "minecraft:iron_ore", + "unlocalizedName": "tile.oreIron", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 16, + "id": "minecraft:coal_ore", + "unlocalizedName": "tile.oreCoal", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 17, + "id": "minecraft:log", + "unlocalizedName": "tile.log", + "states": { + "variant": { + "dataMask": 3, + "values": { + "oak": { "data": 0 }, + "spruce": { "data": 1 }, + "birch": { "data": 2 }, + "jungle": { "data": 3 } + } + }, + "facing": { + "dataMask": 12, + "values": { + "up": { "data": 0, "direction": [0, 1, 0] }, + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 4, "direction": [1, 0, 0] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "north": { "data": 8, "direction": [0, 0, -1] }, + "south": { "data": 8, "direction": [0, 0, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 10.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 18, + "id": "minecraft:leaves", + "unlocalizedName": "tile.leaves", + "states": { + "variant": { + "dataMask": 3, + "values": { + "oak": { "data": 0 }, + "spruce": { "data": 1 }, + "birch": { "data": 2 }, + "jungle": { "data": 3 } + } + }, + "decay": { + "dataMask": 7, + "values": { + "no_decay": { "data": 4 }, + "decay": { "data": 0 } + } + }, + "check_decay": { + "dataMask": 12, + "values": { + "no_decay": { "data": 12 }, + "decay": { "data": 0 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 1, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 19, + "id": "minecraft:sponge", + "unlocalizedName": "tile.sponge", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.6, + "resistance": 3.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 20, + "id": "minecraft:glass", + "unlocalizedName": "tile.glass", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.3, + "resistance": 1.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 21, + "id": "minecraft:lapis_ore", + "unlocalizedName": "tile.oreLapis", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 22, + "id": "minecraft:lapis_block", + "unlocalizedName": "tile.blockLapis", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 23, + "id": "minecraft:dispenser", + "unlocalizedName": "tile.dispenser", + "states": { + "facing": { + "dataMask": 7, + "values": { + "down": { "data": 0, "direction": [0, -1, 0] }, + "up": { "data": 1, "direction": [0, 1, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + }, + "powered": { + "dataMask": 8, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.5, + "resistance": 17.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 24, + "id": "minecraft:sandstone", + "unlocalizedName": "tile.sandStone", + "states": { + "variant": { + "values": { + "sandstone": { "data": 0 }, + "chiseled": { "data": 1 }, + "smooth": { "data": 2 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.8, + "resistance": 4.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 25, + "id": "minecraft:noteblock", + "unlocalizedName": "tile.musicBlock", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.8, + "resistance": 4.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 26, + "id": "minecraft:bed", + "unlocalizedName": "tile.bed", + "states": { + "facing": { + "dataMask": 3, + "values": { + "east": { "data": 3, "direction": [1, 0, 0] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 0, "direction": [0, 0, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 27, + "id": "minecraft:golden_rail", + "unlocalizedName": "tile.goldenRail", + "states": { + "facing": { + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "south": { "data": 0, "direction": [0, 0, 1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "east_ascending": { "data": 2, "direction": [1, 1, 0] }, + "east_descending": { "data": 2, "direction": [-1, -1, 0] }, + "west_ascending": { "data": 3, "direction": [-1, 1, 0] }, + "west_descending": { "data": 3, "direction": [1, -1, 0] }, + "north_ascending": { "data": 4, "direction": [0, 1, -1] }, + "north_descending": { "data": 4, "direction": [0, -1, 1] }, + "south_ascending": { "data": 5, "direction": [0, 1, 1] }, + "south_descending": { "data": 5, "direction": [0, -1, -1] }, + "southeast": { "data": 6, "direction": [1, 0, 1] }, + "southwest": { "data": 7, "direction": [-1, 0, 1] }, + "northwest": { "data": 8, "direction": [-1, 0, -1] }, + "northeast": { "data": 9, "direction": [1, 0, -1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.7, + "resistance": 3.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 28, + "id": "minecraft:detector_rail", + "unlocalizedName": "tile.detectorRail", + "states": { + "facing": { + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "south": { "data": 0, "direction": [0, 0, 1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "east_ascending": { "data": 2, "direction": [1, 1, 0] }, + "east_descending": { "data": 2, "direction": [-1, -1, 0] }, + "west_ascending": { "data": 3, "direction": [-1, 1, 0] }, + "west_descending": { "data": 3, "direction": [1, -1, 0] }, + "north_ascending": { "data": 4, "direction": [0, 1, -1] }, + "north_descending": { "data": 4, "direction": [0, -1, 1] }, + "south_ascending": { "data": 5, "direction": [0, 1, 1] }, + "south_descending": { "data": 5, "direction": [0, -1, -1] }, + "southeast": { "data": 6, "direction": [1, 0, 1] }, + "southwest": { "data": 7, "direction": [-1, 0, 1] }, + "northwest": { "data": 8, "direction": [-1, 0, -1] }, + "northeast": { "data": 9, "direction": [1, 0, -1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.7, + "resistance": 3.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 29, + "id": "minecraft:sticky_piston", + "unlocalizedName": "tile.pistonStickyBase", + "states": { + "facing": { + "dataMask": 7, + "values": { + "up": { "data": 1, "direction": [0, 1, 0] }, + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": true, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 30, + "id": "minecraft:web", + "unlocalizedName": "tile.web", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 4.0, + "resistance": 20.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 1, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": false, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 31, + "id": "minecraft:tallgrass", + "unlocalizedName": "tile.tallgrass", + "states": { + "variant": { + "values": { + "shrub": { "data": 0 }, + "grass": { "data": 1 }, + "fern": { "data": 2 }, + "biome_grass": { "data": 3 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 32, + "id": "minecraft:deadbush", + "unlocalizedName": "tile.deadbush", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 33, + "id": "minecraft:piston", + "unlocalizedName": "tile.pistonBase", + "states": { + "facing": { + "dataMask": 7, + "values": { + "up": { "data": 1, "direction": [0, 1, 0] }, + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": true, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 34, + "id": "minecraft:piston_head", + "unlocalizedName": "tile.null", + "states": { + "facing": { + "dataMask": 7, + "values": { + "up": { "data": 1, "direction": [0, 1, 0] }, + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": true, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 35, + "id": "minecraft:wool", + "unlocalizedName": "tile.cloth", + "states": { + "color": { + "values": { + "white": { "data": 0 }, + "orange": { "data": 1 }, + "magenta": { "data": 2 }, + "light_blue": { "data": 3 }, + "yellow": { "data": 4 }, + "lime": { "data": 5 }, + "pink": { "data": 6 }, + "gray": { "data": 7 }, + "light_gray": { "data": 8 }, + "cyan": { "data": 9 }, + "purple": { "data": 10 }, + "blue": { "data": 11 }, + "grown": { "data": 12 }, + "green": { "data": 13 }, + "red": { "data": 14 }, + "black": { "data": 15 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.8, + "resistance": 4.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 36, + "id": "minecraft:piston_extension", + "unlocalizedName": "tile.null", + "states": { + "facing": { + "dataMask": 7, + "values": { + "up": { "data": 1, "direction": [0, 1, 0] }, + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": -1.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": true, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 37, + "id": "minecraft:yellow_flower", + "unlocalizedName": "tile.flower1", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 38, + "id": "minecraft:red_flower", + "unlocalizedName": "tile.flower2", + "states": { + "variant": { + "values": { + "poppy": { "data": 0 }, + "blue_orchid": { "data": 1 }, + "allium": { "data": 2 }, + "azure_bluet": { "data": 3 }, + "red_tulip": { "data": 4 }, + "orange_tulip": { "data": 5 }, + "white_tulip": { "data": 6 }, + "pink_tulip": { "data": 7 }, + "oxyeye_daisy": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 39, + "id": "minecraft:brown_mushroom", + "unlocalizedName": "tile.mushroom", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 1, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 40, + "id": "minecraft:red_mushroom", + "unlocalizedName": "tile.mushroom", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 41, + "id": "minecraft:gold_block", + "unlocalizedName": "tile.blockGold", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 42, + "id": "minecraft:iron_block", + "unlocalizedName": "tile.blockIron", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 43, + "id": "minecraft:double_stone_slab", + "unlocalizedName": "tile.stoneSlab", + "states": { + "variant": { + "values": { + "stone": { "data": 0 }, + "sandstone": { "data": 1 }, + "wood": { "data": 2 }, + "cobble": { "data": 3 }, + "brick": { "data": 4 }, + "stoneBrick": { "data": 5 }, + "netherBrick": { "data": 6 }, + "quartz": { "data": 7 }, + "smoothStoneBrick": { "data": 8 }, + "smoothSandstone": { "data": 9 }, + "tileQuartz": { "data": 10 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 44, + "id": "minecraft:stone_slab", + "unlocalizedName": "tile.stoneSlab", + "states": { + "variant": { + "dataMask": 12, + "values": { + "stone": { "data": 0 }, + "sandstone": { "data": 1 }, + "wood": { "data": 2 }, + "cobble": { "data": 3 }, + "brick": { "data": 4 }, + "stoneBrick": { "data": 5 }, + "netherBrick": { "data": 6 }, + "quartz": { "data": 7 } + } + }, + "half": { + "dataMask": 8, + "values": { + "top": { "data": 0, "direction": [0, 1, 0] }, + "bottom": { "data": 8, "direction": [0, -1, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 45, + "id": "minecraft:brick_block", + "unlocalizedName": "tile.brick", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 46, + "id": "minecraft:tnt", + "unlocalizedName": "tile.tnt", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 47, + "id": "minecraft:bookshelf", + "unlocalizedName": "tile.bookshelf", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.5, + "resistance": 7.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 48, + "id": "minecraft:mossy_cobblestone", + "unlocalizedName": "tile.stoneMoss", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 49, + "id": "minecraft:obsidian", + "unlocalizedName": "tile.obsidian", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 50.0, + "resistance": 6000.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 50, + "id": "minecraft:torch", + "unlocalizedName": "tile.torch", + "states": { + "facing": { + "values": { + "east": { "data": 1, "direction": [1, 0, 0] }, + "west": { "data": 2, "direction": [-1, 0, 0] }, + "north": { "data": 3, "direction": [0, 0, -1] }, + "south": { "data": 4, "direction": [0, 0, 1] }, + "up": { "data": 5, "direction": [0, 1, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 14, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 51, + "id": "minecraft:fire", + "unlocalizedName": "tile.fire", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 15, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 52, + "id": "minecraft:mob_spawner", + "unlocalizedName": "tile.mobSpawner", + "material": { + "renderedAsNormalBlock": true, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 25.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 53, + "id": "minecraft:oak_stairs", + "unlocalizedName": "tile.stairsWood", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 54, + "id": "minecraft:chest", + "unlocalizedName": "tile.chest", + "states": { + "facing": { + "dataMask": 7, + "values": { + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.5, + "resistance": 12.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 55, + "id": "minecraft:redstone_wire", + "unlocalizedName": "tile.redstoneDust", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 56, + "id": "minecraft:diamond_ore", + "unlocalizedName": "tile.oreDiamond", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 57, + "id": "minecraft:diamond_block", + "unlocalizedName": "tile.blockDiamond", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 58, + "id": "minecraft:crafting_table", + "unlocalizedName": "tile.workbench", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.5, + "resistance": 12.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 59, + "id": "minecraft:wheat", + "unlocalizedName": "tile.crops", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 60, + "id": "minecraft:farmland", + "unlocalizedName": "tile.farmland", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.6, + "resistance": 3.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 61, + "id": "minecraft:furnace", + "unlocalizedName": "tile.furnace", + "states": { + "facing": { + "dataMask": 7, + "values": { + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.5, + "resistance": 17.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 62, + "id": "minecraft:lit_furnace", + "unlocalizedName": "tile.furnace", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.5, + "resistance": 17.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 13, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 63, + "id": "minecraft:standing_sign", + "unlocalizedName": "tile.sign", + "states": { + "facing": { + "values": { + "south": { "data": 0, "direction": [0, 0, 1] }, + "south_southwest": { "data": 1, "direction": [-0.3826, 0, 0.9238] }, + "southwest": { "data": 2, "direction": [-0.7071, 0, 0.7071] }, + "west_southwest": { "data": 3, "direction": [-0.9238, 0, 0.3826] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "west_northwest": { "data": 5, "direction": [-0.9238, 0, -0.3826] }, + "northwest": { "data": 6, "direction": [-0.7071, 0, -0.7071] }, + "north_northwest": { "data": 7, "direction": [-0.3826, 0, -0.9238] }, + "north": { "data": 8, "direction": [0, 0, -1] }, + "north_northeast": { "data": 9, "direction": [0.3826, 0, -0.9238] }, + "northeast": { "data": 10, "direction": [0.7071, 0, -0.7071] }, + "east_northeast": { "data": 11, "direction": [0.9238, 0, -0.3826] }, + "east": { "data": 12, "direction": [1, 0, 0] }, + "east_southeast": { "data": 13, "direction": [0.9238, 0, 0.3826] }, + "southeast": { "data": 14, "direction": [0.7071, 0, 0.7071] }, + "south_southeast": { "data": 15, "direction": [0.3826, 0, 0.9238] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.0, + "resistance": 5.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 64, + "id": "minecraft:wooden_door", + "unlocalizedName": "tile.doorWood", + "states": { + "half": { + "dataMask": 8, + "values": { + "bottom": { "data": 0 }, + "top": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 65, + "id": "minecraft:ladder", + "unlocalizedName": "tile.ladder", + "states": { + "facing": { + "dataMask": 7, + "values": { + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.4, + "resistance": 2.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 66, + "id": "minecraft:rail", + "unlocalizedName": "tile.rail", + "states": { + "facing": { + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "south": { "data": 0, "direction": [0, 0, 1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "east_ascending": { "data": 2, "direction": [1, 1, 0] }, + "east_descending": { "data": 2, "direction": [-1, -1, 0] }, + "west_ascending": { "data": 3, "direction": [-1, 1, 0] }, + "west_descending": { "data": 3, "direction": [1, -1, 0] }, + "north_ascending": { "data": 4, "direction": [0, 1, -1] }, + "north_descending": { "data": 4, "direction": [0, -1, 1] }, + "south_ascending": { "data": 5, "direction": [0, 1, 1] }, + "south_descending": { "data": 5, "direction": [0, -1, -1] }, + "southeast": { "data": 6, "direction": [1, 0, 1] }, + "southwest": { "data": 7, "direction": [-1, 0, 1] }, + "northwest": { "data": 8, "direction": [-1, 0, -1] }, + "northeast": { "data": 9, "direction": [1, 0, -1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.7, + "resistance": 3.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 67, + "id": "minecraft:stone_stairs", + "unlocalizedName": "tile.stairsStone", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 68, + "id": "minecraft:wall_sign", + "unlocalizedName": "tile.sign", + "states": { + "facing": { + "dataMask": 7, + "values": { + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.0, + "resistance": 5.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 69, + "id": "minecraft:lever", + "unlocalizedName": "tile.lever", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 2, "direction": [-1, 0, 0] }, + "north": { "data": 4, "direction": [0, 0, -1] }, + "ground_south": { "data": 5, "direction": [0, 1, 1] }, + "ground_north": { "data": 5, "direction": [0, 1, -1] }, + "ground_east": { "data": 6, "direction": [1, 1, 0] }, + "ground_west": { "data": 6, "direction": [-1, 1, 0] }, + "ceiling_south": { "data": 7, "direction": [0, -1, -1] }, + "ceiling_north": { "data": 7, "direction": [0, -1, 1] }, + "ceiling_east": { "data": 0, "direction": [1, -1, 0] }, + "ceiling_west": { "data": 0, "direction": [-1, -1, 0] } + } + }, + "powered": { + "dataMask": 8, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 70, + "id": "minecraft:stone_pressure_plate", + "unlocalizedName": "tile.pressurePlate", + "states": { + "powered": { + "dataMask": 1, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 1 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": true, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 71, + "id": "minecraft:iron_door", + "unlocalizedName": "tile.doorIron", + "states": { + "half": { + "dataMask": 8, + "values": { + "bottom": { "data": 0 }, + "top": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 25.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 72, + "id": "minecraft:wooden_pressure_plate", + "unlocalizedName": "tile.pressurePlate", + "states": { + "powered": { + "dataMask": 1, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 1 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": true, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 73, + "id": "minecraft:redstone_ore", + "unlocalizedName": "tile.oreRedstone", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 74, + "id": "minecraft:lit_redstone_ore", + "unlocalizedName": "tile.oreRedstone", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 9, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 75, + "id": "minecraft:unlit_redstone_torch", + "unlocalizedName": "tile.notGate", + "states": { + "facing": { + "values": { + "east": { "data": 1, "direction": [1, 0, 0] }, + "west": { "data": 2, "direction": [-1, 0, 0] }, + "north": { "data": 3, "direction": [0, 0, -1] }, + "south": { "data": 4, "direction": [0, 0, 1] }, + "up": { "data": 5, "direction": [0, 1, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 76, + "id": "minecraft:redstone_torch", + "unlocalizedName": "tile.notGate", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 7, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 77, + "id": "minecraft:stone_button", + "unlocalizedName": "tile.button", + "states": { + "facing": { + "dataMask": 7, + "values": { + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 2, "direction": [0, 0, 1] }, + "west": { "data": 3, "direction": [-1, 0, 0] }, + "north": { "data": 4, "direction": [0, 0, -1] }, + "up": { "data": 5, "direction": [0, 1, 0] } + } + }, + "powered": { + "dataMask": 8, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 1 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 78, + "id": "minecraft:snow_layer", + "unlocalizedName": "tile.snow", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.1, + "resistance": 0.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 79, + "id": "minecraft:ice", + "unlocalizedName": "tile.ice", + "material": { + "renderedAsNormalBlock": true, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.98, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 3, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 80, + "id": "minecraft:snow", + "unlocalizedName": "tile.snow", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 81, + "id": "minecraft:cactus", + "unlocalizedName": "tile.cactus", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.4, + "resistance": 2.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 82, + "id": "minecraft:clay", + "unlocalizedName": "tile.clay", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.6, + "resistance": 3.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 83, + "id": "minecraft:reeds", + "unlocalizedName": "tile.reeds", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 84, + "id": "minecraft:jukebox", + "unlocalizedName": "tile.jukebox", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 85, + "id": "minecraft:fence", + "unlocalizedName": "tile.fence", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 86, + "id": "minecraft:pumpkin", + "unlocalizedName": "tile.pumpkin", + "states": { + "facing": { + "dataMask": 7, + "values": { + "south": { "data": 0, "direction": [0, 0, 1] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "east": { "data": 3, "direction": [1, 0, 0] }, + "none": { "data": 4 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.0, + "resistance": 5.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 87, + "id": "minecraft:netherrack", + "unlocalizedName": "tile.hellrock", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.4, + "resistance": 2.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 88, + "id": "minecraft:soul_sand", + "unlocalizedName": "tile.hellsand", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 89, + "id": "minecraft:glowstone", + "unlocalizedName": "tile.lightgem", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.3, + "resistance": 1.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 15, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 90, + "id": "minecraft:portal", + "unlocalizedName": "tile.portal", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": -1.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 11, + "fragileWhenPushed": false, + "unpushable": true, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 91, + "id": "minecraft:lit_pumpkin", + "unlocalizedName": "tile.litpumpkin", + "states": { + "facing": { + "dataMask": 7, + "values": { + "south": { "data": 0, "direction": [0, 0, 1] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "east": { "data": 3, "direction": [1, 0, 0] }, + "none": { "data": 4 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.0, + "resistance": 5.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 15, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 92, + "id": "minecraft:cake", + "unlocalizedName": "tile.cake", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 93, + "id": "minecraft:unpowered_repeater", + "unlocalizedName": "tile.diode", + "states": { + "facing": { + "dataMask": 3, + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 2, "direction": [0, 0, 1] }, + "west": { "data": 3, "direction": [-1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 94, + "id": "minecraft:powered_repeater", + "unlocalizedName": "tile.diode", + "states": { + "facing": { + "dataMask": 3, + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 2, "direction": [0, 0, 1] }, + "west": { "data": 3, "direction": [-1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 9, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 95, + "id": "minecraft:stained_glass", + "unlocalizedName": "tile.stainedGlass", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.3, + "resistance": 1.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 96, + "id": "minecraft:trapdoor", + "unlocalizedName": "tile.trapdoor", + "states": { + "facing": { + "dataMask": 11, + "values": { + "south": { "data": 0, "direction": [0, 1, 1] }, + "north": { "data": 1, "direction": [0, 1, -1] }, + "east": { "data": 2, "direction": [1, 1, 0] }, + "west": { "data": 3, "direction": [-1, 1, 0] }, + "south_upper": { "data": 8, "direction": [0, -1, 1] }, + "north_upper": { "data": 9, "direction": [0, -1, -1] }, + "east_upper": { "data": 10, "direction": [1, -1, 0] }, + "west_upper": { "data": 11, "direction": [-1, -1, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 97, + "id": "minecraft:monster_egg", + "unlocalizedName": "tile.monsterStoneEgg", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.75, + "resistance": 3.75, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 98, + "id": "minecraft:stonebrick", + "unlocalizedName": "tile.stonebricksmooth", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.5, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 99, + "id": "minecraft:brown_mushroom_block", + "unlocalizedName": "tile.mushroom", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 100, + "id": "minecraft:red_mushroom_block", + "unlocalizedName": "tile.mushroom", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 101, + "id": "minecraft:iron_bars", + "unlocalizedName": "tile.fenceIron", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 102, + "id": "minecraft:glass_pane", + "unlocalizedName": "tile.thinGlass", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.3, + "resistance": 1.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 103, + "id": "minecraft:melon_block", + "unlocalizedName": "tile.melon", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.0, + "resistance": 5.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 104, + "id": "minecraft:pumpkin_stem", + "unlocalizedName": "tile.pumpkinStem", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 105, + "id": "minecraft:melon_stem", + "unlocalizedName": "tile.pumpkinStem", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 106, + "id": "minecraft:vine", + "unlocalizedName": "tile.vine", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": true + } + }, + { + "legacyId": 107, + "id": "minecraft:fence_gate", + "unlocalizedName": "tile.fenceGate", + "states": { + "facing": { + "dataMask": 3, + "values": { + "south": { "data": 0, "direction": [0, 0, 1] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "east": { "data": 3, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 108, + "id": "minecraft:brick_stairs", + "unlocalizedName": "tile.stairsBrick", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 109, + "id": "minecraft:stone_brick_stairs", + "unlocalizedName": "tile.stairsStoneBrickSmooth", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.5, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 110, + "id": "minecraft:mycelium", + "unlocalizedName": "tile.mycel", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.6, + "resistance": 3.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 111, + "id": "minecraft:waterlily", + "unlocalizedName": "tile.waterlily", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 112, + "id": "minecraft:nether_brick", + "unlocalizedName": "tile.netherBrick", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 113, + "id": "minecraft:nether_brick_fence", + "unlocalizedName": "tile.netherFence", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 114, + "id": "minecraft:nether_brick_stairs", + "unlocalizedName": "tile.stairsNetherBrick", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 115, + "id": "minecraft:nether_wart", + "unlocalizedName": "tile.netherStalk", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 116, + "id": "minecraft:enchanting_table", + "unlocalizedName": "tile.enchantmentTable", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 6000.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 117, + "id": "minecraft:brewing_stand", + "unlocalizedName": "tile.brewingStand", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 1, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 118, + "id": "minecraft:cauldron", + "unlocalizedName": "tile.cauldron", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 10.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 119, + "id": "minecraft:end_portal", + "unlocalizedName": "tile.null", + "states": { + "facing": { + "dataMask": 3, + "values": { + "south": { "data": 0, "direction": [0, 0, 1] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "east": { "data": 3, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": -1.0, + "resistance": 1.8E7, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 15, + "fragileWhenPushed": false, + "unpushable": true, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 120, + "id": "minecraft:end_portal_frame", + "unlocalizedName": "tile.endPortalFrame", + "material": { + "renderedAsNormalBlock": true, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": -1.0, + "resistance": 1.8E7, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 0, + "lightValue": 1, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 121, + "id": "minecraft:end_stone", + "unlocalizedName": "tile.whiteStone", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 45.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 122, + "id": "minecraft:dragon_egg", + "unlocalizedName": "tile.dragonEgg", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 45.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 1, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 123, + "id": "minecraft:redstone_lamp", + "unlocalizedName": "tile.redstoneLight", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.3, + "resistance": 1.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 124, + "id": "minecraft:lit_redstone_lamp", + "unlocalizedName": "tile.redstoneLight", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.3, + "resistance": 1.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 15, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 125, + "id": "minecraft:double_wooden_slab", + "unlocalizedName": "tile.woodSlab", + "states": { + "variant": { + "dataMask": 12, + "values": { + "oak": { "data": 0 }, + "spruce": { "data": 1 }, + "birch": { "data": 2 }, + "jungle": { "data": 3 }, + "acacia": { "data": 4 }, + "darkoak": { "data": 5 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 126, + "id": "minecraft:wooden_slab", + "unlocalizedName": "tile.woodSlab", + "states": { + "variant": { + "dataMask": 12, + "values": { + "oak": { "data": 0 }, + "spruce": { "data": 1 }, + "birch": { "data": 2 }, + "jungle": { "data": 3 }, + "acacia": { "data": 4 }, + "darkoak": { "data": 5 } + } + }, + "half": { + "dataMask": 8, + "values": { + "top": { "data": 0, "direction": [0, 1, 0] }, + "bottom": { "data": 8, "direction": [0, -1, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 127, + "id": "minecraft:cocoa", + "unlocalizedName": "tile.cocoa", + "states": { + "facing": { + "dataMask": 3, + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 2, "direction": [0, 0, 1] }, + "west": { "data": 3, "direction": [-1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.2, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 128, + "id": "minecraft:sandstone_stairs", + "unlocalizedName": "tile.stairsSandStone", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.8, + "resistance": 4.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 129, + "id": "minecraft:emerald_ore", + "unlocalizedName": "tile.oreEmerald", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 130, + "id": "minecraft:ender_chest", + "unlocalizedName": "tile.enderChest", + "states": { + "facing": { + "dataMask": 7, + "values": { + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 22.5, + "resistance": 3000.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 7, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 131, + "id": "minecraft:tripwire_hook", + "unlocalizedName": "tile.tripWireSource", + "states": { + "facing": { + "dataMask": 3, + "values": { + "south": { "data": 0, "direction": [0, 0, 1] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "east": { "data": 3, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 132, + "id": "minecraft:tripwire", + "unlocalizedName": "tile.tripWire", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 133, + "id": "minecraft:emerald_block", + "unlocalizedName": "tile.blockEmerald", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 134, + "id": "minecraft:spruce_stairs", + "unlocalizedName": "tile.stairsWoodSpruce", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 135, + "id": "minecraft:birch_stairs", + "unlocalizedName": "tile.stairsWoodBirch", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 136, + "id": "minecraft:jungle_stairs", + "unlocalizedName": "tile.stairsWoodJungle", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 137, + "id": "minecraft:command_block", + "unlocalizedName": "tile.commandBlock", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": -1.0, + "resistance": 1.8E7, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 138, + "id": "minecraft:beacon", + "unlocalizedName": "tile.beacon", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 15, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 139, + "id": "minecraft:cobblestone_wall", + "unlocalizedName": "tile.cobbleWall", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 140, + "id": "minecraft:flower_pot", + "unlocalizedName": "tile.flowerPot", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 141, + "id": "minecraft:carrots", + "unlocalizedName": "tile.carrots", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 142, + "id": "minecraft:potatoes", + "unlocalizedName": "tile.potatoes", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 143, + "id": "minecraft:wooden_button", + "unlocalizedName": "tile.button", + "states": { + "facing": { + "dataMask": 7, + "values": { + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 2, "direction": [0, 0, 1] }, + "west": { "data": 3, "direction": [-1, 0, 0] }, + "north": { "data": 4, "direction": [0, 0, -1] }, + "up": { "data": 5, "direction": [0, 1, 0] } + } + }, + "powered": { + "dataMask": 8, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 1 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 144, + "id": "minecraft:skull", + "unlocalizedName": "tile.skull", + "states": { + "facing": { + "dataMask": 7, + "values": { + "down": { "data": 1, "direction": [0, -1, 0] }, + "up": { "data": 1, "direction": [0, 1, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "east": { "data": 4, "direction": [1, 0, 0] }, + "west": { "data": 5, "direction": [-1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 1.0, + "resistance": 5.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 145, + "id": "minecraft:anvil", + "unlocalizedName": "tile.anvil", + "states": { + "facing": { + "dataMask": 3, + "values": { + "south": { "data": 0, "direction": [0, 0, 1] }, + "north": { "data": 0, "direction": [0, 0, -1] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "east": { "data": 1, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 6000.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": true, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 146, + "id": "minecraft:trapped_chest", + "unlocalizedName": "tile.chestTrap", + "states": { + "facing": { + "dataMask": 7, + "values": { + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": true, + "liquid": false, + "solid": true, + "hardness": 2.5, + "resistance": 12.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 147, + "id": "minecraft:light_weighted_pressure_plate", + "unlocalizedName": "tile.weightedPlate_light", + "states": { + "powered": { + "dataMask": 1, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 1 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": true, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 148, + "id": "minecraft:heavy_weighted_pressure_plate", + "unlocalizedName": "tile.weightedPlate_heavy", + "states": { + "powered": { + "dataMask": 1, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 1 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": true, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 149, + "id": "minecraft:unpowered_comparator", + "unlocalizedName": "tile.comparator", + "states": { + "facing": { + "dataMask": 3, + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 2, "direction": [0, 0, 1] }, + "west": { "data": 3, "direction": [-1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 150, + "id": "minecraft:powered_comparator", + "unlocalizedName": "tile.comparator", + "states": { + "facing": { + "dataMask": 3, + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "south": { "data": 2, "direction": [0, 0, 1] }, + "west": { "data": 3, "direction": [-1, 0, 0] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": true, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 9, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 151, + "id": "minecraft:daylight_detector", + "unlocalizedName": "tile.daylightDetector", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": true, + "liquid": false, + "solid": true, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 152, + "id": "minecraft:redstone_block", + "unlocalizedName": "tile.blockRedstone", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": true, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 153, + "id": "minecraft:quartz_ore", + "unlocalizedName": "tile.netherquartz", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 154, + "id": "minecraft:hopper", + "unlocalizedName": "tile.hopper", + "states": { + "facing": { + "dataMask": 7, + "values": { + "down": { "data": 0, "direction": [0, -1, 0] }, + "up": { "data": 1, "direction": [0, 1, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + }, + "powered": { + "dataMask": 8, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.0, + "resistance": 24.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 155, + "id": "minecraft:quartz_block", + "unlocalizedName": "tile.quartzBlock", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.8, + "resistance": 4.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 156, + "id": "minecraft:quartz_stairs", + "unlocalizedName": "tile.stairsQuartz", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.8, + "resistance": 4.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 157, + "id": "minecraft:activator_rail", + "unlocalizedName": "tile.activatorRail", + "states": { + "facing": { + "values": { + "north": { "data": 0, "direction": [0, 0, -1] }, + "south": { "data": 0, "direction": [0, 0, 1] }, + "east": { "data": 1, "direction": [1, 0, 0] }, + "west": { "data": 1, "direction": [-1, 0, 0] }, + "east_ascending": { "data": 2, "direction": [1, 1, 0] }, + "east_descending": { "data": 2, "direction": [-1, -1, 0] }, + "west_ascending": { "data": 3, "direction": [-1, 1, 0] }, + "west_descending": { "data": 3, "direction": [1, -1, 0] }, + "north_ascending": { "data": 4, "direction": [0, 1, -1] }, + "north_descending": { "data": 4, "direction": [0, -1, 1] }, + "south_ascending": { "data": 5, "direction": [0, 1, 1] }, + "south_descending": { "data": 5, "direction": [0, -1, -1] }, + "southeast": { "data": 6, "direction": [1, 0, 1] }, + "southwest": { "data": 7, "direction": [-1, 0, 1] }, + "northwest": { "data": 8, "direction": [-1, 0, -1] }, + "northeast": { "data": 9, "direction": [1, 0, -1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.7, + "resistance": 3.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 158, + "id": "minecraft:dropper", + "unlocalizedName": "tile.dropper", + "states": { + "facing": { + "dataMask": 7, + "values": { + "down": { "data": 0, "direction": [0, -1, 0] }, + "up": { "data": 1, "direction": [0, 1, 0] }, + "north": { "data": 2, "direction": [0, 0, -1] }, + "south": { "data": 3, "direction": [0, 0, 1] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "east": { "data": 5, "direction": [1, 0, 0] } + } + }, + "powered": { + "dataMask": 8, + "values": { + "unpowered": { "data": 0 }, + "powered": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 3.5, + "resistance": 17.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 159, + "id": "minecraft:stained_hardened_clay", + "unlocalizedName": "tile.clayHardenedStained", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.25, + "resistance": 21.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 160, + "id": "minecraft:stained_glass_pane", + "unlocalizedName": "tile.thinStainedGlass", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.3, + "resistance": 1.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 161, + "id": "minecraft:leaves2", + "unlocalizedName": "tile.leaves", + "states": { + "variant": { + "dataMask": 3, + "values": { + "acacia": { "data": 0 }, + "darkoak": { "data": 1 } + } + }, + "decay": { + "dataMask": 7, + "values": { + "no_decay": { "data": 4 }, + "decay": { "data": 0 } + } + }, + "check_decay": { + "dataMask": 12, + "values": { + "no_decay": { "data": 12 }, + "decay": { "data": 0 } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.2, + "resistance": 1.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 1, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": true, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 162, + "id": "minecraft:log2", + "unlocalizedName": "tile.log", + "states": { + "variant": { + "dataMask": 3, + "values": { + "acacia": { "data": 0 }, + "darkoak": { "data": 1 } + } + }, + "facing": { + "dataMask": 12, + "values": { + "up": { "data": 0, "direction": [0, 1, 0] }, + "down": { "data": 0, "direction": [0, -1, 0] }, + "east": { "data": 4, "direction": [1, 0, 0] }, + "west": { "data": 4, "direction": [-1, 0, 0] }, + "north": { "data": 8, "direction": [0, 0, -1] }, + "south": { "data": 8, "direction": [0, 0, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 10.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 163, + "id": "minecraft:acacia_stairs", + "unlocalizedName": "tile.stairsWoodAcacia", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 164, + "id": "minecraft:dark_oak_stairs", + "unlocalizedName": "tile.stairsWoodDarkOak", + "states": { + "facing": { + "dataMask": 7, + "values": { + "east": { "data": 4, "direction": [1, 1, 0] }, + "west": { "data": 5, "direction": [-1, 1, 0] }, + "north": { "data": 7, "direction": [0, 1, -1] }, + "south": { "data": 6, "direction": [0, 1, 1] }, + "east_upsidedown": { "data": 0, "direction": [1, -1, 0] }, + "west_upsidedown": { "data": 1, "direction": [-1, -1, 0] }, + "north_upsidedown": { "data": 3, "direction": [0, -1, -1] }, + "south_upsidedown": { "data": 2, "direction": [0, -1, 1] } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 2.0, + "resistance": 15.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": true, + "movementBlocker": true, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 170, + "id": "minecraft:hay_block", + "unlocalizedName": "tile.hayBlock", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 171, + "id": "minecraft:carpet", + "unlocalizedName": "tile.woolCarpet", + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.1, + "resistance": 0.5, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": true, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 172, + "id": "minecraft:hardened_clay", + "unlocalizedName": "tile.clayHardened", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 1.25, + "resistance": 21.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 173, + "id": "minecraft:coal_block", + "unlocalizedName": "tile.blockCoal", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 5.0, + "resistance": 30.0, + "slipperiness": 0.6, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": false, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": true, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 174, + "id": "minecraft:packed_ice", + "unlocalizedName": "tile.icePacked", + "material": { + "renderedAsNormalBlock": true, + "fullCube": true, + "opaque": true, + "powerSource": false, + "liquid": false, + "solid": true, + "hardness": 0.5, + "resistance": 2.5, + "slipperiness": 0.98, + "grassBlocking": false, + "ambientOcclusionLightValue": 0.2, + "lightOpacity": 255, + "lightValue": 0, + "fragileWhenPushed": false, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": false, + "usingNeighborLight": false, + "movementBlocker": true, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + }, + { + "legacyId": 175, + "id": "minecraft:double_plant", + "unlocalizedName": "tile.doublePlant", + "states": { + "variant": { + "dataMask": 7, + "values": { + "sunflower": { "data": 0 }, + "lilac": { "data": 1 }, + "double_tallgrass": { "data": 2 }, + "large_fern": { "data": 3 }, + "rose_bush": { "data": 4 }, + "peony": { "data": 5 } + } + }, + "half": { + "dataMask": 8, + "values": { + "top": { "data": 0 }, + "bottom": { "data": 8 } + } + } + }, + "material": { + "renderedAsNormalBlock": false, + "fullCube": false, + "opaque": false, + "powerSource": false, + "liquid": false, + "solid": false, + "hardness": 0.0, + "resistance": 0.0, + "slipperiness": 0.6, + "grassBlocking": true, + "ambientOcclusionLightValue": 1.0, + "lightOpacity": 0, + "lightValue": 0, + "fragileWhenPushed": true, + "unpushable": false, + "adventureModeExempt": true, + "ticksRandomly": true, + "usingNeighborLight": true, + "movementBlocker": false, + "burnable": false, + "toolRequired": false, + "replacedDuringPlacement": false + } + } +] \ No newline at end of file diff --git a/src/test/java/com/sk89q/worldedit/extent/transform/BlockTransformExtentTest.java b/src/test/java/com/sk89q/worldedit/extent/transform/BlockTransformExtentTest.java new file mode 100644 index 000000000..b3ae0763f --- /dev/null +++ b/src/test/java/com/sk89q/worldedit/extent/transform/BlockTransformExtentTest.java @@ -0,0 +1,79 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.transform; + +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockData; +import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.math.transform.AffineTransform; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.registry.BlockRegistry; +import com.sk89q.worldedit.world.registry.LegacyBlockRegistry; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +public class BlockTransformExtentTest { + + private static final Transform ROTATE_90 = new AffineTransform().rotateY(-90); + private static final Transform ROTATE_NEG_90 = new AffineTransform().rotateY(90); + private final Set ignored = new HashSet(); + + @Before + public void setUp() throws Exception { + ignored.add(BlockType.BED); // Broken in existing rotation code? + ignored.add(BlockType.WOODEN_DOOR); // Complicated + ignored.add(BlockType.IRON_DOOR); // Complicated + ignored.add(BlockType.STONE_BUTTON); // Existing rotation code doesn't handle down/up directions + ignored.add(BlockType.WOODEN_BUTTON); // Existing rotation code doesn't handle down/up directions + ignored.add(BlockType.END_PORTAL); // Not supported in existing rotation code + } + + @Test + public void testTransform() throws Exception { + BlockRegistry blockRegistry = new LegacyBlockRegistry(); + for (BlockType type : BlockType.values()) { + if (ignored.contains(type)) { + continue; + } + + BaseBlock orig = new BaseBlock(type.getID()); + for (int i = 1; i < 4; i++) { + BaseBlock rotated = BlockTransformExtent.transform(new BaseBlock(orig), ROTATE_90, blockRegistry); + BaseBlock reference = new BaseBlock(orig.getType(), BlockData.rotate90(orig.getType(), orig.getData())); + assertThat(type + "#" + type.getID() + " rotated " + (90 * i) + " degrees did not match BlockData.rotate90()'s expected result", rotated, equalTo(reference)); + orig = rotated; + } + + orig = new BaseBlock(type.getID()); + for (int i = 0; i < 4; i++) { + BaseBlock rotated = BlockTransformExtent.transform(new BaseBlock(orig), ROTATE_NEG_90, blockRegistry); + BaseBlock reference = new BaseBlock(orig.getType(), BlockData.rotate90Reverse(orig.getType(), orig.getData())); + assertThat(type + "#" + type.getID() + " rotated " + (-90 * i) + " degrees did not match BlockData.rotate90Reverse()'s expected result", rotated, equalTo(reference)); + orig = rotated; + } + } + } +} \ No newline at end of file diff --git a/src/test/java/com/sk89q/worldedit/util/LocationTest.java b/src/test/java/com/sk89q/worldedit/util/LocationTest.java index ecf3bc327..1acdbc892 100644 --- a/src/test/java/com/sk89q/worldedit/util/LocationTest.java +++ b/src/test/java/com/sk89q/worldedit/util/LocationTest.java @@ -38,7 +38,7 @@ public class LocationTest { public void testGetWorld() throws Exception { World world = mock(World.class); Location location = new Location(world); - assertEquals(world, location.getWorld()); + assertEquals(world, location.getExtent()); } @Test @@ -46,28 +46,9 @@ public class LocationTest { World world1 = mock(World.class); World world2 = mock(World.class); Location location1 = new Location(world1); - Location location2 = location1.setWorld(world2); - assertEquals(world1, location1.getWorld()); - assertEquals(world2, location2.getWorld()); - } - - @Test - public void testGetDirection() throws Exception { - World world = mock(World.class); - Vector direction = new Vector(1, 1, 1); - Location location = new Location(world, new Vector(), direction); - assertEquals(direction, location.getDirection()); - } - - @Test - public void testSetDirection() throws Exception { - World world = mock(World.class); - Vector direction1 = new Vector(1, 1, 1); - Vector direction2 = new Vector(2, 2, 2); - Location location1 = new Location(world, new Vector(), direction1); - Location location2 = location1.setDirection(direction2); - assertEquals(direction1, location1.getDirection()); - assertEquals(direction2, location2.getDirection()); + Location location2 = location1.setExtent(world2); + assertEquals(world1, location1.getExtent()); + assertEquals(world2, location2.getExtent()); } @Test