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.Collections; 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.blocks.BlockInput; import net.minecraft.commands.arguments.blocks.BlockPredicateArgument; import net.minecraft.commands.arguments.blocks.BlockStateArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.levelgen.structure.BoundingBox; import org.jetbrains.annotations.Nullable; public class FillCommand { private static final Dynamic2CommandExceptionType ERROR_AREA_TOO_LARGE = new Dynamic2CommandExceptionType( (object, object2) -> Component.translatableEscape("commands.fill.toobig", object, object2) ); static final BlockInput HOLLOW_CORE = new BlockInput(Blocks.AIR.defaultBlockState(), Collections.emptySet(), null); private static final SimpleCommandExceptionType ERROR_FAILED = new SimpleCommandExceptionType(Component.translatable("commands.fill.failed")); public static void register(CommandDispatcher dispatcher, CommandBuildContext buildContext) { dispatcher.register( Commands.literal("fill") .requires(commandSourceStack -> commandSourceStack.hasPermission(2)) .then( Commands.argument("from", BlockPosArgument.blockPos()) .then( Commands.argument("to", BlockPosArgument.blockPos()) .then( wrapWithMode( buildContext, Commands.argument("block", BlockStateArgument.block(buildContext)), commandContext -> BlockPosArgument.getLoadedBlockPos(commandContext, "from"), commandContext -> BlockPosArgument.getLoadedBlockPos(commandContext, "to"), commandContext -> BlockStateArgument.getBlock(commandContext, "block"), commandContext -> null ) .then( Commands.literal("replace") .executes( commandContext -> fillBlocks( commandContext.getSource(), BoundingBox.fromCorners(BlockPosArgument.getLoadedBlockPos(commandContext, "from"), BlockPosArgument.getLoadedBlockPos(commandContext, "to")), BlockStateArgument.getBlock(commandContext, "block"), FillCommand.Mode.REPLACE, null, false ) ) .then( wrapWithMode( buildContext, Commands.argument("filter", BlockPredicateArgument.blockPredicate(buildContext)), commandContext -> BlockPosArgument.getLoadedBlockPos(commandContext, "from"), commandContext -> BlockPosArgument.getLoadedBlockPos(commandContext, "to"), commandContext -> BlockStateArgument.getBlock(commandContext, "block"), commandContext -> BlockPredicateArgument.getBlockPredicate(commandContext, "filter") ) ) ) .then( Commands.literal("keep") .executes( commandContext -> fillBlocks( commandContext.getSource(), BoundingBox.fromCorners(BlockPosArgument.getLoadedBlockPos(commandContext, "from"), BlockPosArgument.getLoadedBlockPos(commandContext, "to")), BlockStateArgument.getBlock(commandContext, "block"), FillCommand.Mode.REPLACE, blockInWorld -> blockInWorld.getLevel().isEmptyBlock(blockInWorld.getPos()), false ) ) ) ) ) ) ); } private static ArgumentBuilder wrapWithMode( CommandBuildContext buildContext, ArgumentBuilder argumentBuilder, InCommandFunction, BlockPos> from, InCommandFunction, BlockPos> to, InCommandFunction, BlockInput> block, FillCommand.NullableCommandFunction, Predicate> filter ) { return argumentBuilder.executes( commandContext -> fillBlocks( commandContext.getSource(), BoundingBox.fromCorners(from.apply(commandContext), to.apply(commandContext)), block.apply(commandContext), FillCommand.Mode.REPLACE, filter.apply(commandContext), false ) ) .then( Commands.literal("outline") .executes( commandContext -> fillBlocks( commandContext.getSource(), BoundingBox.fromCorners(from.apply(commandContext), to.apply(commandContext)), block.apply(commandContext), FillCommand.Mode.OUTLINE, filter.apply(commandContext), false ) ) ) .then( Commands.literal("hollow") .executes( commandContext -> fillBlocks( commandContext.getSource(), BoundingBox.fromCorners(from.apply(commandContext), to.apply(commandContext)), block.apply(commandContext), FillCommand.Mode.HOLLOW, filter.apply(commandContext), false ) ) ) .then( Commands.literal("destroy") .executes( commandContext -> fillBlocks( commandContext.getSource(), BoundingBox.fromCorners(from.apply(commandContext), to.apply(commandContext)), block.apply(commandContext), FillCommand.Mode.DESTROY, filter.apply(commandContext), false ) ) ) .then( Commands.literal("strict") .executes( commandContext -> fillBlocks( commandContext.getSource(), BoundingBox.fromCorners(from.apply(commandContext), to.apply(commandContext)), block.apply(commandContext), FillCommand.Mode.REPLACE, filter.apply(commandContext), true ) ) ); } private static int fillBlocks( CommandSourceStack source, BoundingBox box, BlockInput block, FillCommand.Mode mode, @Nullable Predicate filter, boolean strict ) throws CommandSyntaxException { int i = box.getXSpan() * box.getYSpan() * box.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 { List list = Lists.newArrayList(); ServerLevel serverLevel = source.getLevel(); if (serverLevel.isDebug()) { throw ERROR_FAILED.create(); } else { int k = 0; for (BlockPos blockPos : BlockPos.betweenClosed(box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ())) { if (filter == null || filter.test(new BlockInWorld(serverLevel, blockPos, true))) { boolean bl = false; if (mode.affector.affect(serverLevel, blockPos)) { bl = true; } BlockInput blockInput = mode.filter.filter(box, blockPos, block, serverLevel); if (blockInput == null) { if (bl) { k++; } } else if (!blockInput.place(serverLevel, blockPos, 2 | (strict ? 816 : 256))) { if (bl) { k++; } } else { if (!strict) { list.add(blockPos.immutable()); } k++; } } } for (BlockPos blockPosx : list) { Block block2 = serverLevel.getBlockState(blockPosx).getBlock(); serverLevel.updateNeighborsAt(blockPosx, block2); } if (k == 0) { throw ERROR_FAILED.create(); } else { int l = k; source.sendSuccess(() -> Component.translatable("commands.fill.success", l), true); return k; } } } } @FunctionalInterface public interface Affector { FillCommand.Affector NOOP = (serverLevel, blockPos) -> false; boolean affect(ServerLevel serverLevel, BlockPos blockPos); } @FunctionalInterface public interface Filter { FillCommand.Filter NOOP = (boundingBox, blockPos, blockInput, serverLevel) -> blockInput; @Nullable BlockInput filter(BoundingBox boundingBox, BlockPos blockPos, BlockInput blockInput, ServerLevel serverLevel); } static enum Mode { REPLACE(FillCommand.Affector.NOOP, FillCommand.Filter.NOOP), OUTLINE( FillCommand.Affector.NOOP, (boundingBox, blockPos, blockInput, serverLevel) -> blockPos.getX() != boundingBox.minX() && blockPos.getX() != boundingBox.maxX() && blockPos.getY() != boundingBox.minY() && blockPos.getY() != boundingBox.maxY() && blockPos.getZ() != boundingBox.minZ() && blockPos.getZ() != boundingBox.maxZ() ? null : blockInput ), HOLLOW( FillCommand.Affector.NOOP, (boundingBox, blockPos, blockInput, serverLevel) -> blockPos.getX() != boundingBox.minX() && blockPos.getX() != boundingBox.maxX() && blockPos.getY() != boundingBox.minY() && blockPos.getY() != boundingBox.maxY() && blockPos.getZ() != boundingBox.minZ() && blockPos.getZ() != boundingBox.maxZ() ? FillCommand.HOLLOW_CORE : blockInput ), DESTROY((serverLevel, blockPos) -> serverLevel.destroyBlock(blockPos, true), FillCommand.Filter.NOOP); public final FillCommand.Filter filter; public final FillCommand.Affector affector; private Mode(final FillCommand.Affector affector, final FillCommand.Filter filter) { this.affector = affector; this.filter = filter; } } @FunctionalInterface interface NullableCommandFunction { @Nullable R apply(T object) throws CommandSyntaxException; } }