diff --git a/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/StructureNBT.java b/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/StructureNBT.java index e7141b98..b9b7f1aa 100644 --- a/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/StructureNBT.java +++ b/src/main/java/org/betterx/bclib/api/v2/levelgen/structures/StructureNBT.java @@ -24,6 +24,7 @@ import com.google.common.collect.Maps; import java.io.IOException; import java.io.InputStream; import java.util.Map; +import org.jetbrains.annotations.Nullable; public class StructureNBT { public final ResourceLocation location; @@ -55,19 +56,9 @@ public class StructureNBT { } public boolean generateCentered(ServerLevelAccessor world, BlockPos pos, Rotation rotation, Mirror mirror) { - if (structure == null) { - BCLib.LOGGER.error("No structure: " + location.toString()); - return false; - } - - MutableBlockPos blockpos2 = new MutableBlockPos().set(structure.getSize()); - if (mirror == Mirror.FRONT_BACK) - blockpos2.setX(-blockpos2.getX()); - if (mirror == Mirror.LEFT_RIGHT) - blockpos2.setZ(-blockpos2.getZ()); - blockpos2.set(blockpos2.rotate(rotation)); + BlockPos newPos = getCenteredPos(pos, rotation, mirror); + if (newPos == null) return false; StructurePlaceSettings data = new StructurePlaceSettings().setRotation(rotation).setMirror(mirror); - BlockPos newPos = pos.offset(-blockpos2.getX() >> 1, 0, -blockpos2.getZ() >> 1); structure.placeInWorld( world, newPos, @@ -79,6 +70,35 @@ public class StructureNBT { return true; } + public boolean generateAt(ServerLevelAccessor world, BlockPos pos, Rotation rotation, Mirror mirror) { + StructurePlaceSettings data = new StructurePlaceSettings().setRotation(rotation).setMirror(mirror); + structure.placeInWorld( + world, + pos, + pos, + data, + world.getRandom(), + Block.UPDATE_CLIENTS + ); + return true; + } + + @Nullable + private BlockPos getCenteredPos(BlockPos pos, Rotation rotation, Mirror mirror) { + if (structure == null) { + BCLib.LOGGER.error("No structure: " + location.toString()); + return null; + } + + MutableBlockPos blockpos2 = new MutableBlockPos().set(structure.getSize()); + if (mirror == Mirror.FRONT_BACK) + blockpos2.setX(-blockpos2.getX()); + if (mirror == Mirror.LEFT_RIGHT) + blockpos2.setZ(-blockpos2.getZ()); + blockpos2.set(blockpos2.rotate(rotation)); + return pos.offset(-blockpos2.getX() >> 1, 0, -blockpos2.getZ() >> 1); + } + private static final Map READER_CACHE = Maps.newHashMap(); private static StructureTemplate readStructureFromJar(ResourceLocation resource) { @@ -127,4 +147,11 @@ public class StructureNBT { public BoundingBox getBoundingBox(BlockPos pos, Rotation rotation, Mirror mirror) { return structure.getBoundingBox(new StructurePlaceSettings().setRotation(rotation).setMirror(mirror), pos); } + + public BoundingBox getCenteredBoundingBox(BlockPos pos, Rotation rotation, Mirror mirror) { + return structure.getBoundingBox( + new StructurePlaceSettings().setRotation(rotation).setMirror(mirror), + getCenteredPos(pos, rotation, mirror) + ); + } } diff --git a/src/main/java/org/betterx/bclib/commands/CommandRegistry.java b/src/main/java/org/betterx/bclib/commands/CommandRegistry.java index dd4e9da3..bb89dd0f 100644 --- a/src/main/java/org/betterx/bclib/commands/CommandRegistry.java +++ b/src/main/java/org/betterx/bclib/commands/CommandRegistry.java @@ -5,6 +5,7 @@ import org.betterx.worlds.together.tag.v3.CommonBlockTags; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandBuildContext; @@ -37,30 +38,22 @@ public class CommandRegistry { CommandBuildContext commandBuildContext, Commands.CommandSelection commandSelection ) { + LiteralArgumentBuilder bnContext = Commands.literal("bclib") + .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)); + + bnContext = Place.register(bnContext, commandBuildContext); + bnContext = PrintInfo.register(bnContext); + bnContext = DumpDatapack.register(bnContext); + dispatcher.register( - Commands.literal("bclib") - .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) + bnContext .then(Commands.literal("request_garbage_collection") .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) - .executes(ctx -> requestGC(ctx)) - ) - .then(Commands.literal("dump_datapack") - .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) - .executes(ctx -> DumpDatapack.dumpDatapack(ctx)) - ) - .then(Commands.literal("print") - .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) - .then(Commands.literal("dimensions") - .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) - .executes(ctx -> PrintInfo.printDimensions(ctx)) - ).then(Commands.literal("updates") - .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) - .executes(ctx -> PrintInfo.printUpdates(ctx, true)) - ) + .executes(CommandRegistry::requestGC) ) .then(Commands.literal("debug_ore") .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) - .executes(ctx -> revealOre(ctx)) + .executes(CommandRegistry::revealOre) ) .then(Commands.literal("sliceZ") .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) diff --git a/src/main/java/org/betterx/bclib/commands/DumpDatapack.java b/src/main/java/org/betterx/bclib/commands/DumpDatapack.java index 08eacfb9..258298fe 100644 --- a/src/main/java/org/betterx/bclib/commands/DumpDatapack.java +++ b/src/main/java/org/betterx/bclib/commands/DumpDatapack.java @@ -9,11 +9,13 @@ import org.betterx.worlds.together.surfaceRules.AssignedSurfaceRule; import org.betterx.worlds.together.surfaceRules.SurfaceRuleRegistry; import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.KeyDispatchCodec; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; @@ -67,6 +69,14 @@ import java.util.Map; import java.util.function.Function; public class DumpDatapack { + public static LiteralArgumentBuilder register(LiteralArgumentBuilder bnContext) { + return bnContext + .then(Commands.literal("dump_datapack") + .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) + .executes(DumpDatapack::dumpDatapack) + ); + } + private record Dumper, H extends Holder>( Function codecFunction, Function, T> contentTransform diff --git a/src/main/java/org/betterx/bclib/commands/Place.java b/src/main/java/org/betterx/bclib/commands/Place.java new file mode 100644 index 00000000..ea62d63d --- /dev/null +++ b/src/main/java/org/betterx/bclib/commands/Place.java @@ -0,0 +1,466 @@ +package org.betterx.bclib.commands; + +import de.ambertation.wunderlib.math.Float3; +import org.betterx.bclib.api.v2.levelgen.structures.StructureNBT; +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 net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.blocks.BlockInput; +import net.minecraft.commands.arguments.blocks.BlockStateArgument; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.CommandBlockEntity; +import net.minecraft.world.level.block.entity.StructureBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +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; + +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 + )); + } + + protected > T buildPosArgument( + CommandBuildContext commandBuildContext, + T builder, + BlockPos searchDir + ) { + final var posBuilder = 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)) + ) +// .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); + } + + @NotNull + protected LiteralArgumentBuilder buildFindCommand( + CommandBuildContext commandBuildContext, + Map directions + ) { + final LiteralArgumentBuilder executeFindFree = Commands.literal("find"); + for (var dir : directions.entrySet()) { + executeFindFree.then(buildPosArgument( + commandBuildContext, + Commands.literal(dir.getKey()), + dir.getValue().toBlockPos() + )); + } + return executeFindFree; + } + + 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, + blockInput, + structureBlock, + 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 + )); + } + + @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, + blockInput, + structureBlock, + new ResourceLocation(location), + (p) -> BoundingBox.fromCorners(p, p.offset(span)), + (level, p) -> { + var box = BoundingBox.fromCorners(p, p.offset(span)); + Place.fillStructureVoid(level, box); + if (blockInput != null) { + Place.fill( + level, + new BoundingBox( + box.minX(), + box.minY() - 1, + box.minZ(), + box.maxX(), + box.minY() - 1, + box.maxZ() + ), + blockInput.getState() + ); + } + } + ); + } +} + +public class Place { + public Place() { + } + + 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); + + return bnContext.then(command); + } + + + private static boolean isEmpty(Level level, BoundingBox bb) { + for (int x = bb.minX(); x <= bb.maxX(); x++) + for (int y = bb.minY(); y <= bb.maxY(); y++) + for (int z = bb.minZ(); z <= bb.maxZ(); z++) + if (!level.isEmptyBlock(new BlockPos(x, y, z))) { + return false; + } + + return true; + } + + private static void replaceAir(Level level, BoundingBox bb) { + for (int x = bb.minX(); x <= bb.maxX(); x++) + for (int y = bb.minY(); y <= bb.maxY(); y++) + for (int z = bb.minZ(); z <= bb.maxZ(); z++) { + BlockPos bp = new BlockPos(x, y, z); + if (level.getBlockState(bp).is(Blocks.AIR)) { + level.setBlock(bp, Blocks.STRUCTURE_VOID.defaultBlockState(), BlocksHelper.SET_OBSERV); + } + } + } + + static void fill(Level level, BoundingBox bb, BlockState blockState) { + for (int x = bb.minX(); x <= bb.maxX(); x++) + for (int y = bb.minY(); y <= bb.maxY(); y++) + for (int z = bb.minZ(); z <= bb.maxZ(); z++) { + BlockPos bp = new BlockPos(x, y, z); + level.setBlock(bp, blockState, BlocksHelper.SET_OBSERV); + } + } + + static void fillStructureVoid(Level level, BoundingBox bb) { + fill(level, bb, Blocks.STRUCTURE_VOID.defaultBlockState()); + } + + //Draws a border around the bounding box + private static void outline(Level level, BoundingBox bb, BlockState outlineState) { + for (int x = bb.minX(); x <= bb.maxX(); x++) { + level.setBlock(new BlockPos(x, bb.minY(), bb.minZ()), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(x, bb.maxY(), bb.minZ()), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(x, bb.minY(), bb.maxZ()), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(x, bb.maxY(), bb.maxZ()), outlineState, BlocksHelper.SET_OBSERV); + } + for (int y = bb.minY(); y <= bb.maxY(); y++) { + level.setBlock(new BlockPos(bb.minX(), y, bb.minZ()), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(bb.maxX(), y, bb.minZ()), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(bb.minX(), y, bb.maxZ()), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(bb.maxX(), y, bb.maxZ()), outlineState, BlocksHelper.SET_OBSERV); + } + for (int z = bb.minZ(); z <= bb.maxZ(); z++) { + level.setBlock(new BlockPos(bb.minX(), bb.minY(), z), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(bb.maxX(), bb.minY(), z), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(bb.minX(), bb.maxY(), z), outlineState, BlocksHelper.SET_OBSERV); + level.setBlock(new BlockPos(bb.maxX(), bb.maxY(), z), outlineState, BlocksHelper.SET_OBSERV); + } + } + + private static BoundingBox adapt(BoundingBox bb, boolean border, boolean structureBlock) { + if (border) { + return bb.inflatedBy(1); + } else if (structureBlock) { + return new BoundingBox( + bb.minX() - 1, + bb.minY() - 1, + bb.minZ() - 1, + bb.maxX(), + bb.maxY(), + bb.maxZ() + ); + } + return bb; + } + + private static void createControlBlocks(CommandSourceStack stack, ResourceLocation location, BoundingBox bbNBT) { + BlockPos structureBlockPos = new BlockPos(bbNBT.minX() - 1, bbNBT.minY() - 1, bbNBT.minZ() - 1); + BlockPos commandBlockPos = new BlockPos(bbNBT.minX() - 1, bbNBT.minY() - 1, bbNBT.minZ()); + BlockPos buttonBlockPos = new BlockPos(bbNBT.minX() - 1, bbNBT.minY(), bbNBT.minZ()); + BlockState state = Blocks.STRUCTURE_BLOCK + .defaultBlockState() + .setValue(StructureBlock.MODE, StructureMode.SAVE); + + stack.getLevel().setBlock(structureBlockPos, state, BlocksHelper.SET_OBSERV); + if (stack.getLevel().getBlockEntity(structureBlockPos) instanceof StructureBlockEntity entity) { + entity.setIgnoreEntities(false); + entity.setShowAir(true); + entity.setMirror(Mirror.NONE); + entity.setRotation(Rotation.NONE); + entity.setShowBoundingBox(true); + entity.setStructureName(location); + entity.setStructurePos(new BlockPos(1, 1, 1)); + entity.setStructureSize(new Vec3i(bbNBT.getXSpan(), bbNBT.getYSpan(), bbNBT.getZSpan())); + } + + state = Blocks.COMMAND_BLOCK.defaultBlockState().setValue(CommandBlock.FACING, Direction.DOWN); + stack.getLevel().setBlock(commandBlockPos, state, BlocksHelper.SET_OBSERV); + if (stack.getLevel().getBlockEntity(commandBlockPos) instanceof CommandBlockEntity entity) { + entity.setAutomatic(false); + entity.setPowered(false); + entity.onlyOpCanSetNbt(); + entity.getCommandBlock().shouldInformAdmins(); + entity.getCommandBlock() + .setCommand( + "fill ~1 ~1 ~" + + " ~" + bbNBT.getXSpan() + " ~" + bbNBT.getYSpan() + " ~" + bbNBT.getZSpan() + + " " + BuiltInRegistries.BLOCK.getKey(Blocks.STRUCTURE_VOID) + + " replace " + BuiltInRegistries.BLOCK.getKey(Blocks.AIR) + ); + } + + state = Blocks.OAK_BUTTON.defaultBlockState() + .setValue(ButtonBlock.FACING, Direction.EAST) + .setValue(ButtonBlock.FACE, AttachFace.FLOOR); + stack.getLevel().setBlock(buttonBlockPos, state, BlocksHelper.SET_OBSERV); + } + + + static int placeBlocks( + CommandSourceStack stack, + BlockPos pos, + BlockPos searchDir, + BlockInput blockInput, + boolean structureBlock, + ResourceLocation location, + Function getBounds, + BiConsumer generate + ) { + if (searchDir != null) { + int tries = 16 * 16; + while (tries > 0 && !isEmpty( + stack.getLevel(), + adapt( + getBounds.apply(pos), + blockInput != null, + structureBlock + ) + )) { + + pos = pos.offset(searchDir); + tries--; + } + + if (tries <= 0) { + stack.sendFailure(Component.literal("Failed to find free space") + .setStyle(Style.EMPTY.withColor(ChatFormatting.RED))); + return -1; + } + } + + final BoundingBox bbNBT = getBounds.apply(pos); + if (blockInput != null) { + final BoundingBox bb = bbNBT.inflatedBy(1); + outline(stack.getLevel(), bb, blockInput.getState()); + stack.sendSuccess(() -> Component.literal("Placed border: " + bb.toString()) + .setStyle(Style.EMPTY.withColor(ChatFormatting.GREEN)), true); + } + + generate.accept(stack.getLevel(), pos); + replaceAir(stack.getLevel(), bbNBT); + + stack.sendSuccess(() -> Component.literal("Placed NBT: " + bbNBT.toString()) + .setStyle(Style.EMPTY.withColor(ChatFormatting.GREEN)), true); + + if (structureBlock) { + createControlBlocks(stack, location, bbNBT); + } + return Command.SINGLE_SUCCESS; + } + + +} diff --git a/src/main/java/org/betterx/bclib/commands/PrintInfo.java b/src/main/java/org/betterx/bclib/commands/PrintInfo.java index 4436515b..9ffc34ac 100644 --- a/src/main/java/org/betterx/bclib/commands/PrintInfo.java +++ b/src/main/java/org/betterx/bclib/commands/PrintInfo.java @@ -7,10 +7,12 @@ import org.betterx.bclib.networking.VersionChecker; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -21,6 +23,20 @@ import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; public class PrintInfo { + public static LiteralArgumentBuilder register(LiteralArgumentBuilder bnContext) { + return bnContext + .then(Commands.literal("print") + .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) + .then(Commands.literal("dimensions") + .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) + .executes(PrintInfo::printDimensions) + ).then(Commands.literal("updates") + .requires(source -> source.hasPermission(Commands.LEVEL_OWNERS)) + .executes(ctx -> PrintInfo.printUpdates(ctx, true)) + ) + ); + } + static int printDimensions(CommandContext ctx) { MutableComponent result = Component.literal("World Dimensions: ") .setStyle(Style.EMPTY.withBold(true).withColor(ChatFormatting.BLUE));