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.Clearable; 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, CloneCommands.CommandFunction, ServerLevel> levelGetter ) { return Commands.argument("begin", BlockPosArgument.blockPos()) .then( Commands.argument("end", BlockPosArgument.blockPos()) .then(destinationAndModeSuffix(buildContext, levelGetter, commandContext -> commandContext.getSource().getLevel())) .then( Commands.literal("to") .then( Commands.argument("targetDimension", DimensionArgument.dimension()) .then(destinationAndModeSuffix(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 destinationAndModeSuffix( CommandBuildContext buildContext, CloneCommands.CommandFunction, ServerLevel> sourceLevelGetter, CloneCommands.CommandFunction, ServerLevel> destinationLevelGetter ) { CloneCommands.CommandFunction, CloneCommands.DimensionAndPosition> commandFunction = commandContext -> getLoadedDimensionAndPosition( commandContext, sourceLevelGetter.apply(commandContext), "begin" ); CloneCommands.CommandFunction, CloneCommands.DimensionAndPosition> commandFunction2 = commandContext -> getLoadedDimensionAndPosition( commandContext, sourceLevelGetter.apply(commandContext), "end" ); CloneCommands.CommandFunction, CloneCommands.DimensionAndPosition> commandFunction3 = commandContext -> getLoadedDimensionAndPosition( commandContext, destinationLevelGetter.apply(commandContext), "destination" ); return Commands.argument("destination", BlockPosArgument.blockPos()) .executes( commandContext -> clone( commandContext.getSource(), commandFunction.apply(commandContext), commandFunction2.apply(commandContext), commandFunction3.apply(commandContext), blockInWorld -> true, CloneCommands.Mode.NORMAL ) ) .then( wrapWithCloneMode( commandFunction, commandFunction2, commandFunction3, commandContext -> blockInWorld -> true, Commands.literal("replace") .executes( commandContext -> clone( commandContext.getSource(), commandFunction.apply(commandContext), commandFunction2.apply(commandContext), commandFunction3.apply(commandContext), blockInWorld -> true, CloneCommands.Mode.NORMAL ) ) ) ) .then( wrapWithCloneMode( commandFunction, commandFunction2, commandFunction3, commandContext -> FILTER_AIR, Commands.literal("masked") .executes( commandContext -> clone( commandContext.getSource(), commandFunction.apply(commandContext), commandFunction2.apply(commandContext), commandFunction3.apply(commandContext), FILTER_AIR, CloneCommands.Mode.NORMAL ) ) ) ) .then( Commands.literal("filtered") .then( wrapWithCloneMode( commandFunction, commandFunction2, commandFunction3, commandContext -> BlockPredicateArgument.getBlockPredicate(commandContext, "filter"), Commands.argument("filter", BlockPredicateArgument.blockPredicate(buildContext)) .executes( commandContext -> clone( commandContext.getSource(), commandFunction.apply(commandContext), commandFunction2.apply(commandContext), commandFunction3.apply(commandContext), BlockPredicateArgument.getBlockPredicate(commandContext, "filter"), CloneCommands.Mode.NORMAL ) ) ) ) ); } private static ArgumentBuilder wrapWithCloneMode( CloneCommands.CommandFunction, CloneCommands.DimensionAndPosition> beginGetter, CloneCommands.CommandFunction, CloneCommands.DimensionAndPosition> endGetter, CloneCommands.CommandFunction, CloneCommands.DimensionAndPosition> targetGetter, CloneCommands.CommandFunction, Predicate> filterGetter, ArgumentBuilder argumentBuilder ) { return argumentBuilder.then( Commands.literal("force") .executes( commandContext -> clone( commandContext.getSource(), beginGetter.apply(commandContext), endGetter.apply(commandContext), targetGetter.apply(commandContext), filterGetter.apply(commandContext), CloneCommands.Mode.FORCE ) ) ) .then( Commands.literal("move") .executes( commandContext -> clone( commandContext.getSource(), beginGetter.apply(commandContext), endGetter.apply(commandContext), targetGetter.apply(commandContext), filterGetter.apply(commandContext), CloneCommands.Mode.MOVE ) ) ) .then( Commands.literal("normal") .executes( commandContext -> clone( commandContext.getSource(), beginGetter.apply(commandContext), endGetter.apply(commandContext), targetGetter.apply(commandContext), filterGetter.apply(commandContext), CloneCommands.Mode.NORMAL ) ) ); } private static int clone( CommandSourceStack source, CloneCommands.DimensionAndPosition begin, CloneCommands.DimensionAndPosition end, CloneCommands.DimensionAndPosition target, Predicate filter, CloneCommands.Mode mode ) throws CommandSyntaxException { BlockPos blockPos = begin.position(); BlockPos blockPos2 = end.position(); BoundingBox boundingBox = BoundingBox.fromCorners(blockPos, blockPos2); BlockPos blockPos3 = target.position(); BlockPos blockPos4 = blockPos3.offset(boundingBox.getLength()); BoundingBox boundingBox2 = BoundingBox.fromCorners(blockPos3, blockPos4); ServerLevel serverLevel = begin.dimension(); ServerLevel serverLevel2 = target.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)) { 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); } } } } } if (mode == CloneCommands.Mode.MOVE) { for (BlockPos blockPos8 : deque) { BlockEntity blockEntity2 = serverLevel.getBlockEntity(blockPos8); Clearable.tryClear(blockEntity2); serverLevel.setBlock(blockPos8, Blocks.BARRIER.defaultBlockState(), 2); } for (BlockPos blockPos8 : deque) { serverLevel.setBlock(blockPos8, Blocks.AIR.defaultBlockState(), 3); } } List list4 = Lists.newArrayList(); list4.addAll(list); list4.addAll(list2); list4.addAll(list3); List list5 = Lists.reverse(list4); for (CloneCommands.CloneBlockInfo cloneBlockInfo : list5) { BlockEntity blockEntity3 = serverLevel2.getBlockEntity(cloneBlockInfo.pos); Clearable.tryClear(blockEntity3); serverLevel2.setBlock(cloneBlockInfo.pos, Blocks.BARRIER.defaultBlockState(), 2); } int mx = 0; for (CloneCommands.CloneBlockInfo cloneBlockInfo2 : list4) { if (serverLevel2.setBlock(cloneBlockInfo2.pos, cloneBlockInfo2.state, 2)) { mx++; } } for (CloneCommands.CloneBlockInfo cloneBlockInfo2x : list2) { BlockEntity blockEntity4 = serverLevel2.getBlockEntity(cloneBlockInfo2x.pos); if (cloneBlockInfo2x.blockEntityInfo != null && blockEntity4 != null) { blockEntity4.loadCustomOnly(cloneBlockInfo2x.blockEntityInfo.tag, serverLevel2.registryAccess()); blockEntity4.setComponents(cloneBlockInfo2x.blockEntityInfo.components); blockEntity4.setChanged(); } serverLevel2.setBlock(cloneBlockInfo2x.pos, cloneBlockInfo2x.state, 2); } for (CloneCommands.CloneBlockInfo cloneBlockInfo2x : list5) { serverLevel2.blockUpdated(cloneBlockInfo2x.pos, cloneBlockInfo2x.state.getBlock()); } serverLevel2.getBlockTicks().copyAreaFrom(serverLevel.getBlockTicks(), boundingBox, blockPos5); if (mx == 0) { throw ERROR_FAILED.create(); } else { int n = mx; source.sendSuccess(() -> Component.translatable("commands.clone.success", n), true); return mx; } } else { throw BlockPosArgument.ERROR_NOT_LOADED.create(); } } } record CloneBlockEntityInfo(CompoundTag tag, DataComponentMap components) { } record CloneBlockInfo(BlockPos pos, BlockState state, @Nullable CloneCommands.CloneBlockEntityInfo blockEntityInfo) { } @FunctionalInterface interface CommandFunction { R apply(T object) throws CommandSyntaxException; } 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; } } }