From efc4ebe3096601b184f9ae406aed9f915ef0bc85 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Tue, 16 Apr 2019 23:36:03 -0700 Subject: [PATCH 01/12] Fix 1.13 entity direction code, port old schematics --- .../clipboard/io/MCEditSchematicReader.java | 22 +++++++--- .../EntityNBTCompatibilityHandler.java | 32 +++++++++++++++ .../Pre13HangingCompatibilityHandler.java | 25 ++++++++++++ .../internal/helper/MCDirections.java | 40 ++++++++++++++++++- 4 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/EntityNBTCompatibilityHandler.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java 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 3997f28c7..6ae3d6780 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 @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extent.clipboard.io; +import com.google.common.collect.ImmutableList; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; @@ -32,7 +33,9 @@ import com.sk89q.worldedit.WorldEditException; 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.NBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; @@ -47,7 +50,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -59,12 +61,15 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class MCEditSchematicReader extends NBTSchematicReader { - private static final List COMPATIBILITY_HANDLERS = new ArrayList<>(); - - static { - COMPATIBILITY_HANDLERS.add(new SignCompatibilityHandler()); + private static final ImmutableList COMPATIBILITY_HANDLERS + = ImmutableList.of( + new SignCompatibilityHandler() // TODO Add a handler for skulls, flower pots, note blocks, etc. - } + ); + private static final ImmutableList ENTITY_COMPATIBILITY_HANDLERS + = ImmutableList.of( + new Pre13HangingCompatibilityHandler() + ); private static final Logger log = LoggerFactory.getLogger(MCEditSchematicReader.class); private final NBTInputStream inputStream; @@ -264,6 +269,11 @@ public class MCEditSchematicReader extends NBTSchematicReader { if (!id.isEmpty()) { EntityType entityType = EntityTypes.get(id.toLowerCase()); if (entityType != null) { + for (EntityNBTCompatibilityHandler compatibilityHandler : ENTITY_COMPATIBILITY_HANDLERS) { + if (compatibilityHandler.isAffectedEntity(entityType, compound)) { + compound = compatibilityHandler.updateNBT(entityType, compound); + } + } BaseEntity state = new BaseEntity(entityType, compound); clipboard.createEntity(location, state); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/EntityNBTCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/EntityNBTCompatibilityHandler.java new file mode 100644 index 000000000..ffba0b566 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/EntityNBTCompatibilityHandler.java @@ -0,0 +1,32 @@ +/* + * 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.legacycompat; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.entity.EntityType; + +import java.util.Map; + +public interface EntityNBTCompatibilityHandler { + boolean isAffectedEntity(EntityType type, CompoundTag entityTag); + CompoundTag updateNBT(EntityType type, CompoundTag entityTag); +} 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 new file mode 100644 index 000000000..85a181c83 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/Pre13HangingCompatibilityHandler.java @@ -0,0 +1,25 @@ +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.internal.helper.MCDirections; +import com.sk89q.worldedit.world.entity.EntityType; + +public class Pre13HangingCompatibilityHandler implements EntityNBTCompatibilityHandler { + + @Override + public boolean isAffectedEntity(EntityType type, CompoundTag entityTag) { + return entityTag.getValue().get("Facing") instanceof ByteTag; + } + + @Override + public CompoundTag updateNBT(EntityType type, CompoundTag entityTag) { + int newFacing = MCDirections.toHanging( + MCDirections.fromPre13Hanging(entityTag.getByte("Facing")) + ); + return entityTag.createBuilder() + .putByte("Facing", (byte) newFacing) + .build(); + } + +} 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 16e25e354..3e5d3cc3e 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 @@ -30,6 +30,44 @@ public final class MCDirections { } public static Direction fromHanging(int i) { + switch (i) { + case 0: + return Direction.DOWN; + case 1: + return Direction.UP; + case 2: + return Direction.NORTH; + case 3: + return Direction.SOUTH; + case 4: + return Direction.WEST; + case 5: + return Direction.EAST; + default: + return Direction.DOWN; + } + } + + public static int toHanging(Direction direction) { + switch (direction) { + case DOWN: + return 0; + case UP: + return 1; + case NORTH: + return 2; + case SOUTH: + return 3; + case WEST: + return 4; + case EAST: + return 5; + default: + return 0; + } + } + + public static Direction fromPre13Hanging(int i) { switch (i) { case 0: return Direction.SOUTH; @@ -44,7 +82,7 @@ public final class MCDirections { } } - public static int toHanging(Direction direction) { + public static int toPre13Hanging(Direction direction) { switch (direction) { case SOUTH: return 0; From 56ef7864153e998a865995b4147d1181833bfa03 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Tue, 16 Apr 2019 23:47:29 -0700 Subject: [PATCH 02/12] Don't touch the Direction tag, as possibly only Facing changed. --- .../Pre13HangingCompatibilityHandler.java | 34 ++++++++++++++----- .../function/entity/ExtentEntityCopy.java | 17 +++++----- 2 files changed, 33 insertions(+), 18 deletions(-) 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 85a181c83..845d3beb2 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 @@ -2,24 +2,40 @@ package com.sk89q.worldedit.extent.clipboard.io.legacycompat; import com.sk89q.jnbt.ByteTag; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.CompoundTagBuilder; import com.sk89q.worldedit.internal.helper.MCDirections; +import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.world.entity.EntityType; public class Pre13HangingCompatibilityHandler implements EntityNBTCompatibilityHandler { @Override - public boolean isAffectedEntity(EntityType type, CompoundTag entityTag) { - return entityTag.getValue().get("Facing") instanceof ByteTag; + public boolean isAffectedEntity(EntityType type, CompoundTag tag) { + boolean hasLegacyDirection = tag.containsKey("Dir"); + boolean hasFacing = tag.containsKey("Facing"); + return hasLegacyDirection || hasFacing; } @Override - public CompoundTag updateNBT(EntityType type, CompoundTag entityTag) { - int newFacing = MCDirections.toHanging( - MCDirections.fromPre13Hanging(entityTag.getByte("Facing")) - ); - return entityTag.createBuilder() - .putByte("Facing", (byte) newFacing) - .build(); + 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")); + } else { + d = tag.asInt("Facing"); + } + + 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/function/entity/ExtentEntityCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java index 7205c1212..1521655f6 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 @@ -144,25 +144,24 @@ public class ExtentEntityCopy implements EntityFunction { .putInt("TileZ", newTilePosition.getBlockZ()); if (hasDirection || hasLegacyDirection || hasFacing) { - int d; + Direction direction; if (hasDirection) { - d = tag.asInt("Direction"); + direction = MCDirections.fromPre13Hanging(tag.asInt("Direction")); } else if (hasLegacyDirection) { - d = MCDirections.fromLegacyHanging((byte) tag.asInt("Dir")); + direction = MCDirections.fromPre13Hanging( + MCDirections.fromLegacyHanging((byte) tag.asInt("Dir")) + ); } else { - d = tag.asInt("Facing"); + direction = MCDirections.fromHanging(tag.asInt("Facing")); } - Direction direction = MCDirections.fromHanging(d); - 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) { - byte hangingByte = (byte) MCDirections.toHanging(newDirection); - builder.putByte("Direction", hangingByte); - builder.putByte("Facing", hangingByte); + builder.putByte("Direction", (byte) MCDirections.toPre13Hanging(newDirection)); + builder.putByte("Facing", (byte) MCDirections.toHanging(newDirection)); builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection))); } } From bfc1fd8fd07eb702ca75ef29f45f6de5674d804b Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Tue, 16 Apr 2019 23:50:53 -0700 Subject: [PATCH 03/12] Add licenses --- .../Pre13HangingCompatibilityHandler.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 845d3beb2..782ee2817 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 @@ -1,3 +1,22 @@ +/* + * 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.legacycompat; import com.sk89q.jnbt.ByteTag; From 526b3366b5a600a18568db2b2e56b1486a92908e Mon Sep 17 00:00:00 2001 From: wizjany Date: Sat, 20 Apr 2019 12:13:58 -0400 Subject: [PATCH 04/12] Add more schematic compat, cleanup. --- .../clipboard/io/MCEditSchematicReader.java | 167 ++++++++++-------- .../FlowerPotCompatibilityHandler.java | 74 ++++++++ .../legacycompat/NBTCompatibilityHandler.java | 2 +- .../NoteBlockCompatibilityHandler.java | 43 +++++ .../Pre13HangingCompatibilityHandler.java | 28 +-- .../SignCompatibilityHandler.java | 3 +- .../function/entity/ExtentEntityCopy.java | 17 +- .../internal/helper/MCDirections.java | 24 --- 8 files changed, 230 insertions(+), 128 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java 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: From fcb42f05cf142a682991873b75f5869a4f1deb9e Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 25 Apr 2019 11:13:04 -0400 Subject: [PATCH 05/12] Add skull handler. --- .../clipboard/io/MCEditSchematicReader.java | 6 +- .../NoteBlockCompatibilityHandler.java | 4 +- .../SkullBlockCompatibilityHandler.java | 81 +++++++++++++++++++ 3 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java 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 4d05219d4..dad694e66 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 @@ -39,6 +39,7 @@ import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHand 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.extent.clipboard.io.legacycompat.SkullBlockCompatibilityHandler; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; @@ -67,8 +68,9 @@ public class MCEditSchematicReader extends NBTSchematicReader { = ImmutableList.of( new SignCompatibilityHandler(), new FlowerPotCompatibilityHandler(), - new NoteBlockCompatibilityHandler() - // TODO - skulls, item tags for inventories...? DFUs :> + new NoteBlockCompatibilityHandler(), + new SkullBlockCompatibilityHandler() + // TODO - item tags for inventories...? DFUs :> ); private static final ImmutableList ENTITY_COMPATIBILITY_HANDLERS = ImmutableList.of( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java index 2ba296247..9b21c75f4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java @@ -29,13 +29,13 @@ public class NoteBlockCompatibilityHandler implements NBTCompatibilityHandler { @Override public > B updateNBT(B block, Map values) { - // note that instrument was note stored (in state or nbt) previously. + // note that instrument was not 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.with(NoteProperty, (int) note); } } return block; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java new file mode 100644 index 000000000..b446ac7b8 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java @@ -0,0 +1,81 @@ +package com.sk89q.worldedit.extent.clipboard.io.legacycompat; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.registry.state.DirectionalProperty; +import com.sk89q.worldedit.registry.state.Property; +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 java.util.Map; + +public class SkullBlockCompatibilityHandler implements NBTCompatibilityHandler { + + private static final DirectionalProperty FacingProperty; + + static { + DirectionalProperty tempFacing; + try { + tempFacing = (DirectionalProperty) (Property) BlockTypes.SKELETON_WALL_SKULL.getProperty("facing"); + } catch (NullPointerException | IllegalArgumentException | ClassCastException e) { + tempFacing = null; + } + FacingProperty = tempFacing; + } + + @Override + public > boolean isAffectedBlock(B block) { + return block.getBlockType() == BlockTypes.SKELETON_SKULL + || block.getBlockType() == BlockTypes.SKELETON_WALL_SKULL; + } + + @Override + public > B updateNBT(B block, Map values) { + boolean isWall = block.getBlockType() == BlockTypes.SKELETON_WALL_SKULL; + Tag typeTag = values.get("SkullType"); + if (typeTag instanceof ByteTag) { + String skullType = convertSkullType(((ByteTag) typeTag).getValue(), isWall); + if (skullType != null) { + BlockType type = BlockTypes.get("minecraft:" + skullType); + if (type != null) { + BlockState state = type.getDefaultState(); + if (isWall) { + Property newProp = type.getProperty("facing"); + state = state.with(newProp, block.getState(FacingProperty)); + } else { + Tag rotTag = values.get("Rot"); + if (rotTag instanceof ByteTag) { + Property newProp = type.getProperty("rotation"); + state = state.with(newProp, (int) ((ByteTag) rotTag).getValue()); + } + } + values.remove("SkullType"); + values.remove("Rot"); + return (B) state; + } + } + } + return block; + } + + private String convertSkullType(Byte oldType, boolean isWall) { + switch (oldType) { + case 0: + return isWall ? "skeleton_wall_skull" : "skeleton_skull"; + case 1: + return isWall ? "wither_skeleton_wall_skull" : "wither_skeleton_skull"; + case 2: + return isWall ? "zombie_wall_head" : "zombie_head"; + case 3: + return isWall ? "player_wall_head" : "player_head"; + case 4: + return isWall ? "creeper_wall_head" : "creeper_head"; + case 5: + return isWall ? "dragon_wall_head" : "dragon_head"; + default: + return null; + } + } +} From 26511bcc259c3c627ba5ae111b7f512ab7ef86d5 Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 25 Apr 2019 11:22:39 -0400 Subject: [PATCH 06/12] Add licenses. --- .../FlowerPotCompatibilityHandler.java | 19 +++++++++++++++++++ .../NoteBlockCompatibilityHandler.java | 19 +++++++++++++++++++ .../SkullBlockCompatibilityHandler.java | 19 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java index e34573f11..225a9555e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/FlowerPotCompatibilityHandler.java @@ -1,3 +1,22 @@ +/* + * 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.legacycompat; import com.sk89q.jnbt.IntTag; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java index 9b21c75f4..940086b8f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/NoteBlockCompatibilityHandler.java @@ -1,3 +1,22 @@ +/* + * 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.legacycompat; import com.sk89q.jnbt.ByteTag; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java index b446ac7b8..f4fe1a3ce 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/legacycompat/SkullBlockCompatibilityHandler.java @@ -1,3 +1,22 @@ +/* + * 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.legacycompat; import com.sk89q.jnbt.ByteTag; From af1af43ac177da0398fdef5a561a307ba12ca1b6 Mon Sep 17 00:00:00 2001 From: wizjany Date: Wed, 3 Apr 2019 20:57:51 -0400 Subject: [PATCH 07/12] Allow copy/pasting biomes. Copy takes a -b flag to copy biomes. Paste takes a -b flag to paste biomes (if available). This allows flexibility to create/load schematics with/without biomes (when schematic biome support is added). Also added a -m mask flag to paste to set a source mask, and a -e flag to skip pasting entities if they are loaded. --- .../worldedit/command/ClipboardCommands.java | 28 +++++-- .../command/FlattenedClipboardTransform.java | 3 + .../extent/clipboard/BlockArrayClipboard.java | 5 ++ .../worldedit/extent/clipboard/Clipboard.java | 12 +++ .../function/biome/ExtentBiomeCopy.java | 73 +++++++++++++++++++ .../function/operation/ForwardExtentCopy.java | 54 +++++++++++++- .../sk89q/worldedit/session/PasteBuilder.java | 50 ++++++++++++- 7 files changed, 214 insertions(+), 11 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 0f61f0d2a..52cb67be3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -48,6 +48,7 @@ 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.PasteBuilder; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; @@ -75,19 +76,21 @@ public class ClipboardCommands { help = "Copy the selection to the clipboard\n" + "Flags:\n" + " -e will also copy entities\n" + - " -m sets a source mask so that excluded blocks become air", + " -m sets a source mask so that excluded blocks become air\n" + + " -b will also copy biomes", min = 0, max = 0 ) @CommandPermissions("worldedit.clipboard.copy") public void copy(Player player, LocalSession session, EditSession editSession, @Selection Region region, @Switch('e') boolean copyEntities, - @Switch('m') Mask mask) throws WorldEditException { + @Switch('m') Mask mask, @Switch('b') boolean copyBiomes) throws WorldEditException { BlockArrayClipboard clipboard = new BlockArrayClipboard(region); clipboard.setOrigin(session.getPlacementPosition(player)); ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); copy.setCopyingEntities(copyEntities); + copy.setCopyingBiomes(copyBiomes); if (mask != null) { copy.setSourceMask(mask); } @@ -121,6 +124,8 @@ public class ClipboardCommands { copy.setSourceFunction(new BlockReplace(editSession, leavePattern)); copy.setCopyingEntities(copyEntities); copy.setRemovingEntities(true); + // doesn't really make sense to "cut" biomes? so copy anyway and let them choose when pasting + copy.setCopyingBiomes(true); if (mask != null) { copy.setSourceMask(mask); } @@ -133,14 +138,17 @@ public class ClipboardCommands { @Command( aliases = { "/paste" }, usage = "", - flags = "sao", + flags = "saobem:", desc = "Paste the clipboard's contents", help = "Pastes the clipboard's contents.\n" + "Flags:\n" + " -a skips air blocks\n" + + " -b pastes biomes if available\n" + + " -e skips entities (default is don't skip!)\n" + + " -m [] skips matching blocks in the clipboard\n" + " -o pastes at the original position\n" + - " -s selects the region after pasting", + " -s selects the region after pasting\n", min = 0, max = 0 ) @@ -148,18 +156,24 @@ public class ClipboardCommands { @Logging(PLACEMENT) public void paste(Player player, LocalSession session, EditSession editSession, @Switch('a') boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, - @Switch('s') boolean selectPasted) throws WorldEditException { + @Switch('s') boolean selectPasted, @Switch('e') boolean skipEntities, + @Switch('b') boolean pasteBiomes, @Switch('m') Mask sourceMask) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); Clipboard clipboard = holder.getClipboard(); Region region = clipboard.getRegion(); BlockVector3 to = atOrigin ? clipboard.getOrigin() : session.getPlacementPosition(player); - Operation operation = holder + PasteBuilder builder = holder .createPaste(editSession) .to(to) .ignoreAirBlocks(ignoreAirBlocks) - .build(); + .copyBiomes(pasteBiomes) + .copyEntities(!skipEntities); + if (sourceMask != null) { + builder.maskSource(sourceMask); + } + Operation operation = builder.build(); Operations.completeLegacy(operation); if (selectPasted) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java index 204f062ec..464e1ad97 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/FlattenedClipboardTransform.java @@ -115,6 +115,9 @@ class FlattenedClipboardTransform { BlockTransformExtent extent = new BlockTransformExtent(original, transform); ForwardExtentCopy copy = new ForwardExtentCopy(extent, original.getRegion(), original.getOrigin(), target, original.getOrigin()); copy.setTransform(transform); + if (original.hasBiomes()) { + copy.setCopyingBiomes(true); + } return copy; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index 9d13ea579..e3cea6c89 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -161,6 +161,11 @@ public class BlockArrayClipboard implements Clipboard { } } + @Override + public boolean hasBiomes() { + return biomes != null; + } + @Override public BiomeType getBiome(BlockVector2 position) { if (biomes != null diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index e0022d480..aeaf07e85 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.extent.clipboard; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -58,4 +59,15 @@ public interface Clipboard extends Extent { */ void setOrigin(BlockVector3 origin); + /** + * Returns true if the clipboard has biome data. This can be checked since {@link Extent#getBiome(BlockVector2)} + * strongly suggests returning {@link com.sk89q.worldedit.world.biome.BiomeTypes.OCEAN} instead of {@code null} + * if biomes aren't present. However, it might not be desired to set areas to ocean if the clipboard is defaulting + * to ocean, instead of having biomes explicitly set. + * + * @return true if the clipboard has biome data set + */ + default boolean hasBiomes() { + return false; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java new file mode 100644 index 000000000..43dbf984d --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java @@ -0,0 +1,73 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.biome; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.FlatRegionFunction; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.biome.BiomeType; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Copies the biome from one extent to another. + */ +public class ExtentBiomeCopy implements FlatRegionFunction { + + private final Extent source; + private final Extent destination; + private final BlockVector2 from; + private final BlockVector2 to; + private final Transform transform; + + /** + * Make a new biome copy. + * + * @param source the source extent + * @param from the source offset + * @param destination the destination extent + * @param to the destination offset + * @param transform a transform to apply to positions (after source offset, before destination offset) + */ + public ExtentBiomeCopy(Extent source, BlockVector2 from, Extent destination, BlockVector2 to, Transform transform) { + checkNotNull(source); + checkNotNull(from); + checkNotNull(destination); + checkNotNull(to); + checkNotNull(transform); + this.source = source; + this.from = from; + this.destination = destination; + this.to = to; + this.transform = transform; + } + + @Override + public boolean apply(BlockVector2 position) throws WorldEditException { + BiomeType biome = source.getBiome(position); + BlockVector2 orig = position.subtract(from); + BlockVector2 transformed = transform.apply(orig.toVector3(0)).toVector2().toBlockPoint(); + + return destination.setBiome(transformed.add(to), biome); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index cbf73e857..6ca5ad6a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -28,17 +28,24 @@ import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.CombinedRegionFunction; +import com.sk89q.worldedit.function.FlatRegionFunction; +import com.sk89q.worldedit.function.FlatRegionMaskingFilter; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; +import com.sk89q.worldedit.function.biome.ExtentBiomeCopy; 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.Mask2D; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.visitor.EntityVisitor; +import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; import com.sk89q.worldedit.function.visitor.RegionVisitor; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import java.util.List; @@ -61,6 +68,7 @@ public class ForwardExtentCopy implements Operation { private Mask sourceMask = Masks.alwaysTrue(); private boolean removingEntities; private boolean copyingEntities = true; // default to true for backwards compatibility, sort of + private boolean copyingBiomes; private RegionFunction sourceFunction = null; private Transform transform = new Identity(); private Transform currentTransform = null; @@ -222,6 +230,27 @@ public class ForwardExtentCopy implements Operation { this.removingEntities = removingEntities; } + /** + * Return whether biomes should be copied along with blocks. + * + * @return true if copying biomes + */ + public boolean isCopyingBiomes() { + return copyingBiomes; + } + + /** + * Set whether biomes should be copies along with blocks. + * + * @param copyingBiomes true if copying + */ + public void setCopyingBiomes(boolean copyingBiomes) { + if (copyingBiomes && !(region instanceof FlatRegion)) { + throw new UnsupportedOperationException("Can't copy biomes from region that doesn't implement FlatRegion"); + } + this.copyingBiomes = copyingBiomes; + } + /** * Get the number of affected objects. * @@ -254,6 +283,25 @@ public class ForwardExtentCopy implements Operation { lastVisitor = blockVisitor; + if (!copyingBiomes && !copyingEntities) { + return new DelegateOperation(this, blockVisitor); + } + + List ops = Lists.newArrayList(blockVisitor); + + if (copyingBiomes && region instanceof FlatRegion) { // double-check here even though we checked before + ExtentBiomeCopy biomeCopy = new ExtentBiomeCopy(source, from.toBlockVector2(), + destination, to.toBlockVector2(), currentTransform); + Mask2D biomeMask = sourceMask.toMask2D(); + if (biomeMask != null) { + FlatRegionMaskingFilter filteredBiomeCopy = new FlatRegionMaskingFilter(biomeMask, biomeCopy); + FlatRegionVisitor biomeVisitor = new FlatRegionVisitor(((FlatRegion) region), filteredBiomeCopy); + ops.add(biomeVisitor); + } else { + ops.add(new FlatRegionVisitor(((FlatRegion) region), biomeCopy)); + } + } + if (copyingEntities) { ExtentEntityCopy entityCopy = new ExtentEntityCopy(from.toVector3(), destination, to.toVector3(), currentTransform); entityCopy.setRemoving(removingEntities); @@ -263,10 +311,10 @@ public class ForwardExtentCopy implements Operation { return properties != null && !properties.isPasteable(); }); EntityVisitor entityVisitor = new EntityVisitor(entities.iterator(), entityCopy); - return new DelegateOperation(this, new OperationQueue(blockVisitor, entityVisitor)); - } else { - return new DelegateOperation(this, blockVisitor); + ops.add(entityVisitor); } + + return new DelegateOperation(this, new OperationQueue(ops)); } else { return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java index d50a277ac..c46939661 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java @@ -25,6 +25,9 @@ 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.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector3; @@ -39,8 +42,12 @@ public class PasteBuilder { private final Transform transform; private final Extent targetExtent; + private Mask sourceMask = Masks.alwaysTrue(); + private BlockVector3 to = BlockVector3.ZERO; private boolean ignoreAirBlocks; + private boolean copyEntities = true; // default because it used to be this way + private boolean copyBiomes; /** * Create a new instance. @@ -67,6 +74,19 @@ public class PasteBuilder { return this; } + /** + * Set a custom mask of blocks to ignore from the source. + * This provides a more flexible alternative to {@link #ignoreAirBlocks(boolean)}, for example + * one might want to ignore structure void if copying a Minecraft Structure, etc. + * + * @param sourceMask + * @return this builder instance + */ + public PasteBuilder maskSource(Mask sourceMask) { + this.sourceMask = sourceMask; + return this; + } + /** * Set whether air blocks in the source are skipped over when pasting. * @@ -77,6 +97,29 @@ public class PasteBuilder { return this; } + /** + * Set whether the copy should include source entities. + * Note that this is true by default for legacy reasons. + * + * @param copyEntities + * @return this builder instance + */ + public PasteBuilder copyEntities(boolean copyEntities) { + this.copyEntities = copyEntities; + return this; + } + + /** + * Set whether the copy should include source biomes (if available). + * + * @param copyBiomes + * @return this builder instance + */ + public PasteBuilder copyBiomes(boolean copyBiomes) { + this.copyBiomes = copyBiomes; + return this; + } + /** * Build the operation. * @@ -87,8 +130,13 @@ public class PasteBuilder { ForwardExtentCopy copy = new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(), targetExtent, to); copy.setTransform(transform); if (ignoreAirBlocks) { - copy.setSourceMask(new ExistingBlockMask(clipboard)); + copy.setSourceMask(sourceMask == Masks.alwaysTrue() ? new ExistingBlockMask(clipboard) + : new MaskIntersection(sourceMask, new ExistingBlockMask(clipboard))); + } else { + copy.setSourceMask(sourceMask); } + copy.setCopyingEntities(copyEntities); + copy.setCopyingBiomes(copyBiomes && clipboard.hasBiomes()); return copy; } From 17fba54305770303b5bbbc6539ee895fc818969e Mon Sep 17 00:00:00 2001 From: wizjany Date: Wed, 3 Apr 2019 23:33:10 -0400 Subject: [PATCH 08/12] Update SpongeSchematic format to version 2. Allows saving and loading entities and biomes. --- .../bukkit/BukkitServerInterface.java | 6 + .../com/sk89q/jnbt/CompoundTagBuilder.java | 12 ++ .../extension/platform/Platform.java | 7 + .../extent/clipboard/BlockArrayClipboard.java | 5 +- .../clipboard/io/MCEditSchematicReader.java | 2 + .../clipboard/io/SpongeSchematicReader.java | 120 ++++++++++++++++- .../clipboard/io/SpongeSchematicWriter.java | 121 ++++++++++++++++-- .../world/storage/NBTConversions.java | 2 +- .../sk89q/worldedit/forge/ForgePlatform.java | 6 + .../worldedit/sponge/SpongePlatform.java | 6 + 10 files changed, 273 insertions(+), 14 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index adfc64bc0..0123b452b 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -65,6 +65,12 @@ public class BukkitServerInterface implements MultiUserPlatform { return BukkitRegistries.getInstance(); } + @Override + public int getDataVersion() { + // TODO - add to adapter - CraftMagicNumbers#getDataVersion + return 1631; + } + @Override public boolean isValidMobType(String type) { final EntityType entityType = EntityType.fromName(type); diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java b/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java index b0e873c0d..6b5776619 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/CompoundTagBuilder.java @@ -181,6 +181,18 @@ public class CompoundTagBuilder { return put(key, new StringTag(value)); } + /** + * Remove the given key from the compound tag. Does nothing if the key doesn't exist. + * + * @param key the key + * @return this object + */ + public CompoundTagBuilder remove(String key) { + checkNotNull(key); + entries.remove(key); + return this; + } + /** * Put all the entries from the given map into this map. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index 3b37a98b5..6878235d5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -45,6 +45,13 @@ public interface Platform { */ Registries getRegistries(); + /** + * Gets the Minecraft data version being used by the platform. + * + * @return the data version + */ + int getDataVersion(); + /** * Checks if a mob type is valid. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index e3cea6c89..029d25052 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -171,7 +171,10 @@ public class BlockArrayClipboard implements Clipboard { if (biomes != null && position.containedWithin(getMinimumPoint().toBlockVector2(), getMaximumPoint().toBlockVector2())) { BlockVector2 v = position.subtract(region.getMinimumPoint().toBlockVector2()); - return biomes[v.getBlockX()][v.getBlockZ()]; + BiomeType biomeType = biomes[v.getBlockX()][v.getBlockZ()]; + if (biomeType != null) { + return biomeType; + } } return BiomeTypes.OCEAN; 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 dad694e66..a5e87c356 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 @@ -53,6 +53,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -315,6 +316,7 @@ public class MCEditSchematicReader extends NBTSchematicReader { case "PigZombie": return "zombie_pigman"; default: return id; } + return id; } private String convertBlockEntityId(String id) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index 09594e388..57d8582b2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -28,18 +28,27 @@ import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NamedTag; import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.entity.EntityTypes; +import com.sk89q.worldedit.world.storage.NBTConversions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,6 +98,15 @@ public class SpongeSchematicReader extends NBTSchematicReader { int version = requireTag(schematic, "Version", IntTag.class).getValue(); if (version == 1) { return readVersion1(schematicTag); + } else if (version == 2) { + int dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); + if (dataVersion > WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.WORLD_EDITING).getDataVersion()) { + // maybe should just warn? simple schematics may be compatible still. + throw new IOException("Schematic was made in a newer Minecraft version. Data may be incompatible."); + } + BlockArrayClipboard clip = readVersion1(schematicTag); + return readVersion2(clip, schematicTag); } throw new IOException("This schematic version is currently not supported"); } @@ -123,10 +141,11 @@ public class SpongeSchematicReader extends NBTSchematicReader { region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE)); } + // Note: these aren't actually required by the spec, but we require them I guess? int paletteMax = requireTag(schematic, "PaletteMax", IntTag.class).getValue(); Map paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue(); if (paletteObject.size() != paletteMax) { - throw new IOException("Differing given palette size to actual size"); + throw new IOException("Block palette size does not match expected size."); } Map palette = new HashMap<>(); @@ -168,8 +187,8 @@ public class SpongeSchematicReader extends NBTSchematicReader { int index = 0; int i = 0; - int value = 0; - int varintLength = 0; + int value; + int varintLength; while (i < blocks.length) { value = 0; varintLength = 0; @@ -185,7 +204,7 @@ public class SpongeSchematicReader extends NBTSchematicReader { } i++; } - // index = (y * length + z) * width + x + // index = (y * length * width) + (z * width) + x int y = index / (width * length); int z = (index % (width * length)) / width; int x = (index % (width * length)) % width; @@ -219,6 +238,99 @@ public class SpongeSchematicReader extends NBTSchematicReader { return clipboard; } + private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag) throws IOException { + Map schematic = schematicTag.getValue(); + if (schematic.containsKey("BiomeData")) { + readBiomes(version1, schematic); + } + if (schematic.containsKey("Entities")) { + readEntities(version1, schematic); + } + return version1; + } + + private void readBiomes(BlockArrayClipboard clipboard, Map schematic) throws IOException { + ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class); + // TODO for now, we just assume if biomedata is present, palette will be as well. + // atm the spec doesn't actually require palettes + IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class); + CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class); + + Map palette = new HashMap<>(); + if (maxTag.getValue() != paletteTag.getValue().size()) { + throw new IOException("Biome palette size does not match expected size."); + } + Map paletteEntries = paletteTag.getValue(); + + for (Map.Entry palettePart : paletteEntries.entrySet()) { + BiomeType biome = BiomeTypes.get(palettePart.getKey()); + if (biome == null) { + log.warn("Unknown biome type '" + palettePart.getKey() + "' in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); + } + Tag idTag = palettePart.getValue(); + if (!(idTag instanceof IntTag)) { + throw new IOException("Biome mapped to non-Int tag."); + } + palette.put(((IntTag) idTag).getValue(), biome); + } + + int width = clipboard.getDimensions().getX(); + + byte[] biomes = dataTag.getValue(); + int biomeIndex = 0; + int biomeJ = 0; + int bVal; + int varIntLength; + while (biomeJ < biomes.length) { + bVal = 0; + varIntLength = 0; + + while (true) { + bVal |= (biomes[biomeJ] & 127) << (varIntLength++ * 7); + if (varIntLength > 5) { + throw new RuntimeException("VarInt too big (probably corrupted data)"); + } + if (((biomes[biomeJ] & 128) != 128)) { + biomeJ++; + break; + } + biomeJ++; + } + int z = biomeIndex / width; + int x = biomeIndex % width; + BiomeType type = palette.get(bVal); + clipboard.setBiome(clipboard.getMinimumPoint().toBlockVector2().add(x, z), type); + biomeIndex++; + } + } + + private void readEntities(BlockArrayClipboard clipboard, Map schematic) throws IOException { + List entList = requireTag(schematic, "Entities", ListTag.class).getValue(); + if (entList.isEmpty()) { + return; + } + for (Tag et : entList) { + if (!(et instanceof CompoundTag)) { + continue; + } + CompoundTag entityTag = (CompoundTag) et; + Map tags = entityTag.getValue(); + String id = requireTag(tags, "Id", StringTag.class).getValue(); + + EntityType entityType = EntityTypes.get(id); + if (entityType != null) { + Location location = NBTConversions.toLocation(clipboard, + requireTag(tags, "Pos", ListTag.class), + requireTag(tags, "Rotation", ListTag.class)); + BaseEntity state = new BaseEntity(entityType, + entityTag.createBuilder().putString("id", id).remove("Id").build()); + clipboard.createEntity(location, state); + } else { + log.warn("Unknown entity when pasting schematic: " + id); + } + } + } + @Override public void close() throws IOException { inputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java index 8237106f6..f9d9248a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java @@ -21,8 +21,12 @@ package com.sk89q.worldedit.extent.clipboard.io; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.FloatTag; import com.sk89q.jnbt.IntArrayTag; import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.ListTag; @@ -30,9 +34,16 @@ import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import java.io.ByteArrayOutputStream; @@ -41,12 +52,15 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * Writes schematic files using the Sponge schematic format. */ public class SpongeSchematicWriter implements ClipboardWriter { + private static final int CURRENT_VERSION = 2; + private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; private final NBTOutputStream outputStream; @@ -63,17 +77,17 @@ public class SpongeSchematicWriter implements ClipboardWriter { @Override public void write(Clipboard clipboard) throws IOException { // For now always write the latest version. Maybe provide support for earlier if more appear. - outputStream.writeNamedTag("Schematic", new CompoundTag(write1(clipboard))); + outputStream.writeNamedTag("Schematic", new CompoundTag(write2(clipboard))); } /** - * Writes a version 1 schematic file. + * Writes a version 2 schematic file. * * @param clipboard The clipboard * @return The schematic map * @throws IOException If an error occurs */ - private Map write1(Clipboard clipboard) throws IOException { + private Map write2(Clipboard clipboard) throws IOException { Region region = clipboard.getRegion(); BlockVector3 origin = clipboard.getOrigin(); BlockVector3 min = region.getMinimumPoint(); @@ -93,7 +107,9 @@ public class SpongeSchematicWriter implements ClipboardWriter { } Map schematic = new HashMap<>(); - schematic.put("Version", new IntTag(1)); + schematic.put("Version", new IntTag(CURRENT_VERSION)); + schematic.put("DataVersion", new IntTag( + WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion())); Map metadata = new HashMap<>(); metadata.put("WEOffsetX", new IntTag(offset.getBlockX())); @@ -129,10 +145,7 @@ public class SpongeSchematicWriter implements ClipboardWriter { BlockVector3 point = BlockVector3.at(x0, y0, z0); BaseBlock block = clipboard.getFullBlock(point); if (block.getNbtData() != null) { - Map values = new HashMap<>(); - for (Map.Entry entry : block.getNbtData().getValue().entrySet()) { - values.put(entry.getKey(), entry.getValue()); - } + Map values = new HashMap<>(block.getNbtData().getValue()); values.remove("id"); // Remove 'id' if it exists. We want 'Id' @@ -179,9 +192,101 @@ public class SpongeSchematicWriter implements ClipboardWriter { schematic.put("BlockData", new ByteArrayTag(buffer.toByteArray())); schematic.put("TileEntities", new ListTag(CompoundTag.class, tileEntities)); + // version 2 stuff + if (clipboard.hasBiomes()) { + writeBiomes(clipboard, schematic); + } + + if (!clipboard.getEntities().isEmpty()) { + writeEntities(clipboard, schematic); + } + return schematic; } + private void writeBiomes(Clipboard clipboard, Map schematic) { + BlockVector3 min = clipboard.getMinimumPoint(); + int width = clipboard.getRegion().getWidth(); + int length = clipboard.getRegion().getLength(); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * length); + + int paletteMax = 0; + Map palette = new HashMap<>(); + + for (int z = 0; z < length; z++) { + int z0 = min.getBlockZ() + z; + for (int x = 0; x < width; x++) { + int x0 = min.getBlockX() + x; + BlockVector2 pt = BlockVector2.at(x0, z0); + BiomeType biome = clipboard.getBiome(pt); + + String biomeKey = biome.getId(); + int biomeId; + if (palette.containsKey(biomeKey)) { + biomeId = palette.get(biomeKey); + } else { + biomeId = paletteMax; + palette.put(biomeKey, biomeId); + paletteMax++; + } + + while ((biomeId & -128) != 0) { + buffer.write(biomeId & 127 | 128); + biomeId >>>= 7; + } + buffer.write(biomeId); + } + } + + schematic.put("BiomePaletteMax", new IntTag(paletteMax)); + + Map paletteTag = new HashMap<>(); + palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value))); + + schematic.put("BiomePalette", new CompoundTag(paletteTag)); + schematic.put("BiomeData", new ByteArrayTag(buffer.toByteArray())); + } + + private void writeEntities(Clipboard clipboard, Map schematic) { + List entities = clipboard.getEntities().stream().map(e -> { + BaseEntity state = e.getState(); + if (state == null) { + return null; + } + Map values = Maps.newHashMap(); + CompoundTag rawData = state.getNbtData(); + if (rawData != null) { + values.putAll(rawData.getValue()); + } + values.remove("id"); + values.put("Id", new StringTag(state.getType().getId())); + values.put("Pos", writeVector(e.getLocation().toVector())); + values.put("Rotation", writeRotation(e.getLocation())); + + return new CompoundTag(values); + }).filter(e -> e != null).collect(Collectors.toList()); + if (entities.isEmpty()) { + return; + } + schematic.put("Entities", new ListTag(CompoundTag.class, entities)); + } + + private Tag writeVector(Vector3 vector) { + 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(DoubleTag.class, list); + } + + private Tag writeRotation(Location location) { + List list = new ArrayList(); + list.add(new FloatTag(location.getYaw())); + list.add(new FloatTag(location.getPitch())); + return new ListTag(FloatTag.class, list); + } + @Override public void close() throws IOException { outputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java index 6b61bddf9..3da5c0047 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/NBTConversions.java @@ -52,7 +52,7 @@ public final class NBTConversions { return new Location( extent, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), - (float) directionTag.asDouble(0), (float) directionTag.asDouble(1)); + directionTag.getFloat(0), directionTag.getFloat(1)); } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index 0530acc54..d578c3b9f 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -64,6 +64,12 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { return ForgeRegistries.getInstance(); } + @Override + public int getDataVersion() { + // TODO technically available as WorldInfo#field_209227_p but requires a world ref? + return 1631; + } + @Override public boolean isValidMobType(String type) { return net.minecraftforge.registries.ForgeRegistries.ENTITIES.containsKey(new ResourceLocation(type)); diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java index 774347645..33a4395e0 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java @@ -68,6 +68,12 @@ class SpongePlatform extends AbstractPlatform implements MultiUserPlatform { return SpongeRegistries.getInstance(); } + @Override + public int getDataVersion() { + // TODO add to adapter - org.spongepowered.common.data.util.DataUtil#MINECRAFT_DATA_VERSION + return 1631; + } + @Override public boolean isValidMobType(String type) { return Sponge.getRegistry().getType(EntityType.class, type).isPresent(); From f0587354bec79fa4e1dcffda73735db91053297d Mon Sep 17 00:00:00 2001 From: wizjany Date: Fri, 5 Apr 2019 19:40:25 -0400 Subject: [PATCH 09/12] Cleanup, make copy/paste flags consistent, add status messages. --- .../worldedit/command/ClipboardCommands.java | 35 ++++++++----- .../clipboard/io/SpongeSchematicReader.java | 16 +++--- .../function/operation/ForwardExtentCopy.java | 52 +++++++++++++++---- 3 files changed, 72 insertions(+), 31 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 52cb67be3..cffdbb2ce 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import com.google.common.collect.Lists; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; @@ -52,6 +53,8 @@ import com.sk89q.worldedit.session.PasteBuilder; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; +import java.util.List; + /** * Clipboard commands. */ @@ -76,8 +79,8 @@ public class ClipboardCommands { help = "Copy the selection to the clipboard\n" + "Flags:\n" + " -e will also copy entities\n" + - " -m sets a source mask so that excluded blocks become air\n" + - " -b will also copy biomes", + " -b will also copy biomes\n" + + " -m sets a source mask so that excluded blocks become air", min = 0, max = 0 ) @@ -97,17 +100,21 @@ public class ClipboardCommands { Operations.completeLegacy(copy); session.setClipboard(new ClipboardHolder(clipboard)); - player.print(region.getArea() + " block(s) were copied."); + List messages = Lists.newArrayList(); + copy.addStatusMessages(messages); + messages.forEach(player::print); } @Command( aliases = { "/cut" }, flags = "em", - usage = "[leave-id]", + usage = "[leave-pattern]", desc = "Cut the selection to the clipboard", help = "Copy the selection to the clipboard\n" + + "The space will be filled with the leave pattern if specified, otherwise air." + "Flags:\n" + " -e will also cut entities\n" + + " -b will also copy biomes (source biomes unaffected)\n" + " -m sets a source mask so that excluded blocks become air\n" + "WARNING: Cutting and pasting entities cannot yet be undone!", max = 1 @@ -116,7 +123,7 @@ public class ClipboardCommands { @Logging(REGION) 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 { + @Switch('m') Mask mask, @Switch('b') boolean copyBiomes) throws WorldEditException { BlockArrayClipboard clipboard = new BlockArrayClipboard(region); clipboard.setOrigin(session.getPlacementPosition(player)); @@ -124,20 +131,20 @@ public class ClipboardCommands { copy.setSourceFunction(new BlockReplace(editSession, leavePattern)); copy.setCopyingEntities(copyEntities); copy.setRemovingEntities(true); - // doesn't really make sense to "cut" biomes? so copy anyway and let them choose when pasting - copy.setCopyingBiomes(true); + copy.setCopyingBiomes(copyBiomes); if (mask != null) { copy.setSourceMask(mask); } Operations.completeLegacy(copy); session.setClipboard(new ClipboardHolder(clipboard)); - player.print(region.getArea() + " block(s) were cut."); + List messages = Lists.newArrayList(); + copy.addStatusMessages(messages); + messages.forEach(player::print); } @Command( aliases = { "/paste" }, - usage = "", flags = "saobem:", desc = "Paste the clipboard's contents", help = @@ -145,18 +152,17 @@ public class ClipboardCommands { "Flags:\n" + " -a skips air blocks\n" + " -b pastes biomes if available\n" + - " -e skips entities (default is don't skip!)\n" + + " -e pastes entities if available\n" + " -m [] skips matching blocks in the clipboard\n" + " -o pastes at the original position\n" + " -s selects the region after pasting\n", - min = 0, max = 0 ) @CommandPermissions("worldedit.clipboard.paste") @Logging(PLACEMENT) public void paste(Player player, LocalSession session, EditSession editSession, @Switch('a') boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, - @Switch('s') boolean selectPasted, @Switch('e') boolean skipEntities, + @Switch('s') boolean selectPasted, @Switch('e') boolean pasteEntities, @Switch('b') boolean pasteBiomes, @Switch('m') Mask sourceMask) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); @@ -169,7 +175,7 @@ public class ClipboardCommands { .to(to) .ignoreAirBlocks(ignoreAirBlocks) .copyBiomes(pasteBiomes) - .copyEntities(!skipEntities); + .copyEntities(pasteEntities); if (sourceMask != null) { builder.maskSource(sourceMask); } @@ -187,6 +193,9 @@ public class ClipboardCommands { } player.print("The clipboard has been pasted at " + to); + List messages = Lists.newArrayList(); + operation.addStatusMessages(messages); + messages.forEach(player::print); } @Command( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index 57d8582b2..556a60570 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -39,6 +39,7 @@ import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; @@ -57,6 +58,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; @@ -141,7 +143,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE)); } - // Note: these aren't actually required by the spec, but we require them I guess? int paletteMax = requireTag(schematic, "PaletteMax", IntTag.class).getValue(); Map paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue(); if (paletteObject.size() != paletteMax) { @@ -161,7 +162,8 @@ public class SpongeSchematicReader extends NBTSchematicReader { try { state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState(); } catch (InputParseException e) { - throw new IOException("Invalid BlockState in schematic: " + palettePart + ". Are you missing a mod or using a schematic made in a newer version of Minecraft?"); + throw new IOException("Invalid BlockState in palette: " + palettePart + + ". Are you missing a mod or using a schematic made in a newer version of Minecraft?"); } palette.put(id, state); } @@ -251,8 +253,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { private void readBiomes(BlockArrayClipboard clipboard, Map schematic) throws IOException { ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class); - // TODO for now, we just assume if biomedata is present, palette will be as well. - // atm the spec doesn't actually require palettes IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class); CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class); @@ -262,10 +262,11 @@ public class SpongeSchematicReader extends NBTSchematicReader { } Map paletteEntries = paletteTag.getValue(); - for (Map.Entry palettePart : paletteEntries.entrySet()) { + for (Entry palettePart : paletteEntries.entrySet()) { BiomeType biome = BiomeTypes.get(palettePart.getKey()); if (biome == null) { - log.warn("Unknown biome type '" + palettePart.getKey() + "' in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); + log.warn("Unknown biome type :" + palettePart.getKey() + + " in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); } Tag idTag = palettePart.getValue(); if (!(idTag instanceof IntTag)) { @@ -281,6 +282,7 @@ public class SpongeSchematicReader extends NBTSchematicReader { int biomeJ = 0; int bVal; int varIntLength; + BlockVector2 min = clipboard.getMinimumPoint().toBlockVector2(); while (biomeJ < biomes.length) { bVal = 0; varIntLength = 0; @@ -299,7 +301,7 @@ public class SpongeSchematicReader extends NBTSchematicReader { int z = biomeIndex / width; int x = biomeIndex % width; BiomeType type = palette.get(bVal); - clipboard.setBiome(clipboard.getMinimumPoint().toBlockVector2().add(x, z), type); + clipboard.setBiome(min.add(x, z), type); biomeIndex++; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 6ca5ad6a6..9512b5c3d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -41,7 +41,6 @@ import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; import com.sk89q.worldedit.function.visitor.RegionVisitor; -import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; @@ -72,8 +71,14 @@ public class ForwardExtentCopy implements Operation { private RegionFunction sourceFunction = null; private Transform transform = new Identity(); private Transform currentTransform = null; + private RegionVisitor lastVisitor; - private int affected; + private FlatRegionVisitor lastBiomeVisitor; + private EntityVisitor lastEntityVisitor; + + private int affectedBlocks; + private int affectedBiomeCols; + private int affectedEntities; /** * Create a new copy using the region's lowest minimum point as the @@ -257,15 +262,23 @@ public class ForwardExtentCopy implements Operation { * @return the number of affected */ public int getAffected() { - return affected; + return affectedBlocks + affectedBiomeCols + affectedEntities; } @Override public Operation resume(RunContext run) throws WorldEditException { if (lastVisitor != null) { - affected += lastVisitor.getAffected(); + affectedBlocks += lastVisitor.getAffected(); lastVisitor = null; } + if (lastBiomeVisitor != null) { + affectedBiomeCols += lastBiomeVisitor.getAffected(); + lastBiomeVisitor = null; + } + if (lastEntityVisitor != null) { + affectedEntities += lastEntityVisitor.getAffected(); + lastEntityVisitor = null; + } if (repetitions > 0) { repetitions--; @@ -293,13 +306,11 @@ public class ForwardExtentCopy implements Operation { ExtentBiomeCopy biomeCopy = new ExtentBiomeCopy(source, from.toBlockVector2(), destination, to.toBlockVector2(), currentTransform); Mask2D biomeMask = sourceMask.toMask2D(); - if (biomeMask != null) { - FlatRegionMaskingFilter filteredBiomeCopy = new FlatRegionMaskingFilter(biomeMask, biomeCopy); - FlatRegionVisitor biomeVisitor = new FlatRegionVisitor(((FlatRegion) region), filteredBiomeCopy); - ops.add(biomeVisitor); - } else { - ops.add(new FlatRegionVisitor(((FlatRegion) region), biomeCopy)); - } + FlatRegionFunction biomeFunction = biomeMask == null ? biomeCopy + : new FlatRegionMaskingFilter(biomeMask, biomeCopy); + FlatRegionVisitor biomeVisitor = new FlatRegionVisitor(((FlatRegion) region), biomeFunction); + ops.add(biomeVisitor); + lastBiomeVisitor = biomeVisitor; } if (copyingEntities) { @@ -312,6 +323,7 @@ public class ForwardExtentCopy implements Operation { }); EntityVisitor entityVisitor = new EntityVisitor(entities.iterator(), entityCopy); ops.add(entityVisitor); + lastEntityVisitor = entityVisitor; } return new DelegateOperation(this, new OperationQueue(ops)); @@ -326,6 +338,24 @@ public class ForwardExtentCopy implements Operation { @Override public void addStatusMessages(List messages) { + StringBuilder msg = new StringBuilder(); + msg.append(affectedBlocks).append(" block(s)"); + if (affectedBiomeCols > 0) { + if (affectedEntities > 0) { + msg.append(", "); + } else { + msg.append(" and "); + } + msg.append(affectedBiomeCols).append(" biome(s)"); + } + if (affectedEntities > 0) { + if (affectedBiomeCols > 0) { + msg.append(","); + } + msg.append(" and ").append(affectedEntities).append(" entities(s)"); + } + msg.append(" affected."); + messages.add(msg.toString()); } } From 31a8328fb57ed5497470206dcc165bfdd3f44974 Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 25 Apr 2019 18:58:06 -0400 Subject: [PATCH 10/12] Add data version to BukkitImplAdapter. Also throttle unknown-block warning when loading MCEdit schematics. --- .../bukkit/BukkitServerInterface.java | 6 ++++-- .../bukkit/adapter/BukkitImplAdapter.java | 7 +++++++ .../adapter/impl/Spigot_v1_13_R1$1.class | Bin 959 -> 959 bytes .../bukkit/adapter/impl/Spigot_v1_13_R1.class | Bin 25466 -> 25603 bytes .../adapter/impl/Spigot_v1_13_R2$1.class | Bin 959 -> 959 bytes .../bukkit/adapter/impl/Spigot_v1_13_R2.class | Bin 25488 -> 25718 bytes .../adapter/impl/Spigot_v1_13_R2_2$1.class | Bin 965 -> 965 bytes .../adapter/impl/Spigot_v1_13_R2_2.class | Bin 25554 -> 25784 bytes .../adapter/impl/Spigot_v1_14_R1$1.class | Bin 959 -> 959 bytes .../bukkit/adapter/impl/Spigot_v1_14_R1.class | Bin 25746 -> 25976 bytes .../clipboard/io/MCEditSchematicReader.java | 15 ++++++++++----- 11 files changed, 21 insertions(+), 7 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 0123b452b..9dfc856b2 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -67,8 +67,10 @@ public class BukkitServerInterface implements MultiUserPlatform { @Override public int getDataVersion() { - // TODO - add to adapter - CraftMagicNumbers#getDataVersion - return 1631; + if (plugin.getBukkitImplAdapter() != null) { + return plugin.getBukkitImplAdapter().getDataVersion(); + } + return 0; } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index 1a4329a4f..9ac0fe8de 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -40,6 +40,13 @@ import javax.annotation.Nullable; */ public interface BukkitImplAdapter { + /** + * Get the Minecraft data version for the current world data. + * + * @return the data version + */ + int getDataVersion(); + /** * Get the block at the given location. * diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1$1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R1$1.class index efa78dacc6f7d28a602adf0b9f4189588b996348..68556cab91070ba9b83d00f939940eaabed68d22 100644 GIT binary patch delta 13 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 ItJTW?1I5wJm;e9( delta 9435 zcmZ`<2YeJo)PHYg@8+_%ffO#}fFy(@Kng`bkOPCXCx?2>u~Hx_ZItCx9_e~9sEn80B$sKU)`YN47N)k5wWLdulEENL*I zrOLKdE0tqZuH5saX)R3~Y1;Z#zACU)p(?T|N3}Dmy-kIxgRMHMQ;h0lQ+p99OLdl} zi&3ZAs;lZI&1v#ZcRQ>?sz(C#R6YIbbX9DtUg`|lNpD;AQGJc-XJf3tjFm|5OyP5u zjY$KH8fa4=b+%9j`FMm;gKa8NL$Ibg$Hy1=xKxIQ$~xytb6x^xt6?^cSHlz3`D%nT zBg4{OAWf+>qoldeuSTnj{A!FE>sRAcnKa{#n&78VYNAo)ej2SVw$&tciBKjRHN~i@ zMop9DE;VYpQ8R3ss4hz(QdijON_CZ=rl_lpnk$;R#;9v;nx?LkJzXC*DlGHnNi*N5 z8*Fu>y2+-Q>Sk$fvDK|m2*z%c$FmdE?dlFcEmUJg%sXY^E-}R2(%d7>z0%w#&HZA+ z2NG07Es&)iOd(bkwt7@OCYpTQsD(D&7Ush~^#nTVNoktH>(o;s+tYsaj2dgyvqmj4 z>N!8%r=FMQX=~nyH3Y)@7s>Y`0REFsMO`}%Yv_!2+P^(p~QEP0qR$OkaxZGN}9KEdG zvenz_9i!ISRH@b*^{!2Cz{jAC_oR8>rcUYuqdqihgH0>cMw@EXCZjeRwZ*1YYO77F z)kj8sY}6+KmK3s&B;#-x>9Ng8D)2^{F34hx?4$Z`4mV?U2oFS3eu|i&4MYv`hVF z({A;Q9^YsK1Q*+o*$nYNQU?^u5Y3>abCOf_-A4BQ_r3&LuTtrE3YWZwi?wGpX5f{@y0>jZ6!(4Rp@?YFg~6T>3&4v486 z*}Ab#f`B?%MEwe)=@b$2Tde2`p;OV*O|X3(uyvYlD$@Lp&Fgeq2X!+Wviya;>*h9n z2LW^o*$OZvS!WuZ<Tr0)h6B8Q_eKMZG=0wj_hsHB=Te+ zweqyIG0N%&!&W|;p3k-gpEmk5qq|2o1i!WRL!?Mz^YqBZX8SVoxxmpO-NVs6_31_z zJGz%X!{HpxWh#u6W#pv}E1!8q`AnmGJGzhV>*#)bAX1jsrsbf?vt~0{({OWie_i5m z9=A67Oh=!k2QcMD=9}!u6)kf640QC_{FS2z>A?=SVJK+`q<8c=dZ?q%)#pJjM-S7( z9eutY;pmb20;5YEJxX8b=+XLgrh-Va$&8HgIav

x&#cMvry$I6c(pGDnZ+15m7^ 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 PTdH1GOY3`4we)`gyz#i3 diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2$1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_13_R2$1.class index 97a809b53396eabe2efe1086b896f97291cc9040..e8e38065a243a964ddb4fd2ecca5b25841728727 100644 GIT binary patch delta 13 UcmdnbzMp+V4l|?7}xbu~mvnH7d=g(v8aSt4x(8 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 F{||t2^D+Pc delta 9454 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 diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1$1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_14_R1$1.class index 185607a49fd488a9be2d8a8856622980e432d8de..66d9acaf192621175a8b1f3e3e4e73cb8f1d4e5a 100644 GIT binary patch delta 13 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

SL_>Zx|f+z)ELQk=*V!PCP$U{q9ZVlvnn7rJX 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 YQFGNix3^R+Qx7LbS7SzeUbSlQKTgok=l}o! 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 a5e87c356..d21533e20 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 @@ -53,10 +53,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; @@ -213,6 +214,7 @@ public class MCEditSchematicReader extends NBTSchematicReader { clipboard.setOrigin(origin); + Set unknownBlocks = new HashSet<>(); for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { for (int z = 0; z < length; ++z) { @@ -230,9 +232,13 @@ public class MCEditSchematicReader extends NBTSchematicReader { } else { clipboard.setBlock(region.getMinimumPoint().add(pt), state); } - } else { - if (!useOverride) { - log.warn("Unknown block when pasting schematic: " + blocks[index] + ":" + blockData[index] + ". Please report this issue."); + } else if (!useOverride) { + short block = blocks[index]; + byte data = blockData[index]; + int combined = block << 8 | data; + if (unknownBlocks.add(combined)) { + log.warn("Unknown block when pasting schematic: " + + block + ":" + data + ". Please report this issue."); } } } catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this @@ -316,7 +322,6 @@ public class MCEditSchematicReader extends NBTSchematicReader { case "PigZombie": return "zombie_pigman"; default: return id; } - return id; } private String convertBlockEntityId(String id) { From 648ecf2153718b5cccb49c6534940dad2321a608 Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 25 Apr 2019 20:48:15 -0400 Subject: [PATCH 11/12] Add entity, biome, and mask flags to clipboard brush. --- .../worldedit/command/BrushCommands.java | 7 ++--- .../worldedit/command/ClipboardCommands.java | 10 +++---- .../command/tool/brush/ClipboardBrush.java | 26 ++++++++++++++++--- .../function/biome/ExtentBiomeCopy.java | 3 +-- .../sk89q/worldedit/session/PasteBuilder.java | 4 +++ 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 26fcf4d92..450bf5f51 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -131,7 +131,7 @@ public class BrushCommands { @Command( aliases = { "clipboard", "copy" }, usage = "", - flags = "ap", + flags = "aoebm", desc = "Choose the clipboard brush", help = "Chooses the clipboard brush.\n" + @@ -141,7 +141,8 @@ public class BrushCommands { "stood relative to the copied area when you copied it." ) @CommandPermissions("worldedit.brush.clipboard") - public void clipboardBrush(Player player, LocalSession session, @Switch('a') boolean ignoreAir, @Switch('p') boolean usingOrigin) throws WorldEditException { + public void clipboardBrush(Player player, LocalSession session, @Switch('a') boolean ignoreAir, @Switch('o') boolean usingOrigin, + @Switch('e') boolean pasteEntities, @Switch('b') boolean pasteBiomes, @Switch('m') Mask sourceMask) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); Clipboard clipboard = holder.getClipboard(); @@ -152,7 +153,7 @@ public class BrushCommands { worldEdit.checkMaxBrushRadius(size.getBlockZ() / 2D - 1); BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); - tool.setBrush(new ClipboardBrush(holder, ignoreAir, usingOrigin), "worldedit.brush.clipboard"); + tool.setBrush(new ClipboardBrush(holder, ignoreAir, usingOrigin, pasteEntities, pasteBiomes, sourceMask), "worldedit.brush.clipboard"); player.print("Clipboard brush shape equipped."); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index cffdbb2ce..7c242011f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -170,16 +170,14 @@ public class ClipboardCommands { Region region = clipboard.getRegion(); BlockVector3 to = atOrigin ? clipboard.getOrigin() : session.getPlacementPosition(player); - PasteBuilder builder = holder + Operation operation = holder .createPaste(editSession) .to(to) .ignoreAirBlocks(ignoreAirBlocks) .copyBiomes(pasteBiomes) - .copyEntities(pasteEntities); - if (sourceMask != null) { - builder.maskSource(sourceMask); - } - Operation operation = builder.build(); + .copyEntities(pasteEntities) + .maskSource(sourceMask) + .build(); Operations.completeLegacy(operation); if (selectPasted) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java index d6abe3cc9..bbb489242 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; @@ -31,14 +32,30 @@ import com.sk89q.worldedit.session.ClipboardHolder; public class ClipboardBrush implements Brush { - private ClipboardHolder holder; - private boolean ignoreAirBlocks; - private boolean usingOrigin; + private final ClipboardHolder holder; + private final boolean ignoreAirBlocks; + private final boolean usingOrigin; + private final boolean pasteEntities; + private final boolean pasteBiomes; + private final Mask sourceMask; public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin) { this.holder = holder; this.ignoreAirBlocks = ignoreAirBlocks; this.usingOrigin = usingOrigin; + this.pasteBiomes = false; + this.pasteEntities = false; + this.sourceMask = null; + } + + public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin, boolean pasteEntities, + boolean pasteBiomes, Mask sourceMask) { + this.holder = holder; + this.ignoreAirBlocks = ignoreAirBlocks; + this.usingOrigin = usingOrigin; + this.pasteEntities = pasteEntities; + this.pasteBiomes = pasteBiomes; + this.sourceMask = sourceMask; } @Override @@ -51,6 +68,9 @@ public class ClipboardBrush implements Brush { .createPaste(editSession) .to(usingOrigin ? position : position.subtract(centerOffset)) .ignoreAirBlocks(ignoreAirBlocks) + .copyEntities(pasteEntities) + .copyBiomes(pasteBiomes) + .maskSource(sourceMask) .build(); Operations.completeLegacy(operation); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java index 43dbf984d..fa1ee8b34 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/ExtentBiomeCopy.java @@ -23,7 +23,6 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.FlatRegionFunction; import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.world.biome.BiomeType; @@ -32,7 +31,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * Copies the biome from one extent to another. */ -public class ExtentBiomeCopy implements FlatRegionFunction { +public class ExtentBiomeCopy implements FlatRegionFunction { private final Extent source; private final Extent destination; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java index c46939661..30966535f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java @@ -83,6 +83,10 @@ public class PasteBuilder { * @return this builder instance */ public PasteBuilder maskSource(Mask sourceMask) { + if (sourceMask == null) { + this.sourceMask = Masks.alwaysTrue(); + return this; + } this.sourceMask = sourceMask; return this; } From 302cd8f348ae000446b3d21dceeab55ca4937c7a Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 26 Apr 2019 16:37:49 +1000 Subject: [PATCH 12/12] Update note in ForgePlatform on data version. --- .../src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index d578c3b9f..6175b865f 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -66,7 +66,7 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { @Override public int getDataVersion() { - // TODO technically available as WorldInfo#field_209227_p but requires a world ref? + // TODO switch to SharedConstants in 1.14 return 1631; }