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()); } }