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.BaseHangingSignBlock;
import org.betterx.bclib.blocks.signs.BaseSignBlock; import org.betterx.bclib.blocks.signs.BaseSignBlock;
import org.betterx.bclib.commands.CommandRegistry; import org.betterx.bclib.commands.CommandRegistry;
import org.betterx.bclib.commands.arguments.BCLibArguments;
import org.betterx.bclib.complexmaterials.BCLWoodTypeWrapper; import org.betterx.bclib.complexmaterials.BCLWoodTypeWrapper;
import org.betterx.bclib.config.Configs; import org.betterx.bclib.config.Configs;
import org.betterx.bclib.config.PathConfig; import org.betterx.bclib.config.PathConfig;
@ -67,6 +68,7 @@ public class BCLib implements ModInitializer {
@Override @Override
public void onInitialize() { public void onInitialize() {
WorldsTogether.onInitialize(); WorldsTogether.onInitialize();
BCLibArguments.register();
PresetsRegistry.register(); PresetsRegistry.register();
LevelGenEvents.register(); LevelGenEvents.register();
BlockPredicates.ensureStaticInitialization(); BlockPredicates.ensureStaticInitialization();

View file

@ -41,7 +41,7 @@ public class CommandRegistry {
LiteralArgumentBuilder<CommandSourceStack> bnContext = Commands.literal("bclib") LiteralArgumentBuilder<CommandSourceStack> bnContext = Commands.literal("bclib")
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)); .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS));
bnContext = Place.register(bnContext, commandBuildContext); bnContext = PlaceCommand.register(bnContext, commandBuildContext);
bnContext = PrintInfo.register(bnContext); bnContext = PrintInfo.register(bnContext);
bnContext = DumpDatapack.register(bnContext); bnContext = DumpDatapack.register(bnContext);

View file

