package net.minecraft.command.commands;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.command.CommandException;
import net.minecraft.command.CommandResultStats;
import net.minecraft.command.CommandUtilities;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.SyntaxErrorException;
import net.minecraft.command.arg.CommandArg;
import net.minecraft.command.collections.TypeIDs;
import net.minecraft.command.construction.CommandConstructable;
import net.minecraft.command.construction.CommandDescriptorDefault.CParserData;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos;
import net.minecraft.world.NextTickListEntry;
import net.minecraft.world.World;
import net.minecraft.world.gen.structure.StructureBoundingBox;
public class CommandClone extends CommandArg<Integer>
{
public static final CommandConstructable constructable = new CommandConstructable()
{
@Override
public CommandArg<Integer> construct(final CParserData data) throws SyntaxErrorException
{
return CommandClone.construct(data.getPath(), data.getPath(), data, false);
}
};
public static final CommandConstructable constructableFast = new CommandConstructable()
{
@Override
public CommandArg<Integer> construct(final CParserData data) throws SyntaxErrorException
{
return CommandClone.construct(data.getPath(1), data.getPath(), data, true);
}
};
private static final CommandArg<Integer> construct(final String filterMode, final String moveMode, final CParserData data, final boolean silent)
{
final Mode mode = moveMode == null ? Mode.normal :
"normal".equals(moveMode) ? Mode.normal :
"force".equals(moveMode) ? Mode.force :
"move".equals(moveMode) ? Mode.move : null;
if (filterMode == null)
return new CommandClone(data, mode, silent);
switch (filterMode)
{
case "replace":
return new CommandClone(data, mode, silent);
case "masked":
return new Masked(data, mode, silent);
case "filtered":
return new Filtered(data, mode, silent);
}
return null;
}
private final CommandArg<BlockPos> pos1;
private final CommandArg<BlockPos> pos2;
private final CommandArg<BlockPos> posTarget;
private final Mode mode;
private final boolean fast;
public CommandClone(final CommandArg<BlockPos> pos1, final CommandArg<BlockPos> pos2, final CommandArg<BlockPos> posTarget, final Mode mode, final boolean fast)
{
this.pos1 = pos1;
this.pos2 = pos2;
this.posTarget = posTarget;
this.mode = mode;
this.fast = fast;
}
protected CommandClone(final CParserData data, final Mode mode, final boolean fast)
{
this.pos1 = data.get(TypeIDs.BlockPos);
this.pos2 = data.get(TypeIDs.BlockPos);
this.posTarget = data.get(TypeIDs.BlockPos);
this.mode = mode;
this.fast = fast;
}
private static enum Mode
{
normal, force, move;
}
@Override
public Integer eval(final ICommandSender sender) throws CommandException
{
final BlockPos pos1 = this.pos1.eval(sender);
final BlockPos pos2 = this.pos2.eval(sender);
final BlockPos pos3 = this.posTarget.eval(sender);
this.evalArgs(sender);
sender.func_174794_a(CommandResultStats.Type.AFFECTED_BLOCKS, 0);
final StructureBoundingBox boxOrig = new StructureBoundingBox(pos1, pos2);
final StructureBoundingBox boxTarget = new StructureBoundingBox(pos3, pos3.add(boxOrig.func_175896_b()));
final int size = boxOrig.getXSize() * boxOrig.getYSize() * boxOrig.getZSize();
if (size > 32768)
throw new CommandException("commands.clone.tooManyBlocks", size, 32768);
if (this.mode == Mode.normal && boxOrig.intersectsWith(boxTarget))
throw new CommandException("commands.clone.noOverlap");
if (boxOrig.minY < 0 || boxOrig.maxY >= 256 || boxTarget.minY < 0 || boxTarget.maxY >= 256)
throw new CommandException("commands.clone.outOfWorld");
final World world = sender.getEntityWorld();
if (!world.isAreaLoaded(boxOrig) || !world.isAreaLoaded(boxTarget))
throw new CommandException("commands.clone.outOfWorld");
final BlockPos diff = new BlockPos(boxTarget.minX - boxOrig.minX, boxTarget.minY - boxOrig.minY, boxTarget.minZ - boxOrig.minZ);
if (diff.getX() == 0 && diff.getY() == 0 && diff.getZ() == 0)
throw new CommandException("commands.clone.failed");
this.checkArgs();
final List<?> tileTicks = world.func_175712_a(
new StructureBoundingBox(
boxOrig.minX, boxOrig.minY, boxOrig.minZ,
boxOrig.maxX + 1, boxOrig.maxY + 1, boxOrig.maxZ + 1), false);
int succ = 0;
final boolean xReverse = diff.getX() > 0;
final boolean yReverse = diff.getY() > 0;
final boolean zReverse = diff.getZ() > 0;
final int xIncrement = xReverse ? -1 : 1;
final int yIncrement = yReverse ? -1 : 1;
final int zIncrement = zReverse ? -1 : 1;
final int xStart = xReverse ? boxOrig.maxX : boxOrig.minX;
final int xEnd = xReverse ? boxOrig.minX : boxOrig.maxX + 1;
final int yStart = yReverse ? boxOrig.maxY : boxOrig.minY;
final int yEnd = yReverse ? boxOrig.minY : boxOrig.maxY + 1;
final int zStart = zReverse ? boxOrig.maxZ : boxOrig.minZ;
final int zEnd = zReverse ? boxOrig.minZ : boxOrig.maxZ + 1;
if (this.fast)
for (int z = zStart; zReverse != z < zEnd; z += zIncrement)
for (int y = yStart; yReverse != y < yEnd; y += yIncrement)
for (int x = xStart; xReverse != x < xEnd; x += xIncrement)
{
final BlockPos sourcePos = new BlockPos(x, y, z);
final IBlockState sourceState = world.getBlockState(sourcePos);
if (this.filter(sourceState))
{
final BlockPos targetPos = sourcePos.add(diff);
final boolean hasTe = sourceState.getBlock().hasTileEntity();
if (world.setBlockState(targetPos, sourceState, 18) || hasTe)
++succ;
if (hasTe)
{
final TileEntity te = world.getTileEntity(sourcePos);
if (te != null)
{
final NBTTagCompound nbt = new NBTTagCompound();
te.writeToNBT(nbt);
CommandUtilities.setNBT(world, targetPos, nbt);
}
}
}
if (this.mode == Mode.move && sourceState.getBlock() != Blocks.air && !boxTarget.func_175898_b(sourcePos))
world.setBlockState(sourcePos, Blocks.air.getDefaultState(), 18);
}
else
{
final List<BlockPos> sourcePositions = this.mode == Mode.move ? new ArrayList<BlockPos>(size - intersectionSize(boxTarget, boxOrig)) : null;
final List<BlockPos> targetPositions = new ArrayList<>(size);
final List<IBlockState> states = new ArrayList<>(size);
final List<NBTTagCompound> nbts = new ArrayList<>();
for (int z = zStart; zReverse != z < zEnd; z += zIncrement)
for (int y = yStart; yReverse != y < yEnd; y += yIncrement)
for (int x = xStart; xReverse != x < xEnd; x += xIncrement)
{
final BlockPos sourcePos = new BlockPos(x, y, z);
final IBlockState sourceState = world.getBlockState(sourcePos);
if (this.filter(sourceState))
{
final BlockPos targetPos = sourcePos.add(diff);
final IBlockState targetState = world.getBlockState(targetPos);
final boolean hasTe = sourceState.getBlock().hasTileEntity();
if (sourceState != targetState || hasTe)
{
++succ;
targetPositions.add(targetPos);
states.add(sourceState);
if (hasTe)
{
final TileEntity te = world.getTileEntity(sourcePos);
if (te == null)
nbts.add(null);
else
{
final NBTTagCompound nbt = new NBTTagCompound();
te.writeToNBT(nbt);
nbts.add(nbt);
}
}
if (sourceState != targetState)
world.setBlockState(targetPos, CommandUtilities.getTempState(sourceState, targetState), 18);
}
else
world.func_175722_b(targetPos, targetState.getBlock());
}
if (this.mode == Mode.move && sourceState.getBlock() != Blocks.air && !boxTarget.func_175898_b(sourcePos))
{
sourcePositions.add(sourcePos);
world.setBlockState(sourcePos, CommandUtilities.getTempState(sourceState), 18);
}
}
final Iterator<IBlockState> itState = states.iterator();
final Iterator<NBTTagCompound> itNBT = nbts.iterator();
for (final BlockPos pos : targetPositions)
{
final IBlockState state = itState.next();
world.setBlockState(pos, state, 2);
if (state.getBlock().hasTileEntity())
{
final NBTTagCompound nbt = itNBT.next();
if (nbt != null)
CommandUtilities.setNBT(world, pos, nbt);
}
world.func_175722_b(pos, state.getBlock());
}
if (this.mode == Mode.move)
for (final BlockPos pos : sourcePositions)
{
world.setBlockState(pos, Blocks.air.getDefaultState(), 2);
world.func_175722_b(pos, Blocks.air);
}
}
if (tileTicks != null)
for (final Object item : tileTicks)
{
final NextTickListEntry tlEntry = (NextTickListEntry) item;
if (boxOrig.func_175898_b(tlEntry.field_180282_a))
world.func_180497_b(tlEntry.field_180282_a.add(diff), tlEntry.func_151351_a(), (int) (tlEntry.scheduledTime - world.getWorldInfo().getWorldTotalTime()), tlEntry.priority);
}
if (succ == 0)
throw new CommandException("commands.clone.failed");
sender.func_174794_a(CommandResultStats.Type.AFFECTED_BLOCKS, succ);
CommandUtilities.notifyOperators(sender, "commands.clone.success", succ);
return succ;
}
@SuppressWarnings("unused")
protected void evalArgs(final ICommandSender sender) throws CommandException
{
}
protected void checkArgs() throws CommandException
{
}
@SuppressWarnings("unused")
protected boolean filter(final IBlockState state)
{
return true;
}
private static class Masked extends CommandClone
{
public Masked(final CParserData data, final Mode mode, final boolean silent)
{
super(data, mode, silent);
}
@Override
protected boolean filter(final IBlockState state)
{
return state.getBlock().getMaterial() != Material.air;
}
}
private static class Filtered extends CommandClone
{
private final CommandArg<Block> filterBlock;
private final CommandArg<Integer> filterMeta;
private Block eFilterBlock;
private int eFilterMeta;
public Filtered(final CParserData data, final Mode mode, final boolean silent)
{
super(data, mode, silent);
this.filterBlock = data.get(TypeIDs.BlockID);
this.filterMeta = data.get(TypeIDs.Integer);
}
@Override
protected void evalArgs(final ICommandSender sender) throws CommandException
{
this.eFilterBlock = this.filterBlock.eval(sender);
if (this.filterMeta != null)
this.eFilterMeta = this.filterMeta.eval(sender);
}
@Override
protected void checkArgs() throws CommandException
{
if (this.filterMeta != null)
CommandUtilities.checkInt(this.eFilterMeta, 0, 15);
}
@Override
protected boolean filter(final IBlockState state)
{
return (state.getBlock() == this.eFilterBlock && (this.filterMeta == null || state.getBlock().getMetaFromState(state) == this.eFilterMeta));
}
}
private static int intersectionSize(final StructureBoundingBox box1, final StructureBoundingBox box2)
{
final int minX = Math.max(box1.minX, box2.minX);
final int maxX = Math.min(box1.maxX, box2.maxX);
if (maxX < minX)
return 0;
final int minY = Math.max(box1.minY, box2.minY);
final int maxY = Math.min(box1.maxY, box2.maxY);
if (maxY < minY)
return 0;
final int minZ = Math.max(box1.minZ, box2.minZ);
final int maxZ = Math.min(box1.maxZ, box2.maxZ);
if (maxZ < minZ)
return 0;
return (maxX - minX + 1) * (maxY - minY + 1) * (maxZ - minZ + 1);
}
}