From 28af68b90615c2bd9c79b959cf2e63cf3dbf504a Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 31 May 2023 14:18:02 +0200 Subject: [PATCH] Refactored `/place`-Command --- src/main/java/org/betterx/bclib/BCLib.java | 2 + .../bclib/commands/CommandRegistry.java | 2 +- .../{Place.java => PlaceCommand.java} | 340 +++++++----------- .../commands/arguments/BCLibArguments.java | 23 ++ .../arguments/Float3ArgumentInfo.java | 95 +++++ .../arguments/Float3ArgumentType.java | 198 ++++++++++ .../arguments/PlacementDirections.java | 36 ++ .../arguments/TemplatePlacementArgument.java | 20 ++ 8 files changed, 505 insertions(+), 211 deletions(-) rename src/main/java/org/betterx/bclib/commands/{Place.java => PlaceCommand.java} (55%) create mode 100644 src/main/java/org/betterx/bclib/commands/arguments/BCLibArguments.java create mode 100644 src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentInfo.java create mode 100644 src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentType.java create mode 100644 src/main/java/org/betterx/bclib/commands/arguments/PlacementDirections.java create mode 100644 src/main/java/org/betterx/bclib/commands/arguments/TemplatePlacementArgument.java diff --git a/src/main/java/org/betterx/bclib/BCLib.java b/src/main/java/org/betterx/bclib/BCLib.java index 754568bf..9231c850 100644 --- a/src/main/java/org/betterx/bclib/BCLib.java +++ b/src/main/java/org/betterx/bclib/BCLib.java @@ -16,6 +16,7 @@ import org.betterx.bclib.api.v3.tag.BCLBlockTags; import org.betterx.bclib.blocks.signs.BaseHangingSignBlock; import org.betterx.bclib.blocks.signs.BaseSignBlock; import org.betterx.bclib.commands.CommandRegistry; +import org.betterx.bclib.commands.arguments.BCLibArguments; import org.betterx.bclib.complexmaterials.BCLWoodTypeWrapper; import org.betterx.bclib.config.Configs; import org.betterx.bclib.config.PathConfig; @@ -67,6 +68,7 @@ public class BCLib implements ModInitializer { @Override public void onInitialize() { WorldsTogether.onInitialize(); + BCLibArguments.register(); PresetsRegistry.register(); LevelGenEvents.register(); BlockPredicates.ensureStaticInitialization(); diff --git a/src/main/java/org/betterx/bclib/commands/CommandRegistry.java b/src/main/java/org/betterx/bclib/commands/CommandRegistry.java index bb89dd0f..f8250e7e 100644 --- a/src/main/java/org/betterx/bclib/commands/CommandRegistry.java +++ b/src/main/java/org/betterx/bclib/commands/CommandRegistry.java @@ -41,7 +41,7 @@ public class CommandRegistry { LiteralArgumentBuilder bnContext = Commands.literal("bclib") .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)); - bnContext = Place.register(bnContext, commandBuildContext); + bnContext = PlaceCommand.register(bnContext, commandBuildContext); bnContext = PrintInfo.register(bnContext); bnContext = DumpDatapack.register(bnContext); diff --git a/src/main/java/org/betterx/bclib/commands/Place.java b/src/main/java/org/betterx/bclib/commands/PlaceCommand.java similarity index 55% rename from src/main/java/org/betterx/bclib/commands/Place.java rename to src/main/java/org/betterx/bclib/commands/PlaceCommand.java index ea62d63d..46a421bf 100644 --- a/src/main/java/org/betterx/bclib/commands/Place.java +++ b/src/main/java/org/betterx/bclib/commands/PlaceCommand.java @@ -2,20 +2,25 @@ package org.betterx.bclib.commands; import de.ambertation.wunderlib.math.Float3; import org.betterx.bclib.api.v2.levelgen.structures.StructureNBT; +import org.betterx.bclib.commands.arguments.Float3ArgumentType; +import org.betterx.bclib.commands.arguments.PlacementDirections; +import org.betterx.bclib.commands.arguments.TemplatePlacementArgument; import org.betterx.bclib.util.BlocksHelper; import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.ResourceLocationArgument; import net.minecraft.commands.arguments.blocks.BlockInput; import net.minecraft.commands.arguments.blocks.BlockStateArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.commands.arguments.coordinates.Coordinates; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; @@ -33,230 +38,145 @@ import net.minecraft.world.level.block.state.properties.AttachFace; import net.minecraft.world.level.block.state.properties.StructureMode; import net.minecraft.world.level.levelgen.structure.BoundingBox; -import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Function; -import org.jetbrains.annotations.NotNull; +import java.util.function.Supplier; -class PlaceNBT { - protected > T execute( - T builder, - BlockPos searchDir, boolean border, boolean commandBlock - ) { - return builder.executes(commandContext -> placeNBT( - commandContext.getSource(), - BlockPosArgument.getLoadedBlockPos(commandContext, "pos"), - StringArgumentType.getString(commandContext, "path"), - searchDir, border ? BlockStateArgument.getBlock(commandContext, "block") : null, commandBlock - )); - } +class PlaceCommandBuilder { + public static final String PATH = "path"; + public static final String NBT = "nbt"; + public static final String EMPTY = "empty"; + public static final String PLACEMENT = "placement"; + public static final String POS = "pos"; + public static final String SPAN = "span"; + public static final String BORDER = "border"; - protected > T buildPosArgument( - CommandBuildContext commandBuildContext, - T builder, - BlockPos searchDir + public void register( + CommandBuildContext ctx, + LiteralArgumentBuilder command ) { - final var posBuilder = Commands.argument( - "pos", + final Supplier> path = () -> Commands.argument( + PATH, + ResourceLocationArgument.id() + ); + final Supplier> placement = () -> Commands.argument( + PLACEMENT, + TemplatePlacementArgument.templatePlacement() + ); + final Supplier> pos = () -> Commands.argument( + POS, BlockPosArgument.blockPos() ); - final var posOnly = addPosOnly(searchDir, posBuilder); - final var all = addPosWithAttributes(commandBuildContext, searchDir, posBuilder); - - return builder.then(posOnly).then(all); - } - - protected

> P addPosWithAttributes( - CommandBuildContext commandBuildContext, - BlockPos searchDir, - P posBuilder - ) { - return posBuilder - .then(execute(Commands.literal("structureblock"), searchDir, false, true)) - .then(Commands.literal("border") - .then(execute( - Commands.argument("block", BlockStateArgument.block(commandBuildContext)), - searchDir, - true, - false - ).then(execute(Commands.literal("structureblock"), searchDir, true, true)) - ) - ); - } - - protected

> P addPosOnly( - BlockPos searchDir, - P posBuilder - ) { - return execute(posBuilder, searchDir, false, false); - } - - public void register( - CommandBuildContext commandBuildContext, - Map directions, - LiteralArgumentBuilder command - ) { - - final LiteralArgumentBuilder nbtBuilder = Commands - .literal("nbt") - .then(Commands - .argument("path", StringArgumentType.string()) - .then(buildPosArgument(commandBuildContext, Commands.literal("at"), null)) - .then(buildFindCommand(commandBuildContext, directions)) + final var nbtTree = Commands.literal(NBT).then( + path.get().then( + placement.get().then( + addOptionalsAndExecute( + ctx, + pos.get(), + PlaceCommandBuilder::placeNBT + ) + ) ) -// .then(Commands.literal("test") -// .then(Commands.argument("block", BlockStateArgument.block(commandBuildContext)) -// .executes(commandContext -> placeNBT( -// commandContext.getSource(), -// new BlockPos(100, 10, 0), -// "betternether:altar_01", -// Float3.NORTH.toBlockPos(), -// BlockStateArgument.getBlock(commandContext, "block"), -// true -// ))) -// ) - ; + ); + final var emptyTree = Commands.literal(EMPTY).then( + path.get().then( + placement.get().then( + pos.get().then( + addOptionalsAndExecute( + ctx, + Commands.argument(SPAN, Float3ArgumentType.int3(0, 64)), + PlaceCommandBuilder::placeEmpty + ) + ) + ) + ) + ); - command.then(nbtBuilder); + command + .then(nbtTree) + .then(emptyTree); } - @NotNull - protected LiteralArgumentBuilder buildFindCommand( + private RequiredArgumentBuilder addOptionalsAndExecute( CommandBuildContext commandBuildContext, - Map directions + RequiredArgumentBuilder root, + Executor runner ) { - final LiteralArgumentBuilder executeFindFree = Commands.literal("find"); - for (var dir : directions.entrySet()) { - executeFindFree.then(buildPosArgument( - commandBuildContext, - Commands.literal(dir.getKey()), - dir.getValue().toBlockPos() - )); - } - return executeFindFree; + final Supplier> addControllers = () -> Commands.literal("controller"); + final Supplier> addBorder = () -> Commands.argument( + BORDER, + BlockStateArgument.block(commandBuildContext) + ); + + return root + .executes(c -> runner.exec(c, false, false)) + .then(addBorder.get() + .executes(c -> runner.exec(c, true, false)) + .then(addControllers.get() + .executes(c -> runner.exec(c, true, true))) + ) + .then(addControllers.get().executes(c -> runner.exec(c, false, true))); } - static int placeNBT( - CommandSourceStack stack, - BlockPos pos, - String location, - BlockPos searchDir, - BlockInput blockInput, - boolean structureBlock - ) { - StructureNBT structureNBT = StructureNBT.create(new ResourceLocation(location)); - return Place.placeBlocks( - stack, - pos, - searchDir, + interface Executor { + int exec( + CommandContext ctx, + boolean border, + boolean controlBlocks + ) throws CommandSyntaxException; + } + + protected static int placeNBT( + CommandContext ctx, + boolean border, + boolean controlBlocks + ) throws CommandSyntaxException { + final ResourceLocation id = ResourceLocationArgument.getId(ctx, PATH); + final PlacementDirections searchDir = TemplatePlacementArgument.getPlacement(ctx, PLACEMENT); + final BlockInput blockInput = border ? BlockStateArgument.getBlock(ctx, BORDER) : null; + final StructureNBT structureNBT = StructureNBT.create(id); + + return PlaceCommand.placeBlocks( + ctx.getSource(), + BlockPosArgument.getLoadedBlockPos(ctx, POS), + searchDir.getOffset(), blockInput, - structureBlock, + controlBlocks, structureNBT.location, (p) -> structureNBT.getBoundingBox(p, Rotation.NONE, Mirror.NONE), (level, p) -> structureNBT.generateAt(level, p, Rotation.NONE, Mirror.NONE) ); } -} -class PlaceEmpty extends PlaceNBT { - @Override - protected > T execute( - T builder, - BlockPos searchDir, boolean border, boolean commandBlock - ) { - return builder.executes(commandContext -> placeEmpty( - commandContext.getSource(), - BlockPosArgument.getLoadedBlockPos(commandContext, "pos"), - new BlockPos( - IntegerArgumentType.getInteger(commandContext, "spanX"), - IntegerArgumentType.getInteger(commandContext, "spanY"), - IntegerArgumentType.getInteger(commandContext, "spanZ") - ), - StringArgumentType.getString(commandContext, "path"), - searchDir, border ? BlockStateArgument.getBlock(commandContext, "block") : null, commandBlock - )); - } + protected static int placeEmpty( + CommandContext ctx, + boolean border, + boolean controlBlocks + ) throws CommandSyntaxException { + final ResourceLocation id = ResourceLocationArgument.getId(ctx, PATH); + final PlacementDirections searchDir = TemplatePlacementArgument.getPlacement(ctx, PLACEMENT); + final BlockInput blockInput = border ? BlockStateArgument.getBlock(ctx, BORDER) : null; + final BlockPos span = Float3ArgumentType.getFloat3(ctx, SPAN).toBlockPos(); - @Override - protected > T buildPosArgument( - CommandBuildContext commandBuildContext, - T builder, - BlockPos searchDir - ) { - final var spanX = Commands.argument( - "spanX", - IntegerArgumentType.integer(0, 64) - ); - final var spanY = Commands.argument( - "spanY", - IntegerArgumentType.integer(0, 64) - ); - final var spanZ = Commands.argument( - "spanZ", - IntegerArgumentType.integer(0, 64) - ); - final var posBuilder = Commands.argument( - "pos", - BlockPosArgument.blockPos() - ); - - final var posOnly = addPosOnly(searchDir, spanZ); - final var all = addPosWithAttributes(commandBuildContext, searchDir, spanZ); - - return builder.then(posBuilder.then(spanX.then(spanY.then(posOnly).then(all)))); - } - - @Override - public void register( - CommandBuildContext commandBuildContext, - Map directions, - LiteralArgumentBuilder command - ) { - - final LiteralArgumentBuilder nbtBuilder = Commands - .literal("empty") - .then(Commands - .argument("path", StringArgumentType.string()) - .then(buildPosArgument(commandBuildContext, Commands.literal("at"), null)) - .then(buildFindCommand(commandBuildContext, directions)) - ); - - - command.then(nbtBuilder); - } - - private static int placeEmpty( - CommandSourceStack stack, - BlockPos start, - BlockPos span, - String location, - BlockPos searchDir, - BlockInput blockInput, - boolean structureBlock - ) { - return Place.placeBlocks( - stack, - start, - searchDir, + return PlaceCommand.placeBlocks( + ctx.getSource(), + BlockPosArgument.getLoadedBlockPos(ctx, POS), + searchDir == null || searchDir.dir == Float3.ZERO ? null : searchDir.dir.toBlockPos(), blockInput, - structureBlock, - new ResourceLocation(location), + controlBlocks, + id, (p) -> BoundingBox.fromCorners(p, p.offset(span)), (level, p) -> { var box = BoundingBox.fromCorners(p, p.offset(span)); - Place.fillStructureVoid(level, box); + PlaceCommand.fillStructureVoid(level, box); if (blockInput != null) { - Place.fill( + PlaceCommand.fill( level, new BoundingBox( - box.minX(), - box.minY() - 1, - box.minZ(), - box.maxX(), - box.minY() - 1, - box.maxZ() + box.minX(), box.minY() - 1, box.minZ(), + box.maxX(), box.minY() - 1, box.maxZ() ), blockInput.getState() ); @@ -266,29 +186,19 @@ class PlaceEmpty extends PlaceNBT { } } -public class Place { - public Place() { +public class PlaceCommand { + public PlaceCommand() { } public static LiteralArgumentBuilder register( LiteralArgumentBuilder bnContext, CommandBuildContext commandBuildContext ) { - final Map directions = Map.of( - "northOf", Float3.NORTH, - "southOf", Float3.SOUTH, - "eastOf", Float3.EAST, - "westOf", Float3.WEST, - "above", Float3.UP, - "below", Float3.DOWN - ); - final var command = Commands .literal("place") .requires(commandSourceStack -> commandSourceStack.hasPermission(2)); - new PlaceNBT().register(commandBuildContext, directions, command); - new PlaceEmpty().register(commandBuildContext, directions, command); + new PlaceCommandBuilder().register(commandBuildContext, command); return bnContext.then(command); } @@ -378,7 +288,7 @@ public class Place { stack.getLevel().setBlock(structureBlockPos, state, BlocksHelper.SET_OBSERV); if (stack.getLevel().getBlockEntity(structureBlockPos) instanceof StructureBlockEntity entity) { entity.setIgnoreEntities(false); - entity.setShowAir(true); + entity.setShowAir(false); entity.setMirror(Mirror.NONE); entity.setRotation(Rotation.NONE); entity.setShowBoundingBox(true); @@ -462,5 +372,15 @@ public class Place { return Command.SINGLE_SUCCESS; } - } + +/* + +/bclib place nbt "betternether:city/city_building_01" find northOf 0 -59 0 border glass structureblock +/bclib place nbt "betternether:city/city_center_04" find northOf 32 -59 0 border glass structureblock +/bclib place nbt "betternether:city/city_enchanter_02" find northOf 32 -59 0 border glass structureblock +/bclib place nbt "betternether:city/city_library_03" find northOf 64 -59 0 border glass structureblock +/bclib place nbt "betternether:city/city_park_02" find northOf 64 -59 0 border glass structureblock +/bclib place nbt "betternether:city/city_tower_04" find northOf 96 -59 0 border glass structureblock +/bclib place nbt "betternether:city/road_end_02" find northOf 96 -59 0 border glass structureblock + */ diff --git a/src/main/java/org/betterx/bclib/commands/arguments/BCLibArguments.java b/src/main/java/org/betterx/bclib/commands/arguments/BCLibArguments.java new file mode 100644 index 00000000..15b8f38c --- /dev/null +++ b/src/main/java/org/betterx/bclib/commands/arguments/BCLibArguments.java @@ -0,0 +1,23 @@ +package org.betterx.bclib.commands.arguments; + +import org.betterx.bclib.BCLib; + +import net.minecraft.commands.synchronization.SingletonArgumentInfo; + +import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry; + +public class BCLibArguments { + public static void register() { + ArgumentTypeRegistry.registerArgumentType( + BCLib.makeID("template_placement"), + TemplatePlacementArgument.class, + SingletonArgumentInfo.contextFree(TemplatePlacementArgument::templatePlacement) + ); + + ArgumentTypeRegistry.registerArgumentType( + BCLib.makeID("float3"), + Float3ArgumentType.class, + new Float3ArgumentInfo() + ); + } +} diff --git a/src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentInfo.java b/src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentInfo.java new file mode 100644 index 00000000..c79e7a03 --- /dev/null +++ b/src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentInfo.java @@ -0,0 +1,95 @@ +package org.betterx.bclib.commands.arguments; + +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.commands.synchronization.ArgumentTypeInfo; +import net.minecraft.network.FriendlyByteBuf; + +import com.google.gson.JsonObject; + +public class Float3ArgumentInfo implements ArgumentTypeInfo { + private static byte MIN_FLAG = 1 << 1; + private static byte MAX_FLAG = 1 << 2; + private static byte IS_INT_FLAG = 1 << 0; + + private static byte createNumberFlags(boolean isInt, boolean hasMin, boolean hasMax) { + byte flagByte = 0; + if (isInt) flagByte |= IS_INT_FLAG; + if (hasMin) flagByte |= MIN_FLAG; + if (hasMax) flagByte |= MAX_FLAG; + + return flagByte; + } + + private static boolean numberHasMin(byte flag) { + return (flag & MIN_FLAG) != 0; + } + + private static boolean numberHasMax(byte flag) { + return (flag & MAX_FLAG) != 0; + } + + private static boolean numberIsInt(byte flag) { + return (flag & IS_INT_FLAG) != 0; + } + + public void serializeToNetwork( + Float3ArgumentInfo.Template template, + FriendlyByteBuf friendlyByteBuf + ) { + final boolean hasMin = template.min != -Double.MAX_VALUE; + final boolean hasMax = template.max != Double.MAX_VALUE; + friendlyByteBuf.writeByte(createNumberFlags(template.asInt, hasMin, hasMax)); + if (hasMin) friendlyByteBuf.writeDouble(template.min); + if (hasMax) friendlyByteBuf.writeDouble(template.max); + } + + public Float3ArgumentInfo.Template deserializeFromNetwork( + FriendlyByteBuf friendlyByteBuf + ) { + byte flag = friendlyByteBuf.readByte(); + boolean asInt = numberIsInt(flag); + double min = numberHasMin(flag) ? friendlyByteBuf.readDouble() : -Double.MAX_VALUE; + double max = numberHasMax(flag) ? friendlyByteBuf.readDouble() : Double.MAX_VALUE; + return new Float3ArgumentInfo.Template(asInt, min, max); + } + + public void serializeToJson(Float3ArgumentInfo.Template template, JsonObject jsonObject) { + if (!template.asInt) { + jsonObject.addProperty("asInt", template.asInt); + } + if (template.min != -Double.MAX_VALUE) { + jsonObject.addProperty("min", template.min); + } + if (template.max != Double.MAX_VALUE) { + jsonObject.addProperty("max", template.max); + } + + } + + public Float3ArgumentInfo.Template unpack(Float3ArgumentType type) { + return new Float3ArgumentInfo.Template(type.isInt(), type.getMinimum(), type.getMaximum()); + } + + public final class Template implements ArgumentTypeInfo.Template { + final double min; + final double max; + final boolean asInt; + + Template(boolean asInt, double min, double max) { + this.min = min; + this.max = max; + this.asInt = asInt; + + } + + public Float3ArgumentType instantiate(CommandBuildContext commandBuildContext) { + return this.asInt + ? Float3ArgumentType.int3((int) this.min, (int) this.max) + : Float3ArgumentType.float3(this.min, this.max); + } + + public ArgumentTypeInfo type() { + return Float3ArgumentInfo.this; + } + } +} diff --git a/src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentType.java b/src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentType.java new file mode 100644 index 00000000..f22f4cdb --- /dev/null +++ b/src/main/java/org/betterx/bclib/commands/arguments/Float3ArgumentType.java @@ -0,0 +1,198 @@ +package org.betterx.bclib.commands.arguments; + +import de.ambertation.wunderlib.math.Float3; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.coordinates.Vec3Argument; +import net.minecraft.commands.arguments.coordinates.WorldCoordinate; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +public class Float3ArgumentType implements ArgumentType { + private static final Collection EXAMPLES = List.of("0 0 0"); + + private final double minimum; + private final double maximum; + private final boolean asInt; + + Float3ArgumentType(int minimum, int maximum) { + this(true, minimum, maximum); + } + + Float3ArgumentType(double minimum, double maximum) { + this(false, minimum, maximum); + } + + Float3ArgumentType(boolean asInt, double minimum, double maximum) { + this.asInt = asInt; + this.minimum = minimum; + this.maximum = maximum; + } + + public double getMinimum() { + return minimum; + } + + public double getMaximum() { + return maximum; + } + + public boolean isInt() { + return asInt; + } + + @Override + public Float3 parse(StringReader reader) throws CommandSyntaxException { + int i = reader.getCursor(); + double x = asInt ? parseInt(reader) : parseDouble(reader); + if (!reader.canRead() || reader.peek() != ' ') { + reader.setCursor(i); + throw Vec3Argument.ERROR_NOT_COMPLETE.createWithContext(reader); + } + reader.skip(); + double y = asInt ? parseInt(reader) : parseDouble(reader); + if (!reader.canRead() || reader.peek() != ' ') { + reader.setCursor(i); + throw Vec3Argument.ERROR_NOT_COMPLETE.createWithContext(reader); + } + reader.skip(); + double z = asInt ? parseInt(reader) : parseDouble(reader); + return Float3.of(x, y, z); + } + + private double parseDouble(StringReader reader) throws CommandSyntaxException { + if (!reader.canRead()) { + throw WorldCoordinate.ERROR_EXPECTED_DOUBLE.createWithContext(reader); + } else { + final int start = reader.getCursor(); + double result = reader.canRead() && reader.peek() != ' ' ? reader.readDouble() : 0.0; + + if (result < minimum) { + reader.setCursor(start); + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooLow() + .createWithContext(reader, result, minimum); + } + if (result > maximum) { + reader.setCursor(start); + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooHigh() + .createWithContext(reader, result, maximum); + } + + return result; + } + } + + private double parseInt(StringReader reader) throws CommandSyntaxException { + if (!reader.canRead()) { + throw WorldCoordinate.ERROR_EXPECTED_DOUBLE.createWithContext(reader); + } else { + final int start = reader.getCursor(); + int result = reader.canRead() && reader.peek() != ' ' ? reader.readInt() : 0; + + if (result < minimum) { + reader.setCursor(start); + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooLow() + .createWithContext(reader, result, minimum); + } + if (result > maximum) { + reader.setCursor(start); + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooHigh() + .createWithContext(reader, result, maximum); + } + + return result; + } + } + + public static Float3ArgumentType int3() { + return new Float3ArgumentType(-Double.MAX_VALUE, Double.MAX_VALUE); + } + + public static Float3ArgumentType int3(int min) { + return new Float3ArgumentType(min, Double.MAX_VALUE); + } + + public static Float3ArgumentType int3(int min, int max) { + return new Float3ArgumentType(min, max); + } + + public static Float3ArgumentType float3() { + return new Float3ArgumentType(-Double.MAX_VALUE, Double.MAX_VALUE); + } + + public static Float3ArgumentType float3(double min) { + return new Float3ArgumentType(min, Double.MAX_VALUE); + } + + public static Float3ArgumentType float3(double min, double max) { + return new Float3ArgumentType(min, max); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + final String remaining = builder.getRemaining(); + return SharedSuggestionProvider.suggestCoordinates( + remaining, + List.of(new SharedSuggestionProvider.TextCoordinates("8", "8", "8")), + builder, + Commands.createValidator(this::parse) + ); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } + + public static Float3 getFloat3(CommandContext commandContext, String string) { + return commandContext.getArgument(string, Float3.class); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Float3ArgumentType)) return false; + Float3ArgumentType that = (Float3ArgumentType) o; + return Double.compare(that.minimum, minimum) == 0 && Double.compare( + that.maximum, + maximum + ) == 0 && asInt == that.asInt; + } + + @Override + public int hashCode() { + return Objects.hash(minimum, maximum, asInt); + } + + @Override + public String toString() { + if (asInt) { + if (minimum == -Double.MAX_VALUE && maximum == Double.MAX_VALUE) { + return "int3()"; + } else if (maximum == Double.MAX_VALUE) { + return "int3(" + (int) minimum + ")"; + } else { + return "int3(" + (int) minimum + ", " + (int) maximum + ")"; + } + } else { + if (minimum == -Double.MAX_VALUE && maximum == Double.MAX_VALUE) { + return "float3()"; + } else if (maximum == Double.MAX_VALUE) { + return "float3(" + (int) minimum + ")"; + } else { + return "float3(" + (int) minimum + ", " + (int) maximum + ")"; + } + } + } +} diff --git a/src/main/java/org/betterx/bclib/commands/arguments/PlacementDirections.java b/src/main/java/org/betterx/bclib/commands/arguments/PlacementDirections.java new file mode 100644 index 00000000..e059ad66 --- /dev/null +++ b/src/main/java/org/betterx/bclib/commands/arguments/PlacementDirections.java @@ -0,0 +1,36 @@ +package org.betterx.bclib.commands.arguments; + +import de.ambertation.wunderlib.math.Float3; + +import com.mojang.serialization.Codec; +import net.minecraft.core.BlockPos; +import net.minecraft.util.StringRepresentable; + +public enum PlacementDirections implements StringRepresentable { + NORTH_OF("northOf", Float3.NORTH), + EAST_OF("eastOf", Float3.EAST), + SOUTH_OF("southOf", Float3.SOUTH), + WEST_OF("westOf", Float3.WEST), + ABOVE("above", Float3.UP), + BELOW("below", Float3.DOWN), + AT("at", null); + + public static final Codec CODEC = StringRepresentable.fromEnum(PlacementDirections::values); + + private final String name; + public final Float3 dir; + + PlacementDirections(String name, Float3 dir) { + this.name = name; + this.dir = dir; + } + + public BlockPos getOffset() { + return dir == null || dir == Float3.ZERO ? null : dir.toBlockPos(); + } + + @Override + public String getSerializedName() { + return name; + } +} diff --git a/src/main/java/org/betterx/bclib/commands/arguments/TemplatePlacementArgument.java b/src/main/java/org/betterx/bclib/commands/arguments/TemplatePlacementArgument.java new file mode 100644 index 00000000..220da0ef --- /dev/null +++ b/src/main/java/org/betterx/bclib/commands/arguments/TemplatePlacementArgument.java @@ -0,0 +1,20 @@ +package org.betterx.bclib.commands.arguments; + +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.arguments.StringRepresentableArgument; + +public class TemplatePlacementArgument + extends StringRepresentableArgument { + private TemplatePlacementArgument() { + super(PlacementDirections.CODEC, PlacementDirections::values); + } + + public static TemplatePlacementArgument templatePlacement() { + return new TemplatePlacementArgument(); + } + + public static PlacementDirections getPlacement(CommandContext commandContext, String string) { + return commandContext.getArgument(string, PlacementDirections.class); + } +}