@ -2,20 +2,25 @@ package org.betterx.bclib.commands;
import de.ambertation.wunderlib.math.Float3; import de.ambertation.wunderlib.math.Float3;
import org.betterx.bclib.api.v2.levelgen.structures.StructureNBT; 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 org.betterx.bclib.util.BlocksHelper;
import com.mojang.brigadier.Command; 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.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.ChatFormatting;
import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands; import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.ResourceLocationArgument;
import net.minecraft.commands.arguments.blocks.BlockInput; import net.minecraft.commands.arguments.blocks.BlockInput;
import net.minecraft.commands.arguments.blocks.BlockStateArgument; import net.minecraft.commands.arguments.blocks.BlockStateArgument;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.commands.arguments.coordinates.Coordinates;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i; 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.block.state.properties.StructureMode;
import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.BoundingBox;
import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import org.jetbrains.annotations.NotNull; import java.util.function.Supplier;
class PlaceNBT { class PlaceCommandBuilder {
protected <T extends ArgumentBuilder<CommandSourceStack, T>> T execute( public static final String PATH = "path";
T builder, public static final String NBT = "nbt";
BlockPos searchDir, boolean border, boolean commandBlock public static final String EMPTY = "empty";
) { public static final String PLACEMENT = "placement";
return builder.executes(commandContext -> placeNBT( public static final String POS = "pos";
commandContext.getSource(), public static final String SPAN = "span";
BlockPosArgument.getLoadedBlockPos(commandContext, "pos"), public static final String BORDER = "border";
StringArgumentType.getString(commandContext, "path"),
searchDir, border ? BlockStateArgument.getBlock(commandContext, "block") : null, commandBlock
));
}
protected <T extends ArgumentBuilder<CommandSourceStack, T>> T buildPosArgument( public void register(
CommandBuildContext commandBuildContext, CommandBuildContext ctx,
T builder, LiteralArgumentBuilder<CommandSourceStack> command
BlockPos searchDir
) { ) {
final var posBuilder = Commands.argument( final Supplier<RequiredArgumentBuilder<CommandSourceStack, ResourceLocation>> path = () -> Commands.argument(
"pos", 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() BlockPosArgument.blockPos()
); );
final var posOnly = addPosOnly(searchDir, posBuilder); final var nbtTree = Commands.literal(NBT).then(
final var all = addPosWithAttributes(commandBuildContext, searchDir, posBuilder); path.get().then(
placement.get().then(
return builder.then(posOnly).then(all); addOptionalsAndExecute(
} ctx,
pos.get(),
protected <P extends ArgumentBuilder<CommandSourceStack, P>> P addPosWithAttributes( PlaceCommandBuilder::placeNBT
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 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))
) )
// .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 private <T> RequiredArgumentBuilder<CommandSourceStack, T> addOptionalsAndExecute(
protected LiteralArgumentBuilder<CommandSourceStack> buildFindCommand(
CommandBuildContext commandBuildContext, CommandBuildContext commandBuildContext,
Map<String, Float3> directions RequiredArgumentBuilder<CommandSourceStack, T> root,
Executor runner
) { ) {
final LiteralArgumentBuilder<CommandSourceStack> executeFindFree = Commands.literal("find"); final Supplier<LiteralArgumentBuilder<CommandSourceStack>> addControllers = () -> Commands.literal("controller");
for (var dir : directions.entrySet()) { final Supplier<RequiredArgumentBuilder<CommandSourceStack, BlockInput>> addBorder = () -> Commands.argument(
executeFindFree.then(buildPosArgument( BORDER,
commandBuildContext, BlockStateArgument.block(commandBuildContext)
Commands.literal(dir.getKey()), );
dir.getValue().toBlockPos()
)); return root
} .executes(c -> runner.exec(c, false, false))
return executeFindFree; .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( interface Executor {
CommandSourceStack stack, int exec(
BlockPos pos, CommandContext<CommandSourceStack> ctx,
String location, boolean border,
BlockPos searchDir, boolean controlBlocks
BlockInput blockInput, ) throws CommandSyntaxException;
boolean structureBlock }
) {
StructureNBT structureNBT = StructureNBT.create(new ResourceLocation(location)); protected static int placeNBT(
return Place.placeBlocks( CommandContext<CommandSourceStack> ctx,
stack, boolean border,
pos, boolean controlBlocks
searchDir, ) 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, blockInput,
structureBlock, controlBlocks,
structureNBT.location, structureNBT.location,
(p) -> structureNBT.getBoundingBox(p, Rotation.NONE, Mirror.NONE), (p) -> structureNBT.getBoundingBox(p, Rotation.NONE, Mirror.NONE),
(level, p) -> structureNBT.generateAt(level, p, Rotation.NONE, Mirror.NONE) (level, p) -> structureNBT.generateAt(level, p, Rotation.NONE, Mirror.NONE)
); );
} }
}
class PlaceEmpty extends PlaceNBT { protected static int placeEmpty(
@Override CommandContext<CommandSourceStack> ctx,
protected <T extends ArgumentBuilder<CommandSourceStack, T>> T execute( boolean border,
T builder, boolean controlBlocks
BlockPos searchDir, boolean border, boolean commandBlock ) throws CommandSyntaxException {
) { final ResourceLocation id = ResourceLocationArgument.getId(ctx, PATH);
return builder.executes(commandContext -> placeEmpty( final PlacementDirections searchDir = TemplatePlacementArgument.getPlacement(ctx, PLACEMENT);
commandContext.getSource(), final BlockInput blockInput = border ? BlockStateArgument.getBlock(ctx, BORDER) : null;
BlockPosArgument.getLoadedBlockPos(commandContext, "pos"), final BlockPos span = Float3ArgumentType.getFloat3(ctx, SPAN).toBlockPos();
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
));
}
@Override return PlaceCommand.placeBlocks(
protected <T extends ArgumentBuilder<CommandSourceStack, T>> T buildPosArgument( ctx.getSource(),
CommandBuildContext commandBuildContext, BlockPosArgument.getLoadedBlockPos(ctx, POS),
T builder, searchDir == null || searchDir.dir == Float3.ZERO ? null : searchDir.dir.toBlockPos(),
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,
blockInput, blockInput,
structureBlock, controlBlocks,
new ResourceLocation(location), id,
(p) -> BoundingBox.fromCorners(p, p.offset(span)), (p) -> BoundingBox.fromCorners(p, p.offset(span)),
(level, p) -> { (level, p) -> {
var box = BoundingBox.fromCorners(p, p.offset(span)); var box = BoundingBox.fromCorners(p, p.offset(span));
Place.fillStructureVoid(level, box); PlaceCommand.fillStructureVoid(level, box);
if (blockInput != null) { if (blockInput != null) {
Place.fill( PlaceCommand.fill(
level, level,
new BoundingBox( new BoundingBox(
box.minX(), box.minX(), box.minY() - 1, box.minZ(),
box.minY() - 1, box.maxX(), box.minY() - 1, box.maxZ()
box.minZ(),
box.maxX(),
box.minY() - 1,
box.maxZ()
), ),
blockInput.getState() blockInput.getState()
); );
@ -266,29 +186,19 @@ class PlaceEmpty extends PlaceNBT {
} }
} }
public class Place { public class PlaceCommand {
public Place() { public PlaceCommand() {
} }
public static LiteralArgumentBuilder<CommandSourceStack> register( public static LiteralArgumentBuilder<CommandSourceStack> register(
LiteralArgumentBuilder<CommandSourceStack> bnContext, LiteralArgumentBuilder<CommandSourceStack> bnContext,
CommandBuildContext commandBuildContext 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 final var command = Commands
.literal("place") .literal("place")
.requires(commandSourceStack -> commandSourceStack.hasPermission(2)); .requires(commandSourceStack -> commandSourceStack.hasPermission(2));
new PlaceNBT().register(commandBuildContext, directions, command); new PlaceCommandBuilder().register(commandBuildContext, command);
new PlaceEmpty().register(commandBuildContext, directions, command);
return bnContext.then(command); return bnContext.then(command);
} }
@ -378,7 +288,7 @@ public class Place {
stack.getLevel().setBlock(structureBlockPos, state, BlocksHelper.SET_OBSERV); stack.getLevel().setBlock(structureBlockPos, state, BlocksHelper.SET_OBSERV);
if (stack.getLevel().getBlockEntity(structureBlockPos) instanceof StructureBlockEntity entity) { if (stack.getLevel().getBlockEntity(structureBlockPos) instanceof StructureBlockEntity entity) {
entity.setIgnoreEntities(false); entity.setIgnoreEntities(false);
entity.setShowAir(true); entity.setShowAir(false);
entity.setMirror(Mirror.NONE); entity.setMirror(Mirror.NONE);
entity.setRotation(Rotation.NONE); entity.setRotation(Rotation.NONE);
entity.setShowBoundingBox(true); entity.setShowBoundingBox(true);
@ -462,5 +372,15 @@ public class Place {
return Command.SINGLE_SUCCESS; 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);
}
}