Code/net/minecraft/server/commands/CloneCommands.java

348 lines
18 KiB
Java
Raw Permalink Normal View History

2025-07-01 06:20:03 +00:00
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 javax.annotation.Nullable;
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;
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(
(p_308640_, p_308641_) -> Component.translatableEscape("commands.clone.toobig", p_308640_, p_308641_)
);
private static final SimpleCommandExceptionType ERROR_FAILED = new SimpleCommandExceptionType(Component.translatable("commands.clone.failed"));
public static final Predicate<BlockInWorld> FILTER_AIR = p_358579_ -> !p_358579_.getState().isAir();
public static void register(CommandDispatcher<CommandSourceStack> p_214424_, CommandBuildContext p_214425_) {
p_214424_.register(
Commands.literal("clone")
.requires(p_136734_ -> p_136734_.hasPermission(2))
.then(beginEndDestinationAndModeSuffix(p_214425_, p_264757_ -> p_264757_.getSource().getLevel()))
.then(
Commands.literal("from")
.then(
Commands.argument("sourceDimension", DimensionArgument.dimension())
.then(beginEndDestinationAndModeSuffix(p_214425_, p_264743_ -> DimensionArgument.getDimension(p_264743_, "sourceDimension")))
)
)
);
}
private static ArgumentBuilder<CommandSourceStack, ?> beginEndDestinationAndModeSuffix(
CommandBuildContext p_265681_, InCommandFunction<CommandContext<CommandSourceStack>, ServerLevel> p_396447_
) {
return Commands.argument("begin", BlockPosArgument.blockPos())
.then(
Commands.argument("end", BlockPosArgument.blockPos())
.then(destinationAndStrictSuffix(p_265681_, p_396447_, p_264751_ -> p_264751_.getSource().getLevel()))
.then(
Commands.literal("to")
.then(
Commands.argument("targetDimension", DimensionArgument.dimension())
.then(destinationAndStrictSuffix(p_265681_, p_396447_, p_264756_ -> DimensionArgument.getDimension(p_264756_, "targetDimension")))
)
)
);
}
private static CloneCommands.DimensionAndPosition getLoadedDimensionAndPosition(CommandContext<CommandSourceStack> p_265513_, ServerLevel p_265183_, String p_265511_) throws CommandSyntaxException {
BlockPos blockpos = BlockPosArgument.getLoadedBlockPos(p_265513_, p_265183_, p_265511_);
return new CloneCommands.DimensionAndPosition(p_265183_, blockpos);
}
private static ArgumentBuilder<CommandSourceStack, ?> destinationAndStrictSuffix(
CommandBuildContext p_397869_,
InCommandFunction<CommandContext<CommandSourceStack>, ServerLevel> p_396883_,
InCommandFunction<CommandContext<CommandSourceStack>, ServerLevel> p_391564_
) {
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> incommandfunction = p_389989_ -> getLoadedDimensionAndPosition(
p_389989_, p_396883_.apply(p_389989_), "begin"
);
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> incommandfunction1 = p_389970_ -> getLoadedDimensionAndPosition(
p_389970_, p_396883_.apply(p_389970_), "end"
);
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> incommandfunction2 = p_389991_ -> getLoadedDimensionAndPosition(
p_389991_, p_391564_.apply(p_389991_), "destination"
);
return modeSuffix(
p_397869_, incommandfunction, incommandfunction1, incommandfunction2, false, Commands.argument("destination", BlockPosArgument.blockPos())
)
.then(modeSuffix(p_397869_, incommandfunction, incommandfunction1, incommandfunction2, true, Commands.literal("strict")));
}
private static ArgumentBuilder<CommandSourceStack, ?> modeSuffix(
CommandBuildContext p_391431_,
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> p_391282_,
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> p_397375_,
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> p_391494_,
boolean p_396137_,
ArgumentBuilder<CommandSourceStack, ?> p_391412_
) {
return p_391412_.executes(
p_389981_ -> clone(
p_389981_.getSource(),
p_391282_.apply(p_389981_),
p_397375_.apply(p_389981_),
p_391494_.apply(p_389981_),
p_180033_ -> true,
CloneCommands.Mode.NORMAL,
p_396137_
)
)
.then(wrapWithCloneMode(p_391282_, p_397375_, p_391494_, p_264738_ -> p_180041_ -> true, p_396137_, Commands.literal("replace")))
.then(wrapWithCloneMode(p_391282_, p_397375_, p_391494_, p_264744_ -> FILTER_AIR, p_396137_, Commands.literal("masked")))
.then(
Commands.literal("filtered")
.then(
wrapWithCloneMode(
p_391282_,
p_397375_,
p_391494_,
p_264745_ -> BlockPredicateArgument.getBlockPredicate(p_264745_, "filter"),
p_396137_,
Commands.argument("filter", BlockPredicateArgument.blockPredicate(p_391431_))
)
)
);
}
private static ArgumentBuilder<CommandSourceStack, ?> wrapWithCloneMode(
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> p_394293_,
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> p_396869_,
InCommandFunction<CommandContext<CommandSourceStack>, CloneCommands.DimensionAndPosition> p_392122_,
InCommandFunction<CommandContext<CommandSourceStack>, Predicate<BlockInWorld>> p_392995_,
boolean p_392263_,
ArgumentBuilder<CommandSourceStack, ?> p_265069_
) {
return p_265069_.executes(
p_389997_ -> clone(
p_389997_.getSource(),
p_394293_.apply(p_389997_),
p_396869_.apply(p_389997_),
p_392122_.apply(p_389997_),
p_392995_.apply(p_389997_),
CloneCommands.Mode.NORMAL,
p_392263_
)
)
.then(
Commands.literal("force")
.executes(
p_389976_ -> clone(
p_389976_.getSource(),
p_394293_.apply(p_389976_),
p_396869_.apply(p_389976_),
p_392122_.apply(p_389976_),
p_392995_.apply(p_389976_),
CloneCommands.Mode.FORCE,
p_392263_
)
)
)
.then(
Commands.literal("move")
.executes(
p_389987_ -> clone(
p_389987_.getSource(),
p_394293_.apply(p_389987_),
p_396869_.apply(p_389987_),
p_392122_.apply(p_389987_),
p_392995_.apply(p_389987_),
CloneCommands.Mode.MOVE,
p_392263_
)
)
)
.then(
Commands.literal("normal")
.executes(
p_389968_ -> clone(
p_389968_.getSource(),
p_394293_.apply(p_389968_),
p_396869_.apply(p_389968_),
p_392122_.apply(p_389968_),
p_392995_.apply(p_389968_),
CloneCommands.Mode.NORMAL,
p_392263_
)
)
);
}
private static int clone(
CommandSourceStack p_265047_,
CloneCommands.DimensionAndPosition p_265232_,
CloneCommands.DimensionAndPosition p_265188_,
CloneCommands.DimensionAndPosition p_265594_,
Predicate<BlockInWorld> p_265585_,
CloneCommands.Mode p_265530_,
boolean p_394242_
) throws CommandSyntaxException {
BlockPos blockpos = p_265232_.position();
BlockPos blockpos1 = p_265188_.position();
BoundingBox boundingbox = BoundingBox.fromCorners(blockpos, blockpos1);
BlockPos blockpos2 = p_265594_.position();
BlockPos blockpos3 = blockpos2.offset(boundingbox.getLength());
BoundingBox boundingbox1 = BoundingBox.fromCorners(blockpos2, blockpos3);
ServerLevel serverlevel = p_265232_.dimension();
ServerLevel serverlevel1 = p_265594_.dimension();
if (!p_265530_.canOverlap() && serverlevel == serverlevel1 && boundingbox1.intersects(boundingbox)) {
throw ERROR_OVERLAP.create();
} else {
int i = boundingbox.getXSpan() * boundingbox.getYSpan() * boundingbox.getZSpan();
int j = p_265047_.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, blockpos1) && serverlevel1.hasChunksAt(blockpos2, blockpos3)) {
if (serverlevel1.isDebug()) {
throw ERROR_FAILED.create();
} else {
List<CloneCommands.CloneBlockInfo> list = Lists.newArrayList();
List<CloneCommands.CloneBlockInfo> list1 = Lists.newArrayList();
List<CloneCommands.CloneBlockInfo> list2 = Lists.newArrayList();
Deque<BlockPos> deque = Lists.newLinkedList();
BlockPos blockpos4 = new BlockPos(
boundingbox1.minX() - boundingbox.minX(),
boundingbox1.minY() - boundingbox.minY(),
boundingbox1.minZ() - boundingbox.minZ()
);
for (int k = boundingbox.minZ(); k <= boundingbox.maxZ(); k++) {
for (int l = boundingbox.minY(); l <= boundingbox.maxY(); l++) {
for (int i1 = boundingbox.minX(); i1 <= boundingbox.maxX(); i1++) {
BlockPos blockpos5 = new BlockPos(i1, l, k);
BlockPos blockpos6 = blockpos5.offset(blockpos4);
BlockInWorld blockinworld = new BlockInWorld(serverlevel, blockpos5, false);
BlockState blockstate = blockinworld.getState();
if (p_265585_.test(blockinworld)) {
BlockEntity blockentity = serverlevel.getBlockEntity(blockpos5);
if (blockentity != null) {
CloneCommands.CloneBlockEntityInfo clonecommands$cloneblockentityinfo = new CloneCommands.CloneBlockEntityInfo(
blockentity.saveCustomOnly(p_265047_.registryAccess()), blockentity.components()
);
list1.add(new CloneCommands.CloneBlockInfo(blockpos6, blockstate, clonecommands$cloneblockentityinfo));
deque.addLast(blockpos5);
} else if (!blockstate.isSolidRender() && !blockstate.isCollisionShapeFullBlock(serverlevel, blockpos5)) {
list2.add(new CloneCommands.CloneBlockInfo(blockpos6, blockstate, null));
deque.addFirst(blockpos5);
} else {
list.add(new CloneCommands.CloneBlockInfo(blockpos6, blockstate, null));
deque.addLast(blockpos5);
}
}
}
}
}
int j1 = 2 | (p_394242_ ? 816 : 0);
if (p_265530_ == CloneCommands.Mode.MOVE) {
for (BlockPos blockpos7 : deque) {
serverlevel.setBlock(blockpos7, Blocks.BARRIER.defaultBlockState(), j1 | 816);
}
int k1 = p_394242_ ? j1 : 3;
for (BlockPos blockpos8 : deque) {
serverlevel.setBlock(blockpos8, Blocks.AIR.defaultBlockState(), k1);
}
}
List<CloneCommands.CloneBlockInfo> list3 = Lists.newArrayList();
list3.addAll(list);
list3.addAll(list1);
list3.addAll(list2);
List<CloneCommands.CloneBlockInfo> list4 = Lists.reverse(list3);
for (CloneCommands.CloneBlockInfo clonecommands$cloneblockinfo : list4) {
serverlevel1.setBlock(clonecommands$cloneblockinfo.pos, Blocks.BARRIER.defaultBlockState(), j1 | 816);
}
int l1 = 0;
for (CloneCommands.CloneBlockInfo clonecommands$cloneblockinfo1 : list3) {
if (serverlevel1.setBlock(clonecommands$cloneblockinfo1.pos, clonecommands$cloneblockinfo1.state, j1)) {
l1++;
}
}
for (CloneCommands.CloneBlockInfo clonecommands$cloneblockinfo2 : list1) {
BlockEntity blockentity1 = serverlevel1.getBlockEntity(clonecommands$cloneblockinfo2.pos);
if (clonecommands$cloneblockinfo2.blockEntityInfo != null && blockentity1 != null) {
blockentity1.loadCustomOnly(clonecommands$cloneblockinfo2.blockEntityInfo.tag, serverlevel1.registryAccess());
blockentity1.setComponents(clonecommands$cloneblockinfo2.blockEntityInfo.components);
blockentity1.setChanged();
}
serverlevel1.setBlock(clonecommands$cloneblockinfo2.pos, clonecommands$cloneblockinfo2.state, j1);
}
if (!p_394242_) {
for (CloneCommands.CloneBlockInfo clonecommands$cloneblockinfo3 : list4) {
serverlevel1.updateNeighborsAt(clonecommands$cloneblockinfo3.pos, clonecommands$cloneblockinfo3.state.getBlock());
}
}
serverlevel1.getBlockTicks().copyAreaFrom(serverlevel.getBlockTicks(), boundingbox, blockpos4);
if (l1 == 0) {
throw ERROR_FAILED.create();
} else {
int i2 = l1;
p_265047_.sendSuccess(() -> Component.translatable("commands.clone.success", i2), true);
return l1;
}
}
} 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 p_136795_) {
this.canOverlap = p_136795_;
}
public boolean canOverlap() {
return this.canOverlap;
}
}
}