diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java index 6ae3d6780..4d05219d4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/MCEditSchematicReader.java @@ -34,7 +34,9 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.EntityNBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.FlowerPotCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NoteBlockCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler; import com.sk89q.worldedit.math.BlockVector3; @@ -63,8 +65,10 @@ public class MCEditSchematicReader extends NBTSchematicReader { private static final ImmutableList COMPATIBILITY_HANDLERS = ImmutableList.of( - new SignCompatibilityHandler() - // TODO Add a handler for skulls, flower pots, note blocks, etc. + new SignCompatibilityHandler(), + new FlowerPotCompatibilityHandler(), + new NoteBlockCompatibilityHandler() + // TODO - skulls, item tags for inventories...? DFUs :> ); private static final ImmutableList ENTITY_COMPATIBILITY_HANDLERS = ImmutableList.of( @@ -167,65 +171,54 @@ public class MCEditSchematicReader extends NBTSchematicReader { // Need to pull out tile entities List tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue(); Map> tileEntitiesMap = new HashMap<>(); + Map blockOverrides = new HashMap<>(); for (Tag tag : tileEntities) { if (!(tag instanceof CompoundTag)) continue; CompoundTag t = (CompoundTag) tag; - int x = 0; - int y = 0; - int z = 0; + int x = t.getInt("x"); + int y = t.getInt("y"); + int z = t.getInt("z"); + String id = t.getString("id"); - Map values = new HashMap<>(); - - for (Map.Entry entry : t.getValue().entrySet()) { - switch (entry.getKey()) { - case "x": - if (entry.getValue() instanceof IntTag) { - x = ((IntTag) entry.getValue()).getValue(); - } - break; - case "y": - if (entry.getValue() instanceof IntTag) { - y = ((IntTag) entry.getValue()).getValue(); - } - break; - case "z": - if (entry.getValue() instanceof IntTag) { - z = ((IntTag) entry.getValue()).getValue(); - } - break; - } - - values.put(entry.getKey(), entry.getValue()); - } + Map values = new HashMap<>(t.getValue()); + values.put("id", new StringTag(convertBlockEntityId(id))); int index = y * width * length + z * width + x; BlockState block = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); - if (block != null) { + BlockState newBlock = block; + if (newBlock != null) { for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) { - if (handler.isAffectedBlock(block)) { - handler.updateNBT(block, values); + if (handler.isAffectedBlock(newBlock)) { + newBlock = handler.updateNBT(block, values); + if (newBlock == null) { + break; + } } } } BlockVector3 vec = BlockVector3.at(x, y, z); tileEntitiesMap.put(vec, values); + if (newBlock != block) { + blockOverrides.put(vec, newBlock); + } } 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; BlockVector3 pt = BlockVector3.at(x, y, z); - BlockState state = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); + boolean useOverride = blockOverrides.containsKey(pt); + BlockState state = useOverride + ? blockOverrides.get(pt) + : LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); try { if (state != null) { @@ -235,20 +228,11 @@ public class MCEditSchematicReader extends NBTSchematicReader { clipboard.setBlock(region.getMinimumPoint().add(pt), state); } } else { - log.warn("Unknown block when pasting schematic: " + blocks[index] + ":" + blockData[index] + ". Please report this issue."); + if (!useOverride) { + log.warn("Unknown block when pasting schematic: " + blocks[index] + ":" + blockData[index] + ". Please report this issue."); + } } - } catch (WorldEditException e) { - switch (failedBlockSets) { - case 0: - log.warn("Failed to set block on a Clipboard", e); - break; - case 1: - log.warn("Failed to set block on a Clipboard (again) -- no more messages will be logged", e); - break; - default: - } - - failedBlockSets++; + } catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this } } } @@ -258,8 +242,9 @@ public class MCEditSchematicReader extends NBTSchematicReader { // Entities // ==================================================================== - List entityTags = getTag(schematic, "Entities", ListTag.class).getValue(); - if (entityTags != null) { + ListTag entityList = getTag(schematic, "Entities", ListTag.class); + if (entityList != null) { + List entityTags = entityList.getValue(); for (Tag tag : entityTags) { if (tag instanceof CompoundTag) { CompoundTag compound = (CompoundTag) tag; @@ -289,32 +274,66 @@ public class MCEditSchematicReader extends NBTSchematicReader { private String convertEntityId(String id) { switch(id) { - case "xp_orb": - return "experience_orb"; - case "xp_bottle": - return "experience_bottle"; - case "eye_of_ender_signal": - return "eye_of_ender"; - case "ender_crystal": - return "end_crystal"; - case "fireworks_rocket": - return "firework_rocket"; - case "commandblock_minecart": - return "command_block_minecart"; - case "snowman": - return "snow_golem"; - case "villager_golem": - return "iron_golem"; - case "evocation_fangs": - return "evoker_fangs"; - case "evocation_illager": - return "evoker"; - case "vindication_illager": - return "vindicator"; - case "illusion_illager": - return "illusioner"; + case "AreaEffectCloud": return "area_effect_cloud"; + case "ArmorStand": return "armor_stand"; + case "CaveSpider": return "cave_spider"; + case "MinecartChest": return "chest_minecart"; + case "MinecartCommandBlock": return "commandblock_minecart"; + case "DragonFireball": return "dragon_fireball"; + case "ThrownEgg": return "egg"; + case "EnderCrystal": return "ender_crystal"; + case "EnderDragon": return "ender_dragon"; + case "ThrownEnderpearl": return "ender_pearl"; + case "EyeOfEnderSignal": return "eye_of_ender_signal"; + case "FallingSand": return "falling_block"; + case "FireworksRocketEntity": return "fireworks_rocket"; + case "MinecartFurnace": return "furnace_minecart"; + case "MinecartHopper": return "hopper_minecart"; + case "EntityHorse": return "horse"; + case "ItemFrame": return "item_frame"; + case "LeashKnot": return "leash_knot"; + case "LightningBolt": return "lightning_bolt"; + case "LavaSlime": return "magma_cube"; + case "MinecartRideable": return "minecart"; + case "MushroomCow": return "mooshroom"; + case "Ozelot": return "ocelot"; + case "PolarBear": return "polar_bear"; + case "ThrownPotion": return "potion"; + case "ShulkerBullet": return "shulker_bullet"; + case "SmallFireball": return "small_fireball"; + case "MinecartSpawner": return "spawner_minecart"; + case "SpectralArrow": return "spectral_arrow"; + case "PrimedTnt": return "tnt"; + case "MinecartTNT": return "tnt_minecart"; + case "VillagerGolem": return "villager_golem"; + case "WitherBoss": return "wither"; + case "WitherSkull": return "wither_skull"; + case "ThrownExpBottle": return "xp_bottle"; + case "XPOrb": return "xp_orb"; + case "PigZombie": return "zombie_pigman"; + default: return id; + } + } + + private String convertBlockEntityId(String id) { + switch(id) { + case "Cauldron": return "brewing_stand"; + case "Control": return "command_block"; + case "DLDetector": return "daylight_detector"; + case "Trap": return "dispenser"; + case "EnchantTable": return "enchanting_table"; + case "EndGateway": return "end_gateway"; + case "AirPortal": return "end_portal"; + case "EnderChest": return "ender_chest"; + case "FlowerPot": return "flower_pot"; + case "RecordPlayer": return "jukebox"; + case "MobSpawner": return "mob_spawner"; + case "Music": + case "noteblock": + return "note_block"; + case "Structure": return "structure_block"; + default: return id; } - return id; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java new file mode 100644 index 000000000..e34573f11 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java @@ -0,0 +1,74 @@ +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.registry.LegacyMapper; + +import java.util.Map; + +public class FlowerPotCompatibilityHandler implements NBTCompatibilityHandler { + @Override + public > boolean isAffectedBlock(B block) { + return block.getBlockType() == BlockTypes.FLOWER_POT; + } + + @Override + public > B updateNBT(B block, Map values) { + Tag item = values.get("Item"); + if (item instanceof StringTag) { + String id = ((StringTag) item).getValue(); + int data = 0; + Tag dataTag = values.get("Data"); + if (dataTag instanceof IntTag) { + data = ((IntTag) dataTag).getValue(); + } + BlockState newState = convertLegacyBlockType(id, data); + if (newState != null) { + return (B) newState; // generics pls :\ + } + } + return block; + } + + private BlockState convertLegacyBlockType(String id, int data) { + int newId = 0; + switch (id) { + case "minecraft:red_flower": + newId = 38; // now poppy + break; + case "minecraft:yellow_flower": + newId = 37; // now dandelion + break; + case "minecraft:sapling": + newId = 6; // oak_sapling + break; + case "minecraft:deadbush": + case "minecraft:tallgrass": + newId = 31; // dead_bush with fern and grass (not 32!) + break; + default: + break; + } + String plantedName = null; + if (newId == 0) { + plantedName = id.substring(10); + } else { + BlockState plantedWithData = LegacyMapper.getInstance().getBlockFromLegacy(newId, data); + if (plantedWithData != null) { + plantedName = plantedWithData.getBlockType().getId().substring(10); // remove "minecraft:" + } + } + if (plantedName != null) { + BlockType potAndPlanted = BlockTypes.get("minecraft:potted_" + plantedName); + if (potAndPlanted != null) { + return potAndPlanted.getDefaultState(); + } + } + return null; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java index 88c344566..0f631f561 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NBTCompatibilityHandler.java @@ -26,5 +26,5 @@ import java.util.Map; public interface NBTCompatibilityHandler { > boolean isAffectedBlock(B block); - > void updateNBT(B block, Map values); + > B updateNBT(B block, Map values); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java new file mode 100644 index 000000000..2ba296247 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java @@ -0,0 +1,43 @@ +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.registry.state.IntegerProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.Map; + +public class NoteBlockCompatibilityHandler implements NBTCompatibilityHandler { + private static final IntegerProperty NoteProperty; + + static { + IntegerProperty temp; + try { + temp = (IntegerProperty) (Property) BlockTypes.NOTE_BLOCK.getProperty("note"); + } catch (NullPointerException | IllegalArgumentException | ClassCastException e) { + temp = null; + } + NoteProperty = temp; + } + + @Override + public > boolean isAffectedBlock(B block) { + return NoteProperty != null && block.getBlockType() == BlockTypes.NOTE_BLOCK; + } + + @Override + public > B updateNBT(B block, Map values) { + // note that instrument was note stored (in state or nbt) previously. + // it will be updated to the block below when it gets set into the world for the first time + Tag noteTag = values.get("note"); + if (noteTag instanceof ByteTag) { + Byte note = ((ByteTag) noteTag).getValue(); + if (note != null) { + return (B) block.with(NoteProperty, (int) note); + } + } + return block; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java index 782ee2817..62f40d79c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java @@ -30,30 +30,32 @@ public class Pre13HangingCompatibilityHandler implements EntityNBTCompatibilityH @Override public boolean isAffectedEntity(EntityType type, CompoundTag tag) { - boolean hasLegacyDirection = tag.containsKey("Dir"); + if (!type.getId().startsWith("minecraft:")) { + return false; + } + boolean hasLegacyDirection = tag.containsKey("Dir") || tag.containsKey("Direction"); boolean hasFacing = tag.containsKey("Facing"); return hasLegacyDirection || hasFacing; } @Override public CompoundTag updateNBT(EntityType type, CompoundTag tag) { - boolean hasLegacyDirection = tag.containsKey("Dir"); - boolean hasFacing = tag.containsKey("Facing"); - int d; - if (hasLegacyDirection) { - d = MCDirections.fromLegacyHanging((byte) tag.asInt("Dir")); + boolean hasLegacyDir = tag.containsKey("Dir"); + boolean hasLegacyDirection = tag.containsKey("Direction"); + boolean hasPre113Facing = tag.containsKey("Facing"); + Direction newDirection; + if (hasLegacyDir) { + newDirection = MCDirections.fromPre13Hanging(MCDirections.fromLegacyHanging((byte) tag.asInt("Dir"))); + } else if (hasLegacyDirection) { + newDirection = MCDirections.fromPre13Hanging(tag.asInt("Direction")); + } else if (hasPre113Facing) { + newDirection = MCDirections.fromPre13Hanging(tag.asInt("Facing")); } else { - d = tag.asInt("Facing"); + return tag; } - - Direction newDirection = MCDirections.fromPre13Hanging(d); - byte hangingByte = (byte) MCDirections.toHanging(newDirection); - CompoundTagBuilder builder = tag.createBuilder(); - builder.putByte("Direction", hangingByte); builder.putByte("Facing", hangingByte); - builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection))); return builder.build(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java index ad75ed911..ff1ebd53f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SignCompatibilityHandler.java @@ -39,7 +39,7 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler { } @Override - public > void updateNBT(B block, Map values) { + public > B updateNBT(B block, Map values) { for (int i = 0; i < 4; ++i) { String key = "Text" + (i + 1); Tag value = values.get(key); @@ -69,5 +69,6 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler { values.put("Text" + (i + 1), new StringTag(jsonTextObject.toString())); } } + return block; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java index 1521655f6..3c882a18a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java @@ -130,8 +130,6 @@ public class ExtentEntityCopy implements EntityFunction { 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"); boolean hasFacing = tag.containsKey("Facing"); if (hasTilePosition) { @@ -143,26 +141,15 @@ public class ExtentEntityCopy implements EntityFunction { .putInt("TileY", newTilePosition.getBlockY()) .putInt("TileZ", newTilePosition.getBlockZ()); - if (hasDirection || hasLegacyDirection || hasFacing) { - Direction direction; - if (hasDirection) { - direction = MCDirections.fromPre13Hanging(tag.asInt("Direction")); - } else if (hasLegacyDirection) { - direction = MCDirections.fromPre13Hanging( - MCDirections.fromLegacyHanging((byte) tag.asInt("Dir")) - ); - } else { - direction = MCDirections.fromHanging(tag.asInt("Facing")); - } + if (hasFacing) { + Direction direction = MCDirections.fromHanging(tag.asInt("Facing")); if (direction != null) { Vector3 vector = transform.apply(direction.toVector()).subtract(transform.apply(Vector3.ZERO)).normalize(); Direction newDirection = Direction.findClosest(vector, Flag.CARDINAL); if (newDirection != null) { - builder.putByte("Direction", (byte) MCDirections.toPre13Hanging(newDirection)); builder.putByte("Facing", (byte) MCDirections.toHanging(newDirection)); - builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection))); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java index 3e5d3cc3e..959e30ae4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/helper/MCDirections.java @@ -82,21 +82,6 @@ public final class MCDirections { } } - public static int toPre13Hanging(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; @@ -106,15 +91,6 @@ public final class MCDirections { } } - public static byte toLegacyHanging(int i) { - switch (i) { - case 0: return (byte) 2; - case 1: return (byte) 1; - case 2: return (byte) 0; - default: return (byte) 3; - } - } - public static Direction fromRotation(int i) { switch (i) { case 0: