diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java index 36d48904..08f1a319 100644 --- a/src/main/java/carpet/CarpetSettings.java +++ b/src/main/java/carpet/CarpetSettings.java @@ -299,7 +299,7 @@ public class CarpetSettings ) public static String commandPerimeterInfo = "true"; - @Rule(desc = "Enables /draw commands", extra = "... allows for drawing simple shapes", category = COMMAND) + @Rule(desc = "Enables /draw commands", extra = {"... allows for drawing simple shapes or","other shapes which are sorta difficult to do normally"}, category = COMMAND) public static String commandDraw = "true"; @Rule( diff --git a/src/main/java/carpet/commands/DrawCommand.java b/src/main/java/carpet/commands/DrawCommand.java index c8208ff8..3fa9604c 100644 --- a/src/main/java/carpet/commands/DrawCommand.java +++ b/src/main/java/carpet/commands/DrawCommand.java @@ -7,6 +7,11 @@ import com.google.common.collect.Lists; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; + +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.Block; import net.minecraft.block.pattern.CachedBlockPosition; import net.minecraft.server.command.ServerCommandSource; @@ -18,12 +23,15 @@ import net.minecraft.inventory.Inventory; import net.minecraft.block.entity.BlockEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.MathHelper; import java.util.List; import java.util.function.Predicate; +import java.lang.Math; import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; +import static net.minecraft.server.command.CommandSource.suggestMatching; public class DrawCommand { @@ -32,45 +40,130 @@ public class DrawCommand LiteralArgumentBuilder command = literal("draw"). requires((player) -> SettingsManager.canUseCommand(player, CarpetSettings.commandDraw)). then(literal("sphere"). - then(argument("center",BlockPosArgumentType.blockPos()). - then(argument("radius",IntegerArgumentType.integer(1)). - then(argument("block",BlockStateArgumentType.blockState()). - executes((c)-> drawCircle( - c.getSource(), - BlockPosArgumentType.getBlockPos(c, "center"), - IntegerArgumentType.getInteger(c, "radius"), - BlockStateArgumentType.getBlockState (c,"block"), - null - ) - ). - then(literal("replace"). - then(argument("filter",BlockPredicateArgumentType.blockPredicate()) - .executes((c)-> drawCircle( - c.getSource(), - BlockPosArgumentType.getBlockPos(c, "center"), - IntegerArgumentType.getInteger(c, "radius"), - BlockStateArgumentType.getBlockState(c,"block"), - BlockPredicateArgumentType.getBlockPredicate(c, - "filter") - )))))))); + then(argument("center", BlockPosArgumentType.blockPos()). + then(argument("radius", IntegerArgumentType.integer(1)). + then(drawShape(c -> DrawCommand.drawSphere(c, false)))))). + then(literal("ball"). + then(argument("center", BlockPosArgumentType.blockPos()). + then(argument("radius", IntegerArgumentType.integer(1)). + then(drawShape(c -> DrawCommand.drawSphere(c, true)))))). + then(literal("diamond"). + then(argument("center", BlockPosArgumentType.blockPos()). + then(argument("radius", IntegerArgumentType.integer(1)). + then(drawShape(c -> DrawCommand.drawDiamond(c, true)))))). + then(literal("pyramid"). + then(argument("center", BlockPosArgumentType.blockPos()). + then(argument("radius", IntegerArgumentType.integer(1)). + then(argument("height",IntegerArgumentType.integer(1)). + then(argument("pointing",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"up","down"},b)). + then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b)). + then(drawShape(c -> DrawCommand.drawPyramid(c, "square", true))))))))). + then(literal("cone"). + then(argument("center", BlockPosArgumentType.blockPos()). + then(argument("radius", IntegerArgumentType.integer(1)). + then(argument("height",IntegerArgumentType.integer(1)). + then(argument("pointing",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"up","down"},b)). + then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b)) + .then(drawShape(c -> DrawCommand.drawPyramid(c, "circle", true))))))))); dispatcher.register(command); } - - private static int drawCircle(ServerCommandSource source, BlockPos pos, int radius, BlockStateArgument block, - Predicate replacement) + @FunctionalInterface + private interface OptionalBlockSelector { - return drawCircle(source, pos, (double)radius, (double)radius, (double)radius, block, replacement, false); + Integer apply(final CommandContext ctx) throws CommandSyntaxException; } - private static int drawCircle(ServerCommandSource source, BlockPos pos, double radiusX, double radiusY, double radiusZ, - BlockStateArgument block, Predicate replacement, boolean solid) - { - int affected = 0; - ServerWorld world = source.getWorld(); - radiusX += 0.5; - radiusY += 0.5; - radiusZ += 0.5; + @FunctionalInterface + private interface ArgumentExtractor + { + T apply(final CommandContext ctx, final String argName) throws CommandSyntaxException; + } + + private static RequiredArgumentBuilder + drawShape(OptionalBlockSelector drawer) + { + return argument("block", BlockStateArgumentType.blockState()). + executes(drawer::apply) + .then(literal("replace") + .then(argument("filter", BlockPredicateArgumentType.blockPredicate()) + .executes(drawer::apply))); + } + + private static class ErrorHandled extends RuntimeException {} + + private static T getArg(CommandContext ctx, ArgumentExtractor extract, String hwat) throws CommandSyntaxException + { + return getArg(ctx, extract, hwat, false); + } + + private static T getArg(CommandContext ctx, ArgumentExtractor extract, String hwat, boolean optional) throws CommandSyntaxException + { + T arg = null; + try + { + arg = extract.apply(ctx, hwat); + } + catch (IllegalArgumentException e) + { + if (optional) return null; + Messenger.m(ctx.getSource(), "rb Missing "+hwat); + throw new ErrorHandled(); + } + return arg; + } + + private static double lengthSq(double x, double y, double z) + { + return (x * x) + (y * y) + (z * z); + } + + private static int setBlock( + ServerWorld world, BlockPos.Mutable mbpos, int x, int y, int z, + BlockStateArgument block, Predicate replacement, + List list + ) + { + mbpos.set(x, y, z); + int success=0; + if (replacement == null || replacement.test(new CachedBlockPosition(world, mbpos, true))) + { + BlockEntity tileentity = world.getBlockEntity(mbpos); + if (tileentity instanceof Inventory) + { + ((Inventory) tileentity).clear(); + } + if (block.setBlockState(world, mbpos, 2)) + { + list.add(mbpos.toImmutable()); + ++success; + } + } + + return success; + } + + private static int drawSphere(CommandContext ctx, boolean solid) throws CommandSyntaxException + { + BlockPos pos; + int radius; + BlockStateArgument block; + Predicate replacement; + try + { + pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center"); + radius = getArg(ctx, IntegerArgumentType::getInteger, "radius"); + block = getArg(ctx, BlockStateArgumentType::getBlockState, "block"); + replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true); + } + catch (ErrorHandled ignored) { return 0; } + + int affected = 0; + ServerWorld world = ctx.getSource().getWorld(); + + double radiusX = radius+0.5; + double radiusY = radius+0.5; + double radiusZ = radius+0.5; final double invRadiusX = 1 / radiusX; final double invRadiusY = 1 / radiusY; @@ -85,22 +178,28 @@ public class DrawCommand double nextXn = 0; - forX: for (int x = 0; x <= ceilRadiusX; ++x) { + forX: for (int x = 0; x <= ceilRadiusX; ++x) + { final double xn = nextXn; nextXn = (x + 1) * invRadiusX; double nextYn = 0; - forY: for (int y = 0; y <= ceilRadiusY; ++y) { + forY: for (int y = 0; y <= ceilRadiusY; ++y) + { final double yn = nextYn; nextYn = (y + 1) * invRadiusY; double nextZn = 0; - forZ: for (int z = 0; z <= ceilRadiusZ; ++z) { + forZ: for (int z = 0; z <= ceilRadiusZ; ++z) + { final double zn = nextZn; nextZn = (z + 1) * invRadiusZ; double distanceSq = lengthSq(xn, yn, zn); - if (distanceSq > 1) { - if (z == 0) { - if (y == 0) { + if (distanceSq > 1) + { + if (z == 0) + { + if (y == 0) + { break forX; } break forY; @@ -108,35 +207,22 @@ public class DrawCommand break forZ; } - if (!solid) { - if (lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1) { - continue; - } + if (!solid && lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1) + { + continue; } CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates; - for (int xmod = -1; xmod < 2; xmod+= 2) + for (int xmod = -1; xmod < 2; xmod += 2) { for (int ymod = -1; ymod < 2; ymod += 2) { for (int zmod = -1; zmod < 2; zmod += 2) { - mbpos.set(pos.getX()+xmod*x, pos.getY()+ymod*y, pos.getZ()+zmod*z); - if (replacement == null || replacement.test( - new CachedBlockPosition( world, mbpos, true))) - { - BlockEntity tileentity = world.getBlockEntity(mbpos); - if (tileentity instanceof Inventory) - { - ((Inventory)tileentity).clear(); - } - - if (block.setBlockState(world, mbpos,2)) - { - list.add(mbpos.toImmutable()); - ++affected; - } - } + affected+= setBlock(world, mbpos, + pos.getX() + xmod * x, pos.getY() + ymod * y, pos.getZ() + zmod * z, + block, replacement, list + ); } } } @@ -146,19 +232,161 @@ public class DrawCommand } if (CarpetSettings.fillUpdates) { + list.forEach(blockpos1 -> world.updateNeighbors(blockpos1, world.getBlockState(blockpos1).getBlock())); + } + Messenger.m(ctx.getSource(), "gi Filled " + affected + " blocks"); + return affected; + } - for (BlockPos blockpos1 : list) + private static int drawDiamond(CommandContext ctx, boolean solid) throws CommandSyntaxException + { + BlockPos pos; + int radius; + BlockStateArgument block; + Predicate replacement; + try + { + pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center"); + radius = getArg(ctx, IntegerArgumentType::getInteger, "radius"); + block = getArg(ctx, BlockStateArgumentType::getBlockState, "block"); + replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true); + } + catch (ErrorHandled ignored) { return 0; } + + ServerCommandSource source = ctx.getSource(); + + int affected=0; + + BlockPos.Mutable mbpos = new BlockPos.Mutable(pos); + List list = Lists.newArrayList(); + + ServerWorld world = source.getWorld(); + + CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates; + + for (int r = 0; r < radius; ++r) + { + int y=r-radius+1; + for (int x = -r; x <= r; ++x) { - Block blokc = world.getBlockState(blockpos1).getBlock(); - world.updateNeighbors(blockpos1, blokc); // or always version???? + int z=r-Math.abs(x); + + affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()-y, pos.getZ()+z, block, replacement, list); + affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()-y, pos.getZ()-z, block, replacement, list); + affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()+y, pos.getZ()+z, block, replacement, list); + affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()+y, pos.getZ()-z, block, replacement, list); } } - Messenger.m(source, "gi Filled "+affected+" blocks"); - return 1; - } - private static double lengthSq(double x, double y, double z) + CarpetSettings.impendingFillSkipUpdates = false; + + if (CarpetSettings.fillUpdates) { - return (x * x) + (y * y) + (z * z); + list.forEach(p -> world.updateNeighbors(p, world.getBlockState(p).getBlock())); + } + + Messenger.m(source, "gi Filled " + affected + " blocks"); + + return affected; + } + + private static int fillFlat( + ServerWorld world, BlockPos pos, int offset, double dr, boolean rectangle, String orientation, + BlockStateArgument block, Predicate replacement, + List list, BlockPos.Mutable mbpos + ) + { + int successes=0; + int r = MathHelper.floor(dr); + double drsq = dr*dr; + if (orientation.equalsIgnoreCase("x")) + { + for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq) + { + successes += setBlock( + world, mbpos,pos.getX()+offset, pos.getY()+a, pos.getZ()+b, + block, replacement, list + ); + } + return successes; + } + if (orientation.equalsIgnoreCase("y")) + { + for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq) + { + successes += setBlock( + world, mbpos,pos.getX()+a, pos.getY()+offset, pos.getZ()+b, + block, replacement, list + ); + } + return successes; + } + if (orientation.equalsIgnoreCase("z")) + { + for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq) + { + successes += setBlock( + world, mbpos,pos.getX()+b, pos.getY()+a, pos.getZ()+offset, + block, replacement, list + ); + } + return successes; + } + return 0; + } + + private static int drawPyramid(CommandContext ctx, String base, boolean solid) throws CommandSyntaxException + { + BlockPos pos; + double radius; + int height; + boolean pointup; + String orientation; + BlockStateArgument block; + Predicate replacement; + try + { + pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center"); + radius = getArg(ctx, IntegerArgumentType::getInteger, "radius")+0.5D; + height = getArg(ctx, IntegerArgumentType::getInteger, "height"); + pointup = getArg(ctx, StringArgumentType::getString, "pointing").equalsIgnoreCase("up"); + orientation = getArg(ctx, StringArgumentType::getString,"orientation"); + block = getArg(ctx, BlockStateArgumentType::getBlockState, "block"); + replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true); + } + catch (ErrorHandled ignored) { return 0; } + + ServerCommandSource source = ctx.getSource(); + + int affected = 0; + BlockPos.Mutable mbpos = new BlockPos.Mutable(pos); + + List list = Lists.newArrayList(); + + ServerWorld world = source.getWorld(); + + CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates; + + boolean isSquare = base.equalsIgnoreCase("square"); + + for(int i =0; i iterator = ((AbstractListValue) rval).iterator(); LazyValue expr = lv.get(1); //scoping