package net.minecraft.server.commands; import com.google.common.collect.Lists; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import java.util.Deque; import java.util.List; import java.util.function.Predicate; import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.DimensionArgument; import net.minecraft.commands.arguments.blocks.BlockPredicateArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.core.BlockPos; import net.minecraft.core.component.DataComponentMap; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.levelgen.structure.BoundingBox; import org.jetbrains.annotations.Nullable; public class CloneCommands { private static final SimpleCommandExceptionType ERROR_OVERLAP = new SimpleCommandExceptionType(Component.translatable("commands.clone.overlap")); private static final Dynamic2CommandExceptionType ERROR_AREA_TOO_LARGE = new Dynamic2CommandExceptionType( (object, object2) -> Component.translatableEscape("commands.clone.toobig", object, object2) ); private static final SimpleCommandExceptionType ERROR_FAILED = new SimpleCommandExceptionType(Component.translatable("commands.clone.failed")); public static final Predicate FILTER_AIR = blockInWorld -> !blockInWorld.getState().isAir(); public static void register(CommandDispatcher dispatcher, CommandBuildContext context) { dispatcher.register( Commands.literal("clone") .requires(commandSourceStack -> commandSourceStack.hasPermission(2)) .then(beginEndDestinationAndModeSuffix(context, commandContext -> commandContext.getSource().getLevel())) .then( Commands.literal("from") .then( Commands.argument("sourceDimension", DimensionArgument.dimension()) .then(beginEndDestinationAndModeSuffix(context, commandContext -> DimensionArgument.getDimension(commandContext, "sourceDimension"))) ) ) ); } private static ArgumentBuilder beginEndDestinationAndModeSuffix( CommandBuildContext buildContext, InCommandFunction, ServerLevel> levelGetter ) { return Commands.argument("begin", BlockPosArgument.blockPos()) .then( Commands.argument("end", BlockPosArgument.blockPos()) .then(destinationAndStrictSuffix(buildContext, levelGetter, commandContext -> commandContext.getSource().getLevel())) .then( Commands.literal("to") .then( Commands.argument("targetDimension", DimensionArgument.dimension()) .then(destinationAndStrictSuffix(buildContext, levelGetter, commandContext -> DimensionArgument.getDimension(commandContext, "targetDimension"))) ) ) ); } private static CloneCommands.DimensionAndPosition getLoadedDimensionAndPosition(CommandContext context, ServerLevel level, String name) throws CommandSyntaxException { BlockPos blockPos = BlockPosArgument.getLoadedBlockPos(context, level, name); return new CloneCommands.DimensionAndPosition(level, blockPos); } private static ArgumentBuilder destinationAndStrictSuffix( CommandBuildContext buildContext, InCommandFunction, ServerLevel> sourceLevelGetter, InCommandFunction, ServerLevel> destinationLevelGetter ) { InCommandFunction, CloneCommands.DimensionAndPosition> inCommandFunction = commandContext -> getLoadedDimensionAndPosition( commandContext, sourceLevelGetter.apply(commandContext), "begin" ); InCommandFunction, CloneCommands.DimensionAndPosition> inCommandFunction2 = commandContext -> getLoadedDimensionAndPosition( commandContext, sourceLevelGetter.apply(commandContext), "end" ); InCommandFunction, CloneCommands.DimensionAndPosition> inCommandFunction3 = commandContext -> getLoadedDimensionAndPosition( commandContext, destinationLevelGetter.apply(commandContext), "destination" ); return modeSuffix( buildContext, inCommandFunction, inCommandFunction2, inCommandFunction3, false, Commands.argument("destination", BlockPosArgument.blockPos()) ) .then(modeSuffix(buildContext, inCommandFunction, inCommandFunction2, inCommandFunction3, true, Commands.literal("strict"))); } private static ArgumentBuilder modeSuffix( CommandBuildContext buildContext, InCommandFunction, CloneCommands.DimensionAndPosition> begin, InCommandFunction, CloneCommands.DimensionAndPosition> end, InCommandFunction, CloneCommands.DimensionAndPosition> destination, boolean strict, ArgumentBuilder argumentBuilder ) { return argumentBuilder.executes( commandContext -> clone( commandContext.getSource(), begin.apply(commandContext), end.apply(commandContext), destination.apply(commandContext), blockInWorld -> true, CloneCommands.Mode.NORMAL, strict ) ) .then(wrapWithCloneMode(begin, end, destination, commandContext -> blockInWorld -> true, strict, Commands.literal("replace"))) .then(wrapWithCloneMode(begin, end, destination, commandContext -> FILTER_AIR, strict, Commands.literal("masked"))) .then( Commands.literal("filtered") .then( wrapWithCloneMode( begin, end, destination, commandContext -> BlockPredicateArgument.getBlockPredicate(commandContext, "filter"), strict, Commands.argument("filter", BlockPredicateArgument.blockPredicate(buildContext)) ) ) ); } private static ArgumentBuilder wrapWithCloneMode( InCommandFunction, CloneCommands.DimensionAndPosition> begin, InCommandFunction, CloneCommands.DimensionAndPosition> end, InCommandFunction, CloneCommands.DimensionAndPosition> destination, InCommandFunction, Predicate> filter, boolean strict, ArgumentBuilder argumentBuilder ) { return argumentBuilder.executes( commandContext -> clone( commandContext.getSource(), begin.apply(commandContext), end.apply(commandContext), destination.apply(commandContext), filter.apply(commandContext), CloneCommands.Mode.NORMAL, strict ) ) .then( Commands.literal("force") .executes( commandContext -> clone( commandContext.getSource(), begin.apply(commandContext), end.apply(commandContext), destination.apply(commandContext), filter.apply(commandContext), CloneCommands.Mode.FORCE, strict ) ) ) .then( Commands.literal("move") .executes( commandContext -> clone( commandContext.getSource(), begin.apply(commandContext), end.apply(commandContext), destination.apply(commandContext), filter.apply(commandContext), CloneCommands.Mode.MOVE, strict ) ) ) .then( Commands.literal("normal") .executes( commandContext -> clone( commandContext.getSource(), begin.apply(commandContext), end.apply(commandContext), destination.apply(commandContext), filter.apply(commandContext), CloneCommands.Mode.NORMAL, strict ) ) ); } private static int clone( CommandSourceStack source, CloneCommands.DimensionAndPosition begin, CloneCommands.DimensionAndPosition end, CloneCommands.DimensionAndPosition destination, Predicate filter, CloneCommands.Mode mode, boolean strict ) throws CommandSyntaxException { BlockPos blockPos = begin.position(); BlockPos blockPos2 = end.position(); BoundingBox boundingBox = BoundingBox.fromCorners(blockPos, blockPos2); BlockPos blockPos3 = destination.position(); BlockPos blockPos4 = blockPos3.offset(boundingBox.getLength()); BoundingBox boundingBox2 = BoundingBox.fromCorners(blockPos3, blockPos4); ServerLevel serverLevel = begin.dimension(); ServerLevel serverLevel2 = destination.dimension(); if (!mode.canOverlap() && serverLevel == serverLevel2 && boundingBox2.intersects(boundingBox)) { throw ERROR_OVERLAP.create(); } else { int i = boundingBox.getXSpan() * boundingBox.getYSpan() * boundingBox.getZSpan(); int j = source.getLevel().getGameRules().getInt(GameRules.RULE_COMMAND_MODIFICATION_BLOCK_LIMIT); if (i > j) { throw ERROR_AREA_TOO_LARGE.create(j, i); } else if (serverLevel.hasChunksAt(blockPos, blockPos2) && serverLevel2.hasChunksAt(blockPos3, blockPos4)) { if (serverLevel2.isDebug()) { throw ERROR_FAILED.create(); } else { List list = Lists.newArrayList(); List list2 = Lists.newArrayList(); List list3 = Lists.newArrayList(); Deque deque = Lists.newLinkedList(); BlockPos blockPos5 = new BlockPos( boundingBox2.minX() - boundingBox.minX(), boundingBox2.minY() - boundingBox.minY(), boundingBox2.minZ() - boundingBox.minZ() ); for (int k = boundingBox.minZ(); k <= boundingBox.maxZ(); k++) { for (int l = boundingBox.minY(); l <= boundingBox.maxY(); l++) { for (int m = boundingBox.minX(); m <= boundingBox.maxX(); m++) { BlockPos blockPos6 = new BlockPos(m, l, k); BlockPos blockPos7 = blockPos6.offset(blockPos5); BlockInWorld blockInWorld = new BlockInWorld(serverLevel, blockPos6, false); BlockState blockState = blockInWorld.getState(); if (filter.test(blockInWorld)) { BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos6); if (blockEntity != null) { CloneCommands.CloneBlockEntityInfo cloneBlockEntityInfo = new CloneCommands.CloneBlockEntityInfo( blockEntity.saveCustomOnly(source.registryAccess()), blockEntity.components() ); list2.add(new CloneCommands.CloneBlockInfo(blockPos7, blockState, cloneBlockEntityInfo)); deque.addLast(blockPos6); } else if (!blockState.isSolidRender() && !blockState.isCollisionShapeFullBlock(serverLevel, blockPos6)) { list3.add(new CloneCommands.CloneBlockInfo(blockPos7, blockState, null)); deque.addFirst(blockPos6); } else { list.add(new CloneCommands.CloneBlockInfo(blockPos7, blockState, null)); deque.addLast(blockPos6); } } } } } int k = 2 | (strict ? 816 : 0); if (mode == CloneCommands.Mode.MOVE) { for (BlockPos blockPos8 : deque) { serverLevel.setBlock(blockPos8, Blocks.BARRIER.defaultBlockState(), k | 816); } int l = strict ? k : 3; for (BlockPos blockPos6 : deque) { serverLevel.setBlock(blockPos6, Blocks.AIR.defaultBlockState(), l); } } List list4 = Lists.newArrayList(); list4.addAll(list); list4.addAll(list2); list4.addAll(list3); List list5 = Lists.reverse(list4); for (CloneCommands.CloneBlockInfo cloneBlockInfo : list5) { serverLevel2.setBlock(cloneBlockInfo.pos, Blocks.BARRIER.defaultBlockState(), k | 816); } int n = 0; for (CloneCommands.CloneBlockInfo cloneBlockInfo2 : list4) { if (serverLevel2.setBlock(cloneBlockInfo2.pos, cloneBlockInfo2.state, k)) { n++; } } for (CloneCommands.CloneBlockInfo cloneBlockInfo2x : list2) { BlockEntity blockEntity2 = serverLevel2.getBlockEntity(cloneBlockInfo2x.pos); if (cloneBlockInfo2x.blockEntityInfo != null && blockEntity2 != null) { blockEntity2.loadCustomOnly(cloneBlockInfo2x.blockEntityInfo.tag, serverLevel2.registryAccess()); blockEntity2.setComponents(cloneBlockInfo2x.blockEntityInfo.components); blockEntity2.setChanged(); } serverLevel2.setBlock(cloneBlockInfo2x.pos, cloneBlockInfo2x.state, k); } if (!strict) { for (CloneCommands.CloneBlockInfo cloneBlockInfo2x : list5) { serverLevel2.updateNeighborsAt(cloneBlockInfo2x.pos, cloneBlockInfo2x.state.getBlock()); } } serverLevel2.getBlockTicks().copyAreaFrom(serverLevel.getBlockTicks(), boundingBox, blockPos5); if (n == 0) { throw ERROR_FAILED.create(); } else { int o = n; source.sendSuccess(() -> Component.translatable("commands.clone.success", o), true); return n; } } } else { throw BlockPosArgument.ERROR_NOT_LOADED.create(); } } } record CloneBlockEntityInfo(CompoundTag tag, DataComponentMap components) { } record CloneBlockInfo(BlockPos pos, BlockState state, @Nullable CloneCommands.CloneBlockEntityInfo blockEntityInfo) { } record DimensionAndPosition(ServerLevel dimension, BlockPos position) { } static enum Mode { FORCE(true), MOVE(true), NORMAL(false); private final boolean canOverlap; private Mode(final boolean canOverlap) { this.canOverlap = canOverlap; } public boolean canOverlap() { return this.canOverlap; } } }