Refactored /place-Command

This commit is contained in:
Frank 2023-05-31 14:18:02 +02:00
parent 0485f040b9
commit 28af68b906
8 changed files with 505 additions and 211 deletions

View file

@ -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();

View file

@ -41,7 +41,7 @@ public class CommandRegistry {
LiteralArgumentBuilder<CommandSourceStack> 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);

View file

@ -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 extends ArgumentBuilder<CommandSourceStack, T>> 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 extends ArgumentBuilder<CommandSourceStack, T>> T buildPosArgument(
CommandBuildContext commandBuildContext,
T builder,
BlockPos searchDir
public void register(
CommandBuildContext ctx,
LiteralArgumentBuilder<CommandSourceStack> command
) {
final var posBuilder = Commands.argument(
"pos",
final Supplier<RequiredArgumentBuilder<CommandSourceStack, ResourceLocation>> path = () -> Commands.argument(
PATH,
ResourceLocationArgument.id()
);
final Supplier<RequiredArgumentBuilder<CommandSourceStack, PlacementDirections>> placement = () -> Commands.argument(
PLACEMENT,
TemplatePlacementArgument.templatePlacement()
);
final Supplier<RequiredArgumentBuilder<CommandSourceStack, Coordinates>> 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 extends ArgumentBuilder<CommandSourceStack, P>> 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))
final var nbtTree = Commands.literal(NBT).then(
path.get().then(
placement.get().then(
addOptionalsAndExecute(
ctx,
pos.get(),
PlaceCommandBuilder::placeNBT
)
)
)
);
}
protected <P extends ArgumentBuilder<CommandSourceStack, P>> P addPosOnly(
BlockPos searchDir,
P posBuilder
) {
return execute(posBuilder, searchDir, false, false);
}
public void register(
CommandBuildContext commandBuildContext,
Map<String, Float3> directions,
LiteralArgumentBuilder<CommandSourceStack> command
) {
final LiteralArgumentBuilder<CommandSourceStack> nbtBuilder = Commands
.literal("nbt")
.then(Commands
.argument("path", StringArgumentType.string())
.then(buildPosArgument(commandBuildContext, Commands.literal("at"), null))
.then(buildFindCommand(commandBuildContext, directions))
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
)
// .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
// )))
// )
;
)
)
)
);
command.then(nbtBuilder);
command
.then(nbtTree)
.then(emptyTree);
}
@NotNull
protected LiteralArgumentBuilder<CommandSourceStack> buildFindCommand(
private <T> RequiredArgumentBuilder<CommandSourceStack, T> addOptionalsAndExecute(
CommandBuildContext commandBuildContext,
Map<String, Float3> directions
RequiredArgumentBuilder<CommandSourceStack, T> root,
Executor runner
) {
final LiteralArgumentBuilder<CommandSourceStack> executeFindFree = Commands.literal("find");
for (var dir : directions.entrySet()) {
executeFindFree.then(buildPosArgument(
commandBuildContext,
Commands.literal(dir.getKey()),
dir.getValue().toBlockPos()
));
}
return executeFindFree;
final Supplier<LiteralArgumentBuilder<CommandSourceStack>> addControllers = () -> Commands.literal("controller");
final Supplier<RequiredArgumentBuilder<CommandSourceStack, BlockInput>> 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<CommandSourceStack> ctx,
boolean border,
boolean controlBlocks
) throws CommandSyntaxException;
}
protected static int placeNBT(
CommandContext<CommandSourceStack> 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 extends ArgumentBuilder<CommandSourceStack, T>> 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<CommandSourceStack> 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 extends ArgumentBuilder<CommandSourceStack, T>> 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<String, Float3> directions,
LiteralArgumentBuilder<CommandSourceStack> command
) {
final LiteralArgumentBuilder<CommandSourceStack> 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<CommandSourceStack> register(
LiteralArgumentBuilder<CommandSourceStack> bnContext,
CommandBuildContext commandBuildContext
) {
final Map<String, Float3> 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
*/

View file

@ -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()
);
}
}

View file

@ -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<Float3ArgumentType, Float3ArgumentInfo.Template> {
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<Float3ArgumentType> {
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<Float3ArgumentType, ?> type() {
return Float3ArgumentInfo.this;
}
}
}

View file

@ -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<Float3> {
private static final Collection<String> 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 <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> 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<String> getExamples() {
return EXAMPLES;
}
public static Float3 getFloat3(CommandContext<CommandSourceStack> 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 + ")";
}
}
}
}

View file

@ -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<PlacementDirections> 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;
}
}

View file

@ -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<PlacementDirections> {
private TemplatePlacementArgument() {
super(PlacementDirections.CODEC, PlacementDirections::values);
}
public static TemplatePlacementArgument templatePlacement() {
return new TemplatePlacementArgument();
}
public static PlacementDirections getPlacement(CommandContext<CommandSourceStack> commandContext, String string) {
return commandContext.getArgument(string, PlacementDirections.class);
}
}